1 1.3 christos /* $NetBSD: vs_smap.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */ 2 1.1 christos /*- 3 1.1 christos * Copyright (c) 1993, 1994 4 1.1 christos * The Regents of the University of California. All rights reserved. 5 1.1 christos * Copyright (c) 1993, 1994, 1995, 1996 6 1.1 christos * Keith Bostic. All rights reserved. 7 1.1 christos * 8 1.1 christos * See the LICENSE file for redistribution information. 9 1.1 christos */ 10 1.1 christos 11 1.1 christos #include "config.h" 12 1.1 christos 13 1.3 christos #include <sys/cdefs.h> 14 1.3 christos #if 0 15 1.1 christos #ifndef lint 16 1.1 christos static const char sccsid[] = "Id: vs_smap.c,v 10.30 2002/01/19 21:59:07 skimo Exp (Berkeley) Date: 2002/01/19 21:59:07 "; 17 1.1 christos #endif /* not lint */ 18 1.3 christos #else 19 1.3 christos __RCSID("$NetBSD: vs_smap.c,v 1.3 2014/01/26 21:43:45 christos Exp $"); 20 1.3 christos #endif 21 1.1 christos 22 1.1 christos #include <sys/types.h> 23 1.1 christos #include <sys/queue.h> 24 1.1 christos #include <sys/time.h> 25 1.1 christos 26 1.1 christos #include <bitstring.h> 27 1.1 christos #include <limits.h> 28 1.1 christos #include <stdio.h> 29 1.1 christos #include <stdlib.h> 30 1.1 christos #include <string.h> 31 1.1 christos 32 1.1 christos #include "../common/common.h" 33 1.1 christos #include "vi.h" 34 1.1 christos 35 1.1 christos static int vs_deleteln __P((SCR *, int)); 36 1.1 christos static int vs_insertln __P((SCR *, int)); 37 1.1 christos static int vs_sm_delete __P((SCR *, db_recno_t)); 38 1.1 christos static int vs_sm_down __P((SCR *, MARK *, db_recno_t, scroll_t, SMAP *)); 39 1.1 christos static int vs_sm_erase __P((SCR *)); 40 1.1 christos static int vs_sm_insert __P((SCR *, db_recno_t)); 41 1.1 christos static int vs_sm_reset __P((SCR *, db_recno_t)); 42 1.1 christos static int vs_sm_up __P((SCR *, MARK *, db_recno_t, scroll_t, SMAP *)); 43 1.1 christos 44 1.1 christos /* 45 1.1 christos * vs_change -- 46 1.1 christos * Make a change to the screen. 47 1.1 christos * 48 1.1 christos * PUBLIC: int vs_change __P((SCR *, db_recno_t, lnop_t)); 49 1.1 christos */ 50 1.1 christos int 51 1.1 christos vs_change(SCR *sp, db_recno_t lno, lnop_t op) 52 1.1 christos { 53 1.1 christos VI_PRIVATE *vip; 54 1.1 christos SMAP *p; 55 1.1 christos size_t cnt, oldy, oldx; 56 1.1 christos 57 1.1 christos vip = VIP(sp); 58 1.1 christos 59 1.1 christos /* 60 1.1 christos * XXX 61 1.1 christos * Very nasty special case. The historic vi code displays a single 62 1.1 christos * space (or a '$' if the list option is set) for the first line in 63 1.1 christos * an "empty" file. If we "insert" a line, that line gets scrolled 64 1.1 christos * down, not repainted, so it's incorrect when we refresh the screen. 65 1.1 christos * The vi text input functions detect it explicitly and don't insert 66 1.1 christos * a new line. 67 1.1 christos * 68 1.1 christos * Check for line #2 before going to the end of the file. 69 1.1 christos */ 70 1.1 christos if (((op == LINE_APPEND && lno == 0) || 71 1.1 christos (op == LINE_INSERT && lno == 1)) && 72 1.1 christos !db_exist(sp, 2)) { 73 1.1 christos lno = 1; 74 1.1 christos op = LINE_RESET; 75 1.1 christos } 76 1.1 christos 77 1.1 christos /* Appending is the same as inserting, if the line is incremented. */ 78 1.1 christos if (op == LINE_APPEND) { 79 1.1 christos ++lno; 80 1.1 christos op = LINE_INSERT; 81 1.1 christos } 82 1.1 christos 83 1.1 christos /* Ignore the change if the line is after the map. */ 84 1.1 christos if (lno > TMAP->lno) 85 1.1 christos return (0); 86 1.1 christos 87 1.1 christos /* 88 1.1 christos * If the line is before the map, and it's a decrement, decrement 89 1.1 christos * the map. If it's an increment, increment the map. Otherwise, 90 1.1 christos * ignore it. 91 1.1 christos */ 92 1.1 christos if (lno < HMAP->lno) { 93 1.1 christos switch (op) { 94 1.1 christos case LINE_APPEND: 95 1.1 christos abort(); 96 1.1 christos /* NOTREACHED */ 97 1.1 christos case LINE_DELETE: 98 1.1 christos for (p = HMAP, cnt = sp->t_rows; cnt--; ++p) 99 1.1 christos --p->lno; 100 1.1 christos if (sp->lno >= lno) 101 1.1 christos --sp->lno; 102 1.1 christos F_SET(vip, VIP_N_RENUMBER); 103 1.1 christos break; 104 1.1 christos case LINE_INSERT: 105 1.1 christos for (p = HMAP, cnt = sp->t_rows; cnt--; ++p) 106 1.1 christos ++p->lno; 107 1.1 christos if (sp->lno >= lno) 108 1.1 christos ++sp->lno; 109 1.1 christos F_SET(vip, VIP_N_RENUMBER); 110 1.1 christos break; 111 1.1 christos case LINE_RESET: 112 1.1 christos break; 113 1.1 christos } 114 1.1 christos return (0); 115 1.1 christos } 116 1.1 christos 117 1.1 christos F_SET(vip, VIP_N_REFRESH); 118 1.1 christos 119 1.1 christos /* 120 1.1 christos * Invalidate the line size cache, and invalidate the cursor if it's 121 1.1 christos * on this line, 122 1.1 christos */ 123 1.1 christos VI_SCR_CFLUSH(vip); 124 1.1 christos if (sp->lno == lno) 125 1.1 christos F_SET(vip, VIP_CUR_INVALID); 126 1.1 christos 127 1.1 christos /* 128 1.1 christos * If ex modifies the screen after ex output is already on the screen 129 1.1 christos * or if we've switched into ex canonical mode, don't touch it -- we'll 130 1.1 christos * get scrolling wrong, at best. 131 1.1 christos */ 132 1.1 christos if (!F_ISSET(sp, SC_TINPUT_INFO) && 133 1.1 christos (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) { 134 1.1 christos F_SET(vip, VIP_N_EX_REDRAW); 135 1.1 christos return (0); 136 1.1 christos } 137 1.1 christos 138 1.1 christos /* Save and restore the cursor for these routines. */ 139 1.1 christos (void)sp->gp->scr_cursor(sp, &oldy, &oldx); 140 1.1 christos 141 1.1 christos switch (op) { 142 1.1 christos case LINE_DELETE: 143 1.1 christos if (vs_sm_delete(sp, lno)) 144 1.1 christos return (1); 145 1.1 christos if (sp->lno > lno) 146 1.1 christos --sp->lno; 147 1.1 christos F_SET(vip, VIP_N_RENUMBER); 148 1.1 christos break; 149 1.1 christos case LINE_INSERT: 150 1.1 christos if (vs_sm_insert(sp, lno)) 151 1.1 christos return (1); 152 1.1 christos if (sp->lno > lno) 153 1.1 christos ++sp->lno; 154 1.1 christos F_SET(vip, VIP_N_RENUMBER); 155 1.1 christos break; 156 1.1 christos case LINE_RESET: 157 1.1 christos if (vs_sm_reset(sp, lno)) 158 1.1 christos return (1); 159 1.1 christos break; 160 1.1 christos default: 161 1.1 christos abort(); 162 1.1 christos } 163 1.1 christos 164 1.1 christos (void)sp->gp->scr_move(sp, oldy, oldx); 165 1.1 christos return (0); 166 1.1 christos } 167 1.1 christos 168 1.1 christos /* 169 1.1 christos * vs_sm_fill -- 170 1.1 christos * Fill in the screen map, placing the specified line at the 171 1.1 christos * right position. There isn't any way to tell if an SMAP 172 1.1 christos * entry has been filled in, so this routine had better be 173 1.1 christos * called with P_FILL set before anything else is done. 174 1.1 christos * 175 1.1 christos * !!! 176 1.1 christos * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP 177 1.1 christos * slot is already filled in, P_BOTTOM means that the TMAP slot is 178 1.1 christos * already filled in, and we just finish up the job. 179 1.1 christos * 180 1.1 christos * PUBLIC: int vs_sm_fill __P((SCR *, db_recno_t, pos_t)); 181 1.1 christos */ 182 1.1 christos int 183 1.1 christos vs_sm_fill(SCR *sp, db_recno_t lno, pos_t pos) 184 1.1 christos { 185 1.1 christos SMAP *p, tmp; 186 1.1 christos size_t cnt; 187 1.1 christos 188 1.1 christos /* Flush all cached information from the SMAP. */ 189 1.1 christos for (p = HMAP, cnt = sp->t_rows; cnt--; ++p) 190 1.1 christos SMAP_FLUSH(p); 191 1.1 christos 192 1.1 christos /* 193 1.1 christos * If the map is filled, the screen must be redrawn. 194 1.1 christos * 195 1.1 christos * XXX 196 1.1 christos * This is a bug. We should try and figure out if the desired line 197 1.1 christos * is already in the map or close by -- scrolling the screen would 198 1.1 christos * be a lot better than redrawing. 199 1.1 christos */ 200 1.1 christos F_SET(sp, SC_SCR_REDRAW); 201 1.1 christos 202 1.1 christos switch (pos) { 203 1.1 christos case P_FILL: 204 1.1 christos tmp.lno = 1; 205 1.1 christos tmp.coff = 0; 206 1.1 christos tmp.soff = 1; 207 1.1 christos 208 1.1 christos /* See if less than half a screen from the top. */ 209 1.1 christos if (vs_sm_nlines(sp, 210 1.1 christos &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { 211 1.1 christos lno = 1; 212 1.1 christos goto top; 213 1.1 christos } 214 1.1 christos 215 1.1 christos /* See if less than half a screen from the bottom. */ 216 1.1 christos if (db_last(sp, &tmp.lno)) 217 1.1 christos return (1); 218 1.1 christos tmp.coff = 0; 219 1.1 christos tmp.soff = vs_screens(sp, tmp.lno, NULL); 220 1.1 christos if (vs_sm_nlines(sp, 221 1.1 christos &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { 222 1.1 christos TMAP->lno = tmp.lno; 223 1.1 christos TMAP->coff = tmp.coff; 224 1.1 christos TMAP->soff = tmp.soff; 225 1.1 christos goto bottom; 226 1.1 christos } 227 1.1 christos goto middle; 228 1.1 christos case P_TOP: 229 1.1 christos if (lno != OOBLNO) { 230 1.1 christos top: HMAP->lno = lno; 231 1.1 christos HMAP->coff = 0; 232 1.1 christos HMAP->soff = 1; 233 1.1 christos } else { 234 1.1 christos /* 235 1.1 christos * If number of lines HMAP->lno (top line) spans 236 1.1 christos * changed due to, say reformatting, and now is 237 1.1 christos * fewer than HMAP->soff, reset so the line is 238 1.1 christos * redrawn at the top of the screen. 239 1.1 christos */ 240 1.1 christos cnt = vs_screens(sp, HMAP->lno, NULL); 241 1.1 christos if (cnt < HMAP->soff) 242 1.1 christos HMAP->soff = 1; 243 1.1 christos } 244 1.1 christos /* If we fail, just punt. */ 245 1.1 christos for (p = HMAP, cnt = sp->t_rows; --cnt; ++p) 246 1.1 christos if (vs_sm_next(sp, p, p + 1)) 247 1.1 christos goto err; 248 1.1 christos break; 249 1.1 christos case P_MIDDLE: 250 1.1 christos /* If we fail, guess that the file is too small. */ 251 1.1 christos middle: p = HMAP + sp->t_rows / 2; 252 1.1 christos p->lno = lno; 253 1.1 christos p->coff = 0; 254 1.1 christos p->soff = 1; 255 1.1 christos for (; p > HMAP; --p) 256 1.1 christos if (vs_sm_prev(sp, p, p - 1)) { 257 1.1 christos lno = 1; 258 1.1 christos goto top; 259 1.1 christos } 260 1.1 christos 261 1.1 christos /* If we fail, just punt. */ 262 1.1 christos p = HMAP + sp->t_rows / 2; 263 1.1 christos for (; p < TMAP; ++p) 264 1.1 christos if (vs_sm_next(sp, p, p + 1)) 265 1.1 christos goto err; 266 1.1 christos break; 267 1.1 christos case P_BOTTOM: 268 1.1 christos if (lno != OOBLNO) { 269 1.1 christos TMAP->lno = lno; 270 1.1 christos TMAP->coff = 0; 271 1.1 christos TMAP->soff = vs_screens(sp, lno, NULL); 272 1.1 christos } 273 1.1 christos /* If we fail, guess that the file is too small. */ 274 1.1 christos bottom: for (p = TMAP; p > HMAP; --p) 275 1.1 christos if (vs_sm_prev(sp, p, p - 1)) { 276 1.1 christos lno = 1; 277 1.1 christos goto top; 278 1.1 christos } 279 1.1 christos break; 280 1.1 christos default: 281 1.1 christos abort(); 282 1.1 christos } 283 1.1 christos return (0); 284 1.1 christos 285 1.1 christos /* 286 1.1 christos * Try and put *something* on the screen. If this fails, we have a 287 1.1 christos * serious hard error. 288 1.1 christos */ 289 1.1 christos err: HMAP->lno = 1; 290 1.1 christos HMAP->coff = 0; 291 1.1 christos HMAP->soff = 1; 292 1.1 christos for (p = HMAP; p < TMAP; ++p) 293 1.1 christos if (vs_sm_next(sp, p, p + 1)) 294 1.1 christos return (1); 295 1.1 christos return (0); 296 1.1 christos } 297 1.1 christos 298 1.1 christos /* 299 1.1 christos * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the 300 1.1 christos * screen contains only a single line (whether because the screen is small 301 1.1 christos * or the line large), it gets fairly exciting. Skip the fun, set a flag 302 1.1 christos * so the screen map is refilled and the screen redrawn, and return. This 303 1.1 christos * is amazingly slow, but it's not clear that anyone will care. 304 1.1 christos */ 305 1.1 christos #define HANDLE_WEIRDNESS(cnt) { \ 306 1.1 christos if (cnt >= sp->t_rows) { \ 307 1.1 christos F_SET(sp, SC_SCR_REFORMAT); \ 308 1.1 christos return (0); \ 309 1.1 christos } \ 310 1.1 christos } 311 1.1 christos 312 1.1 christos /* 313 1.1 christos * vs_sm_delete -- 314 1.1 christos * Delete a line out of the SMAP. 315 1.1 christos */ 316 1.1 christos static int 317 1.1 christos vs_sm_delete(SCR *sp, db_recno_t lno) 318 1.1 christos { 319 1.1 christos SMAP *p, *t; 320 1.1 christos size_t cnt_orig; 321 1.1 christos 322 1.1 christos /* 323 1.1 christos * Find the line in the map, and count the number of screen lines 324 1.1 christos * which display any part of the deleted line. 325 1.1 christos */ 326 1.1 christos for (p = HMAP; p->lno != lno; ++p); 327 1.1 christos if (O_ISSET(sp, O_LEFTRIGHT)) 328 1.1 christos cnt_orig = 1; 329 1.1 christos else 330 1.1 christos for (cnt_orig = 1, t = p + 1; 331 1.1 christos t <= TMAP && t->lno == lno; ++cnt_orig, ++t); 332 1.1 christos 333 1.1 christos HANDLE_WEIRDNESS(cnt_orig); 334 1.1 christos 335 1.1 christos /* Delete that many lines from the screen. */ 336 1.1 christos (void)sp->gp->scr_move(sp, p - HMAP, 0); 337 1.1 christos if (vs_deleteln(sp, cnt_orig)) 338 1.1 christos return (1); 339 1.1 christos 340 1.1 christos /* Shift the screen map up. */ 341 1.1 christos memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); 342 1.1 christos 343 1.1 christos /* Decrement the line numbers for the rest of the map. */ 344 1.1 christos for (t = TMAP - cnt_orig; p <= t; ++p) 345 1.1 christos --p->lno; 346 1.1 christos 347 1.1 christos /* Display the new lines. */ 348 1.1 christos for (p = TMAP - cnt_orig;;) { 349 1.1 christos if (p < TMAP && vs_sm_next(sp, p, p + 1)) 350 1.1 christos return (1); 351 1.1 christos /* vs_sm_next() flushed the cache. */ 352 1.1 christos if (vs_line(sp, ++p, NULL, NULL)) 353 1.1 christos return (1); 354 1.1 christos if (p == TMAP) 355 1.1 christos break; 356 1.1 christos } 357 1.1 christos return (0); 358 1.1 christos } 359 1.1 christos 360 1.1 christos /* 361 1.1 christos * vs_sm_insert -- 362 1.1 christos * Insert a line into the SMAP. 363 1.1 christos */ 364 1.1 christos static int 365 1.1 christos vs_sm_insert(SCR *sp, db_recno_t lno) 366 1.1 christos { 367 1.1 christos SMAP *p, *t; 368 1.1 christos size_t cnt_orig, cnt, coff; 369 1.1 christos 370 1.1 christos /* Save the offset. */ 371 1.1 christos coff = HMAP->coff; 372 1.1 christos 373 1.1 christos /* 374 1.1 christos * Find the line in the map, find out how many screen lines 375 1.1 christos * needed to display the line. 376 1.1 christos */ 377 1.1 christos for (p = HMAP; p->lno != lno; ++p); 378 1.1 christos 379 1.1 christos cnt_orig = vs_screens(sp, lno, NULL); 380 1.1 christos HANDLE_WEIRDNESS(cnt_orig); 381 1.1 christos 382 1.1 christos /* 383 1.1 christos * The lines left in the screen override the number of screen 384 1.1 christos * lines in the inserted line. 385 1.1 christos */ 386 1.1 christos cnt = (TMAP - p) + 1; 387 1.1 christos if (cnt_orig > cnt) 388 1.1 christos cnt_orig = cnt; 389 1.1 christos 390 1.1 christos /* Push down that many lines. */ 391 1.1 christos (void)sp->gp->scr_move(sp, p - HMAP, 0); 392 1.1 christos if (vs_insertln(sp, cnt_orig)) 393 1.1 christos return (1); 394 1.1 christos 395 1.1 christos /* Shift the screen map down. */ 396 1.1 christos memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); 397 1.1 christos 398 1.1 christos /* Increment the line numbers for the rest of the map. */ 399 1.1 christos for (t = p + cnt_orig; t <= TMAP; ++t) 400 1.1 christos ++t->lno; 401 1.1 christos 402 1.1 christos /* Fill in the SMAP for the new lines, and display. */ 403 1.1 christos for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) { 404 1.1 christos t->lno = lno; 405 1.1 christos t->coff = coff; 406 1.1 christos t->soff = cnt; 407 1.1 christos SMAP_FLUSH(t); 408 1.1 christos if (vs_line(sp, t, NULL, NULL)) 409 1.1 christos return (1); 410 1.1 christos } 411 1.1 christos return (0); 412 1.1 christos } 413 1.1 christos 414 1.1 christos /* 415 1.1 christos * vs_sm_reset -- 416 1.1 christos * Reset a line in the SMAP. 417 1.1 christos */ 418 1.1 christos static int 419 1.1 christos vs_sm_reset(SCR *sp, db_recno_t lno) 420 1.1 christos { 421 1.1 christos SMAP *p, *t; 422 1.1 christos size_t cnt_orig, cnt_new, cnt, diff; 423 1.1 christos 424 1.1 christos /* 425 1.1 christos * See if the number of on-screen rows taken up by the old display 426 1.1 christos * for the line is the same as the number needed for the new one. 427 1.1 christos * If so, repaint, otherwise do it the hard way. 428 1.1 christos */ 429 1.1 christos for (p = HMAP; p->lno != lno; ++p); 430 1.1 christos if (O_ISSET(sp, O_LEFTRIGHT)) { 431 1.1 christos t = p; 432 1.1 christos cnt_orig = cnt_new = 1; 433 1.1 christos } else { 434 1.1 christos for (cnt_orig = 0, 435 1.1 christos t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); 436 1.1 christos cnt_new = vs_screens(sp, lno, NULL); 437 1.1 christos } 438 1.1 christos 439 1.1 christos HANDLE_WEIRDNESS(cnt_orig); 440 1.1 christos 441 1.1 christos if (cnt_orig == cnt_new) { 442 1.1 christos do { 443 1.1 christos SMAP_FLUSH(p); 444 1.1 christos if (vs_line(sp, p, NULL, NULL)) 445 1.1 christos return (1); 446 1.1 christos } while (++p < t); 447 1.1 christos return (0); 448 1.1 christos } 449 1.1 christos 450 1.1 christos if (cnt_orig < cnt_new) { 451 1.1 christos /* Get the difference. */ 452 1.1 christos diff = cnt_new - cnt_orig; 453 1.1 christos 454 1.1 christos /* 455 1.1 christos * The lines left in the screen override the number of screen 456 1.1 christos * lines in the inserted line. 457 1.1 christos */ 458 1.1 christos cnt = (TMAP - p) + 1; 459 1.1 christos if (diff > cnt) 460 1.1 christos diff = cnt; 461 1.1 christos 462 1.1 christos /* If there are any following lines, push them down. */ 463 1.1 christos if (cnt > 1) { 464 1.1 christos (void)sp->gp->scr_move(sp, p - HMAP, 0); 465 1.1 christos if (vs_insertln(sp, diff)) 466 1.1 christos return (1); 467 1.1 christos 468 1.1 christos /* Shift the screen map down. */ 469 1.1 christos memmove(p + diff, p, 470 1.1 christos (((TMAP - p) - diff) + 1) * sizeof(SMAP)); 471 1.1 christos } 472 1.1 christos 473 1.1 christos /* Fill in the SMAP for the replaced line, and display. */ 474 1.1 christos for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) { 475 1.1 christos t->lno = lno; 476 1.1 christos t->soff = cnt; 477 1.1 christos SMAP_FLUSH(t); 478 1.1 christos if (vs_line(sp, t, NULL, NULL)) 479 1.1 christos return (1); 480 1.1 christos } 481 1.1 christos } else { 482 1.1 christos /* Get the difference. */ 483 1.1 christos diff = cnt_orig - cnt_new; 484 1.1 christos 485 1.1 christos /* Delete that many lines from the screen. */ 486 1.1 christos (void)sp->gp->scr_move(sp, p - HMAP, 0); 487 1.1 christos if (vs_deleteln(sp, diff)) 488 1.1 christos return (1); 489 1.1 christos 490 1.1 christos /* Shift the screen map up. */ 491 1.1 christos memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); 492 1.1 christos 493 1.1 christos /* Fill in the SMAP for the replaced line, and display. */ 494 1.1 christos for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) { 495 1.1 christos t->lno = lno; 496 1.1 christos t->soff = cnt; 497 1.1 christos SMAP_FLUSH(t); 498 1.1 christos if (vs_line(sp, t, NULL, NULL)) 499 1.1 christos return (1); 500 1.1 christos } 501 1.1 christos 502 1.1 christos /* Display the new lines at the bottom of the screen. */ 503 1.1 christos for (t = TMAP - diff;;) { 504 1.1 christos if (t < TMAP && vs_sm_next(sp, t, t + 1)) 505 1.1 christos return (1); 506 1.1 christos /* vs_sm_next() flushed the cache. */ 507 1.1 christos if (vs_line(sp, ++t, NULL, NULL)) 508 1.1 christos return (1); 509 1.1 christos if (t == TMAP) 510 1.1 christos break; 511 1.1 christos } 512 1.1 christos } 513 1.1 christos return (0); 514 1.1 christos } 515 1.1 christos 516 1.1 christos /* 517 1.1 christos * vs_sm_scroll 518 1.1 christos * Scroll the SMAP up/down count logical lines. Different 519 1.1 christos * semantics based on the vi command, *sigh*. 520 1.1 christos * 521 1.1 christos * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, db_recno_t, scroll_t)); 522 1.1 christos */ 523 1.1 christos int 524 1.1 christos vs_sm_scroll(SCR *sp, MARK *rp, db_recno_t count, scroll_t scmd) 525 1.1 christos { 526 1.1 christos SMAP *smp; 527 1.1 christos 528 1.1 christos /* 529 1.1 christos * Invalidate the cursor. The line is probably going to change, 530 1.1 christos * (although for ^E and ^Y it may not). In any case, the scroll 531 1.1 christos * routines move the cursor to draw things. 532 1.1 christos */ 533 1.1 christos F_SET(VIP(sp), VIP_CUR_INVALID); 534 1.1 christos 535 1.1 christos /* Find the cursor in the screen. */ 536 1.1 christos if (vs_sm_cursor(sp, &smp)) 537 1.1 christos return (1); 538 1.1 christos 539 1.1 christos switch (scmd) { 540 1.1 christos case CNTRL_B: 541 1.1 christos case CNTRL_U: 542 1.1 christos case CNTRL_Y: 543 1.1 christos case Z_CARAT: 544 1.1 christos if (vs_sm_down(sp, rp, count, scmd, smp)) 545 1.1 christos return (1); 546 1.1 christos break; 547 1.1 christos case CNTRL_D: 548 1.1 christos case CNTRL_E: 549 1.1 christos case CNTRL_F: 550 1.1 christos case Z_PLUS: 551 1.1 christos if (vs_sm_up(sp, rp, count, scmd, smp)) 552 1.1 christos return (1); 553 1.1 christos break; 554 1.1 christos default: 555 1.1 christos abort(); 556 1.1 christos } 557 1.1 christos 558 1.1 christos /* 559 1.1 christos * !!! 560 1.1 christos * If we're at the start of a line, go for the first non-blank. 561 1.1 christos * This makes it look like the old vi, even though we're moving 562 1.1 christos * around by logical lines, not physical ones. 563 1.1 christos * 564 1.1 christos * XXX 565 1.1 christos * In the presence of a long line, which has more than a screen 566 1.1 christos * width of leading spaces, this code can cause a cursor warp. 567 1.1 christos * Live with it. 568 1.1 christos */ 569 1.1 christos if (scmd != CNTRL_E && scmd != CNTRL_Y && 570 1.1 christos rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno)) 571 1.1 christos return (1); 572 1.1 christos 573 1.1 christos return (0); 574 1.1 christos } 575 1.1 christos 576 1.1 christos /* 577 1.1 christos * vs_sm_up -- 578 1.1 christos * Scroll the SMAP up count logical lines. 579 1.1 christos */ 580 1.1 christos static int 581 1.1 christos vs_sm_up(SCR *sp, MARK *rp, db_recno_t count, scroll_t scmd, SMAP *smp) 582 1.1 christos { 583 1.1 christos int cursor_set, echanged, zset; 584 1.1 christos SMAP *ssmp, s1, s2; 585 1.1 christos 586 1.1 christos /* 587 1.1 christos * Check to see if movement is possible. 588 1.1 christos * 589 1.1 christos * Get the line after the map. If that line is a new one (and if 590 1.1 christos * O_LEFTRIGHT option is set, this has to be true), and the next 591 1.1 christos * line doesn't exist, and the cursor doesn't move, or the cursor 592 1.1 christos * isn't even on the screen, or the cursor is already at the last 593 1.1 christos * line in the map, it's an error. If that test succeeded because 594 1.1 christos * the cursor wasn't at the end of the map, test to see if the map 595 1.1 christos * is mostly empty. 596 1.1 christos */ 597 1.1 christos if (vs_sm_next(sp, TMAP, &s1)) 598 1.1 christos return (1); 599 1.1 christos if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) { 600 1.1 christos if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) { 601 1.1 christos v_eof(sp, NULL); 602 1.1 christos return (1); 603 1.1 christos } 604 1.1 christos if (vs_sm_next(sp, smp, &s1)) 605 1.1 christos return (1); 606 1.1 christos if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) { 607 1.1 christos v_eof(sp, NULL); 608 1.1 christos return (1); 609 1.1 christos } 610 1.1 christos } 611 1.1 christos 612 1.1 christos /* 613 1.1 christos * Small screens: see vs_refresh.c section 6a. 614 1.1 christos * 615 1.1 christos * If it's a small screen, and the movement isn't larger than a 616 1.1 christos * screen, i.e some context will remain, open up the screen and 617 1.1 christos * display by scrolling. In this case, the cursor moves down one 618 1.1 christos * line for each line displayed. Otherwise, erase/compress and 619 1.1 christos * repaint, and move the cursor to the first line in the screen. 620 1.1 christos * Note, the ^F command is always in the latter case, for historical 621 1.1 christos * reasons. 622 1.1 christos */ 623 1.1 christos cursor_set = 0; 624 1.1 christos if (IS_SMALL(sp)) { 625 1.1 christos if (count >= sp->t_maxrows || scmd == CNTRL_F) { 626 1.1 christos s1 = TMAP[0]; 627 1.1 christos if (vs_sm_erase(sp)) 628 1.1 christos return (1); 629 1.1 christos for (; count--; s1 = s2) { 630 1.1 christos if (vs_sm_next(sp, &s1, &s2)) 631 1.1 christos return (1); 632 1.1 christos if (s2.lno != s1.lno && !db_exist(sp, s2.lno)) 633 1.1 christos break; 634 1.1 christos } 635 1.1 christos TMAP[0] = s2; 636 1.1 christos if (vs_sm_fill(sp, OOBLNO, P_BOTTOM)) 637 1.1 christos return (1); 638 1.1 christos return (vs_sm_position(sp, rp, 0, P_TOP)); 639 1.1 christos } 640 1.1 christos cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp); 641 1.1 christos for (; count && 642 1.1 christos sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) { 643 1.1 christos if (vs_sm_next(sp, TMAP, &s1)) 644 1.1 christos return (1); 645 1.1 christos if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno)) 646 1.1 christos break; 647 1.1 christos *++TMAP = s1; 648 1.1 christos /* vs_sm_next() flushed the cache. */ 649 1.1 christos if (vs_line(sp, TMAP, NULL, NULL)) 650 1.1 christos return (1); 651 1.1 christos 652 1.1 christos if (!cursor_set) 653 1.1 christos ++ssmp; 654 1.1 christos } 655 1.1 christos if (!cursor_set) { 656 1.1 christos rp->lno = ssmp->lno; 657 1.1 christos rp->cno = ssmp->c_sboff; 658 1.1 christos } 659 1.1 christos if (count == 0) 660 1.1 christos return (0); 661 1.1 christos } 662 1.1 christos 663 1.1 christos for (echanged = zset = 0; count; --count) { 664 1.1 christos /* Decide what would show up on the screen. */ 665 1.1 christos if (vs_sm_next(sp, TMAP, &s1)) 666 1.1 christos return (1); 667 1.1 christos 668 1.1 christos /* If the line doesn't exist, we're done. */ 669 1.1 christos if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno)) 670 1.1 christos break; 671 1.1 christos 672 1.1 christos /* Scroll the screen cursor up one logical line. */ 673 1.1 christos if (vs_sm_1up(sp)) 674 1.1 christos return (1); 675 1.1 christos switch (scmd) { 676 1.1 christos case CNTRL_E: 677 1.1 christos if (smp > HMAP) 678 1.1 christos --smp; 679 1.1 christos else 680 1.1 christos echanged = 1; 681 1.1 christos break; 682 1.1 christos case Z_PLUS: 683 1.1 christos if (zset) { 684 1.1 christos if (smp > HMAP) 685 1.1 christos --smp; 686 1.1 christos } else { 687 1.1 christos smp = TMAP; 688 1.1 christos zset = 1; 689 1.1 christos } 690 1.1 christos /* FALLTHROUGH */ 691 1.1 christos default: 692 1.1 christos break; 693 1.1 christos } 694 1.1 christos } 695 1.1 christos 696 1.1 christos if (cursor_set) 697 1.1 christos return(0); 698 1.1 christos 699 1.1 christos switch (scmd) { 700 1.1 christos case CNTRL_E: 701 1.1 christos /* 702 1.1 christos * On a ^E that was forced to change lines, try and keep the 703 1.1 christos * cursor as close as possible to the last position, but also 704 1.1 christos * set it up so that the next "real" movement will return the 705 1.1 christos * cursor to the closest position to the last real movement. 706 1.1 christos */ 707 1.1 christos if (echanged) { 708 1.1 christos rp->lno = smp->lno; 709 1.1 christos rp->cno = vs_colpos(sp, smp->lno, 710 1.1 christos (O_ISSET(sp, O_LEFTRIGHT) ? 711 1.1 christos smp->coff : (smp->soff - 1) * sp->cols) + 712 1.1 christos sp->rcm % sp->cols); 713 1.1 christos } 714 1.1 christos return (0); 715 1.1 christos case CNTRL_F: 716 1.1 christos /* 717 1.1 christos * If there are more lines, the ^F command is positioned at 718 1.1 christos * the first line of the screen. 719 1.1 christos */ 720 1.1 christos if (!count) { 721 1.1 christos smp = HMAP; 722 1.1 christos break; 723 1.1 christos } 724 1.1 christos /* FALLTHROUGH */ 725 1.1 christos case CNTRL_D: 726 1.1 christos /* 727 1.1 christos * The ^D and ^F commands move the cursor towards EOF 728 1.1 christos * if there are more lines to move. Check to be sure 729 1.1 christos * the lines actually exist. (They may not if the 730 1.1 christos * file is smaller than the screen.) 731 1.1 christos */ 732 1.1 christos for (; count; --count, ++smp) 733 1.1 christos if (smp == TMAP || !db_exist(sp, smp[1].lno)) 734 1.1 christos break; 735 1.1 christos break; 736 1.1 christos case Z_PLUS: 737 1.1 christos /* The z+ command moves the cursor to the first new line. */ 738 1.1 christos break; 739 1.1 christos default: 740 1.1 christos abort(); 741 1.1 christos } 742 1.1 christos 743 1.1 christos if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL)) 744 1.1 christos return (1); 745 1.1 christos rp->lno = smp->lno; 746 1.1 christos rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff; 747 1.1 christos return (0); 748 1.1 christos } 749 1.1 christos 750 1.1 christos /* 751 1.1 christos * vs_sm_1up -- 752 1.1 christos * Scroll the SMAP up one. 753 1.1 christos * 754 1.1 christos * PUBLIC: int vs_sm_1up __P((SCR *)); 755 1.1 christos */ 756 1.1 christos int 757 1.1 christos vs_sm_1up(SCR *sp) 758 1.1 christos { 759 1.1 christos /* 760 1.1 christos * Delete the top line of the screen. Shift the screen map 761 1.1 christos * up and display a new line at the bottom of the screen. 762 1.1 christos */ 763 1.1 christos (void)sp->gp->scr_move(sp, 0, 0); 764 1.1 christos if (vs_deleteln(sp, 1)) 765 1.1 christos return (1); 766 1.1 christos 767 1.1 christos /* One-line screens can fail. */ 768 1.1 christos if (IS_ONELINE(sp)) { 769 1.1 christos if (vs_sm_next(sp, TMAP, TMAP)) 770 1.1 christos return (1); 771 1.1 christos } else { 772 1.1 christos memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP)); 773 1.1 christos if (vs_sm_next(sp, TMAP - 1, TMAP)) 774 1.1 christos return (1); 775 1.1 christos } 776 1.1 christos /* vs_sm_next() flushed the cache. */ 777 1.1 christos return (vs_line(sp, TMAP, NULL, NULL)); 778 1.1 christos } 779 1.1 christos 780 1.1 christos /* 781 1.1 christos * vs_deleteln -- 782 1.1 christos * Delete a line a la curses, make sure to put the information 783 1.1 christos * line and other screens back. 784 1.1 christos */ 785 1.1 christos static int 786 1.1 christos vs_deleteln(SCR *sp, int cnt) 787 1.1 christos { 788 1.1 christos GS *gp; 789 1.1 christos size_t oldy, oldx; 790 1.1 christos 791 1.1 christos gp = sp->gp; 792 1.1 christos 793 1.1 christos /* If the screen is vertically split, we can't scroll it. */ 794 1.1 christos if (IS_VSPLIT(sp)) { 795 1.1 christos F_SET(sp, SC_SCR_REDRAW); 796 1.1 christos return (0); 797 1.1 christos } 798 1.1 christos 799 1.1 christos if (IS_ONELINE(sp)) 800 1.1 christos (void)gp->scr_clrtoeol(sp); 801 1.1 christos else { 802 1.1 christos (void)gp->scr_cursor(sp, &oldy, &oldx); 803 1.1 christos while (cnt--) { 804 1.1 christos (void)gp->scr_deleteln(sp); 805 1.1 christos (void)gp->scr_move(sp, LASTLINE(sp), 0); 806 1.1 christos (void)gp->scr_insertln(sp); 807 1.1 christos (void)gp->scr_move(sp, oldy, oldx); 808 1.1 christos } 809 1.1 christos } 810 1.1 christos return (0); 811 1.1 christos } 812 1.1 christos 813 1.1 christos /* 814 1.1 christos * vs_sm_down -- 815 1.1 christos * Scroll the SMAP down count logical lines. 816 1.1 christos */ 817 1.1 christos static int 818 1.1 christos vs_sm_down(SCR *sp, MARK *rp, db_recno_t count, scroll_t scmd, SMAP *smp) 819 1.1 christos { 820 1.1 christos SMAP *ssmp, s1, s2; 821 1.1 christos int cursor_set, ychanged, zset; 822 1.1 christos 823 1.1 christos /* Check to see if movement is possible. */ 824 1.1 christos if (HMAP->lno == 1 && 825 1.1 christos (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) && 826 1.1 christos (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) { 827 1.1 christos v_sof(sp, NULL); 828 1.1 christos return (1); 829 1.1 christos } 830 1.1 christos 831 1.1 christos /* 832 1.1 christos * Small screens: see vs_refresh.c section 6a. 833 1.1 christos * 834 1.1 christos * If it's a small screen, and the movement isn't larger than a 835 1.1 christos * screen, i.e some context will remain, open up the screen and 836 1.1 christos * display by scrolling. In this case, the cursor moves up one 837 1.1 christos * line for each line displayed. Otherwise, erase/compress and 838 1.1 christos * repaint, and move the cursor to the first line in the screen. 839 1.1 christos * Note, the ^B command is always in the latter case, for historical 840 1.1 christos * reasons. 841 1.1 christos */ 842 1.1 christos cursor_set = scmd == CNTRL_Y; 843 1.1 christos if (IS_SMALL(sp)) { 844 1.1 christos if (count >= sp->t_maxrows || scmd == CNTRL_B) { 845 1.1 christos s1 = HMAP[0]; 846 1.1 christos if (vs_sm_erase(sp)) 847 1.1 christos return (1); 848 1.1 christos for (; count--; s1 = s2) { 849 1.1 christos if (vs_sm_prev(sp, &s1, &s2)) 850 1.1 christos return (1); 851 1.1 christos if (s2.lno == 1 && 852 1.1 christos (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1)) 853 1.1 christos break; 854 1.1 christos } 855 1.1 christos HMAP[0] = s2; 856 1.1 christos if (vs_sm_fill(sp, OOBLNO, P_TOP)) 857 1.1 christos return (1); 858 1.1 christos return (vs_sm_position(sp, rp, 0, P_BOTTOM)); 859 1.1 christos } 860 1.1 christos cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp); 861 1.1 christos for (; count && 862 1.1 christos sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) { 863 1.1 christos if (HMAP->lno == 1 && 864 1.1 christos (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1)) 865 1.1 christos break; 866 1.1 christos ++TMAP; 867 1.1 christos if (vs_sm_1down(sp)) 868 1.1 christos return (1); 869 1.1 christos } 870 1.1 christos if (!cursor_set) { 871 1.1 christos rp->lno = ssmp->lno; 872 1.1 christos rp->cno = ssmp->c_sboff; 873 1.1 christos } 874 1.1 christos if (count == 0) 875 1.1 christos return (0); 876 1.1 christos } 877 1.1 christos 878 1.1 christos for (ychanged = zset = 0; count; --count) { 879 1.1 christos /* If the line doesn't exist, we're done. */ 880 1.1 christos if (HMAP->lno == 1 && 881 1.1 christos (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1)) 882 1.1 christos break; 883 1.1 christos 884 1.1 christos /* Scroll the screen and cursor down one logical line. */ 885 1.1 christos if (vs_sm_1down(sp)) 886 1.1 christos return (1); 887 1.1 christos switch (scmd) { 888 1.1 christos case CNTRL_Y: 889 1.1 christos if (smp < TMAP) 890 1.1 christos ++smp; 891 1.1 christos else 892 1.1 christos ychanged = 1; 893 1.1 christos break; 894 1.1 christos case Z_CARAT: 895 1.1 christos if (zset) { 896 1.1 christos if (smp < TMAP) 897 1.1 christos ++smp; 898 1.1 christos } else { 899 1.1 christos smp = HMAP; 900 1.1 christos zset = 1; 901 1.1 christos } 902 1.1 christos /* FALLTHROUGH */ 903 1.1 christos default: 904 1.1 christos break; 905 1.1 christos } 906 1.1 christos } 907 1.1 christos 908 1.1 christos if (scmd != CNTRL_Y && cursor_set) 909 1.1 christos return(0); 910 1.1 christos 911 1.1 christos switch (scmd) { 912 1.1 christos case CNTRL_B: 913 1.1 christos /* 914 1.1 christos * If there are more lines, the ^B command is positioned at 915 1.1 christos * the last line of the screen. However, the line may not 916 1.1 christos * exist. 917 1.1 christos */ 918 1.1 christos if (!count) { 919 1.1 christos for (smp = TMAP; smp > HMAP; --smp) 920 1.1 christos if (db_exist(sp, smp->lno)) 921 1.1 christos break; 922 1.1 christos break; 923 1.1 christos } 924 1.1 christos /* FALLTHROUGH */ 925 1.1 christos case CNTRL_U: 926 1.1 christos /* 927 1.1 christos * The ^B and ^U commands move the cursor towards SOF 928 1.1 christos * if there are more lines to move. 929 1.1 christos */ 930 1.2 christos if (count < (db_recno_t)(smp - HMAP)) 931 1.1 christos smp -= count; 932 1.1 christos else 933 1.1 christos smp = HMAP; 934 1.1 christos break; 935 1.1 christos case CNTRL_Y: 936 1.1 christos /* 937 1.1 christos * On a ^Y that was forced to change lines, try and keep the 938 1.1 christos * cursor as close as possible to the last position, but also 939 1.1 christos * set it up so that the next "real" movement will return the 940 1.1 christos * cursor to the closest position to the last real movement. 941 1.1 christos */ 942 1.1 christos if (ychanged) { 943 1.1 christos rp->lno = smp->lno; 944 1.1 christos rp->cno = vs_colpos(sp, smp->lno, 945 1.1 christos (O_ISSET(sp, O_LEFTRIGHT) ? 946 1.1 christos smp->coff : (smp->soff - 1) * sp->cols) + 947 1.1 christos sp->rcm % sp->cols); 948 1.1 christos } 949 1.1 christos return (0); 950 1.1 christos case Z_CARAT: 951 1.1 christos /* The z^ command moves the cursor to the first new line. */ 952 1.1 christos break; 953 1.1 christos default: 954 1.1 christos abort(); 955 1.1 christos } 956 1.1 christos 957 1.1 christos if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL)) 958 1.1 christos return (1); 959 1.1 christos rp->lno = smp->lno; 960 1.1 christos rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff; 961 1.1 christos return (0); 962 1.1 christos } 963 1.1 christos 964 1.1 christos /* 965 1.1 christos * vs_sm_erase -- 966 1.1 christos * Erase the small screen area for the scrolling functions. 967 1.1 christos */ 968 1.1 christos static int 969 1.1 christos vs_sm_erase(SCR *sp) 970 1.1 christos { 971 1.1 christos GS *gp; 972 1.1 christos 973 1.1 christos gp = sp->gp; 974 1.1 christos (void)gp->scr_move(sp, LASTLINE(sp), 0); 975 1.1 christos (void)gp->scr_clrtoeol(sp); 976 1.1 christos for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { 977 1.1 christos (void)gp->scr_move(sp, TMAP - HMAP, 0); 978 1.1 christos (void)gp->scr_clrtoeol(sp); 979 1.1 christos } 980 1.1 christos return (0); 981 1.1 christos } 982 1.1 christos 983 1.1 christos /* 984 1.1 christos * vs_sm_1down -- 985 1.1 christos * Scroll the SMAP down one. 986 1.1 christos * 987 1.1 christos * PUBLIC: int vs_sm_1down __P((SCR *)); 988 1.1 christos */ 989 1.1 christos int 990 1.1 christos vs_sm_1down(SCR *sp) 991 1.1 christos { 992 1.1 christos /* 993 1.1 christos * Insert a line at the top of the screen. Shift the screen map 994 1.1 christos * down and display a new line at the top of the screen. 995 1.1 christos */ 996 1.1 christos (void)sp->gp->scr_move(sp, 0, 0); 997 1.1 christos if (vs_insertln(sp, 1)) 998 1.1 christos return (1); 999 1.1 christos 1000 1.1 christos /* One-line screens can fail. */ 1001 1.1 christos if (IS_ONELINE(sp)) { 1002 1.1 christos if (vs_sm_prev(sp, HMAP, HMAP)) 1003 1.1 christos return (1); 1004 1.1 christos } else { 1005 1.1 christos memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP)); 1006 1.1 christos if (vs_sm_prev(sp, HMAP + 1, HMAP)) 1007 1.1 christos return (1); 1008 1.1 christos } 1009 1.1 christos /* vs_sm_prev() flushed the cache. */ 1010 1.1 christos return (vs_line(sp, HMAP, NULL, NULL)); 1011 1.1 christos } 1012 1.1 christos 1013 1.1 christos /* 1014 1.1 christos * vs_insertln -- 1015 1.1 christos * Insert a line a la curses, make sure to put the information 1016 1.1 christos * line and other screens back. 1017 1.1 christos */ 1018 1.1 christos static int 1019 1.1 christos vs_insertln(SCR *sp, int cnt) 1020 1.1 christos { 1021 1.1 christos GS *gp; 1022 1.1 christos size_t oldy, oldx; 1023 1.1 christos 1024 1.1 christos gp = sp->gp; 1025 1.1 christos 1026 1.1 christos /* If the screen is vertically split, we can't scroll it. */ 1027 1.1 christos if (IS_VSPLIT(sp)) { 1028 1.1 christos F_SET(sp, SC_SCR_REDRAW); 1029 1.1 christos return (0); 1030 1.1 christos } 1031 1.1 christos 1032 1.1 christos if (IS_ONELINE(sp)) { 1033 1.1 christos (void)gp->scr_move(sp, LASTLINE(sp), 0); 1034 1.1 christos (void)gp->scr_clrtoeol(sp); 1035 1.1 christos } else { 1036 1.1 christos (void)gp->scr_cursor(sp, &oldy, &oldx); 1037 1.1 christos while (cnt--) { 1038 1.1 christos (void)gp->scr_move(sp, LASTLINE(sp) - 1, 0); 1039 1.1 christos (void)gp->scr_deleteln(sp); 1040 1.1 christos (void)gp->scr_move(sp, oldy, oldx); 1041 1.1 christos (void)gp->scr_insertln(sp); 1042 1.1 christos } 1043 1.1 christos } 1044 1.1 christos return (0); 1045 1.1 christos } 1046 1.1 christos 1047 1.1 christos /* 1048 1.1 christos * vs_sm_next -- 1049 1.1 christos * Fill in the next entry in the SMAP. 1050 1.1 christos * 1051 1.1 christos * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *)); 1052 1.1 christos */ 1053 1.1 christos int 1054 1.1 christos vs_sm_next(SCR *sp, SMAP *p, SMAP *t) 1055 1.1 christos { 1056 1.1 christos size_t lcnt; 1057 1.1 christos 1058 1.1 christos SMAP_FLUSH(t); 1059 1.1 christos if (O_ISSET(sp, O_LEFTRIGHT)) { 1060 1.1 christos t->lno = p->lno + 1; 1061 1.1 christos t->coff = p->coff; 1062 1.1 christos } else { 1063 1.1 christos lcnt = vs_screens(sp, p->lno, NULL); 1064 1.1 christos if (lcnt == p->soff) { 1065 1.1 christos t->lno = p->lno + 1; 1066 1.1 christos t->soff = 1; 1067 1.1 christos } else { 1068 1.1 christos t->lno = p->lno; 1069 1.1 christos t->soff = p->soff + 1; 1070 1.1 christos } 1071 1.1 christos } 1072 1.1 christos return (0); 1073 1.1 christos } 1074 1.1 christos 1075 1.1 christos /* 1076 1.1 christos * vs_sm_prev -- 1077 1.1 christos * Fill in the previous entry in the SMAP. 1078 1.1 christos * 1079 1.1 christos * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *)); 1080 1.1 christos */ 1081 1.1 christos int 1082 1.1 christos vs_sm_prev(SCR *sp, SMAP *p, SMAP *t) 1083 1.1 christos { 1084 1.1 christos SMAP_FLUSH(t); 1085 1.1 christos if (O_ISSET(sp, O_LEFTRIGHT)) { 1086 1.1 christos t->lno = p->lno - 1; 1087 1.1 christos t->coff = p->coff; 1088 1.1 christos } else { 1089 1.1 christos if (p->soff != 1) { 1090 1.1 christos t->lno = p->lno; 1091 1.1 christos t->soff = p->soff - 1; 1092 1.1 christos } else { 1093 1.1 christos t->lno = p->lno - 1; 1094 1.1 christos t->soff = vs_screens(sp, t->lno, NULL); 1095 1.1 christos } 1096 1.1 christos } 1097 1.1 christos return (t->lno == 0); 1098 1.1 christos } 1099 1.1 christos 1100 1.1 christos /* 1101 1.1 christos * vs_sm_cursor -- 1102 1.1 christos * Return the SMAP entry referenced by the cursor. 1103 1.1 christos * 1104 1.1 christos * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **)); 1105 1.1 christos */ 1106 1.1 christos int 1107 1.1 christos vs_sm_cursor(SCR *sp, SMAP **smpp) 1108 1.1 christos { 1109 1.1 christos SMAP *p; 1110 1.1 christos 1111 1.1 christos /* See if the cursor is not in the map. */ 1112 1.1 christos if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) 1113 1.1 christos return (1); 1114 1.1 christos 1115 1.1 christos /* Find the first occurence of the line. */ 1116 1.1 christos for (p = HMAP; p->lno != sp->lno; ++p); 1117 1.1 christos 1118 1.1 christos /* Fill in the map information until we find the right line. */ 1119 1.1 christos for (; p <= TMAP; ++p) { 1120 1.1 christos /* Short lines are common and easy to detect. */ 1121 1.1 christos if (p != TMAP && (p + 1)->lno != p->lno) { 1122 1.1 christos *smpp = p; 1123 1.1 christos return (0); 1124 1.1 christos } 1125 1.1 christos if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL)) 1126 1.1 christos return (1); 1127 1.1 christos if (p->c_eboff >= sp->cno) { 1128 1.1 christos *smpp = p; 1129 1.1 christos return (0); 1130 1.1 christos } 1131 1.1 christos } 1132 1.1 christos 1133 1.1 christos /* It was past the end of the map after all. */ 1134 1.1 christos return (1); 1135 1.1 christos } 1136 1.1 christos 1137 1.1 christos /* 1138 1.1 christos * vs_sm_position -- 1139 1.1 christos * Return the line/column of the top, middle or last line on the screen. 1140 1.1 christos * (The vi H, M and L commands.) Here because only the screen routines 1141 1.1 christos * know what's really out there. 1142 1.1 christos * 1143 1.1 christos * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t)); 1144 1.1 christos */ 1145 1.1 christos int 1146 1.1 christos vs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos) 1147 1.1 christos { 1148 1.1 christos SMAP *smp; 1149 1.1 christos db_recno_t last; 1150 1.1 christos 1151 1.1 christos switch (pos) { 1152 1.1 christos case P_TOP: 1153 1.1 christos /* 1154 1.1 christos * !!! 1155 1.1 christos * Historically, an invalid count to the H command failed. 1156 1.1 christos * We do nothing special here, just making sure that H in 1157 1.1 christos * an empty screen works. 1158 1.1 christos */ 1159 1.2 christos if (cnt > (u_long)(TMAP - HMAP)) 1160 1.1 christos goto sof; 1161 1.1 christos smp = HMAP + cnt; 1162 1.1 christos if (cnt && !db_exist(sp, smp->lno)) { 1163 1.1 christos sof: msgq(sp, M_BERR, "220|Movement past the end-of-screen"); 1164 1.1 christos return (1); 1165 1.1 christos } 1166 1.1 christos break; 1167 1.1 christos case P_MIDDLE: 1168 1.1 christos /* 1169 1.1 christos * !!! 1170 1.1 christos * Historically, a count to the M command was ignored. 1171 1.1 christos * If the screen isn't filled, find the middle of what's 1172 1.1 christos * real and move there. 1173 1.1 christos */ 1174 1.1 christos if (!db_exist(sp, TMAP->lno)) { 1175 1.1 christos if (db_last(sp, &last)) 1176 1.1 christos return (1); 1177 1.1 christos for (smp = TMAP; smp->lno > last && smp > HMAP; --smp); 1178 1.1 christos if (smp > HMAP) 1179 1.1 christos smp -= (smp - HMAP) / 2; 1180 1.1 christos } else 1181 1.1 christos smp = (HMAP + (TMAP - HMAP) / 2) + cnt; 1182 1.1 christos break; 1183 1.1 christos case P_BOTTOM: 1184 1.1 christos /* 1185 1.1 christos * !!! 1186 1.1 christos * Historically, an invalid count to the L command failed. 1187 1.1 christos * If the screen isn't filled, find the bottom of what's 1188 1.1 christos * real and try to offset from there. 1189 1.1 christos */ 1190 1.2 christos if (cnt > (u_long)(TMAP - HMAP)) 1191 1.1 christos goto eof; 1192 1.1 christos smp = TMAP - cnt; 1193 1.1 christos if (!db_exist(sp, smp->lno)) { 1194 1.1 christos if (db_last(sp, &last)) 1195 1.1 christos return (1); 1196 1.1 christos for (; smp->lno > last && smp > HMAP; --smp); 1197 1.2 christos if (cnt > (u_long)(smp - HMAP)) { 1198 1.1 christos eof: msgq(sp, M_BERR, 1199 1.1 christos "221|Movement past the beginning-of-screen"); 1200 1.1 christos return (1); 1201 1.1 christos } 1202 1.1 christos smp -= cnt; 1203 1.1 christos } 1204 1.1 christos break; 1205 1.1 christos default: 1206 1.1 christos abort(); 1207 1.1 christos } 1208 1.1 christos 1209 1.1 christos /* Make sure that the cached information is valid. */ 1210 1.1 christos if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL)) 1211 1.1 christos return (1); 1212 1.1 christos rp->lno = smp->lno; 1213 1.1 christos rp->cno = smp->c_sboff; 1214 1.1 christos 1215 1.1 christos return (0); 1216 1.1 christos } 1217 1.1 christos 1218 1.1 christos /* 1219 1.1 christos * vs_sm_nlines -- 1220 1.1 christos * Return the number of screen lines from an SMAP entry to the 1221 1.1 christos * start of some file line, less than a maximum value. 1222 1.1 christos * 1223 1.1 christos * PUBLIC: db_recno_t vs_sm_nlines __P((SCR *, SMAP *, db_recno_t, size_t)); 1224 1.1 christos */ 1225 1.1 christos db_recno_t 1226 1.1 christos vs_sm_nlines(SCR *sp, SMAP *from_sp, db_recno_t to_lno, size_t max) 1227 1.1 christos { 1228 1.1 christos db_recno_t lno, lcnt; 1229 1.1 christos 1230 1.1 christos if (O_ISSET(sp, O_LEFTRIGHT)) 1231 1.1 christos return (from_sp->lno > to_lno ? 1232 1.1 christos from_sp->lno - to_lno : to_lno - from_sp->lno); 1233 1.1 christos 1234 1.1 christos if (from_sp->lno == to_lno) 1235 1.1 christos return (from_sp->soff - 1); 1236 1.1 christos 1237 1.1 christos if (from_sp->lno > to_lno) { 1238 1.1 christos lcnt = from_sp->soff - 1; /* Correct for off-by-one. */ 1239 1.1 christos for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;) 1240 1.1 christos lcnt += vs_screens(sp, lno, NULL); 1241 1.1 christos } else { 1242 1.1 christos lno = from_sp->lno; 1243 1.1 christos lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1; 1244 1.1 christos for (; ++lno < to_lno && lcnt <= max;) 1245 1.1 christos lcnt += vs_screens(sp, lno, NULL); 1246 1.1 christos } 1247 1.1 christos return (lcnt); 1248 1.1 christos } 1249