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