xref: /openbsd/usr.bin/vi/vi/vs_msg.c (revision a0242ada)
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, &notused, &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, &notused, &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