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