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