output.c revision 221715
1192886Sedwin/*
2192886Sedwin * Copyright (C) 1984-2011  Mark Nudelman
364499Swollman *
42742Swollman * You may distribute under the terms of either the GNU General Public
52742Swollman * License or the Less License, as specified in the README file.
6243020Sedwin *
72742Swollman * For more information about less, or for information on how to
8158421Swollman * contact the author, see the README file.
92742Swollman */
10158421Swollman
11158421Swollman
122742Swollman/*
13248310Sedwin * High level routines dealing with the output to the screen.
14248310Sedwin */
15248310Sedwin
16248310Sedwin#include "less.h"
1786222Swollman#if MSDOS_COMPILER==WIN32C
1820094Swollman#include "windows.h"
1920094Swollman#endif
2020094Swollman
2120094Swollmanpublic int errmsgs;	/* Count of messages displayed by error() */
2220094Swollmanpublic int need_clr;
23158421Swollmanpublic int final_attr;
24158421Swollmanpublic int at_prompt;
2520094Swollman
2619878Swollmanextern int sigs;
2719878Swollmanextern int sc_width;
2819878Swollmanextern int so_s_width, so_e_width;
2919878Swollmanextern int screen_trashed;
3019878Swollmanextern int any_display;
3119878Swollmanextern int is_tty;
32273438Sdelphijextern int oldbot;
3319878Swollman
3458787Sru#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
3558787Sruextern int ctldisp;
3658787Sruextern int nm_fg_color, nm_bg_color;
37273438Sdelphijextern int bo_fg_color, bo_bg_color;
3858787Sruextern int ul_fg_color, ul_bg_color;
3958787Sruextern int so_fg_color, so_bg_color;
40273438Sdelphijextern int bl_fg_color, bl_bg_color;
41273438Sdelphij#endif
42273438Sdelphij
4358787Sru/*
4458787Sru * Display the line which is in the line buffer.
4558787Sru */
4658787Sru	public void
4758787Sruput_line()
4858787Sru{
49273438Sdelphij	register int c;
5058787Sru	register int i;
5158787Sru	int a;
522742Swollman
532742Swollman	if (ABORT_SIGS())
542742Swollman	{
552742Swollman		/*
562742Swollman		 * Don't output if a signal is pending.
572742Swollman		 */
582742Swollman		screen_trashed = 1;
5919878Swollman		return;
602742Swollman	}
612742Swollman
622742Swollman	final_attr = AT_NORMAL;
63273438Sdelphij
642742Swollman	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
652742Swollman	{
66149514Swollman		at_switch(a);
6721217Swollman		final_attr = a;
689908Swollman		if (c == '\b')
699908Swollman			putbs();
702742Swollman		else
7119878Swollman			putchr(c);
7219878Swollman	}
7319878Swollman
7419878Swollman	at_exit();
7519878Swollman}
7619878Swollman
7719878Swollmanstatic char obuf[OUTBUF_SIZE];
7819878Swollmanstatic char *ob = obuf;
7919878Swollman
8019878Swollman/*
8119878Swollman * Flush buffered output.
8219878Swollman *
8319878Swollman * If we haven't displayed any file data yet,
8419878Swollman * output messages on error output (file descriptor 2),
8519878Swollman * otherwise output on standard output (file descriptor 1).
8619878Swollman *
8793799Swollman * This has the desirable effect of producing all
8858787Sru * error messages on error output if standard output
8958787Sru * is directed to a file.  It also does the same if
9019878Swollman * we never produce any real output; for example, if
9119878Swollman * the input file(s) cannot be opened.  If we do
9219878Swollman * eventually produce output, code in edit() makes
939908Swollman * sure these messages can be seen before they are
94149514Swollman * overwritten or scrolled away.
959908Swollman */
969908Swollman	public void
97273438Sdelphijflush()
9821217Swollman{
9919878Swollman	register int n;
10019878Swollman	register int fd;
1019908Swollman
102149514Swollman	n = ob - obuf;
1039908Swollman	if (n == 0)
1049908Swollman		return;
1059908Swollman
1069908Swollman#if MSDOS_COMPILER==MSOFTC
10758787Sru	if (is_tty && any_display)
10858787Sru	{
10958787Sru		*ob = '\0';
11064499Swollman		_outtext(obuf);
11164499Swollman		ob = obuf;
112175034Sedwin		return;
113175034Sedwin	}
114175034Sedwin#else
115175034Sedwin#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
116175034Sedwin	if (is_tty && any_display)
11758787Sru	{
11858787Sru		*ob = '\0';
119273438Sdelphij		if (ctldisp != OPT_ONPLUS)
12058787Sru			WIN32textout(obuf, ob - obuf);
12158787Sru		else
12258787Sru		{
123273438Sdelphij			/*
12464499Swollman			 * Look for SGR escape sequences, and convert them
125273438Sdelphij			 * to color commands.  Replace bold, underline,
12664499Swollman			 * and italic escapes into colors specified via
12764499Swollman			 * the -D command-line option.
12886222Swollman			 */
12986222Swollman			char *anchor, *p, *p_next;
13086222Swollman			unsigned char fg, bg;
13186222Swollman			static unsigned char at;
13286222Swollman#if MSDOS_COMPILER==WIN32C
13386222Swollman			/* Screen colors used by 3x and 4x SGR commands. */
13486222Swollman			static unsigned char screen_color[] = {
13586222Swollman				0, /* BLACK */
13686222Swollman				FOREGROUND_RED,
13786222Swollman				FOREGROUND_GREEN,
13886222Swollman				FOREGROUND_RED|FOREGROUND_GREEN,
13986222Swollman				FOREGROUND_BLUE,
14086222Swollman				FOREGROUND_BLUE|FOREGROUND_RED,
14186222Swollman				FOREGROUND_BLUE|FOREGROUND_GREEN,
14286222Swollman				FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
14386222Swollman			};
14486222Swollman#else
14586222Swollman			static enum COLORS screen_color[] = {
14686222Swollman				BLACK, RED, GREEN, BROWN,
14786222Swollman				BLUE, MAGENTA, CYAN, LIGHTGRAY
14886222Swollman			};
14986222Swollman#endif
15086222Swollman
151175034Sedwin			for (anchor = p_next = obuf;
152175034Sedwin			     (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
153175034Sedwin			{
154175034Sedwin				p = p_next;
155175034Sedwin				if (p[1] == '[')  /* "ESC-[" sequence */
156175034Sedwin				{
157175034Sedwin					if (p > anchor)
158273438Sdelphij					{
159175034Sedwin						/*
160273438Sdelphij						 * If some chars seen since
161175034Sedwin						 * the last escape sequence,
162175034Sedwin						 * write them out to the screen.
163175034Sedwin						 */
164175034Sedwin						WIN32textout(anchor, p-anchor);
165175034Sedwin						anchor = p;
166175034Sedwin					}
167175034Sedwin					p += 2;  /* Skip the "ESC-[" */
168175034Sedwin					if (is_ansi_end(*p))
169183066Sedwin					{
170183066Sedwin						/*
171183066Sedwin						 * Handle null escape sequence
172183066Sedwin						 * "ESC[m", which restores
173183066Sedwin						 * the normal color.
174183066Sedwin						 */
175183066Sedwin						p++;
176183066Sedwin						anchor = p_next = p;
177183864Sedwin						WIN32setcolors(nm_fg_color, nm_bg_color);
178183864Sedwin						continue;
179183864Sedwin					}
180183864Sedwin					p_next = p;
181183864Sedwin
182183864Sedwin					/*
183183864Sedwin					 * Select foreground/background colors
184183864Sedwin					 * based on the escape sequence.
185183864Sedwin					 */
186183864Sedwin					fg = nm_fg_color;
187183864Sedwin					bg = nm_bg_color;
188183864Sedwin					while (!is_ansi_end(*p))
189184406Sedwin					{
190184406Sedwin						char *q;
191184406Sedwin						long code = strtol(p, &q, 10);
192184406Sedwin
193184406Sedwin						if (*q == '\0')
194273438Sdelphij						{
195273438Sdelphij							/*
196273438Sdelphij							 * Incomplete sequence.
197273438Sdelphij							 * Leave it unprocessed
198184406Sedwin							 * in the buffer.
199184406Sedwin							 */
200184406Sedwin							int slop = q - anchor;
201184406Sedwin							/* {{ strcpy args overlap! }} */
202184406Sedwin							strcpy(obuf, anchor);
203184406Sedwin							ob = &obuf[slop];
204198515Sedwin							return;
205198515Sedwin						}
206198515Sedwin
207198515Sedwin						if (q == p ||
208198515Sedwin						    code > 49 || code < 0 ||
209273438Sdelphij						    (!is_ansi_end(*q) && *q != ';'))
210273438Sdelphij						{
211273438Sdelphij							p_next = q;
212273438Sdelphij							break;
213273438Sdelphij						}
214273438Sdelphij						if (*q == ';')
215198515Sedwin							q++;
216175034Sedwin
217198515Sedwin						switch (code)
218198515Sedwin						{
219240461Sedwin						default:
220136638Swollman						/* case 0: all attrs off */
221136638Swollman							fg = nm_fg_color;
222136638Swollman							bg = nm_bg_color;
223136638Swollman							at = 0;
224136638Swollman							break;
225136638Swollman						case 1:	/* bold on */
226136638Swollman							at |= 1;
22793799Swollman							break;
228158421Swollman						case 3:	/* italic on */
229158421Swollman						case 7: /* inverse on */
230273438Sdelphij							at |= 2;
231273438Sdelphij							break;
23293799Swollman						case 4:	/* underline on */
233158421Swollman							at |= 4;
234136638Swollman							break;
235136638Swollman						case 5: /* slow blink on */
236136638Swollman						case 6: /* fast blink on */
237136638Swollman							at |= 8;
238136638Swollman							break;
239136638Swollman						case 8:	/* concealed on */
240136638Swollman							fg = (bg & 7) | 8;
241136638Swollman							break;
242136638Swollman						case 22: /* bold off */
243136638Swollman							at &= ~1;
244136638Swollman							break;
245136638Swollman						case 23: /* italic off */
246273438Sdelphij						case 27: /* inverse off */
247136638Swollman							at &= ~2;
248136638Swollman							break;
249273438Sdelphij						case 24: /* underline off */
250136638Swollman							at &= ~4;
251136638Swollman							break;
252136638Swollman						case 30: case 31: case 32:
253136638Swollman						case 33: case 34: case 35:
254136638Swollman						case 36: case 37:
255136638Swollman							fg = (fg & 8) | (screen_color[code - 30]);
256136638Swollman							break;
257136638Swollman						case 39: /* default fg */
258136638Swollman							fg = nm_fg_color;
259136638Swollman							break;
260136638Swollman						case 40: case 41: case 42:
261136638Swollman						case 43: case 44: case 45:
262136638Swollman						case 46: case 47:
263136638Swollman							bg = (bg & 8) | (screen_color[code - 40]);
264136638Swollman							break;
265136638Swollman						case 49: /* default fg */
266136638Swollman							bg = nm_bg_color;
267136638Swollman							break;
268136638Swollman						}
269136638Swollman						p = q;
270136638Swollman					}
271136638Swollman					if (!is_ansi_end(*p) || p == p_next)
272136638Swollman						break;
273136638Swollman					if (at & 1)
274136638Swollman					{
275136638Swollman							fg = bo_fg_color;
276136638Swollman							bg = bo_bg_color;
277136638Swollman					} else if (at & 2)
278136638Swollman					{
27993799Swollman							fg = so_fg_color;
280177591Sedwin							bg = so_bg_color;
281177591Sedwin					} else if (at & 4)
282177591Sedwin					{
283177591Sedwin							fg = ul_fg_color;
284273438Sdelphij							bg = ul_bg_color;
285177591Sedwin					} else if (at & 8)
286177591Sedwin					{
287177591Sedwin							fg = bl_fg_color;
288177591Sedwin							bg = bl_bg_color;
289177591Sedwin					}
290177591Sedwin					fg &= 0xf;
291273438Sdelphij					bg &= 0xf;
292177591Sedwin					WIN32setcolors(fg, bg);
293177591Sedwin					p_next = anchor = p + 1;
294273438Sdelphij				} else
295177591Sedwin					p_next++;
296177591Sedwin			}
297177591Sedwin
298177591Sedwin			/* Output what's left in the buffer.  */
299177591Sedwin			WIN32textout(anchor, ob - anchor);
300240461Sedwin		}
301240461Sedwin		ob = obuf;
302240461Sedwin		return;
303177591Sedwin	}
304177591Sedwin#endif
305177591Sedwin#endif
306177591Sedwin	fd = (any_display) ? 1 : 2;
307177591Sedwin	if (write(fd, obuf, n) != n)
308177591Sedwin		screen_trashed = 1;
309273438Sdelphij	ob = obuf;
310177591Sedwin}
311177591Sedwin
312177591Sedwin/*
313177591Sedwin * Output a character.
314177591Sedwin */
315177591Sedwin	public int
316177591Sedwinputchr(c)
317177591Sedwin	int c;
318177591Sedwin{
319177591Sedwin#if 0 /* fake UTF-8 output for testing */
320177591Sedwin	extern int utf_mode;
321177591Sedwin	if (utf_mode)
322177591Sedwin	{
323177591Sedwin		static char ubuf[MAX_UTF_CHAR_LEN];
324177591Sedwin		static int ubuf_len = 0;
325177591Sedwin		static int ubuf_index = 0;
326177591Sedwin		if (ubuf_len == 0)
327177591Sedwin		{
328177591Sedwin			ubuf_len = utf_len(c);
329177591Sedwin			ubuf_index = 0;
330177591Sedwin		}
331177591Sedwin		ubuf[ubuf_index++] = c;
332177591Sedwin		if (ubuf_index < ubuf_len)
333177591Sedwin			return c;
334177591Sedwin		c = get_wchar(ubuf) & 0xFF;
335181421Sedwin		ubuf_len = 0;
336158421Swollman	}
337158421Swollman#endif
338181421Sedwin	if (need_clr)
339181421Sedwin	{
340181421Sedwin		need_clr = 0;
341181421Sedwin		clear_bot();
342181421Sedwin	}
343190372Sedwin#if MSDOS_COMPILER
344190372Sedwin	if (c == '\n' && is_tty)
345190372Sedwin	{
346190372Sedwin		/* remove_top(1); */
34793799Swollman		putchr('\r');
348190372Sedwin	}
349190372Sedwin#else
350273438Sdelphij#ifdef _OSK
351273438Sdelphij	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
352190372Sedwin		putchr(0x0A);
353248310Sedwin#endif
354190372Sedwin#endif
355240461Sedwin	/*
356248310Sedwin	 * Some versions of flush() write to *ob, so we must flush
357248310Sedwin	 * when we are still one char from the end of obuf.
358190372Sedwin	 */
359190372Sedwin	if (ob >= &obuf[sizeof(obuf)-1])
360190372Sedwin		flush();
361190372Sedwin	*ob++ = c;
362190372Sedwin	at_prompt = 0;
363190372Sedwin	return (c);
364198515Sedwin}
365198515Sedwin
366190372Sedwin/*
367198515Sedwin * Output a string.
368198515Sedwin */
369198515Sedwin	public void
370198515Sedwinputstr(s)
371198515Sedwin	register char *s;
372198515Sedwin{
373197597Sedwin	while (*s != '\0')
374198515Sedwin		putchr(*s++);
375197597Sedwin}
376198515Sedwin
377198515Sedwin
378198515Sedwin/*
379198515Sedwin * Convert an integral type to a string.
380198515Sedwin */
381198515Sedwin#define TYPE_TO_A_FUNC(funcname, type) \
382198515Sedwinvoid funcname(num, buf) \
383198515Sedwin	type num; \
384198515Sedwin	char *buf; \
385198515Sedwin{ \
386198515Sedwin	int neg = (num < 0); \
387198515Sedwin	char tbuf[INT_STRLEN_BOUND(num)+2]; \
388198515Sedwin	register char *s = tbuf + sizeof(tbuf); \
389198515Sedwin	if (neg) num = -num; \
390198515Sedwin	*--s = '\0'; \
391198515Sedwin	do { \
392198515Sedwin		*--s = (num % 10) + '0'; \
393198515Sedwin	} while ((num /= 10) != 0); \
394198515Sedwin	if (neg) *--s = '-'; \
395197597Sedwin	strcpy(buf, s); \
396206868Sedwin}
397206868Sedwin
398206868SedwinTYPE_TO_A_FUNC(postoa, POSITION)
399273438SdelphijTYPE_TO_A_FUNC(linenumtoa, LINENUM)
400206868SedwinTYPE_TO_A_FUNC(inttoa, int)
401206868Sedwin
402273438Sdelphij/*
403206868Sedwin * Output an integer in a given radix.
404206868Sedwin */
405206868Sedwin	static int
406206868Sedwiniprint_int(num)
407206868Sedwin	int num;
408206868Sedwin{
409206868Sedwin	char buf[INT_STRLEN_BOUND(num)];
410206868Sedwin
411206868Sedwin	inttoa(num, buf);
412206868Sedwin	putstr(buf);
413257682Sedwin	return (strlen(buf));
414257682Sedwin}
415257682Sedwin
416257682Sedwin/*
417257682Sedwin * Output a line number in a given radix.
418257682Sedwin */
419257682Sedwin	static int
420257682Sedwiniprint_linenum(num)
421257682Sedwin	LINENUM num;
422257682Sedwin{
423257682Sedwin	char buf[INT_STRLEN_BOUND(num)];
424248310Sedwin
425273438Sdelphij	linenumtoa(num, buf);
426248310Sedwin	putstr(buf);
4272742Swollman	return (strlen(buf));
42820094Swollman}
429136638Swollman
430136638Swollman/*
431273438Sdelphij * This function implements printf-like functionality
43219878Swollman * using a more portable argument list mechanism than printf's.
43358787Sru */
43493799Swollman	static int
43593799Swollmanless_printf(fmt, parg)
436175034Sedwin	register char *fmt;
43720094Swollman	PARG *parg;
438273438Sdelphij{
439184406Sedwin	register char *s;
44020094Swollman	register int col;
441158421Swollman
44293799Swollman	col = 0;
44393799Swollman	while (*fmt != '\0')
44493799Swollman	{
44593799Swollman		if (*fmt != '%')
44693799Swollman		{
44793799Swollman			putchr(*fmt++);
448136638Swollman			col++;
44993799Swollman		} else
45020094Swollman		{
45158787Sru			++fmt;
45293799Swollman			switch (*fmt++)
45393799Swollman			{
45493799Swollman			case 's':
45593799Swollman				s = parg->p_string;
456175034Sedwin				parg++;
45720094Swollman				while (*s != '\0')
458273438Sdelphij				{
459184406Sedwin					putchr(*s++);
460184406Sedwin					col++;
461184406Sedwin				}
462184406Sedwin				break;
463184406Sedwin			case 'd':
464184406Sedwin				col += iprint_int(parg->p_int);
465184406Sedwin				parg++;
466184406Sedwin				break;
467184406Sedwin			case 'n':
468184406Sedwin				col += iprint_linenum(parg->p_linenum);
469184406Sedwin				parg++;
470273438Sdelphij				break;
471136638Swollman			}
472136638Swollman		}
473136638Swollman	}
474136638Swollman	return (col);
475136638Swollman}
476136638Swollman
477136638Swollman/*
478136638Swollman * Get a RETURN.
479136638Swollman * If some other non-trivial char is pressed, unget it, so it will
480136638Swollman * become the next command.
481175034Sedwin */
482136638Swollman	public void
483136638Swollmanget_return()
484136638Swollman{
485136638Swollman	int c;
486136638Swollman
487136638Swollman#if ONLY_RETURN
488136638Swollman	while ((c = getchr()) != '\n' && c != '\r')
489136638Swollman		bell();
490136638Swollman#else
491136638Swollman	c = getchr();
492136638Swollman	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
493136638Swollman		ungetcc(c);
494184406Sedwin#endif
495184406Sedwin}
496136638Swollman
497136638Swollman/*
498136638Swollman * Output a message in the lower left corner of the screen
499136638Swollman * and wait for carriage return.
500136638Swollman */
501136638Swollman	public void
502136638Swollmanerror(fmt, parg)
503136638Swollman	char *fmt;
504136638Swollman	PARG *parg;
505136638Swollman{
506136638Swollman	int col = 0;
507136638Swollman	static char return_to_continue[] = "  (press RETURN)";
508184406Sedwin
509184406Sedwin	errmsgs++;
510136638Swollman
51120094Swollman	if (any_display && is_tty)
512136638Swollman	{
51393799Swollman		if (!oldbot)
51420094Swollman			squish_check();
51520094Swollman		at_exit();
51693799Swollman		clear_bot();
51793799Swollman		at_enter(AT_STANDOUT);
51893799Swollman		col += so_s_width;
51920094Swollman	}
52093799Swollman
52193799Swollman	col += less_printf(fmt, parg);
52293799Swollman
523184406Sedwin	if (!(any_display && is_tty))
524184406Sedwin	{
52520094Swollman		putchr('\n');
526149514Swollman		return;
527136638Swollman	}
52893799Swollman
52920094Swollman	putstr(return_to_continue);
53058787Sru	at_exit();
53193799Swollman	col += sizeof(return_to_continue) + so_e_width;
53293799Swollman
53393799Swollman	get_return();
53493799Swollman	lower_left();
535136638Swollman    clear_eol();
536136638Swollman
537184406Sedwin	if (col >= sc_width)
538184406Sedwin		/*
53920094Swollman		 * Printing the message has probably scrolled the screen.
54020094Swollman		 * {{ Unless the terminal doesn't have auto margins,
541136638Swollman		 *    in which case we just hammered on the right margin. }}
54293799Swollman		 */
54320094Swollman		screen_trashed = 1;
54420094Swollman
54593799Swollman	flush();
54693799Swollman}
54793799Swollman
54820094Swollmanstatic char intr_to_abort[] = "... (interrupt to abort)";
54920094Swollman
55020094Swollman/*
55193799Swollman * Output a message in the lower left corner of the screen
55293799Swollman * and don't wait for carriage return.
553136638Swollman * Usually used to warn that we are beginning a potentially
554136638Swollman * time-consuming operation.
555184406Sedwin */
556184406Sedwin	public void
557136638Swollmanierror(fmt, parg)
558177591Sedwin	char *fmt;
559198515Sedwin	PARG *parg;
560206868Sedwin{
561257682Sedwin	at_exit();
562198515Sedwin	clear_bot();
563177591Sedwin	at_enter(AT_STANDOUT);
564177591Sedwin	(void) less_printf(fmt, parg);
565177591Sedwin	putstr(intr_to_abort);
566177591Sedwin	at_exit();
567181421Sedwin	flush();
568181421Sedwin	need_clr = 1;
569181421Sedwin}
570181421Sedwin
571181421Sedwin/*
572181421Sedwin * Output a message in the lower left corner of the screen
573181421Sedwin * and return a single-character response.
574177591Sedwin */
575177591Sedwin	public int
576177591Sedwinquery(fmt, parg)
577257682Sedwin	char *fmt;
578257682Sedwin	PARG *parg;
579177591Sedwin{
580136638Swollman	register int c;
581136638Swollman	int col = 0;
582273438Sdelphij
583136638Swollman	if (any_display && is_tty)
584136638Swollman		clear_bot();
585136638Swollman
586136638Swollman	(void) less_printf(fmt, parg);
587136638Swollman	c = getchr();
588136638Swollman
589184406Sedwin	if (!(any_display && is_tty))
590184406Sedwin	{
591136638Swollman		putchr('\n');
592273438Sdelphij		return (c);
593136638Swollman	}
594273438Sdelphij
595136638Swollman	lower_left();
596136638Swollman	if (col >= sc_width)
597136638Swollman		screen_trashed = 1;
598136638Swollman	flush();
599136638Swollman
600136638Swollman	return (c);
601184406Sedwin}
602184406Sedwin