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