]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/nvi/vi/vs_msg.c
Fix whitespace in r368698
[FreeBSD/FreeBSD.git] / contrib / nvi / vi / vs_msg.c
1 /*-
2  * Copyright (c) 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 <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include "../common/common.h"
24 #include "vi.h"
25
26 typedef enum {
27         SCROLL_W,                       /* User wait. */
28         SCROLL_W_EX,                    /* User wait, or enter : to continue. */
29         SCROLL_W_QUIT                   /* User wait, or enter q to quit. */
30                                         /*
31                                          * SCROLL_W_QUIT has another semantic
32                                          * -- only wait if the screen is full
33                                          */
34 } sw_t;
35
36 static void     vs_divider(SCR *);
37 static void     vs_msgsave(SCR *, mtype_t, char *, size_t);
38 static void     vs_output(SCR *, mtype_t, const char *, int);
39 static void     vs_scroll(SCR *, int *, sw_t);
40 static void     vs_wait(SCR *, int *, sw_t);
41
42 /*
43  * vs_busy --
44  *      Display, update or clear a busy message.
45  *
46  * This routine is the default editor interface for vi busy messages.  It
47  * implements a standard strategy of stealing lines from the bottom of the
48  * vi text screen.  Screens using an alternate method of displaying busy
49  * messages, e.g. X11 clock icons, should set their scr_busy function to the
50  * correct function before calling the main editor routine.
51  *
52  * PUBLIC: void vs_busy(SCR *, const char *, busy_t);
53  */
54 void
55 vs_busy(SCR *sp, const char *msg, busy_t btype)
56 {
57         GS *gp;
58         VI_PRIVATE *vip;
59         static const char flagc[] = "|/-\\";
60         struct timespec ts, ts_diff;
61         const struct timespec ts_min = { 0, 125000000 };
62         size_t len, notused;
63         const char *p;
64
65         /* Ex doesn't display busy messages. */
66         if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
67                 return;
68
69         gp = sp->gp;
70         vip = VIP(sp);
71
72         /*
73          * Most of this routine is to deal with the screen sharing real estate
74          * between the normal edit messages and the busy messages.  Logically,
75          * all that's needed is something that puts up a message, periodically
76          * updates it, and then goes away.
77          */
78         switch (btype) {
79         case BUSY_ON:
80                 ++vip->busy_ref;
81                 if (vip->totalcount != 0 || vip->busy_ref != 1)
82                         break;
83
84                 /* Initialize state for updates. */
85                 vip->busy_ch = 0;
86                 timepoint_steady(&vip->busy_ts);
87
88                 /* Save the current cursor. */
89                 (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
90
91                 /* Display the busy message. */
92                 p = msg_cat(sp, msg, &len);
93                 (void)gp->scr_move(sp, LASTLINE(sp), 0);
94                 (void)gp->scr_addstr(sp, p, len);
95                 (void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
96                 (void)gp->scr_clrtoeol(sp);
97                 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
98                 break;
99         case BUSY_OFF:
100                 if (vip->busy_ref == 0)
101                         break;
102                 --vip->busy_ref;
103
104                 /*
105                  * If the line isn't in use for another purpose, clear it.
106                  * Always return to the original position.
107                  */
108                 if (vip->totalcount == 0 && vip->busy_ref == 0) {
109                         (void)gp->scr_move(sp, LASTLINE(sp), 0);
110                         (void)gp->scr_clrtoeol(sp);
111                 }
112                 (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
113                 break;
114         case BUSY_UPDATE:
115                 if (vip->totalcount != 0 || vip->busy_ref == 0)
116                         break;
117
118                 /* Update no more than every 1/8 of a second. */
119                 timepoint_steady(&ts);
120                 ts_diff = ts;
121                 timespecsub(&ts_diff, &vip->busy_ts);
122                 if (timespeccmp(&ts_diff, &ts_min, <))
123                         return;
124                 vip->busy_ts = ts;
125
126                 /* Display the update. */
127                 if (vip->busy_ch == sizeof(flagc) - 1)
128                         vip->busy_ch = 0;
129                 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
130                 (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
131                 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
132                 break;
133         }
134         (void)gp->scr_refresh(sp, 0);
135 }
136
137 /* 
138  * vs_home --
139  *      Home the cursor to the bottom row, left-most column.
140  *
141  * PUBLIC: void vs_home(SCR *);
142  */
143 void
144 vs_home(SCR *sp)
145 {
146         (void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
147         (void)sp->gp->scr_refresh(sp, 0);
148 }
149
150 /*
151  * vs_update --
152  *      Update a command.
153  *
154  * PUBLIC: void vs_update(SCR *, const char *, const CHAR_T *);
155  */
156 void
157 vs_update(SCR *sp, const char *m1, const CHAR_T *m2)
158 {
159         GS *gp;
160         size_t len, mlen, oldx, oldy;
161         CONST char *np;
162         size_t nlen;
163
164         gp = sp->gp;
165
166         /*
167          * This routine displays a message on the bottom line of the screen,
168          * without updating any of the command structures that would keep it
169          * there for any period of time, i.e. it is overwritten immediately.
170          *
171          * It's used by the ex read and ! commands when the user's command is
172          * expanded, and by the ex substitution confirmation prompt.
173          */
174         if (F_ISSET(sp, SC_SCR_EXWROTE)) {
175                 if (m2 != NULL)
176                         INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen);
177                 (void)ex_printf(sp,
178                     "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np);
179                 (void)ex_fflush(sp);
180         }
181
182         /*
183          * Save the cursor position, the substitute-with-confirmation code
184          * will have already set it correctly.
185          */
186         (void)gp->scr_cursor(sp, &oldy, &oldx);
187
188         /* Clear the bottom line. */
189         (void)gp->scr_move(sp, LASTLINE(sp), 0);
190         (void)gp->scr_clrtoeol(sp);
191
192         /*
193          * XXX
194          * Don't let long file names screw up the screen.
195          */
196         if (m1 != NULL) {
197                 mlen = len = strlen(m1);
198                 if (len > sp->cols - 2)
199                         mlen = len = sp->cols - 2;
200                 (void)gp->scr_addstr(sp, m1, mlen);
201         } else
202                 len = 0;
203         if (m2 != NULL) {
204                 mlen = STRLEN(m2);
205                 if (len + mlen > sp->cols - 2)
206                         mlen = (sp->cols - 2) - len;
207                 (void)gp->scr_waddstr(sp, m2, mlen);
208         }
209
210         (void)gp->scr_move(sp, oldy, oldx);
211         (void)gp->scr_refresh(sp, 0);
212 }
213
214 /*
215  * vs_msg --
216  *      Display ex output or error messages for the screen.
217  *
218  * This routine is the default editor interface for all ex output, and all ex
219  * and vi error/informational messages.  It implements the standard strategy
220  * of stealing lines from the bottom of the vi text screen.  Screens using an
221  * alternate method of displaying messages, e.g. dialog boxes, should set their
222  * scr_msg function to the correct function before calling the editor.
223  *
224  * PUBLIC: void vs_msg(SCR *, mtype_t, char *, size_t);
225  */
226 void
227 vs_msg(SCR *sp, mtype_t mtype, char *line, size_t len)
228 {
229         GS *gp;
230         VI_PRIVATE *vip;
231         size_t maxcols, oldx, oldy, padding;
232         const char *e, *s, *t;
233
234         gp = sp->gp;
235         vip = VIP(sp);
236
237         /*
238          * Ring the bell if it's scheduled.
239          *
240          * XXX
241          * Shouldn't we save this, too?
242          */
243         if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED)) {
244                 if (F_ISSET(sp, SC_SCR_VI)) {
245                         F_CLR(gp, G_BELLSCHED);
246                         (void)gp->scr_bell(sp);
247                 } else
248                         F_SET(gp, G_BELLSCHED);
249         }
250
251         /*
252          * If vi is using the error line for text input, there's no screen
253          * real-estate for the error message.  Nothing to do without some
254          * information as to how important the error message is.
255          */
256         if (F_ISSET(sp, SC_TINPUT_INFO))
257                 return;
258
259         /*
260          * Ex or ex controlled screen output.
261          *
262          * If output happens during startup, e.g., a .exrc file, we may be
263          * in ex mode but haven't initialized the screen.  Initialize here,
264          * and in this case, stay in ex mode.
265          *
266          * If the SC_SCR_EXWROTE bit is set, then we're switching back and
267          * forth between ex and vi, but the screen is trashed and we have
268          * to respect that.  Switch to ex mode long enough to put out the
269          * message.
270          *
271          * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
272          * the screen, so previous opinions are ignored.
273          */
274         if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
275                 if (!F_ISSET(sp, SC_SCR_EX)) {
276                         if (F_ISSET(sp, SC_SCR_EXWROTE)) {
277                                 if (sp->gp->scr_screen(sp, SC_EX))
278                                         return;
279                         } else
280                                 if (ex_init(sp))
281                                         return;
282                 }
283
284                 if (mtype == M_ERR)
285                         (void)gp->scr_attr(sp, SA_INVERSE, 1);
286                 (void)printf("%.*s", (int)len, line);
287                 if (mtype == M_ERR)
288                         (void)gp->scr_attr(sp, SA_INVERSE, 0);
289                 (void)fflush(stdout);
290
291                 F_CLR(sp, SC_EX_WAIT_NO);
292
293                 if (!F_ISSET(sp, SC_SCR_EX))
294                         (void)sp->gp->scr_screen(sp, SC_VI);
295                 return;
296         }
297
298         /* If the vi screen isn't ready, save the message. */
299         if (!F_ISSET(sp, SC_SCR_VI)) {
300                 (void)vs_msgsave(sp, mtype, line, len);
301                 return;
302         }
303
304         /* Save the cursor position. */
305         (void)gp->scr_cursor(sp, &oldy, &oldx);
306
307         /* If it's an ex output message, just write it out. */
308         if (mtype == M_NONE) {
309                 vs_output(sp, mtype, line, len);
310                 goto ret;
311         }
312
313         /*
314          * If it's a vi message, strip the trailing <newline> so we can
315          * try and paste messages together.
316          */
317         if (line[len - 1] == '\n')
318                 --len;
319
320         /*
321          * If a message won't fit on a single line, try to split on a <blank>.
322          * If a subsequent message fits on the same line, write a separator
323          * and output it.  Otherwise, put out a newline.
324          *
325          * Need up to two padding characters normally; a semi-colon and a
326          * separating space.  If only a single line on the screen, add some
327          * more for the trailing continuation message.
328          *
329          * XXX
330          * Assume that periods and semi-colons take up a single column on the
331          * screen.
332          *
333          * XXX
334          * There are almost certainly pathological cases that will break this
335          * code.
336          */
337         if (IS_ONELINE(sp))
338                 (void)msg_cmsg(sp, CMSG_CONT_S, &padding);
339         else
340                 padding = 0;
341         padding += 2;
342
343         maxcols = sp->cols - 1;
344         if (vip->lcontinue != 0) {
345                 if (len + vip->lcontinue + padding > maxcols)
346                         vs_output(sp, vip->mtype, ".\n", 2);
347                 else  {
348                         vs_output(sp, vip->mtype, ";", 1);
349                         vs_output(sp, M_NONE, " ", 1);
350                 }
351         }
352         vip->mtype = mtype;
353         for (s = line;; s = t) {
354                 for (; len > 0 && isblank((u_char)*s); --len, ++s);
355                 if (len == 0)
356                         break;
357                 if (len + vip->lcontinue > maxcols) {
358                         for (e = s + (maxcols - vip->lcontinue);
359                             e > s && !isblank((u_char)*e); --e);
360                         if (e == s)
361                                  e = t = s + (maxcols - vip->lcontinue);
362                         else
363                                 for (t = e; isblank((u_char)e[-1]); --e);
364                 } else
365                         e = t = s + len;
366
367                 /*
368                  * If the message ends in a period, discard it, we want to
369                  * gang messages where possible.
370                  */
371                 len -= t - s;
372                 if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
373                         --e;
374                 vs_output(sp, mtype, s, e - s);
375
376                 if (len != 0)
377                         vs_output(sp, M_NONE, "\n", 1);
378
379                 if (INTERRUPTED(sp))
380                         break;
381         }
382
383 ret:    (void)gp->scr_move(sp, oldy, oldx);
384         (void)gp->scr_refresh(sp, 0);
385 }
386
387 /*
388  * vs_output --
389  *      Output the text to the screen.
390  */
391 static void
392 vs_output(SCR *sp, mtype_t mtype, const char *line, int llen)
393 {
394         GS *gp;
395         VI_PRIVATE *vip;
396         size_t notused;
397         int len, rlen, tlen;
398         const char *p, *t;
399         char *cbp, *ecbp, cbuf[128];
400
401         gp = sp->gp;
402         vip = VIP(sp);
403         for (p = line, rlen = llen; llen > 0;) {
404                 /* Get the next physical line. */
405                 if ((p = memchr(line, '\n', llen)) == NULL)
406                         len = llen;
407                 else
408                         len = p - line;
409
410                 /*
411                  * The max is sp->cols characters, and we may have already
412                  * written part of the line.
413                  */
414                 if (len + vip->lcontinue > sp->cols)
415                         len = sp->cols - vip->lcontinue;
416
417                 /*
418                  * If the first line output, do nothing.  If the second line
419                  * output, draw the divider line.  If drew a full screen, we
420                  * remove the divider line.  If it's a continuation line, move
421                  * to the continuation point, else, move the screen up.
422                  */
423                 if (vip->lcontinue == 0) {
424                         if (!IS_ONELINE(sp)) {
425                                 if (vip->totalcount == 1) {
426                                         (void)gp->scr_move(sp,
427                                             LASTLINE(sp) - 1, 0);
428                                         (void)gp->scr_clrtoeol(sp);
429                                         (void)vs_divider(sp);
430                                         F_SET(vip, VIP_DIVIDER);
431                                         ++vip->totalcount;
432                                         ++vip->linecount;
433                                 }
434                                 if (vip->totalcount == sp->t_maxrows &&
435                                     F_ISSET(vip, VIP_DIVIDER)) {
436                                         --vip->totalcount;
437                                         --vip->linecount;
438                                         F_CLR(vip, VIP_DIVIDER);
439                                 }
440                         }
441                         if (vip->totalcount != 0)
442                                 vs_scroll(sp, NULL, SCROLL_W_QUIT);
443
444                         (void)gp->scr_move(sp, LASTLINE(sp), 0);
445                         ++vip->totalcount;
446                         ++vip->linecount;
447
448                         if (INTERRUPTED(sp))
449                                 break;
450                 } else
451                         (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
452
453                 /* Error messages are in inverse video. */
454                 if (mtype == M_ERR)
455                         (void)gp->scr_attr(sp, SA_INVERSE, 1);
456
457                 /* Display the line, doing character translation. */
458 #define FLUSH do {                                                      \
459         *cbp = '\0';                                                    \
460         (void)gp->scr_addstr(sp, cbuf, cbp - cbuf);                     \
461         cbp = cbuf;                                                     \
462 } while (0)
463                 ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
464                 for (t = line, tlen = len; tlen--; ++t) {
465                         /*
466                          * Replace tabs with spaces, there are places in
467                          * ex that do column calculations without looking
468                          * at <tabs> -- and all routines that care about
469                          * <tabs> do their own expansions.  This catches
470                          * <tabs> in things like tag search strings.
471                          */
472                         if (cbp + 1 >= ecbp)
473                                 FLUSH;
474                         *cbp++ = *t == '\t' ? ' ' : *t;
475                 }
476                 if (cbp > cbuf)
477                         FLUSH;
478                 if (mtype == M_ERR)
479                         (void)gp->scr_attr(sp, SA_INVERSE, 0);
480
481                 /* Clear the rest of the line. */
482                 (void)gp->scr_clrtoeol(sp);
483
484                 /* If we loop, it's a new line. */
485                 vip->lcontinue = 0;
486
487                 /* Reset for the next line. */
488                 line += len;
489                 llen -= len;
490                 if (p != NULL) {
491                         ++line;
492                         --llen;
493                 }
494         }
495
496         /* Set up next continuation line. */
497         if (p == NULL)
498                 gp->scr_cursor(sp, &notused, &vip->lcontinue);
499 }
500
501 /*
502  * vs_ex_resolve --
503  *      Deal with ex message output.
504  *
505  * This routine is called when exiting a colon command to resolve any ex
506  * output that may have occurred.
507  *
508  * PUBLIC: int vs_ex_resolve(SCR *, int *);
509  */
510 int
511 vs_ex_resolve(SCR *sp, int *continuep)
512 {
513         EVENT ev;
514         GS *gp;
515         VI_PRIVATE *vip;
516         sw_t wtype;
517
518         gp = sp->gp;
519         vip = VIP(sp);
520         *continuep = 0;
521
522         /* If we ran any ex command, we can't trust the cursor position. */
523         F_SET(vip, VIP_CUR_INVALID);
524
525         /* Terminate any partially written message. */
526         if (vip->lcontinue != 0) {
527                 vs_output(sp, vip->mtype, ".", 1);
528                 vip->lcontinue = 0;
529
530                 vip->mtype = M_NONE;
531         }
532
533         /*
534          * If we switched out of the vi screen into ex, switch back while we
535          * figure out what to do with the screen and potentially get another
536          * command to execute.
537          *
538          * If we didn't switch into ex, we're not required to wait, and less
539          * than 2 lines of output, we can continue without waiting for the
540          * wait.
541          *
542          * Note, all other code paths require waiting, so we leave the report
543          * of modified lines until later, so that we won't wait for no other
544          * reason than a threshold number of lines were modified.  This means
545          * we display cumulative line modification reports for groups of ex
546          * commands.  That seems right to me (well, at least not wrong).
547          */
548         if (F_ISSET(sp, SC_SCR_EXWROTE)) {
549                 if (sp->gp->scr_screen(sp, SC_VI))
550                         return (1);
551         } else
552                 if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
553                         F_CLR(sp, SC_EX_WAIT_NO);
554                         return (0);
555                 }
556
557         /* Clear the required wait flag, it's no longer needed. */
558         F_CLR(sp, SC_EX_WAIT_YES);
559
560         /*
561          * Wait, unless explicitly told not to wait or the user interrupted
562          * the command.  If the user is leaving the screen, for any reason,
563          * they can't continue with further ex commands.
564          */
565         if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
566                 wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
567                     SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
568                 if (F_ISSET(sp, SC_SCR_EXWROTE))
569                         vs_wait(sp, continuep, wtype);
570                 else
571                         vs_scroll(sp, continuep, wtype);
572                 if (*continuep)
573                         return (0);
574         }
575
576         /* If ex wrote on the screen, refresh the screen image. */
577         if (F_ISSET(sp, SC_SCR_EXWROTE))
578                 F_SET(vip, VIP_N_EX_PAINT);
579
580         /*
581          * If we're not the bottom of the split screen stack, the screen
582          * image itself is wrong, so redraw everything.
583          */
584         if (TAILQ_NEXT(sp, q) != NULL)
585                 F_SET(sp, SC_SCR_REDRAW);
586
587         /* If ex changed the underlying file, the map itself is wrong. */
588         if (F_ISSET(vip, VIP_N_EX_REDRAW))
589                 F_SET(sp, SC_SCR_REFORMAT);
590
591         /* Ex may have switched out of the alternate screen, return. */
592         (void)gp->scr_attr(sp, SA_ALTERNATE, 1);
593
594         /*
595          * Whew.  We're finally back home, after what feels like years.
596          * Kiss the ground.
597          */
598         F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
599
600         /*
601          * We may need to repaint some of the screen, e.g.:
602          *
603          *      :set
604          *      :!ls
605          *
606          * gives us a combination of some lines that are "wrong", and a need
607          * for a full refresh.
608          */
609         if (vip->totalcount > 1) {
610                 /* Set up the redraw of the overwritten lines. */
611                 ev.e_event = E_REPAINT;
612                 ev.e_flno = vip->totalcount >=
613                     sp->rows ? 1 : sp->rows - vip->totalcount;
614                 ev.e_tlno = sp->rows;
615
616                 /* Reset the count of overwriting lines. */
617                 vip->linecount = vip->lcontinue = vip->totalcount = 0;
618
619                 /* Redraw. */
620                 (void)vs_repaint(sp, &ev);
621         } else
622                 /* Reset the count of overwriting lines. */
623                 vip->linecount = vip->lcontinue = vip->totalcount = 0;
624
625         return (0);
626 }
627
628 /*
629  * vs_resolve --
630  *      Deal with message output.
631  *
632  * PUBLIC: int vs_resolve(SCR *, SCR *, int);
633  */
634 int
635 vs_resolve(SCR *sp, SCR *csp, int forcewait)
636 {
637         EVENT ev;
638         GS *gp;
639         MSGS *mp;
640         VI_PRIVATE *vip;
641         size_t oldy, oldx;
642         int redraw;
643
644         /*
645          * Vs_resolve is called from the main vi loop and the refresh function
646          * to periodically ensure that the user has seen any messages that have
647          * been displayed and that any status lines are correct.  The sp screen
648          * is the screen we're checking, usually the current screen.  When it's
649          * not, csp is the current screen, used for final cursor positioning.
650          */
651         gp = sp->gp;
652         vip = VIP(sp);
653         if (csp == NULL)
654                 csp = sp;
655
656         /* Save the cursor position. */
657         (void)gp->scr_cursor(csp, &oldy, &oldx);
658
659         /* Ring the bell if it's scheduled. */
660         if (F_ISSET(gp, G_BELLSCHED)) {
661                 F_CLR(gp, G_BELLSCHED);
662                 (void)gp->scr_bell(sp);
663         }
664
665         /* Display new file status line. */
666         if (F_ISSET(sp, SC_STATUS)) {
667                 F_CLR(sp, SC_STATUS);
668                 msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
669         }
670
671         /* Report on line modifications. */
672         mod_rpt(sp);
673
674         /*
675          * Flush any saved messages.  If the screen isn't ready, refresh
676          * it.  (A side-effect of screen refresh is that we can display
677          * messages.)  Once this is done, don't trust the cursor.  That
678          * extra refresh screwed the pooch.
679          */
680         if (!SLIST_EMPTY(gp->msgq)) {
681                 if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
682                         return (1);
683                 while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
684                         gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
685                         SLIST_REMOVE_HEAD(gp->msgq, q);
686                         free(mp->buf);
687                         free(mp);
688                 }
689                 F_SET(vip, VIP_CUR_INVALID);
690         }
691
692         switch (vip->totalcount) {
693         case 0:
694                 redraw = 0;
695                 break;
696         case 1:
697                 /*
698                  * If we're switching screens, we have to wait for messages,
699                  * regardless.  If we don't wait, skip updating the modeline.
700                  */
701                 if (forcewait)
702                         vs_scroll(sp, NULL, SCROLL_W);
703                 else
704                         F_SET(vip, VIP_S_MODELINE);
705
706                 redraw = 0;
707                 break;
708         default:
709                 /*
710                  * If >1 message line in use, prompt the user to continue and
711                  * repaint overwritten lines.
712                  */
713                 vs_scroll(sp, NULL, SCROLL_W);
714
715                 ev.e_event = E_REPAINT;
716                 ev.e_flno = vip->totalcount >=
717                     sp->rows ? 1 : sp->rows - vip->totalcount;
718                 ev.e_tlno = sp->rows;
719
720                 redraw = 1;
721                 break;
722         }
723
724         /* Reset the count of overwriting lines. */
725         vip->linecount = vip->lcontinue = vip->totalcount = 0;
726
727         /* Redraw. */
728         if (redraw)
729                 (void)vs_repaint(sp, &ev);
730
731         /* Restore the cursor position. */
732         (void)gp->scr_move(csp, oldy, oldx);
733
734         return (0);
735 }
736
737 /*
738  * vs_scroll --
739  *      Scroll the screen for output.
740  */
741 static void
742 vs_scroll(SCR *sp, int *continuep, sw_t wtype)
743 {
744         GS *gp;
745         VI_PRIVATE *vip;
746
747         gp = sp->gp;
748         vip = VIP(sp);
749         if (!IS_ONELINE(sp)) {
750                 /*
751                  * Scroll the screen.  Instead of scrolling the entire screen,
752                  * delete the line above the first line output so preserve the
753                  * maximum amount of the screen.
754                  */
755                 (void)gp->scr_move(sp, vip->totalcount <
756                     sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
757                 (void)gp->scr_deleteln(sp);
758
759                 /* If there are screens below us, push them back into place. */
760                 if (TAILQ_NEXT(sp, q) != NULL) {
761                         (void)gp->scr_move(sp, LASTLINE(sp), 0);
762                         (void)gp->scr_insertln(sp);
763                 }
764         }
765         if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
766                 return;
767         vs_wait(sp, continuep, wtype);
768 }
769
770 /*
771  * vs_wait --
772  *      Prompt the user to continue.
773  */
774 static void
775 vs_wait(SCR *sp, int *continuep, sw_t wtype)
776 {
777         EVENT ev;
778         VI_PRIVATE *vip;
779         const char *p;
780         GS *gp;
781         size_t len;
782
783         gp = sp->gp;
784         vip = VIP(sp);
785
786         (void)gp->scr_move(sp, LASTLINE(sp), 0);
787         if (IS_ONELINE(sp))
788                 p = msg_cmsg(sp, CMSG_CONT_S, &len);
789         else
790                 switch (wtype) {
791                 case SCROLL_W_QUIT:
792                         p = msg_cmsg(sp, CMSG_CONT_Q, &len);
793                         break;
794                 case SCROLL_W_EX:
795                         p = msg_cmsg(sp, CMSG_CONT_EX, &len);
796                         break;
797                 case SCROLL_W:
798                         p = msg_cmsg(sp, CMSG_CONT, &len);
799                         break;
800                 default:
801                         abort();
802                         /* NOTREACHED */
803                 }
804         (void)gp->scr_addstr(sp, p, len);
805
806         ++vip->totalcount;
807         vip->linecount = 0;
808
809         (void)gp->scr_clrtoeol(sp);
810         (void)gp->scr_refresh(sp, 0);
811
812         /* Get a single character from the terminal. */
813         if (continuep != NULL)
814                 *continuep = 0;
815         for (;;) {
816                 if (v_event_get(sp, &ev, 0, 0))
817                         return;
818                 if (ev.e_event == E_CHARACTER)
819                         break;
820                 if (ev.e_event == E_INTERRUPT) {
821                         ev.e_c = CH_QUIT;
822                         F_SET(gp, G_INTERRUPTED);
823                         break;
824                 }
825                 (void)gp->scr_bell(sp);
826         }
827         switch (wtype) {
828         case SCROLL_W_QUIT:
829                 if (ev.e_c == CH_QUIT)
830                         F_SET(gp, G_INTERRUPTED);
831                 break;
832         case SCROLL_W_EX:
833                 if (ev.e_c == ':' && continuep != NULL)
834                         *continuep = 1;
835                 break;
836         case SCROLL_W:
837                 break;
838         }
839 }
840
841 /*
842  * vs_divider --
843  *      Draw a dividing line between the screen and the output.
844  */
845 static void
846 vs_divider(SCR *sp)
847 {
848         GS *gp;
849         size_t len;
850
851 #define DIVIDESTR       "+=+=+=+=+=+=+=+"
852         len =
853             sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
854         gp = sp->gp;
855         (void)gp->scr_attr(sp, SA_INVERSE, 1);
856         (void)gp->scr_addstr(sp, DIVIDESTR, len);
857         (void)gp->scr_attr(sp, SA_INVERSE, 0);
858 }
859
860 /*
861  * vs_msgsave --
862  *      Save a message for later display.
863  */
864 static void
865 vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len)
866 {
867         GS *gp;
868         MSGS *mp_c, *mp_n;
869
870         /*
871          * We have to handle messages before we have any place to put them.
872          * If there's no screen support yet, allocate a msg structure, copy
873          * in the message, and queue it on the global structure.  If we can't
874          * allocate memory here, we're genuinely screwed, dump the message
875          * to stderr in the (probably) vain hope that someone will see it.
876          */
877         CALLOC_GOTO(sp, mp_n, 1, sizeof(MSGS));
878         MALLOC_GOTO(sp, mp_n->buf, len);
879
880         memmove(mp_n->buf, p, len);
881         mp_n->len = len;
882         mp_n->mtype = mt;
883
884         gp = sp->gp;
885         if (SLIST_EMPTY(gp->msgq)) {
886                 SLIST_INSERT_HEAD(gp->msgq, mp_n, q);
887         } else {
888                 SLIST_FOREACH(mp_c, gp->msgq, q)
889                         if (SLIST_NEXT(mp_c, q) == NULL)
890                                 break;
891                 SLIST_INSERT_AFTER(mp_c, mp_n, q);
892         }
893         return;
894
895 alloc_err:
896         free(mp_n);
897         (void)fprintf(stderr, "%.*s\n", (int)len, p);
898 }