output.c revision 191930
160786Sps/*
2191930Sdelphij * Copyright (C) 1984-2008  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 *
760786Sps * For more information about less, or for information on how to
860786Sps * contact the author, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * High level routines dealing with the output to the screen.
1460786Sps */
1560786Sps
1660786Sps#include "less.h"
1760786Sps#if MSDOS_COMPILER==WIN32C
1860786Sps#include "windows.h"
1960786Sps#endif
2060786Sps
2160786Spspublic int errmsgs;	/* Count of messages displayed by error() */
2260786Spspublic int need_clr;
2360786Spspublic int final_attr;
24170256Sdelphijpublic int at_prompt;
2560786Sps
2660786Spsextern int sigs;
2760786Spsextern int sc_width;
2860786Spsextern int so_s_width, so_e_width;
2960786Spsextern int screen_trashed;
3060786Spsextern int any_display;
3160786Spsextern int is_tty;
32170256Sdelphijextern int oldbot;
3360786Sps
34191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
35128345Stjrextern int ctldisp;
36128345Stjrextern int nm_fg_color, nm_bg_color;
37128345Stjrextern int bo_fg_color, bo_bg_color;
38128345Stjrextern int ul_fg_color, ul_bg_color;
39128345Stjrextern int so_fg_color, so_bg_color;
40128345Stjrextern int bl_fg_color, bl_bg_color;
41128345Stjr#endif
42128345Stjr
4360786Sps/*
4460786Sps * Display the line which is in the line buffer.
4560786Sps */
4660786Sps	public void
4760786Spsput_line()
4860786Sps{
4960786Sps	register int c;
5060786Sps	register int i;
5160786Sps	int a;
5260786Sps
5360786Sps	if (ABORT_SIGS())
5460786Sps	{
5560786Sps		/*
5660786Sps		 * Don't output if a signal is pending.
5760786Sps		 */
5860786Sps		screen_trashed = 1;
5960786Sps		return;
6060786Sps	}
6160786Sps
62161475Sdelphij	final_attr = AT_NORMAL;
6360786Sps
6460786Sps	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
6560786Sps	{
66161475Sdelphij		at_switch(a);
67161475Sdelphij		final_attr = a;
6860786Sps		if (c == '\b')
6960786Sps			putbs();
7060786Sps		else
7160786Sps			putchr(c);
7260786Sps	}
7360786Sps
74161475Sdelphij	at_exit();
7560786Sps}
7660786Sps
7760786Spsstatic char obuf[OUTBUF_SIZE];
7860786Spsstatic char *ob = obuf;
7960786Sps
8060786Sps/*
8160786Sps * Flush buffered output.
8260786Sps *
8360786Sps * If we haven't displayed any file data yet,
8460786Sps * output messages on error output (file descriptor 2),
8560786Sps * otherwise output on standard output (file descriptor 1).
8660786Sps *
8760786Sps * This has the desirable effect of producing all
8860786Sps * error messages on error output if standard output
8960786Sps * is directed to a file.  It also does the same if
9060786Sps * we never produce any real output; for example, if
9160786Sps * the input file(s) cannot be opened.  If we do
9260786Sps * eventually produce output, code in edit() makes
9360786Sps * sure these messages can be seen before they are
9460786Sps * overwritten or scrolled away.
9560786Sps */
9660786Sps	public void
9760786Spsflush()
9860786Sps{
9960786Sps	register int n;
10060786Sps	register int fd;
10160786Sps
10260786Sps	n = ob - obuf;
10360786Sps	if (n == 0)
10460786Sps		return;
10560786Sps
10660786Sps#if MSDOS_COMPILER==MSOFTC
10760786Sps	if (is_tty && any_display)
10860786Sps	{
10960786Sps		*ob = '\0';
11060786Sps		_outtext(obuf);
11160786Sps		ob = obuf;
11260786Sps		return;
11360786Sps	}
11460786Sps#else
115191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
11660786Sps	if (is_tty && any_display)
11760786Sps	{
11860786Sps		*ob = '\0';
119128345Stjr		if (ctldisp != OPT_ONPLUS)
120191930Sdelphij			WIN32textout(obuf, ob - obuf);
121128345Stjr		else
122128345Stjr		{
123128345Stjr			/*
124128345Stjr			 * Look for SGR escape sequences, and convert them
125128345Stjr			 * to color commands.  Replace bold, underline,
126128345Stjr			 * and italic escapes into colors specified via
127128345Stjr			 * the -D command-line option.
128128345Stjr			 */
129128345Stjr			char *anchor, *p, *p_next;
130191930Sdelphij			unsigned char fg, bg;
131191930Sdelphij			static unsigned char at;
132191930Sdelphij#if MSDOS_COMPILER==WIN32C
133191930Sdelphij			/* Screen colors used by 3x and 4x SGR commands. */
134191930Sdelphij			static unsigned char screen_color[] = {
135191930Sdelphij				0, /* BLACK */
136191930Sdelphij				FOREGROUND_RED,
137191930Sdelphij				FOREGROUND_GREEN,
138191930Sdelphij				FOREGROUND_RED|FOREGROUND_GREEN,
139191930Sdelphij				FOREGROUND_BLUE,
140191930Sdelphij				FOREGROUND_BLUE|FOREGROUND_RED,
141191930Sdelphij				FOREGROUND_BLUE|FOREGROUND_GREEN,
142191930Sdelphij				FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
143191930Sdelphij			};
144191930Sdelphij#else
145128345Stjr			static enum COLORS screen_color[] = {
146128345Stjr				BLACK, RED, GREEN, BROWN,
147128345Stjr				BLUE, MAGENTA, CYAN, LIGHTGRAY
148128345Stjr			};
149191930Sdelphij#endif
150128345Stjr
151128345Stjr			for (anchor = p_next = obuf;
152191930Sdelphij			     (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
153128345Stjr			{
154128345Stjr				p = p_next;
155191930Sdelphij				if (p[1] == '[')  /* "ESC-[" sequence */
156128345Stjr				{
157128345Stjr					if (p > anchor)
158128345Stjr					{
159191930Sdelphij						/*
160191930Sdelphij						 * If some chars seen since
161191930Sdelphij						 * the last escape sequence,
162191930Sdelphij						 * write them out to the screen.
163191930Sdelphij						 */
164191930Sdelphij						WIN32textout(anchor, p-anchor);
165128345Stjr						anchor = p;
166128345Stjr					}
167191930Sdelphij					p += 2;  /* Skip the "ESC-[" */
168191930Sdelphij					if (is_ansi_end(*p))
169191930Sdelphij					{
170191930Sdelphij						/*
171191930Sdelphij						 * Handle null escape sequence
172191930Sdelphij						 * "ESC[m", which restores
173191930Sdelphij						 * the normal color.
174191930Sdelphij						 */
175191930Sdelphij						p++;
176191930Sdelphij						anchor = p_next = p;
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					{
275191930Sdelphij							fg = bo_fg_color;
276191930Sdelphij							bg = bo_bg_color;
277191930Sdelphij					} else if (at & 2)
278191930Sdelphij					{
279191930Sdelphij							fg = so_fg_color;
280191930Sdelphij							bg = so_bg_color;
281191930Sdelphij					} else if (at & 4)
282191930Sdelphij					{
283191930Sdelphij							fg = ul_fg_color;
284191930Sdelphij							bg = ul_bg_color;
285191930Sdelphij					} else if (at & 8)
286191930Sdelphij					{
287191930Sdelphij							fg = bl_fg_color;
288191930Sdelphij							bg = bl_bg_color;
289191930Sdelphij					}
290191930Sdelphij					fg &= 0xf;
291191930Sdelphij					bg &= 0xf;
292191930Sdelphij					WIN32setcolors(fg, bg);
293191930Sdelphij					p_next = anchor = p + 1;
294128345Stjr				} else
295128345Stjr					p_next++;
296128345Stjr			}
297128345Stjr
298128345Stjr			/* Output what's left in the buffer.  */
299191930Sdelphij			WIN32textout(anchor, ob - anchor);
300128345Stjr		}
30160786Sps		ob = obuf;
30260786Sps		return;
30360786Sps	}
30460786Sps#endif
30560786Sps#endif
30660786Sps	fd = (any_display) ? 1 : 2;
30760786Sps	if (write(fd, obuf, n) != n)
30860786Sps		screen_trashed = 1;
30960786Sps	ob = obuf;
31060786Sps}
31160786Sps
31260786Sps/*
31360786Sps * Output a character.
31460786Sps */
31560786Sps	public int
31660786Spsputchr(c)
31760786Sps	int c;
31860786Sps{
319161475Sdelphij#if 0 /* fake UTF-8 output for testing */
320161475Sdelphij	extern int utf_mode;
321161475Sdelphij	if (utf_mode)
322161475Sdelphij	{
323161475Sdelphij		static char ubuf[MAX_UTF_CHAR_LEN];
324161475Sdelphij		static int ubuf_len = 0;
325161475Sdelphij		static int ubuf_index = 0;
326161475Sdelphij		if (ubuf_len == 0)
327161475Sdelphij		{
328161475Sdelphij			ubuf_len = utf_len(c);
329161475Sdelphij			ubuf_index = 0;
330161475Sdelphij		}
331161475Sdelphij		ubuf[ubuf_index++] = c;
332161475Sdelphij		if (ubuf_index < ubuf_len)
333161475Sdelphij			return c;
334161475Sdelphij		c = get_wchar(ubuf) & 0xFF;
335161475Sdelphij		ubuf_len = 0;
336161475Sdelphij	}
337161475Sdelphij#endif
33860786Sps	if (need_clr)
33960786Sps	{
34060786Sps		need_clr = 0;
34160786Sps		clear_bot();
34260786Sps	}
34360786Sps#if MSDOS_COMPILER
34460786Sps	if (c == '\n' && is_tty)
34560786Sps	{
34660786Sps		/* remove_top(1); */
34760786Sps		putchr('\r');
34860786Sps	}
34960786Sps#else
35060786Sps#ifdef _OSK
35160786Sps	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
35260786Sps		putchr(0x0A);
35360786Sps#endif
35460786Sps#endif
35560786Sps	/*
35660786Sps	 * Some versions of flush() write to *ob, so we must flush
35760786Sps	 * when we are still one char from the end of obuf.
35860786Sps	 */
35960786Sps	if (ob >= &obuf[sizeof(obuf)-1])
36060786Sps		flush();
36160786Sps	*ob++ = c;
362170256Sdelphij	at_prompt = 0;
36360786Sps	return (c);
36460786Sps}
36560786Sps
36660786Sps/*
36760786Sps * Output a string.
36860786Sps */
36960786Sps	public void
37060786Spsputstr(s)
37160786Sps	register char *s;
37260786Sps{
37360786Sps	while (*s != '\0')
37460786Sps		putchr(*s++);
37560786Sps}
37660786Sps
37760786Sps
37860786Sps/*
379128345Stjr * Convert an integral type to a string.
380128345Stjr */
381128345Stjr#define TYPE_TO_A_FUNC(funcname, type) \
382128345Stjrvoid funcname(num, buf) \
383128345Stjr	type num; \
384128345Stjr	char *buf; \
385128345Stjr{ \
386128345Stjr	int neg = (num < 0); \
387128345Stjr	char tbuf[INT_STRLEN_BOUND(num)+2]; \
388128345Stjr	register char *s = tbuf + sizeof(tbuf); \
389128345Stjr	if (neg) num = -num; \
390128345Stjr	*--s = '\0'; \
391128345Stjr	do { \
392128345Stjr		*--s = (num % 10) + '0'; \
393128345Stjr	} while ((num /= 10) != 0); \
394128345Stjr	if (neg) *--s = '-'; \
395128345Stjr	strcpy(buf, s); \
396128345Stjr}
397128345Stjr
398128345StjrTYPE_TO_A_FUNC(postoa, POSITION)
399128345StjrTYPE_TO_A_FUNC(linenumtoa, LINENUM)
400128345StjrTYPE_TO_A_FUNC(inttoa, int)
401128345Stjr
402128345Stjr/*
40360786Sps * Output an integer in a given radix.
40460786Sps */
40560786Sps	static int
406128345Stjriprint_int(num)
40760786Sps	int num;
40860786Sps{
40989019Sps	char buf[INT_STRLEN_BOUND(num)];
41060786Sps
411128345Stjr	inttoa(num, buf);
412128345Stjr	putstr(buf);
413128345Stjr	return (strlen(buf));
414128345Stjr}
41560786Sps
416128345Stjr/*
417128345Stjr * Output a line number in a given radix.
418128345Stjr */
419128345Stjr	static int
420128345Stjriprint_linenum(num)
421128345Stjr	LINENUM num;
422128345Stjr{
423128345Stjr	char buf[INT_STRLEN_BOUND(num)];
42460786Sps
425128345Stjr	linenumtoa(num, buf);
426128345Stjr	putstr(buf);
427128345Stjr	return (strlen(buf));
42860786Sps}
42960786Sps
43060786Sps/*
43160786Sps * This function implements printf-like functionality
43260786Sps * using a more portable argument list mechanism than printf's.
43360786Sps */
43460786Sps	static int
43560786Spsless_printf(fmt, parg)
43660786Sps	register char *fmt;
43760786Sps	PARG *parg;
43860786Sps{
43960786Sps	register char *s;
44060786Sps	register int col;
44160786Sps
44260786Sps	col = 0;
44360786Sps	while (*fmt != '\0')
44460786Sps	{
44560786Sps		if (*fmt != '%')
44660786Sps		{
44760786Sps			putchr(*fmt++);
44860786Sps			col++;
44960786Sps		} else
45060786Sps		{
45160786Sps			++fmt;
452128345Stjr			switch (*fmt++)
453128345Stjr			{
45460786Sps			case 's':
45560786Sps				s = parg->p_string;
45660786Sps				parg++;
45760786Sps				while (*s != '\0')
45860786Sps				{
45960786Sps					putchr(*s++);
46060786Sps					col++;
46160786Sps				}
46260786Sps				break;
46360786Sps			case 'd':
464128345Stjr				col += iprint_int(parg->p_int);
46560786Sps				parg++;
46660786Sps				break;
467128345Stjr			case 'n':
468128345Stjr				col += iprint_linenum(parg->p_linenum);
469128345Stjr				parg++;
470128345Stjr				break;
47160786Sps			}
47260786Sps		}
47360786Sps	}
47460786Sps	return (col);
47560786Sps}
47660786Sps
47760786Sps/*
47860786Sps * Get a RETURN.
47960786Sps * If some other non-trivial char is pressed, unget it, so it will
48060786Sps * become the next command.
48160786Sps */
48260786Sps	public void
48360786Spsget_return()
48460786Sps{
48560786Sps	int c;
48660786Sps
48760786Sps#if ONLY_RETURN
48860786Sps	while ((c = getchr()) != '\n' && c != '\r')
48960786Sps		bell();
49060786Sps#else
49160786Sps	c = getchr();
49260786Sps	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
49360786Sps		ungetcc(c);
49460786Sps#endif
49560786Sps}
49660786Sps
49760786Sps/*
49860786Sps * Output a message in the lower left corner of the screen
49960786Sps * and wait for carriage return.
50060786Sps */
50160786Sps	public void
50260786Spserror(fmt, parg)
50360786Sps	char *fmt;
50460786Sps	PARG *parg;
50560786Sps{
50660786Sps	int col = 0;
50760786Sps	static char return_to_continue[] = "  (press RETURN)";
50860786Sps
50960786Sps	errmsgs++;
51060786Sps
51160786Sps	if (any_display && is_tty)
51260786Sps	{
513170256Sdelphij		if (!oldbot)
514170256Sdelphij			squish_check();
515161475Sdelphij		at_exit();
51660786Sps		clear_bot();
517161475Sdelphij		at_enter(AT_STANDOUT);
51860786Sps		col += so_s_width;
51960786Sps	}
52060786Sps
52160786Sps	col += less_printf(fmt, parg);
52260786Sps
52360786Sps	if (!(any_display && is_tty))
52460786Sps	{
52560786Sps		putchr('\n');
52660786Sps		return;
52760786Sps	}
52860786Sps
52960786Sps	putstr(return_to_continue);
530161475Sdelphij	at_exit();
53160786Sps	col += sizeof(return_to_continue) + so_e_width;
53260786Sps
53360786Sps	get_return();
53460786Sps	lower_left();
53560786Sps
53660786Sps	if (col >= sc_width)
53760786Sps		/*
53860786Sps		 * Printing the message has probably scrolled the screen.
53960786Sps		 * {{ Unless the terminal doesn't have auto margins,
54060786Sps		 *    in which case we just hammered on the right margin. }}
54160786Sps		 */
54260786Sps		screen_trashed = 1;
54360786Sps
54460786Sps	flush();
54560786Sps}
54660786Sps
54760786Spsstatic char intr_to_abort[] = "... (interrupt to abort)";
54860786Sps
54960786Sps/*
55060786Sps * Output a message in the lower left corner of the screen
55160786Sps * and don't wait for carriage return.
55260786Sps * Usually used to warn that we are beginning a potentially
55360786Sps * time-consuming operation.
55460786Sps */
55560786Sps	public void
55660786Spsierror(fmt, parg)
55760786Sps	char *fmt;
55860786Sps	PARG *parg;
55960786Sps{
560161475Sdelphij	at_exit();
56160786Sps	clear_bot();
562161475Sdelphij	at_enter(AT_STANDOUT);
56360786Sps	(void) less_printf(fmt, parg);
56460786Sps	putstr(intr_to_abort);
565161475Sdelphij	at_exit();
56660786Sps	flush();
56760786Sps	need_clr = 1;
56860786Sps}
56960786Sps
57060786Sps/*
57160786Sps * Output a message in the lower left corner of the screen
57260786Sps * and return a single-character response.
57360786Sps */
57460786Sps	public int
57560786Spsquery(fmt, parg)
57660786Sps	char *fmt;
57760786Sps	PARG *parg;
57860786Sps{
57960786Sps	register int c;
58060786Sps	int col = 0;
58160786Sps
58260786Sps	if (any_display && is_tty)
58360786Sps		clear_bot();
58460786Sps
58560786Sps	(void) less_printf(fmt, parg);
58660786Sps	c = getchr();
58760786Sps
58860786Sps	if (!(any_display && is_tty))
58960786Sps	{
59060786Sps		putchr('\n');
59160786Sps		return (c);
59260786Sps	}
59360786Sps
59460786Sps	lower_left();
59560786Sps	if (col >= sc_width)
59660786Sps		screen_trashed = 1;
59760786Sps	flush();
59860786Sps
59960786Sps	return (c);
60060786Sps}
601