lint
[nvi.git] / vi / vs_smap.c
blob8a774d582a8d44428f6f70036216fa47f63d2659
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.23 1993/11/03 17:15:36 bostic Exp $ (Berkeley) $Date: 1993/11/03 17:15:36 $";
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));
26 * svi_change --
27 * Make a change to the screen.
29 int
30 svi_change(sp, ep, lno, op)
31 SCR *sp;
32 EXF *ep;
33 recno_t lno;
34 enum operation op;
36 SMAP *p;
37 size_t oldy, oldx;
39 /* Appending is the same as inserting, if the line is incremented. */
40 if (op == LINE_APPEND) {
41 ++lno;
42 op = LINE_INSERT;
45 /* Ignore the change if the line is after the map. */
46 if (lno > TMAP->lno)
47 return (0);
50 * If the line is before the map, and it's a decrement, decrement
51 * the map. If it's an increment, increment the map. Otherwise,
52 * ignore it.
54 if (lno < HMAP->lno) {
55 switch (op) {
56 case LINE_APPEND:
57 abort();
58 /* NOTREACHED */
59 case LINE_DELETE:
60 for (p = HMAP; p <= TMAP; ++p)
61 --p->lno;
62 if (sp->lno >= lno)
63 --sp->lno;
64 break;
65 case LINE_INSERT:
66 for (p = HMAP; p <= TMAP; ++p)
67 ++p->lno;
68 if (sp->lno >= lno)
69 ++sp->lno;
70 break;
71 case LINE_RESET:
72 break;
74 return (0);
77 F_SET(SVP(sp), SVI_SCREENDIRTY);
79 /* Flush cached information from svi_screens(). */
80 SVP(sp)->ss_lno = OOBLNO;
82 /* Invalidate the cursor, if it's on this line. */
83 if (sp->lno == lno)
84 F_SET(SVP(sp), SVI_CUR_INVALID);
86 getyx(stdscr, oldy, oldx);
88 switch (op) {
89 case LINE_DELETE:
90 if (svi_sm_delete(sp, ep, lno))
91 return (1);
92 break;
93 case LINE_INSERT:
94 if (svi_sm_insert(sp, ep, lno))
95 return (1);
96 break;
97 case LINE_RESET:
98 if (svi_sm_reset(sp, ep, lno))
99 return (1);
100 break;
101 default:
102 abort();
105 MOVEA(sp, oldy, oldx);
107 return (0);
111 * svi_sm_fill --
112 * Fill in the screen map, placing the specified line at the
113 * right position. There isn't any way to tell if an SMAP
114 * entry has been filled in, so this routine had better be
115 * called with P_FILL set before anything else is done.
117 * !!!
118 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
119 * slot is already filled in, P_BOTTOM means that the TMAP slot is
120 * already filled in, and we just finish up the job.
123 svi_sm_fill(sp, ep, lno, pos)
124 SCR *sp;
125 EXF *ep;
126 recno_t lno;
127 enum position pos;
129 SMAP *p, tmp;
131 /* Flush all cached information from the SMAP. */
132 for (p = HMAP; p <= TMAP; ++p)
133 SMAP_FLUSH(p);
135 switch (pos) {
136 case P_FILL:
137 tmp.lno = 1;
138 tmp.off = 1;
140 /* See if less than half a screen from the top. */
141 if (svi_sm_nlines(sp, ep,
142 &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
143 lno = 1;
144 goto top;
147 /* See if less than half a screen from the bottom. */
148 if (file_lline(sp, ep, &tmp.lno))
149 return (1);
150 tmp.off = svi_screens(sp, ep, tmp.lno, NULL);
151 if (svi_sm_nlines(sp, ep,
152 &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
153 TMAP->lno = tmp.lno;
154 TMAP->off = tmp.off;
155 goto bottom;
157 goto middle;
158 case P_TOP:
159 if (lno != OOBLNO) {
160 top: HMAP->lno = lno;
161 HMAP->off = 1;
163 /* If we fail, just punt. */
164 for (p = HMAP; p < TMAP; ++p)
165 if (svi_sm_next(sp, ep, p, p + 1))
166 goto err;
167 break;
168 case P_MIDDLE:
169 /* If we fail, guess that the file is too small. */
170 middle: p = HMAP + (TMAP - HMAP) / 2;
171 for (p->lno = lno, p->off = 1; p > HMAP; --p)
172 if (svi_sm_prev(sp, ep, p, p - 1)) {
173 lno = 1;
174 goto top;
177 /* If we fail, just punt. */
178 p = HMAP + (TMAP - HMAP) / 2;
179 for (; p < TMAP; ++p)
180 if (svi_sm_next(sp, ep, p, p + 1))
181 goto err;
182 break;
183 case P_BOTTOM:
184 if (lno != OOBLNO) {
185 TMAP->lno = lno;
186 TMAP->off = svi_screens(sp, ep, lno, NULL);
188 /* If we fail, guess that the file is too small. */
189 bottom: for (p = TMAP; p > HMAP; --p)
190 if (svi_sm_prev(sp, ep, p, p - 1)) {
191 lno = 1;
192 goto top;
194 break;
196 return (0);
199 * Try and put *something* on the screen. If this fails,
200 * we have a serious hard error.
202 err: HMAP->lno = 1;
203 HMAP->off = 1;
204 for (p = HMAP; p < TMAP; ++p)
205 if (svi_sm_next(sp, ep, p, p + 1))
206 return (1);
207 return (0);
211 * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
212 * screen only contains one line, or, if the line is the entire screen, this
213 * gets fairly exciting. Skip the fun and simply return if there's only one
214 * line in the screen, or just call fill. Fill may not be entirely accurate,
215 * i.e. we may be painting the screen with something not even close to the
216 * cursor, but it's not like we're into serious performance issues here, and
217 * the refresh routine will fix it for us.
219 #define TOO_WEIRD { \
220 if (cnt_orig >= sp->t_rows) { \
221 if (cnt_orig == 1) \
222 return (0); \
223 if (file_gline(sp, ep, lno, NULL) == NULL) \
224 if (file_lline(sp, ep, &lno)) \
225 return (1); \
226 F_SET(sp, S_REDRAW); \
227 return (svi_sm_fill(sp, ep, lno, P_TOP)); \
232 * svi_sm_delete --
233 * Delete a line out of the SMAP.
236 svi_sm_delete(sp, ep, lno)
237 SCR *sp;
238 EXF *ep;
239 recno_t lno;
241 SMAP *p, *t;
242 size_t cnt_orig;
245 * Find the line in the map, and count the number of screen lines
246 * which display any part of the deleted line.
248 for (p = HMAP; p->lno != lno; ++p);
249 for (cnt_orig = 1, t = p + 1;
250 t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
252 TOO_WEIRD;
254 /* Delete that many lines from the screen. */
255 MOVE(sp, p - HMAP, 0);
256 if (svi_deleteln(sp, cnt_orig))
257 return (1);
259 /* Shift the screen map up. */
260 memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
262 /* Decrement the line numbers for the rest of the map. */
263 for (t = TMAP - cnt_orig; p <= t; ++p)
264 --p->lno;
266 /* Display the new lines. */
267 for (p = TMAP - cnt_orig;;) {
268 if (p < TMAP && svi_sm_next(sp, ep, p, p + 1))
269 return (1);
270 if (svi_line(sp, ep, ++p, NULL, NULL))
271 return (1);
272 if (p == TMAP)
273 break;
275 return (0);
279 * svi_sm_insert --
280 * Insert a line into the SMAP.
283 svi_sm_insert(sp, ep, lno)
284 SCR *sp;
285 EXF *ep;
286 recno_t lno;
288 SMAP *p, *t;
289 size_t cnt_orig, cnt;
292 * Find the line in the map, find out how many screen lines
293 * needed to display the line.
295 for (p = HMAP; p->lno != lno; ++p);
296 cnt_orig = svi_screens(sp, ep, lno, NULL);
298 TOO_WEIRD;
301 * The lines left in the screen override the number of screen
302 * lines in the inserted line.
304 cnt = (TMAP - p) + 1;
305 if (cnt_orig > cnt)
306 cnt_orig = cnt;
308 /* Push down that many lines. */
309 MOVE(sp, p - HMAP, 0);
310 if (svi_insertln(sp, cnt_orig))
311 return (1);
313 /* Shift the screen map down. */
314 memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
316 /* Increment the line numbers for the rest of the map. */
317 for (t = p + cnt_orig; t <= TMAP; ++t)
318 ++t->lno;
320 /* Fill in the SMAP for the new lines, and display. */
321 for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
322 t->lno = lno;
323 t->off = cnt;
324 SMAP_FLUSH(t);
325 if (svi_line(sp, ep, t, NULL, NULL))
326 return (1);
328 return (0);
332 * svi_sm_reset --
333 * Reset a line in the SMAP.
336 svi_sm_reset(sp, ep, lno)
337 SCR *sp;
338 EXF *ep;
339 recno_t lno;
341 SMAP *p, *t;
342 size_t cnt_orig, cnt_new, cnt, diff;
345 * See if the number of on-screen rows taken up by the old display
346 * for the line is the same as the number needed for the new one.
347 * If so, repaint, otherwise do it the hard way.
349 for (p = HMAP; p->lno != lno; ++p);
350 for (cnt_orig = 0, t = p;
351 t <= TMAP && t->lno == lno; ++cnt_orig, ++t)
352 SMAP_FLUSH(t);
353 cnt_new = svi_screens(sp, ep, lno, NULL);
355 TOO_WEIRD;
357 if (cnt_orig == cnt_new) {
358 do {
359 if (svi_line(sp, ep, p, NULL, NULL))
360 return (1);
361 } while (++p < t);
362 return (0);
365 if (cnt_orig < cnt_new) {
366 /* Get the difference. */
367 diff = cnt_new - cnt_orig;
370 * The lines left in the screen override the number of screen
371 * lines in the inserted line.
373 cnt = (TMAP - p) + 1;
374 if (diff > cnt)
375 diff = cnt;
377 /* Push down the extra lines. */
378 MOVE(sp, p - HMAP, 0);
379 if (svi_insertln(sp, diff))
380 return (1);
382 /* Shift the screen map down. */
383 memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
385 /* Fill in the SMAP for the replaced line, and display. */
386 for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
387 t->lno = lno;
388 t->off = cnt;
389 if (svi_line(sp, ep, t, NULL, NULL))
390 return (1);
392 } else {
393 /* Get the difference. */
394 diff = cnt_orig - cnt_new;
396 /* Delete that many lines from the screen. */
397 MOVE(sp, p - HMAP, 0);
398 if (svi_deleteln(sp, diff))
399 return (1);
401 /* Shift the screen map up. */
402 memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
404 /* Fill in the SMAP for the replaced line, and display. */
405 for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
406 t->lno = lno;
407 t->off = cnt;
408 if (svi_line(sp, ep, t, NULL, NULL))
409 return (1);
412 /* Display the new lines at the bottom of the screen. */
413 for (t = TMAP - diff;;) {
414 if (t < TMAP && svi_sm_next(sp, ep, t, t + 1))
415 return (1);
416 if (svi_line(sp, ep, ++t, NULL, NULL))
417 return (1);
418 if (t == TMAP)
419 break;
422 return (0);
426 * svi_sm_up --
427 * Scroll the SMAP up count logical lines.
430 svi_sm_up(sp, ep, rp, count, cursor_move)
431 SCR *sp;
432 EXF *ep;
433 MARK *rp;
434 recno_t count;
435 int cursor_move;
437 SMAP *p, svmap, tmp;
438 int ignore_cursor;
440 /* Set the default return position. */
441 rp->lno = sp->lno;
442 rp->cno = sp->cno;
445 * There are two forms of this command, one where the cursor tries to
446 * follow the line, and one where it doesn't. In the latter, we try
447 * and keep the cursor at the same position on the screen, but, if the
448 * screen is small enough and the line length large enough, the cursor
449 * can end up in very strange places. Probably not worth fixing.
451 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
452 * screen.
454 if (svi_sm_cursor(sp, ep, &p))
455 return (1);
456 if (p == NULL)
457 ignore_cursor = 1;
458 else {
459 svmap = *p;
460 ignore_cursor = 0;
463 /* Check to see if movement is possible. */
464 if (svi_sm_next(sp, ep, TMAP, &tmp))
465 return (1);
466 if (tmp.lno > TMAP->lno &&
467 !file_gline(sp, ep, tmp.lno, NULL) ||
468 tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
469 if (!cursor_move || ignore_cursor || p == TMAP) {
470 v_eof(sp, ep, NULL);
471 return (1);
476 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
478 * If it's a small screen, and the movement is small, open up the
479 * screen. Otherwise, compress and repaint. If we compress, we
480 * ignore the cursor, the movement is too large to care.
482 if (ISSMALLSCREEN(sp))
483 if (count <= HALFTEXT(sp)) {
484 for (; count && sp->t_rows != sp->t_maxrows;
485 --count, ++sp->t_rows) {
486 if (svi_sm_next(sp, ep, TMAP, &tmp))
487 return (1);
488 if (TMAP->lno != tmp.lno &&
489 !file_gline(sp, ep, tmp.lno, NULL))
490 break;
491 *++TMAP = tmp;
492 if (svi_line(sp, ep, TMAP, NULL, NULL))
493 return (1);
495 if (count == 0)
496 return (0);
497 } else {
498 MOVE(sp, INFOLINE(sp), 0);
499 clrtoeol();
500 for (;
501 sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
502 MOVE(sp, TMAP - HMAP, 0);
503 clrtoeol();
505 ignore_cursor = 1;
508 for (; count; --count) {
509 /* Decide what would show up on the screen. */
510 if (svi_sm_next(sp, ep, TMAP, &tmp))
511 return (1);
513 /* If the line doesn't exist, we're done. */
514 if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL))
515 break;
517 /* Scroll the screen cursor up one logical line. */
518 if (svi_sm_1up(sp, ep))
519 return (1);
520 if (!cursor_move && !ignore_cursor && p > HMAP)
521 --p;
524 /* If ignoring the cursor, we're done. */
525 if (ignore_cursor)
526 return (0);
528 if (cursor_move) {
529 /* If we didn't move enough, head toward EOF. */
530 for (; count; --count, ++p)
531 if (p == TMAP)
532 break;
533 } else {
535 * If the line itself moved, invalidate the cursor, because
536 * the comparison with the old line/new line won't be right
538 F_SET(SVP(sp), SVI_CUR_INVALID);
540 /* If didn't move enough, it's an error. */
541 if (count) {
542 v_eof(sp, ep, NULL);
543 return (1);
546 /* If the cursor moved off the screen, move it to the top. */
547 if (sp->lno < HMAP->lno)
548 p = HMAP;
551 * On a logical movement, we try and keep the cursor as close as
552 * possible to the last position, but also set it up so that the
553 * next "real" movement will return the cursor to the closest position
554 * to the last real movement.
556 if (p->lno != svmap.lno || p->off != svmap.off) {
557 rp->lno = p->lno;
558 rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
560 return (0);
564 * svi_sm_1up --
565 * Scroll the SMAP up one.
568 svi_sm_1up(sp, ep)
569 SCR *sp;
570 EXF *ep;
573 * Delete the top line of the screen. Shift the screen map up.
574 * Display a new line at the bottom of the screen.
576 MOVE(sp, 0, 0);
577 if (svi_deleteln(sp, 1))
578 return (1);
580 /* One-line screens can fail. */
581 if (HMAP == TMAP) {
582 if (svi_sm_next(sp, ep, TMAP, TMAP))
583 return (1);
584 } else {
585 memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
586 if (svi_sm_next(sp, ep, TMAP - 1, TMAP))
587 return (1);
589 if (svi_line(sp, ep, TMAP, NULL, NULL))
590 return (1);
591 return (0);
595 * svi_deleteln --
596 * Delete a line a la curses, make sure to put the information
597 * line and other screens back.
599 static int
600 svi_deleteln(sp, cnt)
601 SCR *sp;
602 int cnt;
604 size_t oldy, oldx;
606 getyx(stdscr, oldy, oldx);
607 while (cnt--) {
608 deleteln();
609 MOVE(sp, INFOLINE(sp) - 1, 0);
610 insertln();
611 MOVEA(sp, oldy, oldx);
613 return (0);
617 * svi_sm_down --
618 * Scroll the SMAP down count logical lines.
621 svi_sm_down(sp, ep, rp, count, cursor_move)
622 SCR *sp;
623 EXF *ep;
624 MARK *rp;
625 recno_t count;
626 int cursor_move;
628 SMAP *p, svmap;
629 int ignore_cursor;
631 /* Set the default return position. */
632 rp->lno = sp->lno;
633 rp->cno = sp->cno;
636 * There are two forms of this command, one where the cursor tries to
637 * follow the line, and one where it doesn't. In the latter, we try
638 * and keep the cursor at the same position on the screen, but, if the
639 * screen is small enough and the line length large enough, the cursor
640 * can end up in very strange places. Probably not worth fixing.
642 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
643 * screen.
645 if (svi_sm_cursor(sp, ep, &p))
646 return (1);
647 if (p == NULL)
648 ignore_cursor = 1;
649 else {
650 svmap = *p;
651 ignore_cursor = 0;
654 /* Check to see if movement is possible. */
655 if (HMAP->lno == 1 && HMAP->off == 1 &&
656 !cursor_move || ignore_cursor || p == HMAP) {
657 v_sof(sp, NULL);
658 return (1);
662 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
664 * If it's a small screen, and the movement is small, open up the
665 * screen. Otherwise, compress and repaint. If we compress, we
666 * ignore the cursor, the movement is too large to care.
668 if (ISSMALLSCREEN(sp))
669 if (count <= HALFTEXT(sp)) {
670 for (; count && sp->t_rows != sp->t_maxrows &&
671 (HMAP->lno > 1 || HMAP->off > 1);
672 --count, ++sp->t_rows) {
673 ++TMAP;
674 if (svi_sm_1down(sp, ep))
675 return (1);
676 if (!cursor_move)
677 ++p;
679 if (count == 0)
680 return (0);
681 } else {
682 MOVE(sp, INFOLINE(sp), 0);
683 clrtoeol();
684 for (;
685 sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
686 MOVE(sp, TMAP - HMAP, 0);
687 clrtoeol();
689 ignore_cursor = 1;
692 for (; count; --count) {
693 /* If the line doesn't exist, we're done. */
694 if (HMAP->lno == 1 && HMAP->off == 1)
695 break;
697 /* Scroll the screen and cursor down one logical line. */
698 if (svi_sm_1down(sp, ep))
699 return (1);
700 if (!cursor_move && !ignore_cursor && p < TMAP)
701 ++p;
704 /* If ignoring the cursor, we're done. */
705 if (ignore_cursor)
706 return (0);
708 if (cursor_move) {
709 /* If we didn't move enough, move to SOF. */
710 if (count)
711 p = HMAP;
712 } else {
714 * If the line itself moved, invalidate the cursor, because
715 * the comparison with the old line/new line won't be right.
717 F_SET(SVP(sp), SVI_CUR_INVALID);
719 /* If didn't move enough, it's an error. */
720 if (count) {
721 v_sof(sp, NULL);
722 return (1);
725 /* If the cursor moved off the screen, move it to the bottom. */
726 if (sp->lno > TMAP->lno)
727 p = TMAP;
731 * On a logical movement, we try and keep the cursor as close as
732 * possible to the last position, but also set it up so that the
733 * next "real" movement will return the cursor to the closest position
734 * to the last real movement.
736 if (p->lno != svmap.lno || p->off != svmap.off) {
737 rp->lno = p->lno;
738 rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
740 return (0);
744 * svi_sm_1down --
745 * Scroll the SMAP down one.
748 svi_sm_1down(sp, ep)
749 SCR *sp;
750 EXF *ep;
753 * Clear the bottom line of the screen, insert a line at the top
754 * of the screen. Shift the screen map down, display a new line
755 * at the top of the screen.
757 MOVE(sp, sp->t_rows, 0);
758 clrtoeol();
759 MOVE(sp, 0, 0);
760 if (svi_insertln(sp, 1))
761 return (1);
762 memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
763 if (svi_sm_prev(sp, ep, HMAP + 1, HMAP))
764 return (1);
765 if (svi_line(sp, ep, HMAP, NULL, NULL))
766 return (1);
767 return (0);
771 * svi_insertln --
772 * Insert a line a la curses, make sure to put the information
773 * line and other screens back.
775 static int
776 svi_insertln(sp, cnt)
777 SCR *sp;
778 int cnt;
780 size_t oldy, oldx;
782 getyx(stdscr, oldy, oldx);
783 while (cnt--) {
784 MOVE(sp, INFOLINE(sp) - 1, 0);
785 deleteln();
786 MOVEA(sp, oldy, oldx);
787 insertln();
789 return (0);
793 * svi_sm_next --
794 * Fill in the next entry in the SMAP.
797 svi_sm_next(sp, ep, p, t)
798 SCR *sp;
799 EXF *ep;
800 SMAP *p, *t;
802 size_t lcnt;
804 SMAP_FLUSH(t);
805 if (O_ISSET(sp, O_LEFTRIGHT)) {
806 t->lno = p->lno + 1;
807 t->off = p->off;
808 } else {
809 lcnt = svi_screens(sp, ep, p->lno, NULL);
810 if (lcnt == p->off) {
811 t->lno = p->lno + 1;
812 t->off = 1;
813 } else {
814 t->lno = p->lno;
815 t->off = p->off + 1;
818 return (0);
822 * svi_sm_prev --
823 * Fill in the previous entry in the SMAP.
826 svi_sm_prev(sp, ep, p, t)
827 SCR *sp;
828 EXF *ep;
829 SMAP *p, *t;
831 SMAP_FLUSH(t);
832 if (O_ISSET(sp, O_LEFTRIGHT)) {
833 t->lno = p->lno - 1;
834 t->off = p->off;
835 } else if (p->off != 1) {
836 t->lno = p->lno;
837 t->off = p->off - 1;
838 } else {
839 t->lno = p->lno - 1;
840 t->off = svi_screens(sp, ep, t->lno, NULL);
842 return (t->lno == 0);
846 * svi_sm_cursor --
847 * Return the SMAP entry referenced by the cursor.
850 svi_sm_cursor(sp, ep, smp)
851 SCR *sp;
852 EXF *ep;
853 SMAP **smp;
855 SMAP *p;
857 /* See if the cursor is not in the map. */
858 if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) {
859 *smp = NULL;
860 return (0);
863 /* Find the first occurence of the line. */
864 for (p = HMAP; p->lno != sp->lno; ++p);
866 /* Fill in the map information until we find the right line. */
867 for (; p <= TMAP; ++p) {
868 /* Short lines are common and easy to detect. */
869 if (p != TMAP && (p + 1)->lno != p->lno) {
870 *smp = p;
871 return (0);
873 if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
874 return (1);
875 if (p->c_eboff >= sp->cno) {
876 *smp = p;
877 return (0);
881 /* It was past the end of the map after all. */
882 *smp = NULL;
883 return (0);
887 * svi_sm_position --
888 * Return the line/column of the top, middle or last line on the screen.
889 * (The vi H, M and L commands.) Here because only the screen routines
890 * know what's really out there.
893 svi_sm_position(sp, ep, rp, cnt, pos)
894 SCR *sp;
895 EXF *ep;
896 MARK *rp;
897 u_long cnt;
898 enum position pos;
900 SMAP *smp;
901 recno_t last;
903 switch (pos) {
904 case P_TOP:
905 if (cnt > TMAP - HMAP)
906 goto err;
907 smp = HMAP + cnt;
908 break;
909 case P_MIDDLE:
910 if (cnt > (TMAP - HMAP) / 2)
911 goto err;
912 smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
913 goto eof;
914 case P_BOTTOM:
915 if (cnt > TMAP - HMAP) {
916 err: msgq(sp, M_BERR, "Movement past the end-of-screen.");
917 return (1);
919 smp = TMAP - cnt;
920 eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
921 if (file_lline(sp, ep, &last))
922 return (1);
923 for (; smp->lno > last && smp > HMAP; --smp);
925 break;
926 default:
927 abort();
930 if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
931 return (1);
932 rp->lno = smp->lno;
933 rp->cno = smp->c_sboff;
935 return (0);
939 * svi_sm_nlines --
940 * Return the number of screen lines from an SMAP entry to the
941 * start of some file line, less than a maximum value.
943 recno_t
944 svi_sm_nlines(sp, ep, from_sp, to_lno, max)
945 SCR *sp;
946 EXF *ep;
947 SMAP *from_sp;
948 recno_t to_lno;
949 size_t max;
951 recno_t lno, lcnt;
953 if (O_ISSET(sp, O_LEFTRIGHT))
954 return (from_sp->lno > to_lno ?
955 from_sp->lno - to_lno : to_lno - from_sp->lno);
957 if (from_sp->lno == to_lno)
958 return (from_sp->off - 1);
960 if (from_sp->lno > to_lno) {
961 lcnt = from_sp->off - 1; /* Correct for off-by-one. */
962 for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
963 lcnt += svi_screens(sp, ep, lno, NULL);
964 } else {
965 lno = from_sp->lno;
966 lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
967 for (; ++lno < to_lno && lcnt <= max;)
968 lcnt += svi_screens(sp, ep, lno, NULL);
970 return (lcnt);