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