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, ¬used, &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, ¬used, &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