119304Speter/*- 219304Speter * Copyright (c) 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: vs_smap.c,v 10.31 2011/02/26 13:56:21 skimo Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <limits.h> 2219304Speter#include <stdio.h> 2319304Speter#include <stdlib.h> 2419304Speter#include <string.h> 2519304Speter 2619304Speter#include "../common/common.h" 2719304Speter#include "vi.h" 2819304Speter 2919304Speterstatic int vs_deleteln __P((SCR *, int)); 3019304Speterstatic int vs_insertln __P((SCR *, int)); 3119304Speterstatic int vs_sm_delete __P((SCR *, recno_t)); 3219304Speterstatic int vs_sm_down __P((SCR *, MARK *, recno_t, scroll_t, SMAP *)); 3319304Speterstatic int vs_sm_erase __P((SCR *)); 3419304Speterstatic int vs_sm_insert __P((SCR *, recno_t)); 3519304Speterstatic int vs_sm_reset __P((SCR *, recno_t)); 3619304Speterstatic int vs_sm_up __P((SCR *, MARK *, recno_t, scroll_t, SMAP *)); 3719304Speter 3819304Speter/* 3919304Speter * vs_change -- 4019304Speter * Make a change to the screen. 4119304Speter * 4219304Speter * PUBLIC: int vs_change __P((SCR *, recno_t, lnop_t)); 4319304Speter */ 4419304Speterint 45254225Spetervs_change(SCR *sp, recno_t lno, lnop_t op) 4619304Speter{ 4719304Speter VI_PRIVATE *vip; 4819304Speter SMAP *p; 4919304Speter size_t cnt, oldy, oldx; 5019304Speter 5119304Speter vip = VIP(sp); 5219304Speter 5319304Speter /* 5419304Speter * XXX 5519304Speter * Very nasty special case. The historic vi code displays a single 5619304Speter * space (or a '$' if the list option is set) for the first line in 5719304Speter * an "empty" file. If we "insert" a line, that line gets scrolled 5819304Speter * down, not repainted, so it's incorrect when we refresh the screen. 5919304Speter * The vi text input functions detect it explicitly and don't insert 6019304Speter * a new line. 6119304Speter * 6219304Speter * Check for line #2 before going to the end of the file. 6319304Speter */ 64254225Speter if (((op == LINE_APPEND && lno == 0) || 65254225Speter (op == LINE_INSERT && lno == 1)) && 6619304Speter !db_exist(sp, 2)) { 6719304Speter lno = 1; 6819304Speter op = LINE_RESET; 6919304Speter } 7019304Speter 7119304Speter /* Appending is the same as inserting, if the line is incremented. */ 7219304Speter if (op == LINE_APPEND) { 7319304Speter ++lno; 7419304Speter op = LINE_INSERT; 7519304Speter } 7619304Speter 7719304Speter /* Ignore the change if the line is after the map. */ 7819304Speter if (lno > TMAP->lno) 7919304Speter return (0); 8019304Speter 8119304Speter /* 8219304Speter * If the line is before the map, and it's a decrement, decrement 8319304Speter * the map. If it's an increment, increment the map. Otherwise, 8419304Speter * ignore it. 8519304Speter */ 8619304Speter if (lno < HMAP->lno) { 8719304Speter switch (op) { 8819304Speter case LINE_APPEND: 8919304Speter abort(); 9019304Speter /* NOTREACHED */ 9119304Speter case LINE_DELETE: 9219304Speter for (p = HMAP, cnt = sp->t_rows; cnt--; ++p) 9319304Speter --p->lno; 9419304Speter if (sp->lno >= lno) 9519304Speter --sp->lno; 9619304Speter F_SET(vip, VIP_N_RENUMBER); 9719304Speter break; 9819304Speter case LINE_INSERT: 9919304Speter for (p = HMAP, cnt = sp->t_rows; cnt--; ++p) 10019304Speter ++p->lno; 10119304Speter if (sp->lno >= lno) 10219304Speter ++sp->lno; 10319304Speter F_SET(vip, VIP_N_RENUMBER); 10419304Speter break; 10519304Speter case LINE_RESET: 10619304Speter break; 10719304Speter } 10819304Speter return (0); 10919304Speter } 11019304Speter 11119304Speter F_SET(vip, VIP_N_REFRESH); 11219304Speter 11319304Speter /* 11419304Speter * Invalidate the line size cache, and invalidate the cursor if it's 11519304Speter * on this line, 11619304Speter */ 11719304Speter VI_SCR_CFLUSH(vip); 11819304Speter if (sp->lno == lno) 11919304Speter F_SET(vip, VIP_CUR_INVALID); 12019304Speter 12119304Speter /* 12219304Speter * If ex modifies the screen after ex output is already on the screen 12319304Speter * or if we've switched into ex canonical mode, don't touch it -- we'll 12419304Speter * get scrolling wrong, at best. 12519304Speter */ 12619304Speter if (!F_ISSET(sp, SC_TINPUT_INFO) && 12719304Speter (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) { 12819304Speter F_SET(vip, VIP_N_EX_REDRAW); 12919304Speter return (0); 13019304Speter } 13119304Speter 13219304Speter /* Save and restore the cursor for these routines. */ 13319304Speter (void)sp->gp->scr_cursor(sp, &oldy, &oldx); 13419304Speter 13519304Speter switch (op) { 13619304Speter case LINE_DELETE: 13719304Speter if (vs_sm_delete(sp, lno)) 13819304Speter return (1); 139254225Speter if (sp->lno > lno) 140254225Speter --sp->lno; 14119304Speter F_SET(vip, VIP_N_RENUMBER); 14219304Speter break; 14319304Speter case LINE_INSERT: 14419304Speter if (vs_sm_insert(sp, lno)) 14519304Speter return (1); 146254225Speter if (sp->lno > lno) 147254225Speter ++sp->lno; 14819304Speter F_SET(vip, VIP_N_RENUMBER); 14919304Speter break; 15019304Speter case LINE_RESET: 15119304Speter if (vs_sm_reset(sp, lno)) 15219304Speter return (1); 15319304Speter break; 15419304Speter default: 15519304Speter abort(); 15619304Speter } 15719304Speter 15819304Speter (void)sp->gp->scr_move(sp, oldy, oldx); 15919304Speter return (0); 16019304Speter} 16119304Speter 16219304Speter/* 16319304Speter * vs_sm_fill -- 16419304Speter * Fill in the screen map, placing the specified line at the 16519304Speter * right position. There isn't any way to tell if an SMAP 16619304Speter * entry has been filled in, so this routine had better be 16719304Speter * called with P_FILL set before anything else is done. 16819304Speter * 16919304Speter * !!! 17019304Speter * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP 17119304Speter * slot is already filled in, P_BOTTOM means that the TMAP slot is 17219304Speter * already filled in, and we just finish up the job. 17319304Speter * 17419304Speter * PUBLIC: int vs_sm_fill __P((SCR *, recno_t, pos_t)); 17519304Speter */ 17619304Speterint 177254225Spetervs_sm_fill(SCR *sp, recno_t lno, pos_t pos) 17819304Speter{ 17919304Speter SMAP *p, tmp; 18019304Speter size_t cnt; 18119304Speter 18219304Speter /* Flush all cached information from the SMAP. */ 18319304Speter for (p = HMAP, cnt = sp->t_rows; cnt--; ++p) 18419304Speter SMAP_FLUSH(p); 18519304Speter 18619304Speter /* 18719304Speter * If the map is filled, the screen must be redrawn. 18819304Speter * 18919304Speter * XXX 19019304Speter * This is a bug. We should try and figure out if the desired line 19119304Speter * is already in the map or close by -- scrolling the screen would 19219304Speter * be a lot better than redrawing. 19319304Speter */ 19419304Speter F_SET(sp, SC_SCR_REDRAW); 19519304Speter 19619304Speter switch (pos) { 19719304Speter case P_FILL: 19819304Speter tmp.lno = 1; 19919304Speter tmp.coff = 0; 20019304Speter tmp.soff = 1; 20119304Speter 20219304Speter /* See if less than half a screen from the top. */ 20319304Speter if (vs_sm_nlines(sp, 20419304Speter &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { 20519304Speter lno = 1; 20619304Speter goto top; 20719304Speter } 20819304Speter 20919304Speter /* See if less than half a screen from the bottom. */ 21019304Speter if (db_last(sp, &tmp.lno)) 21119304Speter return (1); 21219304Speter tmp.coff = 0; 21319304Speter tmp.soff = vs_screens(sp, tmp.lno, NULL); 21419304Speter if (vs_sm_nlines(sp, 21519304Speter &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { 21619304Speter TMAP->lno = tmp.lno; 21719304Speter TMAP->coff = tmp.coff; 21819304Speter TMAP->soff = tmp.soff; 21919304Speter goto bottom; 22019304Speter } 22119304Speter goto middle; 22219304Speter case P_TOP: 22319304Speter if (lno != OOBLNO) { 22419304Spetertop: HMAP->lno = lno; 22519304Speter HMAP->coff = 0; 22619304Speter HMAP->soff = 1; 227254225Speter } else { 228254225Speter /* 229254225Speter * If number of lines HMAP->lno (top line) spans 230254225Speter * changed due to, say reformatting, and now is 231254225Speter * fewer than HMAP->soff, reset so the line is 232254225Speter * redrawn at the top of the screen. 233254225Speter */ 234254225Speter cnt = vs_screens(sp, HMAP->lno, NULL); 235254225Speter if (cnt < HMAP->soff) 236254225Speter HMAP->soff = 1; 23719304Speter } 23819304Speter /* If we fail, just punt. */ 23919304Speter for (p = HMAP, cnt = sp->t_rows; --cnt; ++p) 24019304Speter if (vs_sm_next(sp, p, p + 1)) 24119304Speter goto err; 24219304Speter break; 24319304Speter case P_MIDDLE: 24419304Speter /* If we fail, guess that the file is too small. */ 24519304Spetermiddle: p = HMAP + sp->t_rows / 2; 24619304Speter p->lno = lno; 24719304Speter p->coff = 0; 24819304Speter p->soff = 1; 24919304Speter for (; p > HMAP; --p) 25019304Speter if (vs_sm_prev(sp, p, p - 1)) { 25119304Speter lno = 1; 25219304Speter goto top; 25319304Speter } 25419304Speter 25519304Speter /* If we fail, just punt. */ 25619304Speter p = HMAP + sp->t_rows / 2; 25719304Speter for (; p < TMAP; ++p) 25819304Speter if (vs_sm_next(sp, p, p + 1)) 25919304Speter goto err; 26019304Speter break; 26119304Speter case P_BOTTOM: 26219304Speter if (lno != OOBLNO) { 26319304Speter TMAP->lno = lno; 26419304Speter TMAP->coff = 0; 26519304Speter TMAP->soff = vs_screens(sp, lno, NULL); 26619304Speter } 26719304Speter /* If we fail, guess that the file is too small. */ 26819304Speterbottom: for (p = TMAP; p > HMAP; --p) 26919304Speter if (vs_sm_prev(sp, p, p - 1)) { 27019304Speter lno = 1; 27119304Speter goto top; 27219304Speter } 27319304Speter break; 27419304Speter default: 27519304Speter abort(); 27619304Speter } 27719304Speter return (0); 27819304Speter 27919304Speter /* 28019304Speter * Try and put *something* on the screen. If this fails, we have a 28119304Speter * serious hard error. 28219304Speter */ 28319304Spetererr: HMAP->lno = 1; 28419304Speter HMAP->coff = 0; 28519304Speter HMAP->soff = 1; 28619304Speter for (p = HMAP; p < TMAP; ++p) 28719304Speter if (vs_sm_next(sp, p, p + 1)) 28819304Speter return (1); 28919304Speter return (0); 29019304Speter} 29119304Speter 29219304Speter/* 29319304Speter * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the 29419304Speter * screen contains only a single line (whether because the screen is small 29519304Speter * or the line large), it gets fairly exciting. Skip the fun, set a flag 29619304Speter * so the screen map is refilled and the screen redrawn, and return. This 29719304Speter * is amazingly slow, but it's not clear that anyone will care. 29819304Speter */ 29919304Speter#define HANDLE_WEIRDNESS(cnt) { \ 30019304Speter if (cnt >= sp->t_rows) { \ 30119304Speter F_SET(sp, SC_SCR_REFORMAT); \ 30219304Speter return (0); \ 30319304Speter } \ 30419304Speter} 30519304Speter 30619304Speter/* 30719304Speter * vs_sm_delete -- 30819304Speter * Delete a line out of the SMAP. 30919304Speter */ 31019304Speterstatic int 311254225Spetervs_sm_delete(SCR *sp, recno_t lno) 31219304Speter{ 31319304Speter SMAP *p, *t; 31419304Speter size_t cnt_orig; 31519304Speter 31619304Speter /* 31719304Speter * Find the line in the map, and count the number of screen lines 31819304Speter * which display any part of the deleted line. 31919304Speter */ 32019304Speter for (p = HMAP; p->lno != lno; ++p); 32119304Speter if (O_ISSET(sp, O_LEFTRIGHT)) 32219304Speter cnt_orig = 1; 32319304Speter else 32419304Speter for (cnt_orig = 1, t = p + 1; 32519304Speter t <= TMAP && t->lno == lno; ++cnt_orig, ++t); 32619304Speter 32719304Speter HANDLE_WEIRDNESS(cnt_orig); 32819304Speter 32919304Speter /* Delete that many lines from the screen. */ 33019304Speter (void)sp->gp->scr_move(sp, p - HMAP, 0); 33119304Speter if (vs_deleteln(sp, cnt_orig)) 33219304Speter return (1); 33319304Speter 33419304Speter /* Shift the screen map up. */ 33519304Speter memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); 33619304Speter 33719304Speter /* Decrement the line numbers for the rest of the map. */ 33819304Speter for (t = TMAP - cnt_orig; p <= t; ++p) 33919304Speter --p->lno; 34019304Speter 34119304Speter /* Display the new lines. */ 34219304Speter for (p = TMAP - cnt_orig;;) { 34319304Speter if (p < TMAP && vs_sm_next(sp, p, p + 1)) 34419304Speter return (1); 34519304Speter /* vs_sm_next() flushed the cache. */ 34619304Speter if (vs_line(sp, ++p, NULL, NULL)) 34719304Speter return (1); 34819304Speter if (p == TMAP) 34919304Speter break; 35019304Speter } 35119304Speter return (0); 35219304Speter} 35319304Speter 35419304Speter/* 35519304Speter * vs_sm_insert -- 35619304Speter * Insert a line into the SMAP. 35719304Speter */ 35819304Speterstatic int 359254225Spetervs_sm_insert(SCR *sp, recno_t lno) 36019304Speter{ 36119304Speter SMAP *p, *t; 36219304Speter size_t cnt_orig, cnt, coff; 36319304Speter 36419304Speter /* Save the offset. */ 36519304Speter coff = HMAP->coff; 36619304Speter 36719304Speter /* 36819304Speter * Find the line in the map, find out how many screen lines 36919304Speter * needed to display the line. 37019304Speter */ 37119304Speter for (p = HMAP; p->lno != lno; ++p); 37219304Speter 37319304Speter cnt_orig = vs_screens(sp, lno, NULL); 37419304Speter HANDLE_WEIRDNESS(cnt_orig); 37519304Speter 37619304Speter /* 37719304Speter * The lines left in the screen override the number of screen 37819304Speter * lines in the inserted line. 37919304Speter */ 38019304Speter cnt = (TMAP - p) + 1; 38119304Speter if (cnt_orig > cnt) 38219304Speter cnt_orig = cnt; 38319304Speter 38419304Speter /* Push down that many lines. */ 38519304Speter (void)sp->gp->scr_move(sp, p - HMAP, 0); 38619304Speter if (vs_insertln(sp, cnt_orig)) 38719304Speter return (1); 38819304Speter 38919304Speter /* Shift the screen map down. */ 39019304Speter memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); 39119304Speter 39219304Speter /* Increment the line numbers for the rest of the map. */ 39319304Speter for (t = p + cnt_orig; t <= TMAP; ++t) 39419304Speter ++t->lno; 39519304Speter 39619304Speter /* Fill in the SMAP for the new lines, and display. */ 39719304Speter for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) { 39819304Speter t->lno = lno; 39919304Speter t->coff = coff; 40019304Speter t->soff = cnt; 40119304Speter SMAP_FLUSH(t); 40219304Speter if (vs_line(sp, t, NULL, NULL)) 40319304Speter return (1); 40419304Speter } 40519304Speter return (0); 40619304Speter} 40719304Speter 40819304Speter/* 40919304Speter * vs_sm_reset -- 41019304Speter * Reset a line in the SMAP. 41119304Speter */ 41219304Speterstatic int 413254225Spetervs_sm_reset(SCR *sp, recno_t lno) 41419304Speter{ 41519304Speter SMAP *p, *t; 41619304Speter size_t cnt_orig, cnt_new, cnt, diff; 41719304Speter 41819304Speter /* 41919304Speter * See if the number of on-screen rows taken up by the old display 42019304Speter * for the line is the same as the number needed for the new one. 42119304Speter * If so, repaint, otherwise do it the hard way. 42219304Speter */ 42319304Speter for (p = HMAP; p->lno != lno; ++p); 42419304Speter if (O_ISSET(sp, O_LEFTRIGHT)) { 42519304Speter t = p; 42619304Speter cnt_orig = cnt_new = 1; 42719304Speter } else { 42819304Speter for (cnt_orig = 0, 42919304Speter t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); 43019304Speter cnt_new = vs_screens(sp, lno, NULL); 43119304Speter } 43219304Speter 43319304Speter HANDLE_WEIRDNESS(cnt_orig); 43419304Speter 43519304Speter if (cnt_orig == cnt_new) { 43619304Speter do { 43719304Speter SMAP_FLUSH(p); 43819304Speter if (vs_line(sp, p, NULL, NULL)) 43919304Speter return (1); 44019304Speter } while (++p < t); 44119304Speter return (0); 44219304Speter } 44319304Speter 44419304Speter if (cnt_orig < cnt_new) { 44519304Speter /* Get the difference. */ 44619304Speter diff = cnt_new - cnt_orig; 44719304Speter 44819304Speter /* 44919304Speter * The lines left in the screen override the number of screen 45019304Speter * lines in the inserted line. 45119304Speter */ 45219304Speter cnt = (TMAP - p) + 1; 45319304Speter if (diff > cnt) 45419304Speter diff = cnt; 45519304Speter 45619304Speter /* If there are any following lines, push them down. */ 45719304Speter if (cnt > 1) { 45819304Speter (void)sp->gp->scr_move(sp, p - HMAP, 0); 45919304Speter if (vs_insertln(sp, diff)) 46019304Speter return (1); 46119304Speter 46219304Speter /* Shift the screen map down. */ 46319304Speter memmove(p + diff, p, 46419304Speter (((TMAP - p) - diff) + 1) * sizeof(SMAP)); 46519304Speter } 46619304Speter 46719304Speter /* Fill in the SMAP for the replaced line, and display. */ 46819304Speter for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) { 46919304Speter t->lno = lno; 47019304Speter t->soff = cnt; 47119304Speter SMAP_FLUSH(t); 47219304Speter if (vs_line(sp, t, NULL, NULL)) 47319304Speter return (1); 47419304Speter } 47519304Speter } else { 47619304Speter /* Get the difference. */ 47719304Speter diff = cnt_orig - cnt_new; 47819304Speter 47919304Speter /* Delete that many lines from the screen. */ 48019304Speter (void)sp->gp->scr_move(sp, p - HMAP, 0); 48119304Speter if (vs_deleteln(sp, diff)) 48219304Speter return (1); 48319304Speter 48419304Speter /* Shift the screen map up. */ 48519304Speter memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); 48619304Speter 48719304Speter /* Fill in the SMAP for the replaced line, and display. */ 48819304Speter for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) { 48919304Speter t->lno = lno; 49019304Speter t->soff = cnt; 49119304Speter SMAP_FLUSH(t); 49219304Speter if (vs_line(sp, t, NULL, NULL)) 49319304Speter return (1); 49419304Speter } 49519304Speter 49619304Speter /* Display the new lines at the bottom of the screen. */ 49719304Speter for (t = TMAP - diff;;) { 49819304Speter if (t < TMAP && vs_sm_next(sp, t, t + 1)) 49919304Speter return (1); 50019304Speter /* vs_sm_next() flushed the cache. */ 50119304Speter if (vs_line(sp, ++t, NULL, NULL)) 50219304Speter return (1); 50319304Speter if (t == TMAP) 50419304Speter break; 50519304Speter } 50619304Speter } 50719304Speter return (0); 50819304Speter} 50919304Speter 51019304Speter/* 51119304Speter * vs_sm_scroll 51219304Speter * Scroll the SMAP up/down count logical lines. Different 51319304Speter * semantics based on the vi command, *sigh*. 51419304Speter * 51519304Speter * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, recno_t, scroll_t)); 51619304Speter */ 51719304Speterint 518254225Spetervs_sm_scroll(SCR *sp, MARK *rp, recno_t count, scroll_t scmd) 51919304Speter{ 52019304Speter SMAP *smp; 52119304Speter 52219304Speter /* 52319304Speter * Invalidate the cursor. The line is probably going to change, 52419304Speter * (although for ^E and ^Y it may not). In any case, the scroll 52519304Speter * routines move the cursor to draw things. 52619304Speter */ 52719304Speter F_SET(VIP(sp), VIP_CUR_INVALID); 52819304Speter 52919304Speter /* Find the cursor in the screen. */ 53019304Speter if (vs_sm_cursor(sp, &smp)) 53119304Speter return (1); 53219304Speter 53319304Speter switch (scmd) { 53419304Speter case CNTRL_B: 53519304Speter case CNTRL_U: 53619304Speter case CNTRL_Y: 53719304Speter case Z_CARAT: 53819304Speter if (vs_sm_down(sp, rp, count, scmd, smp)) 53919304Speter return (1); 54019304Speter break; 54119304Speter case CNTRL_D: 54219304Speter case CNTRL_E: 54319304Speter case CNTRL_F: 54419304Speter case Z_PLUS: 54519304Speter if (vs_sm_up(sp, rp, count, scmd, smp)) 54619304Speter return (1); 54719304Speter break; 54819304Speter default: 54919304Speter abort(); 55019304Speter } 55119304Speter 55219304Speter /* 55319304Speter * !!! 55419304Speter * If we're at the start of a line, go for the first non-blank. 55519304Speter * This makes it look like the old vi, even though we're moving 55619304Speter * around by logical lines, not physical ones. 55719304Speter * 55819304Speter * XXX 55919304Speter * In the presence of a long line, which has more than a screen 56019304Speter * width of leading spaces, this code can cause a cursor warp. 56119304Speter * Live with it. 56219304Speter */ 56319304Speter if (scmd != CNTRL_E && scmd != CNTRL_Y && 56419304Speter rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno)) 56519304Speter return (1); 56619304Speter 56719304Speter return (0); 56819304Speter} 56919304Speter 57019304Speter/* 57119304Speter * vs_sm_up -- 57219304Speter * Scroll the SMAP up count logical lines. 57319304Speter */ 57419304Speterstatic int 575254225Spetervs_sm_up(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp) 57619304Speter{ 57719304Speter int cursor_set, echanged, zset; 57819304Speter SMAP *ssmp, s1, s2; 57919304Speter 58019304Speter /* 58119304Speter * Check to see if movement is possible. 58219304Speter * 58319304Speter * Get the line after the map. If that line is a new one (and if 58419304Speter * O_LEFTRIGHT option is set, this has to be true), and the next 58519304Speter * line doesn't exist, and the cursor doesn't move, or the cursor 58619304Speter * isn't even on the screen, or the cursor is already at the last 58719304Speter * line in the map, it's an error. If that test succeeded because 58819304Speter * the cursor wasn't at the end of the map, test to see if the map 58919304Speter * is mostly empty. 59019304Speter */ 59119304Speter if (vs_sm_next(sp, TMAP, &s1)) 59219304Speter return (1); 59319304Speter if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) { 59419304Speter if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) { 59519304Speter v_eof(sp, NULL); 59619304Speter return (1); 59719304Speter } 59819304Speter if (vs_sm_next(sp, smp, &s1)) 59919304Speter return (1); 60019304Speter if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) { 60119304Speter v_eof(sp, NULL); 60219304Speter return (1); 60319304Speter } 60419304Speter } 60519304Speter 60619304Speter /* 60719304Speter * Small screens: see vs_refresh.c section 6a. 60819304Speter * 60919304Speter * If it's a small screen, and the movement isn't larger than a 61019304Speter * screen, i.e some context will remain, open up the screen and 61119304Speter * display by scrolling. In this case, the cursor moves down one 61219304Speter * line for each line displayed. Otherwise, erase/compress and 61319304Speter * repaint, and move the cursor to the first line in the screen. 61419304Speter * Note, the ^F command is always in the latter case, for historical 61519304Speter * reasons. 61619304Speter */ 61719304Speter cursor_set = 0; 61819304Speter if (IS_SMALL(sp)) { 61919304Speter if (count >= sp->t_maxrows || scmd == CNTRL_F) { 62019304Speter s1 = TMAP[0]; 62119304Speter if (vs_sm_erase(sp)) 62219304Speter return (1); 62319304Speter for (; count--; s1 = s2) { 62419304Speter if (vs_sm_next(sp, &s1, &s2)) 62519304Speter return (1); 62619304Speter if (s2.lno != s1.lno && !db_exist(sp, s2.lno)) 62719304Speter break; 62819304Speter } 62919304Speter TMAP[0] = s2; 63019304Speter if (vs_sm_fill(sp, OOBLNO, P_BOTTOM)) 63119304Speter return (1); 63219304Speter return (vs_sm_position(sp, rp, 0, P_TOP)); 63319304Speter } 63419304Speter cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp); 63519304Speter for (; count && 63619304Speter sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) { 63719304Speter if (vs_sm_next(sp, TMAP, &s1)) 63819304Speter return (1); 63919304Speter if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno)) 64019304Speter break; 64119304Speter *++TMAP = s1; 64219304Speter /* vs_sm_next() flushed the cache. */ 64319304Speter if (vs_line(sp, TMAP, NULL, NULL)) 64419304Speter return (1); 64519304Speter 64619304Speter if (!cursor_set) 64719304Speter ++ssmp; 64819304Speter } 64919304Speter if (!cursor_set) { 65019304Speter rp->lno = ssmp->lno; 65119304Speter rp->cno = ssmp->c_sboff; 65219304Speter } 65319304Speter if (count == 0) 65419304Speter return (0); 65519304Speter } 65619304Speter 65719304Speter for (echanged = zset = 0; count; --count) { 65819304Speter /* Decide what would show up on the screen. */ 65919304Speter if (vs_sm_next(sp, TMAP, &s1)) 66019304Speter return (1); 66119304Speter 66219304Speter /* If the line doesn't exist, we're done. */ 66319304Speter if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno)) 66419304Speter break; 66519304Speter 66619304Speter /* Scroll the screen cursor up one logical line. */ 66719304Speter if (vs_sm_1up(sp)) 66819304Speter return (1); 66919304Speter switch (scmd) { 67019304Speter case CNTRL_E: 67119304Speter if (smp > HMAP) 67219304Speter --smp; 67319304Speter else 67419304Speter echanged = 1; 67519304Speter break; 67619304Speter case Z_PLUS: 67719304Speter if (zset) { 67819304Speter if (smp > HMAP) 67919304Speter --smp; 68019304Speter } else { 68119304Speter smp = TMAP; 68219304Speter zset = 1; 68319304Speter } 68419304Speter /* FALLTHROUGH */ 68519304Speter default: 68619304Speter break; 68719304Speter } 68819304Speter } 68919304Speter 69019304Speter if (cursor_set) 69119304Speter return(0); 69219304Speter 69319304Speter switch (scmd) { 69419304Speter case CNTRL_E: 69519304Speter /* 69619304Speter * On a ^E that was forced to change lines, try and keep the 69719304Speter * cursor as close as possible to the last position, but also 69819304Speter * set it up so that the next "real" movement will return the 69919304Speter * cursor to the closest position to the last real movement. 70019304Speter */ 70119304Speter if (echanged) { 70219304Speter rp->lno = smp->lno; 70319304Speter rp->cno = vs_colpos(sp, smp->lno, 70419304Speter (O_ISSET(sp, O_LEFTRIGHT) ? 70519304Speter smp->coff : (smp->soff - 1) * sp->cols) + 70619304Speter sp->rcm % sp->cols); 70719304Speter } 70819304Speter return (0); 70919304Speter case CNTRL_F: 71019304Speter /* 71119304Speter * If there are more lines, the ^F command is positioned at 71219304Speter * the first line of the screen. 71319304Speter */ 71419304Speter if (!count) { 71519304Speter smp = HMAP; 71619304Speter break; 71719304Speter } 71819304Speter /* FALLTHROUGH */ 71919304Speter case CNTRL_D: 72019304Speter /* 72119304Speter * The ^D and ^F commands move the cursor towards EOF 72219304Speter * if there are more lines to move. Check to be sure 72319304Speter * the lines actually exist. (They may not if the 72419304Speter * file is smaller than the screen.) 72519304Speter */ 72619304Speter for (; count; --count, ++smp) 72719304Speter if (smp == TMAP || !db_exist(sp, smp[1].lno)) 72819304Speter break; 72919304Speter break; 73019304Speter case Z_PLUS: 73119304Speter /* The z+ command moves the cursor to the first new line. */ 73219304Speter break; 73319304Speter default: 73419304Speter abort(); 73519304Speter } 73619304Speter 73719304Speter if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL)) 73819304Speter return (1); 73919304Speter rp->lno = smp->lno; 74090019Ssheldonh rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff; 74119304Speter return (0); 74219304Speter} 74319304Speter 74419304Speter/* 74519304Speter * vs_sm_1up -- 74619304Speter * Scroll the SMAP up one. 74719304Speter * 74819304Speter * PUBLIC: int vs_sm_1up __P((SCR *)); 74919304Speter */ 75019304Speterint 751254225Spetervs_sm_1up(SCR *sp) 75219304Speter{ 75319304Speter /* 75419304Speter * Delete the top line of the screen. Shift the screen map 75519304Speter * up and display a new line at the bottom of the screen. 75619304Speter */ 75719304Speter (void)sp->gp->scr_move(sp, 0, 0); 75819304Speter if (vs_deleteln(sp, 1)) 75919304Speter return (1); 76019304Speter 76119304Speter /* One-line screens can fail. */ 76219304Speter if (IS_ONELINE(sp)) { 76319304Speter if (vs_sm_next(sp, TMAP, TMAP)) 76419304Speter return (1); 76519304Speter } else { 76619304Speter memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP)); 76719304Speter if (vs_sm_next(sp, TMAP - 1, TMAP)) 76819304Speter return (1); 76919304Speter } 77019304Speter /* vs_sm_next() flushed the cache. */ 77119304Speter return (vs_line(sp, TMAP, NULL, NULL)); 77219304Speter} 77319304Speter 77419304Speter/* 77519304Speter * vs_deleteln -- 77619304Speter * Delete a line a la curses, make sure to put the information 77719304Speter * line and other screens back. 77819304Speter */ 77919304Speterstatic int 780254225Spetervs_deleteln(SCR *sp, int cnt) 78119304Speter{ 78219304Speter GS *gp; 78319304Speter size_t oldy, oldx; 78419304Speter 78519304Speter gp = sp->gp; 786254225Speter 787254225Speter /* If the screen is vertically split, we can't scroll it. */ 788254225Speter if (IS_VSPLIT(sp)) { 789254225Speter F_SET(sp, SC_SCR_REDRAW); 790254225Speter return (0); 791254225Speter } 792254225Speter 79319304Speter if (IS_ONELINE(sp)) 79419304Speter (void)gp->scr_clrtoeol(sp); 79519304Speter else { 79619304Speter (void)gp->scr_cursor(sp, &oldy, &oldx); 79719304Speter while (cnt--) { 79819304Speter (void)gp->scr_deleteln(sp); 79919304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 80019304Speter (void)gp->scr_insertln(sp); 80119304Speter (void)gp->scr_move(sp, oldy, oldx); 80219304Speter } 80319304Speter } 80419304Speter return (0); 80519304Speter} 80619304Speter 80719304Speter/* 80819304Speter * vs_sm_down -- 80919304Speter * Scroll the SMAP down count logical lines. 81019304Speter */ 81119304Speterstatic int 812254225Spetervs_sm_down(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp) 81319304Speter{ 81419304Speter SMAP *ssmp, s1, s2; 81519304Speter int cursor_set, ychanged, zset; 81619304Speter 81719304Speter /* Check to see if movement is possible. */ 81819304Speter if (HMAP->lno == 1 && 81919304Speter (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) && 82019304Speter (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) { 82119304Speter v_sof(sp, NULL); 82219304Speter return (1); 82319304Speter } 82419304Speter 82519304Speter /* 82619304Speter * Small screens: see vs_refresh.c section 6a. 82719304Speter * 82819304Speter * If it's a small screen, and the movement isn't larger than a 82919304Speter * screen, i.e some context will remain, open up the screen and 83019304Speter * display by scrolling. In this case, the cursor moves up one 83119304Speter * line for each line displayed. Otherwise, erase/compress and 83219304Speter * repaint, and move the cursor to the first line in the screen. 83319304Speter * Note, the ^B command is always in the latter case, for historical 83419304Speter * reasons. 83519304Speter */ 83619304Speter cursor_set = scmd == CNTRL_Y; 83719304Speter if (IS_SMALL(sp)) { 83819304Speter if (count >= sp->t_maxrows || scmd == CNTRL_B) { 83919304Speter s1 = HMAP[0]; 84019304Speter if (vs_sm_erase(sp)) 84119304Speter return (1); 84219304Speter for (; count--; s1 = s2) { 84319304Speter if (vs_sm_prev(sp, &s1, &s2)) 84419304Speter return (1); 84519304Speter if (s2.lno == 1 && 84619304Speter (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1)) 84719304Speter break; 84819304Speter } 84919304Speter HMAP[0] = s2; 85019304Speter if (vs_sm_fill(sp, OOBLNO, P_TOP)) 85119304Speter return (1); 85219304Speter return (vs_sm_position(sp, rp, 0, P_BOTTOM)); 85319304Speter } 85419304Speter cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp); 85519304Speter for (; count && 85619304Speter sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) { 85719304Speter if (HMAP->lno == 1 && 85819304Speter (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1)) 85919304Speter break; 86019304Speter ++TMAP; 86119304Speter if (vs_sm_1down(sp)) 86219304Speter return (1); 86319304Speter } 86419304Speter if (!cursor_set) { 86519304Speter rp->lno = ssmp->lno; 86619304Speter rp->cno = ssmp->c_sboff; 86719304Speter } 86819304Speter if (count == 0) 86919304Speter return (0); 87019304Speter } 87119304Speter 87219304Speter for (ychanged = zset = 0; count; --count) { 87319304Speter /* If the line doesn't exist, we're done. */ 87419304Speter if (HMAP->lno == 1 && 87519304Speter (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1)) 87619304Speter break; 87719304Speter 87819304Speter /* Scroll the screen and cursor down one logical line. */ 87919304Speter if (vs_sm_1down(sp)) 88019304Speter return (1); 88119304Speter switch (scmd) { 88219304Speter case CNTRL_Y: 88319304Speter if (smp < TMAP) 88419304Speter ++smp; 88519304Speter else 88619304Speter ychanged = 1; 88719304Speter break; 88819304Speter case Z_CARAT: 88919304Speter if (zset) { 89019304Speter if (smp < TMAP) 89119304Speter ++smp; 89219304Speter } else { 89319304Speter smp = HMAP; 89419304Speter zset = 1; 89519304Speter } 89619304Speter /* FALLTHROUGH */ 89719304Speter default: 89819304Speter break; 89919304Speter } 90019304Speter } 90119304Speter 90219304Speter if (scmd != CNTRL_Y && cursor_set) 90319304Speter return(0); 90419304Speter 90519304Speter switch (scmd) { 90619304Speter case CNTRL_B: 90719304Speter /* 90819304Speter * If there are more lines, the ^B command is positioned at 90919304Speter * the last line of the screen. However, the line may not 91019304Speter * exist. 91119304Speter */ 91219304Speter if (!count) { 91319304Speter for (smp = TMAP; smp > HMAP; --smp) 91419304Speter if (db_exist(sp, smp->lno)) 91519304Speter break; 91619304Speter break; 91719304Speter } 91819304Speter /* FALLTHROUGH */ 91919304Speter case CNTRL_U: 92019304Speter /* 92119304Speter * The ^B and ^U commands move the cursor towards SOF 92219304Speter * if there are more lines to move. 92319304Speter */ 92419304Speter if (count < smp - HMAP) 92519304Speter smp -= count; 92619304Speter else 92719304Speter smp = HMAP; 92819304Speter break; 92919304Speter case CNTRL_Y: 93019304Speter /* 93119304Speter * On a ^Y that was forced to change lines, try and keep the 93219304Speter * cursor as close as possible to the last position, but also 93319304Speter * set it up so that the next "real" movement will return the 93419304Speter * cursor to the closest position to the last real movement. 93519304Speter */ 93619304Speter if (ychanged) { 93719304Speter rp->lno = smp->lno; 93819304Speter rp->cno = vs_colpos(sp, smp->lno, 93919304Speter (O_ISSET(sp, O_LEFTRIGHT) ? 94019304Speter smp->coff : (smp->soff - 1) * sp->cols) + 94119304Speter sp->rcm % sp->cols); 94219304Speter } 94319304Speter return (0); 94419304Speter case Z_CARAT: 94519304Speter /* The z^ command moves the cursor to the first new line. */ 94619304Speter break; 94719304Speter default: 94819304Speter abort(); 94919304Speter } 95019304Speter 95119304Speter if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL)) 95219304Speter return (1); 95319304Speter rp->lno = smp->lno; 95490019Ssheldonh rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff; 95519304Speter return (0); 95619304Speter} 95719304Speter 95819304Speter/* 95919304Speter * vs_sm_erase -- 96019304Speter * Erase the small screen area for the scrolling functions. 96119304Speter */ 96219304Speterstatic int 963254225Spetervs_sm_erase(SCR *sp) 96419304Speter{ 96519304Speter GS *gp; 96619304Speter 96719304Speter gp = sp->gp; 96819304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 96919304Speter (void)gp->scr_clrtoeol(sp); 97019304Speter for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { 97119304Speter (void)gp->scr_move(sp, TMAP - HMAP, 0); 97219304Speter (void)gp->scr_clrtoeol(sp); 97319304Speter } 97419304Speter return (0); 97519304Speter} 97619304Speter 97719304Speter/* 97819304Speter * vs_sm_1down -- 97919304Speter * Scroll the SMAP down one. 98019304Speter * 98119304Speter * PUBLIC: int vs_sm_1down __P((SCR *)); 98219304Speter */ 98319304Speterint 984254225Spetervs_sm_1down(SCR *sp) 98519304Speter{ 98619304Speter /* 98719304Speter * Insert a line at the top of the screen. Shift the screen map 98819304Speter * down and display a new line at the top of the screen. 98919304Speter */ 99019304Speter (void)sp->gp->scr_move(sp, 0, 0); 99119304Speter if (vs_insertln(sp, 1)) 99219304Speter return (1); 99319304Speter 99419304Speter /* One-line screens can fail. */ 99519304Speter if (IS_ONELINE(sp)) { 99619304Speter if (vs_sm_prev(sp, HMAP, HMAP)) 99719304Speter return (1); 99819304Speter } else { 99919304Speter memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP)); 100019304Speter if (vs_sm_prev(sp, HMAP + 1, HMAP)) 100119304Speter return (1); 100219304Speter } 100319304Speter /* vs_sm_prev() flushed the cache. */ 100419304Speter return (vs_line(sp, HMAP, NULL, NULL)); 100519304Speter} 100619304Speter 100719304Speter/* 100819304Speter * vs_insertln -- 100919304Speter * Insert a line a la curses, make sure to put the information 101019304Speter * line and other screens back. 101119304Speter */ 101219304Speterstatic int 1013254225Spetervs_insertln(SCR *sp, int cnt) 101419304Speter{ 101519304Speter GS *gp; 101619304Speter size_t oldy, oldx; 101719304Speter 101819304Speter gp = sp->gp; 1019254225Speter 1020254225Speter /* If the screen is vertically split, we can't scroll it. */ 1021254225Speter if (IS_VSPLIT(sp)) { 1022254225Speter F_SET(sp, SC_SCR_REDRAW); 1023254225Speter return (0); 1024254225Speter } 1025254225Speter 102619304Speter if (IS_ONELINE(sp)) { 102719304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 102819304Speter (void)gp->scr_clrtoeol(sp); 102919304Speter } else { 103019304Speter (void)gp->scr_cursor(sp, &oldy, &oldx); 103119304Speter while (cnt--) { 103219304Speter (void)gp->scr_move(sp, LASTLINE(sp) - 1, 0); 103319304Speter (void)gp->scr_deleteln(sp); 103419304Speter (void)gp->scr_move(sp, oldy, oldx); 103519304Speter (void)gp->scr_insertln(sp); 103619304Speter } 103719304Speter } 103819304Speter return (0); 103919304Speter} 104019304Speter 104119304Speter/* 104219304Speter * vs_sm_next -- 104319304Speter * Fill in the next entry in the SMAP. 104419304Speter * 104519304Speter * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *)); 104619304Speter */ 104719304Speterint 1048254225Spetervs_sm_next(SCR *sp, SMAP *p, SMAP *t) 104919304Speter{ 105019304Speter size_t lcnt; 105119304Speter 105219304Speter SMAP_FLUSH(t); 105319304Speter if (O_ISSET(sp, O_LEFTRIGHT)) { 105419304Speter t->lno = p->lno + 1; 105519304Speter t->coff = p->coff; 105619304Speter } else { 105719304Speter lcnt = vs_screens(sp, p->lno, NULL); 105819304Speter if (lcnt == p->soff) { 105919304Speter t->lno = p->lno + 1; 106019304Speter t->soff = 1; 106119304Speter } else { 106219304Speter t->lno = p->lno; 106319304Speter t->soff = p->soff + 1; 106419304Speter } 106519304Speter } 106619304Speter return (0); 106719304Speter} 106819304Speter 106919304Speter/* 107019304Speter * vs_sm_prev -- 107119304Speter * Fill in the previous entry in the SMAP. 107219304Speter * 107319304Speter * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *)); 107419304Speter */ 107519304Speterint 1076254225Spetervs_sm_prev(SCR *sp, SMAP *p, SMAP *t) 107719304Speter{ 107819304Speter SMAP_FLUSH(t); 107919304Speter if (O_ISSET(sp, O_LEFTRIGHT)) { 108019304Speter t->lno = p->lno - 1; 108119304Speter t->coff = p->coff; 108219304Speter } else { 108319304Speter if (p->soff != 1) { 108419304Speter t->lno = p->lno; 108519304Speter t->soff = p->soff - 1; 108619304Speter } else { 108719304Speter t->lno = p->lno - 1; 108819304Speter t->soff = vs_screens(sp, t->lno, NULL); 108919304Speter } 109019304Speter } 109119304Speter return (t->lno == 0); 109219304Speter} 109319304Speter 109419304Speter/* 109519304Speter * vs_sm_cursor -- 109619304Speter * Return the SMAP entry referenced by the cursor. 109719304Speter * 109819304Speter * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **)); 109919304Speter */ 110019304Speterint 1101254225Spetervs_sm_cursor(SCR *sp, SMAP **smpp) 110219304Speter{ 110319304Speter SMAP *p; 110419304Speter 110519304Speter /* See if the cursor is not in the map. */ 110619304Speter if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) 110719304Speter return (1); 110819304Speter 110919304Speter /* Find the first occurence of the line. */ 111019304Speter for (p = HMAP; p->lno != sp->lno; ++p); 111119304Speter 111219304Speter /* Fill in the map information until we find the right line. */ 111319304Speter for (; p <= TMAP; ++p) { 111419304Speter /* Short lines are common and easy to detect. */ 111519304Speter if (p != TMAP && (p + 1)->lno != p->lno) { 111619304Speter *smpp = p; 111719304Speter return (0); 111819304Speter } 111919304Speter if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL)) 112019304Speter return (1); 112119304Speter if (p->c_eboff >= sp->cno) { 112219304Speter *smpp = p; 112319304Speter return (0); 112419304Speter } 112519304Speter } 112619304Speter 112719304Speter /* It was past the end of the map after all. */ 112819304Speter return (1); 112919304Speter} 113019304Speter 113119304Speter/* 113219304Speter * vs_sm_position -- 113319304Speter * Return the line/column of the top, middle or last line on the screen. 113419304Speter * (The vi H, M and L commands.) Here because only the screen routines 113519304Speter * know what's really out there. 113619304Speter * 113719304Speter * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t)); 113819304Speter */ 113919304Speterint 1140254225Spetervs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos) 114119304Speter{ 114219304Speter SMAP *smp; 114319304Speter recno_t last; 114419304Speter 114519304Speter switch (pos) { 114619304Speter case P_TOP: 114719304Speter /* 114819304Speter * !!! 114919304Speter * Historically, an invalid count to the H command failed. 115019304Speter * We do nothing special here, just making sure that H in 115119304Speter * an empty screen works. 115219304Speter */ 115319304Speter if (cnt > TMAP - HMAP) 115419304Speter goto sof; 115519304Speter smp = HMAP + cnt; 115619304Speter if (cnt && !db_exist(sp, smp->lno)) { 115719304Spetersof: msgq(sp, M_BERR, "220|Movement past the end-of-screen"); 115819304Speter return (1); 115919304Speter } 116019304Speter break; 116119304Speter case P_MIDDLE: 116219304Speter /* 116319304Speter * !!! 116419304Speter * Historically, a count to the M command was ignored. 116519304Speter * If the screen isn't filled, find the middle of what's 116619304Speter * real and move there. 116719304Speter */ 116819304Speter if (!db_exist(sp, TMAP->lno)) { 116919304Speter if (db_last(sp, &last)) 117019304Speter return (1); 117119304Speter for (smp = TMAP; smp->lno > last && smp > HMAP; --smp); 117219304Speter if (smp > HMAP) 117319304Speter smp -= (smp - HMAP) / 2; 117419304Speter } else 117519304Speter smp = (HMAP + (TMAP - HMAP) / 2) + cnt; 117619304Speter break; 117719304Speter case P_BOTTOM: 117819304Speter /* 117919304Speter * !!! 118019304Speter * Historically, an invalid count to the L command failed. 118119304Speter * If the screen isn't filled, find the bottom of what's 118219304Speter * real and try to offset from there. 118319304Speter */ 118419304Speter if (cnt > TMAP - HMAP) 118519304Speter goto eof; 118619304Speter smp = TMAP - cnt; 118719304Speter if (!db_exist(sp, smp->lno)) { 118819304Speter if (db_last(sp, &last)) 118919304Speter return (1); 119019304Speter for (; smp->lno > last && smp > HMAP; --smp); 119119304Speter if (cnt > smp - HMAP) { 119219304Spetereof: msgq(sp, M_BERR, 119319304Speter "221|Movement past the beginning-of-screen"); 119419304Speter return (1); 119519304Speter } 119619304Speter smp -= cnt; 119719304Speter } 119819304Speter break; 119919304Speter default: 120019304Speter abort(); 120119304Speter } 120219304Speter 120319304Speter /* Make sure that the cached information is valid. */ 120419304Speter if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL)) 120519304Speter return (1); 120619304Speter rp->lno = smp->lno; 120719304Speter rp->cno = smp->c_sboff; 120819304Speter 120919304Speter return (0); 121019304Speter} 121119304Speter 121219304Speter/* 121319304Speter * vs_sm_nlines -- 121419304Speter * Return the number of screen lines from an SMAP entry to the 121519304Speter * start of some file line, less than a maximum value. 121619304Speter * 121719304Speter * PUBLIC: recno_t vs_sm_nlines __P((SCR *, SMAP *, recno_t, size_t)); 121819304Speter */ 121919304Speterrecno_t 1220254225Spetervs_sm_nlines(SCR *sp, SMAP *from_sp, recno_t to_lno, size_t max) 122119304Speter{ 122219304Speter recno_t lno, lcnt; 122319304Speter 122419304Speter if (O_ISSET(sp, O_LEFTRIGHT)) 122519304Speter return (from_sp->lno > to_lno ? 122619304Speter from_sp->lno - to_lno : to_lno - from_sp->lno); 122719304Speter 122819304Speter if (from_sp->lno == to_lno) 122919304Speter return (from_sp->soff - 1); 123019304Speter 123119304Speter if (from_sp->lno > to_lno) { 123219304Speter lcnt = from_sp->soff - 1; /* Correct for off-by-one. */ 123319304Speter for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;) 123419304Speter lcnt += vs_screens(sp, lno, NULL); 123519304Speter } else { 123619304Speter lno = from_sp->lno; 123719304Speter lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1; 123819304Speter for (; ++lno < to_lno && lcnt <= max;) 123919304Speter lcnt += vs_screens(sp, lno, NULL); 124019304Speter } 124119304Speter return (lcnt); 124219304Speter} 1243