xref: /freebsd/contrib/less/output.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*
2  * Copyright (C) 1984-2024  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information, see the README file.
8  */
9 
10 
11 /*
12  * High level routines dealing with the output to the screen.
13  */
14 
15 #include "less.h"
16 #if MSDOS_COMPILER==WIN32C
17 #include "windows.h"
18 #ifndef COMMON_LVB_UNDERSCORE
19 #define COMMON_LVB_UNDERSCORE 0x8000
20 #endif
21 #endif
22 
23 public int errmsgs;    /* Count of messages displayed by error() */
24 public int need_clr;
25 public int final_attr;
26 public int at_prompt;
27 
28 extern int sigs;
29 extern int sc_width;
30 extern int so_s_width, so_e_width;
31 extern int is_tty;
32 extern int oldbot;
33 extern int utf_mode;
34 extern char intr_char;
35 
36 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
37 extern int ctldisp;
38 extern int nm_fg_color, nm_bg_color;
39 extern int bo_fg_color, bo_bg_color;
40 extern int ul_fg_color, ul_bg_color;
41 extern int so_fg_color, so_bg_color;
42 extern int bl_fg_color, bl_bg_color;
43 extern int sgr_mode;
44 #if MSDOS_COMPILER==WIN32C
45 extern int vt_enabled;
46 #endif
47 #endif
48 
49 /*
50  * Display the line which is in the line buffer.
51  */
52 public void put_line(void)
53 {
54 	int c;
55 	size_t i;
56 	int a;
57 
58 	if (ABORT_SIGS())
59 	{
60 		/*
61 		 * Don't output if a signal is pending.
62 		 */
63 		screen_trashed();
64 		return;
65 	}
66 
67 	final_attr = AT_NORMAL;
68 
69 	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
70 	{
71 		at_switch(a);
72 		final_attr = a;
73 		if (c == '\b')
74 			putbs();
75 		else
76 			putchr(c);
77 	}
78 
79 	at_exit();
80 }
81 
82 /*
83  * win_flush has at least one non-critical issue when an escape sequence
84  * begins at the last char of the buffer, and possibly more issues.
85  * as a temporary measure to reduce likelyhood of encountering end-of-buffer
86  * issues till the SGR parser is replaced, OUTBUF_SIZE is 8K on Windows.
87  */
88 static char obuf[OUTBUF_SIZE];
89 static char *ob = obuf;
90 static int outfd = 2; /* stderr */
91 
92 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
93 
94 typedef unsigned t_attr;
95 
96 #define A_BOLD      (1u<<0)
97 #define A_ITALIC    (1u<<1)
98 #define A_UNDERLINE (1u<<2)
99 #define A_BLINK     (1u<<3)
100 #define A_INVERSE   (1u<<4)
101 #define A_CONCEAL   (1u<<5)
102 
103 /* long is guaranteed 32 bits, and we reserve bits for type + RGB */
104 typedef unsigned long t_color;
105 
106 #define T_DEFAULT   0ul
107 #define T_ANSI      1ul  /* colors 0-7 */
108 
109 #define CGET_ANSI(c) ((c) & 0x7)
110 
111 #define C_DEFAULT    (T_DEFAULT <<24) /* 0 */
112 #define C_ANSI(c)   ((T_ANSI    <<24) | (c))
113 
114 /* attr/fg/bg/all 0 is the default attr/fg/bg/all, respectively */
115 typedef struct t_sgr {
116 	t_attr attr;
117 	t_color fg;
118 	t_color bg;
119 } t_sgr;
120 
121 static constant t_sgr SGR_DEFAULT; /* = {0} */
122 
123 /* returns 0 on success, non-0 on unknown SGR code */
124 static int update_sgr(t_sgr *sgr, long code)
125 {
126 	switch (code)
127 	{
128 	case  0: *sgr = SGR_DEFAULT; break;
129 
130 	case  1: sgr->attr |=  A_BOLD; break;
131 	case 22: sgr->attr &= ~A_BOLD; break;
132 
133 	case  3: sgr->attr |=  A_ITALIC; break;
134 	case 23: sgr->attr &= ~A_ITALIC; break;
135 
136 	case  4: sgr->attr |=  A_UNDERLINE; break;
137 	case 24: sgr->attr &= ~A_UNDERLINE; break;
138 
139 	case  6: /* fast-blink, fallthrough */
140 	case  5: sgr->attr |=  A_BLINK; break;
141 	case 25: sgr->attr &= ~A_BLINK; break;
142 
143 	case  7: sgr->attr |=  A_INVERSE; break;
144 	case 27: sgr->attr &= ~A_INVERSE; break;
145 
146 	case  8: sgr->attr |=  A_CONCEAL; break;
147 	case 28: sgr->attr &= ~A_CONCEAL; break;
148 
149 	case 39: sgr->fg = C_DEFAULT; break;
150 	case 49: sgr->bg = C_DEFAULT; break;
151 
152 	case 30: case 31: case 32: case 33:
153 	case 34: case 35: case 36: case 37:
154 		sgr->fg = C_ANSI(code - 30);
155 		break;
156 
157 	case 40: case 41: case 42: case 43:
158 	case 44: case 45: case 46: case 47:
159 		sgr->bg = C_ANSI(code - 40);
160 		break;
161 	default:
162 		return 1;
163 	}
164 
165 	return 0;
166 }
167 
168 static void set_win_colors(t_sgr *sgr)
169 {
170 #if MSDOS_COMPILER==WIN32C
171 	/* Screen colors used by 3x and 4x SGR commands. */
172 	static unsigned char screen_color[] = {
173 		0, /* BLACK */
174 		FOREGROUND_RED,
175 		FOREGROUND_GREEN,
176 		FOREGROUND_RED|FOREGROUND_GREEN,
177 		FOREGROUND_BLUE,
178 		FOREGROUND_BLUE|FOREGROUND_RED,
179 		FOREGROUND_BLUE|FOREGROUND_GREEN,
180 		FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
181 	};
182 #else
183 	static enum COLORS screen_color[] = {
184 		BLACK, RED, GREEN, BROWN,
185 		BLUE, MAGENTA, CYAN, LIGHTGRAY
186 	};
187 #endif
188 
189 	int fg, bg, tmp;  /* Windows colors */
190 
191 	/* Not "SGR mode": apply -D<x> to default fg+bg with one attribute */
192 	if (!sgr_mode && sgr->fg == C_DEFAULT && sgr->bg == C_DEFAULT)
193 	{
194 		switch (sgr->attr)
195 		{
196 		case A_BOLD:
197 			WIN32setcolors(bo_fg_color, bo_bg_color);
198 			return;
199 		case A_UNDERLINE:
200 			WIN32setcolors(ul_fg_color, ul_bg_color);
201 			return;
202 		case A_BLINK:
203 			WIN32setcolors(bl_fg_color, bl_bg_color);
204 			return;
205 		case A_INVERSE:
206 			WIN32setcolors(so_fg_color, so_bg_color);
207 			return;
208 		/*
209 		 * There's no -Di so italic should not be here, but to
210 		 * preserve legacy behavior, apply -Ds to italic too.
211 		 */
212 		case A_ITALIC:
213 			WIN32setcolors(so_fg_color, so_bg_color);
214 			return;
215 		}
216 	}
217 
218 	/* generic application of the SGR state as Windows colors */
219 
220 	fg = sgr->fg == C_DEFAULT ? nm_fg_color
221 	                          : screen_color[CGET_ANSI(sgr->fg)];
222 
223 	bg = sgr->bg == C_DEFAULT ? nm_bg_color
224 	                          : screen_color[CGET_ANSI(sgr->bg)];
225 
226 	if (sgr->attr & A_BOLD)
227 		fg |= 8;
228 
229 	if (sgr->attr & (A_BLINK | A_UNDERLINE))
230 		bg |= 8;  /* TODO: can be illegible */
231 
232 	if (sgr->attr & (A_INVERSE | A_ITALIC))
233 	{
234 		tmp = fg;
235 		fg = bg;
236 		bg = tmp;
237 	}
238 
239 	if (sgr->attr & A_CONCEAL)
240 		fg = bg ^ 8;
241 
242 	WIN32setcolors(fg, bg);
243 }
244 
245 /* like is_ansi_end, but doesn't assume c != 0  (returns 0 for c == 0) */
246 static int is_ansi_end_0(char c)
247 {
248 	return c && is_ansi_end((unsigned char)c);
249 }
250 
251 static void win_flush(void)
252 {
253 	if (ctldisp != OPT_ONPLUS
254 #if MSDOS_COMPILER==WIN32C
255 	    || (vt_enabled && sgr_mode)
256 #endif
257 	   )
258 		WIN32textout(obuf, ptr_diff(ob, obuf));
259 	else
260 	{
261 		/*
262 		 * Digest text, apply embedded SGR sequences as Windows-colors.
263 		 * By default - when -Da ("SGR mode") is unset - also apply
264 		 * translation of -D command-line options (at set_win_colors)
265 		 */
266 		char *anchor, *p, *p_next;
267 		static t_sgr sgr;
268 
269 		/* when unsupported SGR value is encountered, like 38/48 for
270 		 * 256/true colors, then we abort processing this sequence,
271 		 * because it may expect followup values, but we don't know
272 		 * how many, so we've lost sync of this sequence parsing.
273 		 * Without VT enabled it's OK because we can't do much anyway,
274 		 * but with VT enabled we choose to passthrough this sequence
275 		 * to the terminal - which can handle it better than us.
276 		 * however, this means that our "sgr" var is no longer in sync
277 		 * with the actual terminal state, which can lead to broken
278 		 * colors with future sequences which we _can_ fully parse.
279 		 * in such case, once it happens, we keep passthrough sequences
280 		 * until we know we're in sync again - on a valid reset.
281 		 */
282 		static int sgr_bad_sync;
283 
284 		for (anchor = p_next = obuf;
285 			 (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
286 		{
287 			p = p_next;
288 			if (p[1] == '[')  /* "ESC-[" sequence */
289 			{
290 				/*
291 				* unknown SGR code ignores the rest of the seq,
292 				* and allows ignoring sequences such as
293 				* ^[[38;5;123m or ^[[38;2;5;6;7m
294 				* (prior known codes at the same seq do apply)
295 				*/
296 				int bad_code = 0;
297 
298 				if (p > anchor)
299 				{
300 					/*
301 					 * If some chars seen since
302 					 * the last escape sequence,
303 					 * write them out to the screen.
304 					 */
305 					WIN32textout(anchor, ptr_diff(p, anchor));
306 					anchor = p;
307 				}
308 				p += 2;  /* Skip the "ESC-[" */
309 				if (is_ansi_end_0(*p))
310 				{
311 					/*
312 					 * Handle null escape sequence
313 					 * "ESC[m" as if it was "ESC[0m"
314 					 */
315 					p++;
316 					anchor = p_next = p;
317 					update_sgr(&sgr, 0);
318 					set_win_colors(&sgr);
319 					sgr_bad_sync = 0;
320 					continue;
321 				}
322 				p_next = p;
323 
324 				/*
325 				 * Parse and apply SGR values to the SGR state
326 				 * based on the escape sequence.
327 				 */
328 				while (!is_ansi_end_0(*p))
329 				{
330 					char *q;
331 					long code = strtol(p, &q, 10);
332 
333 					if (*q == '\0')
334 					{
335 						/*
336 						 * Incomplete sequence.
337 						 * Leave it unprocessed
338 						 * in the buffer.
339 						 */
340 						size_t slop = ptr_diff(q, anchor);
341 						memmove(obuf, anchor, slop);
342 						ob = &obuf[slop];
343 						return;
344 					}
345 
346 					if (q == p ||
347 						(!is_ansi_end_0(*q) && *q != ';'))
348 					{
349 						/*
350 						 * can't parse. passthrough
351 						 * till the end of the buffer
352 						 */
353 						p_next = q;
354 						break;
355 					}
356 					if (*q == ';')
357 						q++;
358 
359 					if (!bad_code)
360 						bad_code = update_sgr(&sgr, code);
361 
362 					if (bad_code)
363 						sgr_bad_sync = 1;
364 					else if (code == 0)
365 						sgr_bad_sync = 0;
366 
367 					p = q;
368 				}
369 				if (!is_ansi_end_0(*p) || p == p_next)
370 					break;
371 
372 				if (sgr_bad_sync && vt_enabled) {
373 					/* this or a prior sequence had unknown
374 					 * SGR value. passthrough all sequences
375 					 * until we're in-sync again
376 					 */
377 					WIN32textout(anchor, ptr_diff(p+1, anchor));
378 				} else {
379 					set_win_colors(&sgr);
380 				}
381 				p_next = anchor = p + 1;
382 			} else
383 				p_next++;
384 		}
385 
386 		/* Output what's left in the buffer.  */
387 		WIN32textout(anchor, ptr_diff(ob, anchor));
388 	}
389 	ob = obuf;
390 }
391 #endif
392 
393 /*
394  * Flush buffered output.
395  *
396  * If we haven't displayed any file data yet,
397  * output messages on error output (file descriptor 2),
398  * otherwise output on standard output (file descriptor 1).
399  *
400  * This has the desirable effect of producing all
401  * error messages on error output if standard output
402  * is directed to a file.  It also does the same if
403  * we never produce any real output; for example, if
404  * the input file(s) cannot be opened.  If we do
405  * eventually produce output, code in edit() makes
406  * sure these messages can be seen before they are
407  * overwritten or scrolled away.
408  */
409 public void flush(void)
410 {
411 	size_t n;
412 
413 	n = ptr_diff(ob, obuf);
414 	if (n == 0)
415 		return;
416 	ob = obuf;
417 
418 #if MSDOS_COMPILER==MSOFTC
419 	if (interactive())
420 	{
421 		obuf[n] = '\0';
422 		_outtext(obuf);
423 		return;
424 	}
425 #else
426 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
427 	if (interactive())
428 	{
429 		ob = obuf + n;
430 		*ob = '\0';
431 		win_flush();
432 		return;
433 	}
434 #endif
435 #endif
436 
437 	if (write(outfd, obuf, n) != n)
438 		screen_trashed();
439 }
440 
441 /*
442  * Set the output file descriptor (1=stdout or 2=stderr).
443  */
444 public void set_output(int fd)
445 {
446 	flush();
447 	outfd = fd;
448 }
449 
450 /*
451  * Output a character.
452  * ch is int for compatibility with tputs.
453  */
454 public int putchr(int ch)
455 {
456 	char c = (char) ch;
457 #if 0 /* fake UTF-8 output for testing */
458 	extern int utf_mode;
459 	if (utf_mode)
460 	{
461 		static char ubuf[MAX_UTF_CHAR_LEN];
462 		static int ubuf_len = 0;
463 		static int ubuf_index = 0;
464 		if (ubuf_len == 0)
465 		{
466 			ubuf_len = utf_len(c);
467 			ubuf_index = 0;
468 		}
469 		ubuf[ubuf_index++] = c;
470 		if (ubuf_index < ubuf_len)
471 			return c;
472 		c = get_wchar(ubuf) & 0xFF;
473 		ubuf_len = 0;
474 	}
475 #endif
476 	clear_bot_if_needed();
477 #if MSDOS_COMPILER
478 	if (c == '\n' && is_tty)
479 	{
480 		/* remove_top(1); */
481 		putchr('\r');
482 	}
483 #else
484 #ifdef _OSK
485 	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
486 		putchr(0x0A);
487 #endif
488 #endif
489 	/*
490 	 * Some versions of flush() write to *ob, so we must flush
491 	 * when we are still one char from the end of obuf.
492 	 */
493 	if (ob >= &obuf[sizeof(obuf)-1])
494 		flush();
495 	*ob++ = c;
496 	at_prompt = 0;
497 	return (c);
498 }
499 
500 public void clear_bot_if_needed(void)
501 {
502 	if (!need_clr)
503 		return;
504 	need_clr = 0;
505 	clear_bot();
506 }
507 
508 /*
509  * Output a string.
510  */
511 public void putstr(constant char *s)
512 {
513 	while (*s != '\0')
514 		putchr(*s++);
515 }
516 
517 
518 /*
519  * Convert an integral type to a string.
520  */
521 #define TYPE_TO_A_FUNC(funcname, type) \
522 void funcname(type num, char *buf, int radix) \
523 { \
524 	int neg = (num < 0); \
525 	char tbuf[INT_STRLEN_BOUND(num)+2]; \
526 	char *s = tbuf + sizeof(tbuf); \
527 	if (neg) num = -num; \
528 	*--s = '\0'; \
529 	do { \
530 		*--s = "0123456789ABCDEF"[num % radix]; \
531 	} while ((num /= radix) != 0); \
532 	if (neg) *--s = '-'; \
533 	strcpy(buf, s); \
534 }
535 
536 TYPE_TO_A_FUNC(postoa, POSITION)
537 TYPE_TO_A_FUNC(linenumtoa, LINENUM)
538 TYPE_TO_A_FUNC(inttoa, int)
539 
540 /*
541  * Convert a string to an integral type.  Return ((type) -1) on overflow.
542  */
543 #define STR_TO_TYPE_FUNC(funcname, cfuncname, type) \
544 type cfuncname(constant char *buf, constant char **ebuf, int radix) \
545 { \
546 	type val = 0; \
547 	lbool v = 0; \
548 	for (;; buf++) { \
549 		char c = *buf; \
550 		int digit = (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : -1; \
551 		if (digit < 0 || digit >= radix) break; \
552 		v = v || ckd_mul(&val, val, radix); \
553 		v = v || ckd_add(&val, val, digit); \
554 	} \
555 	if (ebuf != NULL) *ebuf = buf; \
556 	return v ? (type)(-1) : val; \
557 } \
558 type funcname(char *buf, char **ebuf, int radix) \
559 { \
560 	constant char *cbuf = buf; \
561 	type r = cfuncname(cbuf, &cbuf, radix); \
562 	if (ebuf != NULL) *ebuf = (char *) cbuf; /*{{const-issue}}*/ \
563 	return r; \
564 }
565 
566 STR_TO_TYPE_FUNC(lstrtopos, lstrtoposc, POSITION)
567 STR_TO_TYPE_FUNC(lstrtoi, lstrtoic, int)
568 STR_TO_TYPE_FUNC(lstrtoul, lstrtoulc, unsigned long)
569 
570 /*
571  * Print an integral type.
572  */
573 #define IPRINT_FUNC(funcname, type, typetoa) \
574 static int funcname(type num, int radix) \
575 { \
576 	char buf[INT_STRLEN_BOUND(num)]; \
577 	typetoa(num, buf, radix); \
578 	putstr(buf); \
579 	return (int) strlen(buf); \
580 }
581 
582 IPRINT_FUNC(iprint_int, int, inttoa)
583 IPRINT_FUNC(iprint_linenum, LINENUM, linenumtoa)
584 
585 /*
586  * This function implements printf-like functionality
587  * using a more portable argument list mechanism than printf's.
588  *
589  * {{ This paranoia about the portability of printf dates from experiences
590  *    with systems in the 1980s and is of course no longer necessary. }}
591  */
592 public int less_printf(constant char *fmt, PARG *parg)
593 {
594 	constant char *s;
595 	constant char *es;
596 	int col;
597 
598 	col = 0;
599 	while (*fmt != '\0')
600 	{
601 		if (*fmt != '%')
602 		{
603 			putchr(*fmt++);
604 			col++;
605 		} else
606 		{
607 			++fmt;
608 			switch (*fmt++)
609 			{
610 			case 's':
611 				s = parg->p_string;
612 				es = s + strlen(s);
613 				parg++;
614 				while (*s != '\0')
615 				{
616 					LWCHAR ch = step_charc(&s, +1, es);
617 					constant char *ps = utf_mode ? prutfchar(ch) : prchar(ch);
618 					while (*ps != '\0')
619 					{
620 						putchr(*ps++);
621 						col++;
622 					}
623 				}
624 				break;
625 			case 'd':
626 				col += iprint_int(parg->p_int, 10);
627 				parg++;
628 				break;
629 			case 'x':
630 				col += iprint_int(parg->p_int, 16);
631 				parg++;
632 				break;
633 			case 'n':
634 				col += iprint_linenum(parg->p_linenum, 10);
635 				parg++;
636 				break;
637 			case 'c':
638 				s = prchar((LWCHAR) parg->p_char);
639 				parg++;
640 				while (*s != '\0')
641 				{
642 					putchr(*s++);
643 					col++;
644 				}
645 				break;
646 			case '%':
647 				putchr('%');
648 				break;
649 			}
650 		}
651 	}
652 	return (col);
653 }
654 
655 /*
656  * Get a RETURN.
657  * If some other non-trivial char is pressed, unget it, so it will
658  * become the next command.
659  */
660 public void get_return(void)
661 {
662 	int c;
663 
664 #if ONLY_RETURN
665 	while ((c = getchr()) != '\n' && c != '\r')
666 		bell();
667 #else
668 	c = getchr();
669 	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
670 		ungetcc((char) c);
671 #endif
672 }
673 
674 /*
675  * Output a message in the lower left corner of the screen
676  * and wait for carriage return.
677  */
678 public void error(constant char *fmt, PARG *parg)
679 {
680 	int col = 0;
681 	static char return_to_continue[] = "  (press RETURN)";
682 
683 	errmsgs++;
684 
685 	if (!interactive())
686 	{
687 		less_printf(fmt, parg);
688 		putchr('\n');
689 		return;
690 	}
691 
692 	if (!oldbot)
693 		squish_check();
694 	at_exit();
695 	clear_bot();
696 	at_enter(AT_STANDOUT|AT_COLOR_ERROR);
697 	col += so_s_width;
698 	col += less_printf(fmt, parg);
699 	putstr(return_to_continue);
700 	at_exit();
701 	col += (int) sizeof(return_to_continue) + so_e_width;
702 
703 	get_return();
704 	lower_left();
705 	clear_eol();
706 
707 	if (col >= sc_width)
708 		/*
709 		 * Printing the message has probably scrolled the screen.
710 		 * {{ Unless the terminal doesn't have auto margins,
711 		 *    in which case we just hammered on the right margin. }}
712 		 */
713 		screen_trashed();
714 
715 	flush();
716 }
717 
718 /*
719  * Output a message in the lower left corner of the screen
720  * and don't wait for carriage return.
721  * Usually used to warn that we are beginning a potentially
722  * time-consuming operation.
723  */
724 static void ierror_suffix(constant char *fmt, PARG *parg, constant char *suffix1, constant char *suffix2, constant char *suffix3)
725 {
726 	at_exit();
727 	clear_bot();
728 	at_enter(AT_STANDOUT|AT_COLOR_ERROR);
729 	(void) less_printf(fmt, parg);
730 	putstr(suffix1);
731 	putstr(suffix2);
732 	putstr(suffix3);
733 	at_exit();
734 	flush();
735 	need_clr = 1;
736 }
737 
738 public void ierror(constant char *fmt, PARG *parg)
739 {
740 	ierror_suffix(fmt, parg, "... (interrupt to abort)", "", "");
741 }
742 
743 public void ixerror(constant char *fmt, PARG *parg)
744 {
745 	if (!supports_ctrl_x())
746 		ierror(fmt, parg);
747 	else
748 	{
749 		char ichar[MAX_PRCHAR_LEN+1];
750 		strcpy(ichar, prchar((LWCHAR) intr_char));
751 		ierror_suffix(fmt, parg, "... (", ichar, " or interrupt to abort)");
752 	}
753 }
754 
755 /*
756  * Output a message in the lower left corner of the screen
757  * and return a single-character response.
758  */
759 public int query(constant char *fmt, PARG *parg)
760 {
761 	int c;
762 	int col = 0;
763 
764 	if (interactive())
765 		clear_bot();
766 
767 	(void) less_printf(fmt, parg);
768 	c = getchr();
769 
770 	if (interactive())
771 	{
772 		lower_left();
773 		if (col >= sc_width)
774 			screen_trashed();
775 		flush();
776 	} else
777 	{
778 		putchr('\n');
779 	}
780 
781 	if (c == 'Q')
782 		quit(QUIT_OK);
783 	return (c);
784 }
785