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