]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/nvi/vi/vs_refresh.c
Update to version 3.2.0
[FreeBSD/FreeBSD.git] / contrib / nvi / vi / vs_refresh.c
1 /*-
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1992, 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 #include <sys/types.h>
13 #include <sys/queue.h>
14 #include <sys/time.h>
15
16 #include <bitstring.h>
17 #include <ctype.h>
18 #include <libgen.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "../common/common.h"
25 #include "vi.h"
26
27 #define UPDATE_CURSOR   0x01                    /* Update the cursor. */
28 #define UPDATE_SCREEN   0x02                    /* Flush to screen. */
29
30 static void     vs_modeline(SCR *);
31 static int      vs_paint(SCR *, u_int);
32
33 /*
34  * v_repaint --
35  *      Repaint selected lines from the screen.
36  *
37  * PUBLIC: int vs_repaint(SCR *, EVENT *);
38  */
39 int
40 vs_repaint(
41         SCR *sp,
42         EVENT *evp)
43 {
44         SMAP *smp;
45
46         for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) {
47                 smp = HMAP + evp->e_flno - 1;
48                 SMAP_FLUSH(smp);
49                 if (vs_line(sp, smp, NULL, NULL))
50                         return (1);
51         }
52         return (0);
53 }
54
55 /*
56  * vs_refresh --
57  *      Refresh all screens.
58  *
59  * PUBLIC: int vs_refresh(SCR *, int);
60  */
61 int
62 vs_refresh(
63         SCR *sp,
64         int forcepaint)
65 {
66         GS *gp;
67         SCR *tsp;
68         int need_refresh = 0;
69         u_int priv_paint, pub_paint;
70
71         gp = sp->gp;
72
73         /*
74          * 1: Refresh the screen.
75          *
76          * If SC_SCR_REDRAW is set in the current screen, repaint everything
77          * that we can find, including status lines.
78          */
79         if (F_ISSET(sp, SC_SCR_REDRAW))
80                 TAILQ_FOREACH(tsp, gp->dq, q)
81                         if (tsp != sp)
82                                 F_SET(tsp, SC_SCR_REDRAW | SC_STATUS);
83
84         /*
85          * 2: Related or dirtied screens, or screens with messages.
86          *
87          * If related screens share a view into a file, they may have been
88          * modified as well.  Refresh any screens that aren't exiting that
89          * have paint or dirty bits set.  Always update their screens, we
90          * are not likely to get another chance.  Finally, if we refresh any
91          * screens other than the current one, the cursor will be trashed.
92          */
93         pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW;
94         priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH;
95         if (O_ISSET(sp, O_NUMBER))
96                 priv_paint |= VIP_N_RENUMBER;
97         TAILQ_FOREACH(tsp, gp->dq, q)
98                 if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) &&
99                     (F_ISSET(tsp, pub_paint) ||
100                     F_ISSET(VIP(tsp), priv_paint))) {
101                         (void)vs_paint(tsp,
102                             (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ?
103                             UPDATE_CURSOR : 0) | UPDATE_SCREEN);
104                         F_SET(VIP(sp), VIP_CUR_INVALID);
105                 }
106
107         /*
108          * 3: Refresh the current screen.
109          *
110          * Always refresh the current screen, it may be a cursor movement.
111          * Also, always do it last -- that way, SC_SCR_REDRAW can be set
112          * in the current screen only, and the screen won't flash.
113          */
114         if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint &&
115             F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN)))
116                 return (1);
117
118         /*
119          * 4: Paint any missing status lines.
120          *
121          * XXX
122          * This is fairly evil.  Status lines are written using the vi message
123          * mechanism, since we have no idea how long they are.  Since we may be
124          * painting screens other than the current one, we don't want to make
125          * the user wait.  We depend heavily on there not being any other lines
126          * currently waiting to be displayed and the message truncation code in
127          * the msgq_status routine working.
128          *
129          * And, finally, if we updated any status lines, make sure the cursor
130          * gets back to where it belongs.
131          */
132         TAILQ_FOREACH(tsp, gp->dq, q)
133                 if (F_ISSET(tsp, SC_STATUS)) {
134                         need_refresh = 1;
135                         vs_resolve(tsp, sp, 0);
136                 }
137         if (need_refresh)
138                 (void)gp->scr_refresh(sp, 0);
139
140         /*
141          * A side-effect of refreshing the screen is that it's now ready
142          * for everything else, i.e. messages.
143          */
144         F_SET(sp, SC_SCR_VI);
145         return (0);
146 }
147
148 /*
149  * vs_paint --
150  *      This is the guts of the vi curses screen code.  The idea is that
151  *      the SCR structure passed in contains the new coordinates of the
152  *      screen.  What makes this hard is that we don't know how big
153  *      characters are, doing input can put the cursor in illegal places,
154  *      and we're frantically trying to avoid repainting unless it's
155  *      absolutely necessary.  If you change this code, you'd better know
156  *      what you're doing.  It's subtle and quick to anger.
157  */
158 static int
159 vs_paint(
160         SCR *sp,
161         u_int flags)
162 {
163         GS *gp;
164         SMAP *smp, tmp;
165         VI_PRIVATE *vip;
166         recno_t lastline, lcnt;
167         size_t cwtotal, cnt, len, notused, off, y;
168         int ch = 0, didpaint, isempty, leftright_warp;
169         CHAR_T *p;
170
171 #define  LNO    sp->lno                 /* Current file line. */
172 #define OLNO    vip->olno               /* Remembered file line. */
173 #define  CNO    sp->cno                 /* Current file column. */
174 #define OCNO    vip->ocno               /* Remembered file column. */
175 #define SCNO    vip->sc_col             /* Current screen column. */
176
177         gp = sp->gp;
178         vip = VIP(sp);
179         didpaint = leftright_warp = 0;
180
181         /*
182          * 5: Reformat the lines.
183          *
184          * If the lines themselves have changed (:set list, for example),
185          * fill in the map from scratch.  Adjust the screen that's being
186          * displayed if the leftright flag is set.
187          */
188         if (F_ISSET(sp, SC_SCR_REFORMAT)) {
189                 /* Invalidate the line size cache. */
190                 VI_SCR_CFLUSH(vip);
191
192                 /* Toss vs_line() cached information. */
193                 if (F_ISSET(sp, SC_SCR_TOP)) {
194                         if (vs_sm_fill(sp, LNO, P_TOP))
195                                 return (1);
196                 }
197                 else if (F_ISSET(sp, SC_SCR_CENTER)) {
198                         if (vs_sm_fill(sp, LNO, P_MIDDLE))
199                                 return (1);
200                 } else
201                         if (vs_sm_fill(sp, OOBLNO, P_TOP))
202                                 return (1);
203                 F_SET(sp, SC_SCR_REDRAW);
204         }
205
206         /*
207          * 6: Line movement.
208          *
209          * Line changes can cause the top line to change as well.  As
210          * before, if the movement is large, the screen is repainted.
211          *
212          * 6a: Small screens.
213          *
214          * Users can use the window, w300, w1200 and w9600 options to make
215          * the screen artificially small.  The behavior of these options
216          * in the historic vi wasn't all that consistent, and, in fact, it
217          * was never documented how various screen movements affected the
218          * screen size.  Generally, one of three things would happen:
219          *      1: The screen would expand in size, showing the line
220          *      2: The screen would scroll, showing the line
221          *      3: The screen would compress to its smallest size and
222          *              repaint.
223          * In general, scrolling didn't cause compression (200^D was handled
224          * the same as ^D), movement to a specific line would (:N where N
225          * was 1 line below the screen caused a screen compress), and cursor
226          * movement would scroll if it was 11 lines or less, and compress if
227          * it was more than 11 lines.  (And, no, I have no idea where the 11
228          * comes from.)
229          *
230          * What we do is try and figure out if the line is less than half of
231          * a full screen away.  If it is, we expand the screen if there's
232          * room, and then scroll as necessary.  The alternative is to compress
233          * and repaint.
234          *
235          * !!!
236          * This code is a special case from beginning to end.  Unfortunately,
237          * home modems are still slow enough that it's worth having.
238          *
239          * XXX
240          * If the line a really long one, i.e. part of the line is on the
241          * screen but the column offset is not, we'll end up in the adjust
242          * code, when we should probably have compressed the screen.
243          */
244         if (IS_SMALL(sp)) {
245                 if (LNO < HMAP->lno) {
246                         lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows);
247                         if (lcnt <= HALFSCREEN(sp))
248                                 for (; lcnt && sp->t_rows != sp->t_maxrows;
249                                      --lcnt, ++sp->t_rows) {
250                                         ++TMAP;
251                                         if (vs_sm_1down(sp))
252                                                 return (1);
253                                 }
254                         else
255                                 goto small_fill;
256                 } else if (LNO > TMAP->lno) {
257                         lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows);
258                         if (lcnt <= HALFSCREEN(sp))
259                                 for (; lcnt && sp->t_rows != sp->t_maxrows;
260                                      --lcnt, ++sp->t_rows) {
261                                         if (vs_sm_next(sp, TMAP, TMAP + 1))
262                                                 return (1);
263                                         ++TMAP;
264                                         if (vs_line(sp, TMAP, NULL, NULL))
265                                                 return (1);
266                                 }
267                         else {
268 small_fill:                     (void)gp->scr_move(sp, LASTLINE(sp), 0);
269                                 (void)gp->scr_clrtoeol(sp);
270                                 for (; sp->t_rows > sp->t_minrows;
271                                     --sp->t_rows, --TMAP) {
272                                         (void)gp->scr_move(sp, TMAP - HMAP, 0);
273                                         (void)gp->scr_clrtoeol(sp);
274                                 }
275                                 if (vs_sm_fill(sp, LNO, P_FILL))
276                                         return (1);
277                                 F_SET(sp, SC_SCR_REDRAW);
278                                 goto adjust;
279                         }
280                 }
281         }
282
283         /*
284          * 6b: Line down, or current screen.
285          */
286         if (LNO >= HMAP->lno) {
287                 /* Current screen. */
288                 if (LNO <= TMAP->lno)
289                         goto adjust;
290                 if (F_ISSET(sp, SC_SCR_TOP))
291                         goto top;
292                 if (F_ISSET(sp, SC_SCR_CENTER))
293                         goto middle;
294
295                 /*
296                  * If less than half a screen above the line, scroll down
297                  * until the line is on the screen.
298                  */
299                 lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp));
300                 if (lcnt < HALFTEXT(sp)) {
301                         while (lcnt--)
302                                 if (vs_sm_1up(sp))
303                                         return (1);
304                         goto adjust;
305                 }
306                 goto bottom;
307         }
308
309         /*
310          * 6c: If not on the current screen, may request center or top.
311          */
312         if (F_ISSET(sp, SC_SCR_TOP))
313                 goto top;
314         if (F_ISSET(sp, SC_SCR_CENTER))
315                 goto middle;
316
317         /*
318          * 6d: Line up.
319          */
320         lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp));
321         if (lcnt < HALFTEXT(sp)) {
322                 /*
323                  * If less than half a screen below the line, scroll up until
324                  * the line is the first line on the screen.  Special check so
325                  * that if the screen has been emptied, we refill it.
326                  */
327                 if (db_exist(sp, HMAP->lno)) {
328                         while (lcnt--)
329                                 if (vs_sm_1down(sp))
330                                         return (1);
331                         goto adjust;
332                 } else
333                         goto top;       /* XXX No such line. */
334
335                 /*
336                  * If less than a half screen from the bottom of the file,
337                  * put the last line of the file on the bottom of the screen.
338                  */
339 bottom:         if (db_last(sp, &lastline))
340                         return (1);
341                 tmp.lno = LNO;
342                 tmp.coff = HMAP->coff;
343                 tmp.soff = 1;
344                 lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows);
345                 if (lcnt < HALFTEXT(sp)) {
346                         if (vs_sm_fill(sp, lastline, P_BOTTOM))
347                                 return (1);
348                         F_SET(sp, SC_SCR_REDRAW);
349                         goto adjust;
350                 }
351                 /* It's not close, just put the line in the middle. */
352                 goto middle;
353         }
354
355         /*
356          * If less than half a screen from the top of the file, put the first
357          * line of the file at the top of the screen.  Otherwise, put the line
358          * in the middle of the screen.
359          */
360         tmp.lno = 1;
361         tmp.coff = HMAP->coff;
362         tmp.soff = 1;
363         lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp));
364         if (lcnt < HALFTEXT(sp)) {
365                 if (vs_sm_fill(sp, 1, P_TOP))
366                         return (1);
367         } else
368 middle:         if (vs_sm_fill(sp, LNO, P_MIDDLE))
369                         return (1);
370         if (0) {
371 top:            if (vs_sm_fill(sp, LNO, P_TOP))
372                         return (1);
373         }
374         F_SET(sp, SC_SCR_REDRAW);
375
376         /*
377          * At this point we know part of the line is on the screen.  Since
378          * scrolling is done using logical lines, not physical, all of the
379          * line may not be on the screen.  While that's not necessarily bad,
380          * if the part the cursor is on isn't there, we're going to lose.
381          * This can be tricky; if the line covers the entire screen, lno
382          * may be the same as both ends of the map, that's why we test BOTH
383          * the top and the bottom of the map.  This isn't a problem for
384          * left-right scrolling, the cursor movement code handles the problem.
385          *
386          * There's a performance issue here if editing *really* long lines.
387          * This gets to the right spot by scrolling, and, in a binary, by
388          * scrolling hundreds of lines.  If the adjustment looks like it's
389          * going to be a serious problem, refill the screen and repaint.
390          */
391 adjust: if (!O_ISSET(sp, O_LEFTRIGHT) &&
392             (LNO == HMAP->lno || LNO == TMAP->lno)) {
393                 cnt = vs_screens(sp, LNO, &CNO);
394                 if (LNO == HMAP->lno && cnt < HMAP->soff) {
395                         if ((HMAP->soff - cnt) > HALFTEXT(sp)) {
396                                 HMAP->soff = cnt;
397                                 vs_sm_fill(sp, OOBLNO, P_TOP);
398                                 F_SET(sp, SC_SCR_REDRAW);
399                         } else
400                                 while (cnt < HMAP->soff)
401                                         if (vs_sm_1down(sp))
402                                                 return (1);
403                 }
404                 if (LNO == TMAP->lno && cnt > TMAP->soff) {
405                         if ((cnt - TMAP->soff) > HALFTEXT(sp)) {
406                                 TMAP->soff = cnt;
407                                 vs_sm_fill(sp, OOBLNO, P_BOTTOM);
408                                 F_SET(sp, SC_SCR_REDRAW);
409                         } else
410                                 while (cnt > TMAP->soff)
411                                         if (vs_sm_1up(sp))
412                                                 return (1);
413                 }
414         }
415
416         /*
417          * If the screen needs to be repainted, skip cursor optimization.
418          * However, in the code above we skipped leftright scrolling on
419          * the grounds that the cursor code would handle it.  Make sure
420          * the right screen is up.
421          */
422         if (F_ISSET(sp, SC_SCR_REDRAW)) {
423                 if (O_ISSET(sp, O_LEFTRIGHT))
424                         goto slow;
425                 goto paint;
426         }
427
428         /*
429          * 7: Cursor movements (current screen only).
430          */
431         if (!LF_ISSET(UPDATE_CURSOR))
432                 goto number;
433
434         /*
435          * Decide cursor position.  If the line has changed, the cursor has
436          * moved over a tab, or don't know where the cursor was, reparse the
437          * line.  Otherwise, we've just moved over fixed-width characters,
438          * and can calculate the left/right scrolling and cursor movement
439          * without reparsing the line.  Note that we don't know which (if any)
440          * of the characters between the old and new cursor positions changed.
441          *
442          * XXX
443          * With some work, it should be possible to handle tabs quickly, at
444          * least in obvious situations, like moving right and encountering
445          * a tab, without reparsing the whole line.
446          *
447          * If the line we're working with has changed, reread it..
448          */
449         if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO)
450                 goto slow;
451
452         /* Otherwise, if nothing's changed, ignore the cursor. */
453         if (CNO == OCNO)
454                 goto fast;
455
456         /*
457          * Get the current line.  If this fails, we either have an empty
458          * file and can just repaint, or there's a real problem.  This
459          * isn't a performance issue because there aren't any ways to get
460          * here repeatedly.
461          */
462         if (db_eget(sp, LNO, &p, &len, &isempty)) {
463                 if (isempty)
464                         goto slow;
465                 return (1);
466         }
467
468 #ifdef DEBUG
469         /* Sanity checking. */
470         if (CNO >= len && len != 0) {
471                 msgq(sp, M_ERR, "Error: %s/%d: cno (%zu) >= len (%zu)",
472                      basename(__FILE__), __LINE__, CNO, len);
473                 return (1);
474         }
475 #endif
476         /*
477          * The basic scheme here is to look at the characters in between
478          * the old and new positions and decide how big they are on the
479          * screen, and therefore, how many screen positions to move.
480          */
481         if (CNO < OCNO) {
482                 /*
483                  * 7a: Cursor moved left.
484                  *
485                  * Point to the old character.  The old cursor position can
486                  * be past EOL if, for example, we just deleted the rest of
487                  * the line.  In this case, since we don't know the width of
488                  * the characters we traversed, we have to do it slowly.
489                  */
490                 p += OCNO;
491                 cnt = (OCNO - CNO) + 1;
492                 if (OCNO >= len)
493                         goto slow;
494
495                 /*
496                  * Quick sanity check -- it's hard to figure out exactly when
497                  * we cross a screen boundary as we do in the cursor right
498                  * movement.  If cnt is so large that we're going to cross the
499                  * boundary no matter what, stop now.
500                  */
501                 if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
502                         goto slow;
503
504                 /*
505                  * Count up the widths of the characters.  If it's a tab
506                  * character, go do it the slow way.
507                  */
508                 for (cwtotal = 0; cnt--; cwtotal += KEY_COL(sp, ch))
509                         if ((ch = *(UCHAR_T *)p--) == '\t')
510                                 goto slow;
511
512                 /*
513                  * Decrement the screen cursor by the total width of the
514                  * characters minus 1.
515                  */
516                 cwtotal -= 1;
517
518                 /*
519                  * If we're moving left, and there's a wide character in the
520                  * current position, go to the end of the character.
521                  */
522                 if (KEY_COL(sp, ch) > 1)
523                         cwtotal -= KEY_COL(sp, ch) - 1;
524
525                 /*
526                  * If the new column moved us off of the current logical line,
527                  * calculate a new one.  If doing leftright scrolling, we've
528                  * moved off of the current screen, as well.
529                  */
530                 if (SCNO < cwtotal)
531                         goto slow;
532                 SCNO -= cwtotal;
533         } else {
534                 /*
535                  * 7b: Cursor moved right.
536                  *
537                  * Point to the first character to the right.
538                  */
539                 p += OCNO + 1;
540                 cnt = CNO - OCNO;
541
542                 /*
543                  * Count up the widths of the characters.  If it's a tab
544                  * character, go do it the slow way.  If we cross a
545                  * screen boundary, we can quit.
546                  */
547                 for (cwtotal = SCNO; cnt--;) {
548                         if ((ch = *(UCHAR_T *)p++) == '\t')
549                                 goto slow;
550                         if ((cwtotal += KEY_COL(sp, ch)) >= SCREEN_COLS(sp))
551                                 break;
552                 }
553
554                 /*
555                  * Increment the screen cursor by the total width of the
556                  * characters.
557                  */
558                 SCNO = cwtotal;
559
560                 /* See screen change comment in section 6a. */
561                 if (SCNO >= SCREEN_COLS(sp))
562                         goto slow;
563         }
564
565         /*
566          * 7c: Fast cursor update.
567          *
568          * We have the current column, retrieve the current row.
569          */
570 fast:   (void)gp->scr_cursor(sp, &y, &notused);
571         goto done_cursor;
572
573         /*
574          * 7d: Slow cursor update.
575          *
576          * Walk through the map and find the current line.
577          */
578 slow:   for (smp = HMAP; smp->lno != LNO; ++smp);
579
580         /*
581          * 7e: Leftright scrolling adjustment.
582          *
583          * If doing left-right scrolling and the cursor movement has changed
584          * the displayed screen, scroll the screen left or right, unless we're
585          * updating the info line in which case we just scroll that one line.
586          * We adjust the offset up or down until we have a window that covers
587          * the current column, making sure that we adjust differently for the
588          * first screen as compared to subsequent ones.
589          */
590         if (O_ISSET(sp, O_LEFTRIGHT)) {
591                 /*
592                  * Get the screen column for this character, and correct
593                  * for the number option offset.
594                  */
595                 cnt = vs_columns(sp, NULL, LNO, &CNO, NULL);
596                 if (O_ISSET(sp, O_NUMBER))
597                         cnt -= O_NUMBER_LENGTH;
598
599                 /* Adjust the window towards the beginning of the line. */
600                 off = smp->coff;
601                 if (off >= cnt) {
602                         do {
603                                 if (off >= O_VAL(sp, O_SIDESCROLL))
604                                         off -= O_VAL(sp, O_SIDESCROLL);
605                                 else {
606                                         off = 0;
607                                         break;
608                                 }
609                         } while (off >= cnt);
610                         goto shifted;
611                 }
612
613                 /* Adjust the window towards the end of the line. */
614                 if ((off == 0 && off + SCREEN_COLS(sp) < cnt) ||
615                     (off != 0 && off + sp->cols < cnt)) {
616                         do {
617                                 off += O_VAL(sp, O_SIDESCROLL);
618                         } while (off + sp->cols < cnt);
619
620 shifted:                /* Fill in screen map with the new offset. */
621                         if (F_ISSET(sp, SC_TINPUT_INFO))
622                                 smp->coff = off;
623                         else {
624                                 for (smp = HMAP; smp <= TMAP; ++smp)
625                                         smp->coff = off;
626                                 leftright_warp = 1;
627                         }
628                         goto paint;
629                 }
630
631                 /*
632                  * We may have jumped here to adjust a leftright screen because
633                  * redraw was set.  If so, we have to paint the entire screen.
634                  */
635                 if (F_ISSET(sp, SC_SCR_REDRAW))
636                         goto paint;
637         }
638
639         /*
640          * Update the screen lines for this particular file line until we
641          * have a new screen cursor position.
642          */
643         for (y = -1,
644             vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) {
645                 if (vs_line(sp, smp, &y, &SCNO))
646                         return (1);
647                 if (y != -1) {
648                         vip->sc_smap = smp;
649                         break;
650                 }
651         }
652         goto done_cursor;
653
654         /*
655          * 8: Repaint the entire screen.
656          *
657          * Lost big, do what you have to do.  We flush the cache, since
658          * SC_SCR_REDRAW gets set when the screen isn't worth fixing, and
659          * it's simpler to repaint.  So, don't trust anything that we
660          * think we know about it.
661          */
662 paint:  for (smp = HMAP; smp <= TMAP; ++smp)
663                 SMAP_FLUSH(smp);
664         for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) {
665                 if (vs_line(sp, smp, &y, &SCNO))
666                         return (1);
667                 if (y != -1 && vip->sc_smap == NULL)
668                         vip->sc_smap = smp;
669         }
670         /*
671          * If it's a small screen and we're redrawing, clear the unused lines,
672          * ex may have overwritten them.
673          */
674         if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp))
675                 for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
676                         (void)gp->scr_move(sp, cnt, 0);
677                         (void)gp->scr_clrtoeol(sp);
678                 }
679
680         didpaint = 1;
681
682 done_cursor:
683         /*
684          * Sanity checking.  When the repainting code messes up, the usual
685          * result is we don't repaint the cursor and so sc_smap will be
686          * NULL.  If we're debugging, die, otherwise restart from scratch.
687          */
688 #ifdef DEBUG
689         if (vip->sc_smap == NULL)
690                 abort();
691 #else
692         if (vip->sc_smap == NULL) {
693                 F_SET(sp, SC_SCR_REFORMAT);
694                 return (vs_paint(sp, flags));
695         }
696 #endif
697
698         /*
699          * 9: Set the remembered cursor values.
700          */
701         OCNO = CNO;
702         OLNO = LNO;
703
704         /*
705          * 10: Repaint the line numbers.
706          *
707          * If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we
708          * didn't repaint the screen, repaint all of the line numbers,
709          * they've changed.
710          */
711 number: if (O_ISSET(sp, O_NUMBER) &&
712             F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp))
713                 return (1);
714
715         /*
716          * 11: Update the mode line, position the cursor, and flush changes.
717          *
718          * If we warped the screen, we have to refresh everything.
719          */
720         if (leftright_warp)
721                 LF_SET(UPDATE_CURSOR | UPDATE_SCREEN);
722
723         if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) &&
724             !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO))
725                 vs_modeline(sp);
726
727         if (LF_ISSET(UPDATE_CURSOR)) {
728                 (void)gp->scr_move(sp, y, SCNO);
729
730                 /*
731                  * XXX
732                  * If the screen shifted, we recalculate the "most favorite"
733                  * cursor position.  Vi won't know that we've warped the
734                  * screen, so it's going to have a wrong idea about where the
735                  * cursor should be.  This is vi's problem, and fixing it here
736                  * is a gross layering violation.
737                  */
738                 if (leftright_warp)
739                         (void)vs_column(sp, &sp->rcm);
740         }
741
742         if (LF_ISSET(UPDATE_SCREEN))
743                 (void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT));
744
745         /* 12: Clear the flags that are handled by this routine. */
746         F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP);
747         F_CLR(vip, VIP_CUR_INVALID |
748             VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE);
749
750         return (0);
751
752 #undef   LNO
753 #undef  OLNO
754 #undef   CNO
755 #undef  OCNO
756 #undef  SCNO
757 }
758
759 /*
760  * vs_modeline --
761  *      Update the mode line.
762  */
763 static void
764 vs_modeline(SCR *sp)
765 {
766         static char * const modes[] = {
767                 "215|Append",                   /* SM_APPEND */
768                 "216|Change",                   /* SM_CHANGE */
769                 "217|Command",                  /* SM_COMMAND */
770                 "218|Insert",                   /* SM_INSERT */
771                 "219|Replace",                  /* SM_REPLACE */
772         };
773         GS *gp;
774         size_t cols, curcol, curlen, endpoint, len, midpoint;
775         const char *t = NULL;
776         int ellipsis;
777         char buf[20];
778
779         gp = sp->gp;
780
781         /*
782          * We put down the file name, the ruler, the mode and the dirty flag.
783          * If there's not enough room, there's not enough room, we don't play
784          * any special games.  We try to put the ruler in the middle and the
785          * mode and dirty flag at the end.
786          *
787          * !!!
788          * Leave the last character blank, in case it's a really dumb terminal
789          * with hardware scroll.  Second, don't paint the last character in the
790          * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
791          *
792          * Move to the last line on the screen.
793          */
794         (void)gp->scr_move(sp, LASTLINE(sp), 0);
795
796         /* If more than one screen in the display, show the file name. */
797         curlen = 0;
798         if (IS_SPLIT(sp)) {
799                 CHAR_T *wp, *p;
800                 size_t l;
801
802                 CHAR2INT(sp, sp->frp->name, strlen(sp->frp->name) + 1, wp, l);
803                 p = wp + l;
804                 for (ellipsis = 0, cols = sp->cols / 2; --p > wp;) {
805                         if (*p == '/') {
806                                 ++p;
807                                 break;
808                         }
809                         if ((curlen += KEY_COL(sp, *p)) > cols) {
810                                 ellipsis = 3;
811                                 curlen +=
812                                     KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' ');
813                                 while (curlen > cols) {
814                                         ++p;
815                                         curlen -= KEY_COL(sp, *p);
816                                 }
817                                 break;
818                         }
819                 }
820                 if (ellipsis) {
821                         while (ellipsis--)
822                                 (void)gp->scr_addstr(sp,
823                                     KEY_NAME(sp, '.'), KEY_LEN(sp, '.'));
824                         (void)gp->scr_addstr(sp,
825                             KEY_NAME(sp, ' '), KEY_LEN(sp, ' '));
826                 }
827                 for (; *p != '\0'; ++p)
828                         (void)gp->scr_addstr(sp,
829                             KEY_NAME(sp, *p), KEY_COL(sp, *p));
830         }
831
832         /* Clear the rest of the line. */
833         (void)gp->scr_clrtoeol(sp);
834
835         /*
836          * Display the ruler.  If we're not at the midpoint yet, move there.
837          * Otherwise, add in two extra spaces.
838          *
839          * Adjust the current column for the fact that the editor uses it as
840          * a zero-based number.
841          *
842          * XXX
843          * Assume that numbers, commas, and spaces only take up a single
844          * column on the screen.
845          */
846         cols = sp->cols - 1;
847         if (O_ISSET(sp, O_RULER)) {
848                 vs_column(sp, &curcol);
849                 len = snprintf(buf, sizeof(buf), "%lu,%lu",
850                     (u_long)sp->lno, (u_long)(curcol + 1));
851
852                 midpoint = (cols - ((len + 1) / 2)) / 2;
853                 if (curlen < midpoint) {
854                         (void)gp->scr_move(sp, LASTLINE(sp), midpoint);
855                         curlen += len;
856                 } else if (curlen + 2 + len < cols) {
857                         (void)gp->scr_addstr(sp, "  ", 2);
858                         curlen += 2 + len;
859                 }
860                 (void)gp->scr_addstr(sp, buf, len);
861         }
862
863         /*
864          * Display the mode and the modified flag, as close to the end of the
865          * line as possible, but guaranteeing at least two spaces between the
866          * ruler and the modified flag.
867          */
868 #define MODESIZE        9
869         endpoint = cols;
870         if (O_ISSET(sp, O_SHOWMODE)) {
871                 if (F_ISSET(sp->ep, F_MODIFIED))
872                         --endpoint;
873                 t = msg_cat(sp, modes[sp->showmode], &len);
874                 endpoint -= len;
875         }
876
877         if (endpoint > curlen + 2) {
878                 (void)gp->scr_move(sp, LASTLINE(sp), endpoint);
879                 if (O_ISSET(sp, O_SHOWMODE)) {
880                         if (F_ISSET(sp->ep, F_MODIFIED))
881                                 (void)gp->scr_addstr(sp,
882                                     KEY_NAME(sp, '*'), KEY_LEN(sp, '*'));
883                         (void)gp->scr_addstr(sp, t, len);
884                 }
885         }
886 }