160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
7240121Sdelphij * For more information, see the README file.
860786Sps */
960786Sps
1060786Sps
1160786Sps/*
1260786Sps * High level routines dealing with the output to the screen.
1360786Sps */
1460786Sps
1560786Sps#include "less.h"
1660786Sps#if MSDOS_COMPILER==WIN32C
1760786Sps#include "windows.h"
1860786Sps#endif
1960786Sps
2060786Spspublic int errmsgs;	/* Count of messages displayed by error() */
2160786Spspublic int need_clr;
2260786Spspublic int final_attr;
23170256Sdelphijpublic int at_prompt;
2460786Sps
2560786Spsextern int sigs;
2660786Spsextern int sc_width;
2760786Spsextern int so_s_width, so_e_width;
2860786Spsextern int screen_trashed;
2960786Spsextern int any_display;
3060786Spsextern int is_tty;
31170256Sdelphijextern int oldbot;
3260786Sps
33191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
34128345Stjrextern int ctldisp;
35128345Stjrextern int nm_fg_color, nm_bg_color;
36128345Stjrextern int bo_fg_color, bo_bg_color;
37128345Stjrextern int ul_fg_color, ul_bg_color;
38128345Stjrextern int so_fg_color, so_bg_color;
39128345Stjrextern int bl_fg_color, bl_bg_color;
40128345Stjr#endif
41128345Stjr
4260786Sps/*
4360786Sps * Display the line which is in the line buffer.
4460786Sps */
4560786Sps	public void
4660786Spsput_line()
4760786Sps{
4860786Sps	register int c;
4960786Sps	register int i;
5060786Sps	int a;
5160786Sps
5260786Sps	if (ABORT_SIGS())
5360786Sps	{
5460786Sps		/*
5560786Sps		 * Don't output if a signal is pending.
5660786Sps		 */
5760786Sps		screen_trashed = 1;
5860786Sps		return;
5960786Sps	}
6060786Sps
61161475Sdelphij	final_attr = AT_NORMAL;
6260786Sps
6360786Sps	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
6460786Sps	{
65161475Sdelphij		at_switch(a);
66161475Sdelphij		final_attr = a;
6760786Sps		if (c == '\b')
6860786Sps			putbs();
6960786Sps		else
7060786Sps			putchr(c);
7160786Sps	}
7260786Sps
73161475Sdelphij	at_exit();
7460786Sps}
7560786Sps
7660786Spsstatic char obuf[OUTBUF_SIZE];
7760786Spsstatic char *ob = obuf;
7860786Sps
7960786Sps/*
8060786Sps * Flush buffered output.
8160786Sps *
8260786Sps * If we haven't displayed any file data yet,
8360786Sps * output messages on error output (file descriptor 2),
8460786Sps * otherwise output on standard output (file descriptor 1).
8560786Sps *
8660786Sps * This has the desirable effect of producing all
8760786Sps * error messages on error output if standard output
8860786Sps * is directed to a file.  It also does the same if
8960786Sps * we never produce any real output; for example, if
9060786Sps * the input file(s) cannot be opened.  If we do
9160786Sps * eventually produce output, code in edit() makes
9260786Sps * sure these messages can be seen before they are
9360786Sps * overwritten or scrolled away.
9460786Sps */
9560786Sps	public void
9660786Spsflush()
9760786Sps{
9860786Sps	register int n;
9960786Sps	register int fd;
10060786Sps
10160786Sps	n = ob - obuf;
10260786Sps	if (n == 0)
10360786Sps		return;
10460786Sps
10560786Sps#if MSDOS_COMPILER==MSOFTC
10660786Sps	if (is_tty && any_display)
10760786Sps	{
10860786Sps		*ob = '\0';
10960786Sps		_outtext(obuf);
11060786Sps		ob = obuf;
11160786Sps		return;
11260786Sps	}
11360786Sps#else
114191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
11560786Sps	if (is_tty && any_display)
11660786Sps	{
11760786Sps		*ob = '\0';
118128345Stjr		if (ctldisp != OPT_ONPLUS)
119191930Sdelphij			WIN32textout(obuf, ob - obuf);
120128345Stjr		else
121128345Stjr		{
122128345Stjr			/*
123128345Stjr			 * Look for SGR escape sequences, and convert them
124128345Stjr			 * to color commands.  Replace bold, underline,
125128345Stjr			 * and italic escapes into colors specified via
126128345Stjr			 * the -D command-line option.
127128345Stjr			 */
128128345Stjr			char *anchor, *p, *p_next;
129191930Sdelphij			unsigned char fg, bg;
130191930Sdelphij			static unsigned char at;
131191930Sdelphij#if MSDOS_COMPILER==WIN32C
132191930Sdelphij			/* Screen colors used by 3x and 4x SGR commands. */
133191930Sdelphij			static unsigned char screen_color[] = {
134191930Sdelphij				0, /* BLACK */
135191930Sdelphij				FOREGROUND_RED,
136191930Sdelphij				FOREGROUND_GREEN,
137191930Sdelphij				FOREGROUND_RED|FOREGROUND_GREEN,
138191930Sdelphij				FOREGROUND_BLUE,
139191930Sdelphij				FOREGROUND_BLUE|FOREGROUND_RED,
140191930Sdelphij				FOREGROUND_BLUE|FOREGROUND_GREEN,
141191930Sdelphij				FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
142191930Sdelphij			};
143191930Sdelphij#else
144128345Stjr			static enum COLORS screen_color[] = {
145128345Stjr				BLACK, RED, GREEN, BROWN,
146128345Stjr				BLUE, MAGENTA, CYAN, LIGHTGRAY
147128345Stjr			};
148191930Sdelphij#endif
149128345Stjr
150128345Stjr			for (anchor = p_next = obuf;
151191930Sdelphij			     (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
152128345Stjr			{
153128345Stjr				p = p_next;
154191930Sdelphij				if (p[1] == '[')  /* "ESC-[" sequence */
155128345Stjr				{
156128345Stjr					if (p > anchor)
157128345Stjr					{
158191930Sdelphij						/*
159191930Sdelphij						 * If some chars seen since
160191930Sdelphij						 * the last escape sequence,
161191930Sdelphij						 * write them out to the screen.
162191930Sdelphij						 */
163191930Sdelphij						WIN32textout(anchor, p-anchor);
164128345Stjr						anchor = p;
165128345Stjr					}
166191930Sdelphij					p += 2;  /* Skip the "ESC-[" */
167191930Sdelphij					if (is_ansi_end(*p))
168191930Sdelphij					{
169191930Sdelphij						/*
170191930Sdelphij						 * Handle null escape sequence
171191930Sdelphij						 * "ESC[m", which restores
172191930Sdelphij						 * the normal color.
173191930Sdelphij						 */
174191930Sdelphij						p++;
175191930Sdelphij						anchor = p_next = p;
176240121Sdelphij						at = 0;
177191930Sdelphij						WIN32setcolors(nm_fg_color, nm_bg_color);
178191930Sdelphij						continue;
179191930Sdelphij					}
180128345Stjr					p_next = p;
181191930Sdelphij
182191930Sdelphij					/*
183191930Sdelphij					 * Select foreground/background colors
184191930Sdelphij					 * based on the escape sequence.
185191930Sdelphij					 */
186191930Sdelphij					fg = nm_fg_color;
187191930Sdelphij					bg = nm_bg_color;
188128345Stjr					while (!is_ansi_end(*p))
189128345Stjr					{
190128345Stjr						char *q;
191128345Stjr						long code = strtol(p, &q, 10);
192128345Stjr
193191930Sdelphij						if (*q == '\0')
194128345Stjr						{
195128345Stjr							/*
196128345Stjr							 * Incomplete sequence.
197128345Stjr							 * Leave it unprocessed
198128345Stjr							 * in the buffer.
199128345Stjr							 */
200128345Stjr							int slop = q - anchor;
201191930Sdelphij							/* {{ strcpy args overlap! }} */
202128345Stjr							strcpy(obuf, anchor);
203128345Stjr							ob = &obuf[slop];
204128345Stjr							return;
205128345Stjr						}
206128345Stjr
207191930Sdelphij						if (q == p ||
208191930Sdelphij						    code > 49 || code < 0 ||
209191930Sdelphij						    (!is_ansi_end(*q) && *q != ';'))
210128345Stjr						{
211128345Stjr							p_next = q;
212128345Stjr							break;
213128345Stjr						}
214128345Stjr						if (*q == ';')
215128345Stjr							q++;
216128345Stjr
217128345Stjr						switch (code)
218128345Stjr						{
219191930Sdelphij						default:
220191930Sdelphij						/* case 0: all attrs off */
221191930Sdelphij							fg = nm_fg_color;
222191930Sdelphij							bg = nm_bg_color;
223191930Sdelphij							at = 0;
224191930Sdelphij							break;
225128345Stjr						case 1:	/* bold on */
226191930Sdelphij							at |= 1;
227128345Stjr							break;
228128345Stjr						case 3:	/* italic on */
229191930Sdelphij						case 7: /* inverse on */
230191930Sdelphij							at |= 2;
231128345Stjr							break;
232128345Stjr						case 4:	/* underline on */
233191930Sdelphij							at |= 4;
234128345Stjr							break;
235191930Sdelphij						case 5: /* slow blink on */
236191930Sdelphij						case 6: /* fast blink on */
237191930Sdelphij							at |= 8;
238191930Sdelphij							break;
239128345Stjr						case 8:	/* concealed on */
240128345Stjr							fg = (bg & 7) | 8;
241128345Stjr							break;
242191930Sdelphij						case 22: /* bold off */
243191930Sdelphij							at &= ~1;
244128345Stjr							break;
245191930Sdelphij						case 23: /* italic off */
246191930Sdelphij						case 27: /* inverse off */
247191930Sdelphij							at &= ~2;
248191930Sdelphij							break;
249191930Sdelphij						case 24: /* underline off */
250191930Sdelphij							at &= ~4;
251191930Sdelphij							break;
252128345Stjr						case 30: case 31: case 32:
253128345Stjr						case 33: case 34: case 35:
254128345Stjr						case 36: case 37:
255128345Stjr							fg = (fg & 8) | (screen_color[code - 30]);
256128345Stjr							break;
257128345Stjr						case 39: /* default fg */
258128345Stjr							fg = nm_fg_color;
259128345Stjr							break;
260128345Stjr						case 40: case 41: case 42:
261128345Stjr						case 43: case 44: case 45:
262128345Stjr						case 46: case 47:
263128345Stjr							bg = (bg & 8) | (screen_color[code - 40]);
264128345Stjr							break;
265128345Stjr						case 49: /* default fg */
266128345Stjr							bg = nm_bg_color;
267128345Stjr							break;
268128345Stjr						}
269128345Stjr						p = q;
270128345Stjr					}
271191930Sdelphij					if (!is_ansi_end(*p) || p == p_next)
272191930Sdelphij						break;
273191930Sdelphij					if (at & 1)
274128345Stjr					{
275240121Sdelphij						/*
276240121Sdelphij						 * If \e[1m use defined bold
277240121Sdelphij						 * color, else set intensity.
278240121Sdelphij						 */
279240121Sdelphij						if (p[-2] == '[')
280240121Sdelphij						{
281251154Sdelphij#if MSDOS_COMPILER==WIN32C
282251154Sdelphij							fg |= FOREGROUND_INTENSITY;
283251154Sdelphij							bg |= BACKGROUND_INTENSITY;
284251154Sdelphij#else
285191930Sdelphij							fg = bo_fg_color;
286191930Sdelphij							bg = bo_bg_color;
287251154Sdelphij#endif
288240121Sdelphij						} else
289240121Sdelphij							fg |= 8;
290191930Sdelphij					} else if (at & 2)
291191930Sdelphij					{
292240121Sdelphij						fg = so_fg_color;
293240121Sdelphij						bg = so_bg_color;
294191930Sdelphij					} else if (at & 4)
295191930Sdelphij					{
296240121Sdelphij						fg = ul_fg_color;
297240121Sdelphij						bg = ul_bg_color;
298191930Sdelphij					} else if (at & 8)
299191930Sdelphij					{
300240121Sdelphij						fg = bl_fg_color;
301240121Sdelphij						bg = bl_bg_color;
302191930Sdelphij					}
303191930Sdelphij					fg &= 0xf;
304191930Sdelphij					bg &= 0xf;
305191930Sdelphij					WIN32setcolors(fg, bg);
306191930Sdelphij					p_next = anchor = p + 1;
307128345Stjr				} else
308128345Stjr					p_next++;
309128345Stjr			}
310128345Stjr
311128345Stjr			/* Output what's left in the buffer.  */
312191930Sdelphij			WIN32textout(anchor, ob - anchor);
313128345Stjr		}
31460786Sps		ob = obuf;
31560786Sps		return;
31660786Sps	}
31760786Sps#endif
31860786Sps#endif
31960786Sps	fd = (any_display) ? 1 : 2;
32060786Sps	if (write(fd, obuf, n) != n)
32160786Sps		screen_trashed = 1;
32260786Sps	ob = obuf;
32360786Sps}
32460786Sps
32560786Sps/*
32660786Sps * Output a character.
32760786Sps */
32860786Sps	public int
32960786Spsputchr(c)
33060786Sps	int c;
33160786Sps{
332161475Sdelphij#if 0 /* fake UTF-8 output for testing */
333161475Sdelphij	extern int utf_mode;
334161475Sdelphij	if (utf_mode)
335161475Sdelphij	{
336161475Sdelphij		static char ubuf[MAX_UTF_CHAR_LEN];
337161475Sdelphij		static int ubuf_len = 0;
338161475Sdelphij		static int ubuf_index = 0;
339161475Sdelphij		if (ubuf_len == 0)
340161475Sdelphij		{
341161475Sdelphij			ubuf_len = utf_len(c);
342161475Sdelphij			ubuf_index = 0;
343161475Sdelphij		}
344161475Sdelphij		ubuf[ubuf_index++] = c;
345161475Sdelphij		if (ubuf_index < ubuf_len)
346161475Sdelphij			return c;
347161475Sdelphij		c = get_wchar(ubuf) & 0xFF;
348161475Sdelphij		ubuf_len = 0;
349161475Sdelphij	}
350161475Sdelphij#endif
35160786Sps	if (need_clr)
35260786Sps	{
35360786Sps		need_clr = 0;
35460786Sps		clear_bot();
35560786Sps	}
35660786Sps#if MSDOS_COMPILER
35760786Sps	if (c == '\n' && is_tty)
35860786Sps	{
35960786Sps		/* remove_top(1); */
36060786Sps		putchr('\r');
36160786Sps	}
36260786Sps#else
36360786Sps#ifdef _OSK
36460786Sps	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
36560786Sps		putchr(0x0A);
36660786Sps#endif
36760786Sps#endif
36860786Sps	/*
36960786Sps	 * Some versions of flush() write to *ob, so we must flush
37060786Sps	 * when we are still one char from the end of obuf.
37160786Sps	 */
37260786Sps	if (ob >= &obuf[sizeof(obuf)-1])
37360786Sps		flush();
37460786Sps	*ob++ = c;
375170256Sdelphij	at_prompt = 0;
37660786Sps	return (c);
37760786Sps}
37860786Sps
37960786Sps/*
38060786Sps * Output a string.
38160786Sps */
38260786Sps	public void
38360786Spsputstr(s)
38460786Sps	register char *s;
38560786Sps{
38660786Sps	while (*s != '\0')
38760786Sps		putchr(*s++);
38860786Sps}
38960786Sps
39060786Sps
39160786Sps/*
392128345Stjr * Convert an integral type to a string.
393128345Stjr */
394128345Stjr#define TYPE_TO_A_FUNC(funcname, type) \
395128345Stjrvoid funcname(num, buf) \
396128345Stjr	type num; \
397128345Stjr	char *buf; \
398128345Stjr{ \
399128345Stjr	int neg = (num < 0); \
400128345Stjr	char tbuf[INT_STRLEN_BOUND(num)+2]; \
401128345Stjr	register char *s = tbuf + sizeof(tbuf); \
402128345Stjr	if (neg) num = -num; \
403128345Stjr	*--s = '\0'; \
404128345Stjr	do { \
405128345Stjr		*--s = (num % 10) + '0'; \
406128345Stjr	} while ((num /= 10) != 0); \
407128345Stjr	if (neg) *--s = '-'; \
408128345Stjr	strcpy(buf, s); \
409128345Stjr}
410128345Stjr
411128345StjrTYPE_TO_A_FUNC(postoa, POSITION)
412128345StjrTYPE_TO_A_FUNC(linenumtoa, LINENUM)
413128345StjrTYPE_TO_A_FUNC(inttoa, int)
414128345Stjr
415128345Stjr/*
41660786Sps * Output an integer in a given radix.
41760786Sps */
41860786Sps	static int
419128345Stjriprint_int(num)
42060786Sps	int num;
42160786Sps{
42289019Sps	char buf[INT_STRLEN_BOUND(num)];
42360786Sps
424128345Stjr	inttoa(num, buf);
425128345Stjr	putstr(buf);
426128345Stjr	return (strlen(buf));
427128345Stjr}
42860786Sps
429128345Stjr/*
430128345Stjr * Output a line number in a given radix.
431128345Stjr */
432128345Stjr	static int
433128345Stjriprint_linenum(num)
434128345Stjr	LINENUM num;
435128345Stjr{
436128345Stjr	char buf[INT_STRLEN_BOUND(num)];
43760786Sps
438128345Stjr	linenumtoa(num, buf);
439128345Stjr	putstr(buf);
440128345Stjr	return (strlen(buf));
44160786Sps}
44260786Sps
44360786Sps/*
44460786Sps * This function implements printf-like functionality
44560786Sps * using a more portable argument list mechanism than printf's.
44660786Sps */
44760786Sps	static int
44860786Spsless_printf(fmt, parg)
44960786Sps	register char *fmt;
45060786Sps	PARG *parg;
45160786Sps{
45260786Sps	register char *s;
45360786Sps	register int col;
45460786Sps
45560786Sps	col = 0;
45660786Sps	while (*fmt != '\0')
45760786Sps	{
45860786Sps		if (*fmt != '%')
45960786Sps		{
46060786Sps			putchr(*fmt++);
46160786Sps			col++;
46260786Sps		} else
46360786Sps		{
46460786Sps			++fmt;
465128345Stjr			switch (*fmt++)
466128345Stjr			{
46760786Sps			case 's':
46860786Sps				s = parg->p_string;
46960786Sps				parg++;
47060786Sps				while (*s != '\0')
47160786Sps				{
47260786Sps					putchr(*s++);
47360786Sps					col++;
47460786Sps				}
47560786Sps				break;
47660786Sps			case 'd':
477128345Stjr				col += iprint_int(parg->p_int);
47860786Sps				parg++;
47960786Sps				break;
480128345Stjr			case 'n':
481128345Stjr				col += iprint_linenum(parg->p_linenum);
482128345Stjr				parg++;
483128345Stjr				break;
48460786Sps			}
48560786Sps		}
48660786Sps	}
48760786Sps	return (col);
48860786Sps}
48960786Sps
49060786Sps/*
49160786Sps * Get a RETURN.
49260786Sps * If some other non-trivial char is pressed, unget it, so it will
49360786Sps * become the next command.
49460786Sps */
49560786Sps	public void
49660786Spsget_return()
49760786Sps{
49860786Sps	int c;
49960786Sps
50060786Sps#if ONLY_RETURN
50160786Sps	while ((c = getchr()) != '\n' && c != '\r')
50260786Sps		bell();
50360786Sps#else
50460786Sps	c = getchr();
50560786Sps	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
50660786Sps		ungetcc(c);
50760786Sps#endif
50860786Sps}
50960786Sps
51060786Sps/*
51160786Sps * Output a message in the lower left corner of the screen
51260786Sps * and wait for carriage return.
51360786Sps */
51460786Sps	public void
51560786Spserror(fmt, parg)
51660786Sps	char *fmt;
51760786Sps	PARG *parg;
51860786Sps{
51960786Sps	int col = 0;
52060786Sps	static char return_to_continue[] = "  (press RETURN)";
52160786Sps
52260786Sps	errmsgs++;
52360786Sps
52460786Sps	if (any_display && is_tty)
52560786Sps	{
526170256Sdelphij		if (!oldbot)
527170256Sdelphij			squish_check();
528161475Sdelphij		at_exit();
52960786Sps		clear_bot();
530161475Sdelphij		at_enter(AT_STANDOUT);
53160786Sps		col += so_s_width;
53260786Sps	}
53360786Sps
53460786Sps	col += less_printf(fmt, parg);
53560786Sps
53660786Sps	if (!(any_display && is_tty))
53760786Sps	{
53860786Sps		putchr('\n');
53960786Sps		return;
54060786Sps	}
54160786Sps
54260786Sps	putstr(return_to_continue);
543161475Sdelphij	at_exit();
54460786Sps	col += sizeof(return_to_continue) + so_e_width;
54560786Sps
54660786Sps	get_return();
54760786Sps	lower_left();
548221715Sdelphij    clear_eol();
54960786Sps
55060786Sps	if (col >= sc_width)
55160786Sps		/*
55260786Sps		 * Printing the message has probably scrolled the screen.
55360786Sps		 * {{ Unless the terminal doesn't have auto margins,
55460786Sps		 *    in which case we just hammered on the right margin. }}
55560786Sps		 */
55660786Sps		screen_trashed = 1;
55760786Sps
55860786Sps	flush();
55960786Sps}
56060786Sps
56160786Spsstatic char intr_to_abort[] = "... (interrupt to abort)";
56260786Sps
56360786Sps/*
56460786Sps * Output a message in the lower left corner of the screen
56560786Sps * and don't wait for carriage return.
56660786Sps * Usually used to warn that we are beginning a potentially
56760786Sps * time-consuming operation.
56860786Sps */
56960786Sps	public void
57060786Spsierror(fmt, parg)
57160786Sps	char *fmt;
57260786Sps	PARG *parg;
57360786Sps{
574161475Sdelphij	at_exit();
57560786Sps	clear_bot();
576161475Sdelphij	at_enter(AT_STANDOUT);
57760786Sps	(void) less_printf(fmt, parg);
57860786Sps	putstr(intr_to_abort);
579161475Sdelphij	at_exit();
58060786Sps	flush();
58160786Sps	need_clr = 1;
58260786Sps}
58360786Sps
58460786Sps/*
58560786Sps * Output a message in the lower left corner of the screen
58660786Sps * and return a single-character response.
58760786Sps */
58860786Sps	public int
58960786Spsquery(fmt, parg)
59060786Sps	char *fmt;
59160786Sps	PARG *parg;
59260786Sps{
59360786Sps	register int c;
59460786Sps	int col = 0;
59560786Sps
59660786Sps	if (any_display && is_tty)
59760786Sps		clear_bot();
59860786Sps
59960786Sps	(void) less_printf(fmt, parg);
60060786Sps	c = getchr();
60160786Sps
60260786Sps	if (!(any_display && is_tty))
60360786Sps	{
60460786Sps		putchr('\n');
60560786Sps		return (c);
60660786Sps	}
60760786Sps
60860786Sps	lower_left();
60960786Sps	if (col >= sc_width)
61060786Sps		screen_trashed = 1;
61160786Sps	flush();
61260786Sps
61360786Sps	return (c);
61460786Sps}
615