vs_smap.c revision 1.3 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