GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../vi/vs_msg.c Lines: 0 339 0.0 %
Date: 2017-11-07 Branches: 0 278 0.0 %

Line Branch Exec Source
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
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
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
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
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
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
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
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
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
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
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
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
}