source: trunk/third/nvi/vi/vs_smap.c @ 14302

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