source: trunk/third/nvi/vi/vs_msg.c @ 14302

Revision 14302, 21.9 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
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
13static 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
30typedef 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
40static void     vs_divider __P((SCR *));
41static void     vs_msgsave __P((SCR *, mtype_t, char *, size_t));
42static void     vs_output __P((SCR *, mtype_t, const char *, int));
43static void     vs_scroll __P((SCR *, int *, sw_t));
44static 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 */
58void
59vs_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 */
148void
149vs_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 */
162void
163vs_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 */
230void
231vs_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
388ret:    (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 */
396static void
397vs_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 */
525int
526vs_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 */
651int
652vs_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 */
760static void
761vs_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 */
796static void
797vs_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 */
870static void
871vs_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 */
890static void
891vs_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
923alloc_err:
924        if (mp_n != NULL)
925                free(mp_n);
926        (void)fprintf(stderr, "%.*s\n", (int)len, p);
927}
Note: See TracBrowser for help on using the repository browser.