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