output.c revision 195941
1/*
2 * Copyright (C) 1984-2009  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 about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12/*
13 * High level routines dealing with the output to the screen.
14 */
15
16#include "less.h"
17#if MSDOS_COMPILER==WIN32C
18#include "windows.h"
19#endif
20
21public int errmsgs;	/* Count of messages displayed by error() */
22public int need_clr;
23public int final_attr;
24public int at_prompt;
25
26extern int sigs;
27extern int sc_width;
28extern int so_s_width, so_e_width;
29extern int screen_trashed;
30extern int any_display;
31extern int is_tty;
32extern int oldbot;
33
34#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
35extern int ctldisp;
36extern int nm_fg_color, nm_bg_color;
37extern int bo_fg_color, bo_bg_color;
38extern int ul_fg_color, ul_bg_color;
39extern int so_fg_color, so_bg_color;
40extern int bl_fg_color, bl_bg_color;
41#endif
42
43/*
44 * Display the line which is in the line buffer.
45 */
46	public void
47put_line()
48{
49	register int c;
50	register int i;
51	int a;
52
53	if (ABORT_SIGS())
54	{
55		/*
56		 * Don't output if a signal is pending.
57		 */
58		screen_trashed = 1;
59		return;
60	}
61
62	final_attr = AT_NORMAL;
63
64	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
65	{
66		at_switch(a);
67		final_attr = a;
68		if (c == '\b')
69			putbs();
70		else
71			putchr(c);
72	}
73
74	at_exit();
75}
76
77static char obuf[OUTBUF_SIZE];
78static char *ob = obuf;
79
80/*
81 * Flush buffered output.
82 *
83 * If we haven't displayed any file data yet,
84 * output messages on error output (file descriptor 2),
85 * otherwise output on standard output (file descriptor 1).
86 *
87 * This has the desirable effect of producing all
88 * error messages on error output if standard output
89 * is directed to a file.  It also does the same if
90 * we never produce any real output; for example, if
91 * the input file(s) cannot be opened.  If we do
92 * eventually produce output, code in edit() makes
93 * sure these messages can be seen before they are
94 * overwritten or scrolled away.
95 */
96	public void
97flush()
98{
99	register int n;
100	register int fd;
101
102	n = ob - obuf;
103	if (n == 0)
104		return;
105
106#if MSDOS_COMPILER==MSOFTC
107	if (is_tty && any_display)
108	{
109		*ob = '\0';
110		_outtext(obuf);
111		ob = obuf;
112		return;
113	}
114#else
115#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
116	if (is_tty && any_display)
117	{
118		*ob = '\0';
119		if (ctldisp != OPT_ONPLUS)
120			WIN32textout(obuf, ob - obuf);
121		else
122		{
123			/*
124			 * Look for SGR escape sequences, and convert them
125			 * to color commands.  Replace bold, underline,
126			 * and italic escapes into colors specified via
127			 * the -D command-line option.
128			 */
129			char *anchor, *p, *p_next;
130			unsigned char fg, bg;
131			static unsigned char at;
132#if MSDOS_COMPILER==WIN32C
133			/* Screen colors used by 3x and 4x SGR commands. */
134			static unsigned char screen_color[] = {
135				0, /* BLACK */
136				FOREGROUND_RED,
137				FOREGROUND_GREEN,
138				FOREGROUND_RED|FOREGROUND_GREEN,
139				FOREGROUND_BLUE,
140				FOREGROUND_BLUE|FOREGROUND_RED,
141				FOREGROUND_BLUE|FOREGROUND_GREEN,
142				FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
143			};
144#else
145			static enum COLORS screen_color[] = {
146				BLACK, RED, GREEN, BROWN,
147				BLUE, MAGENTA, CYAN, LIGHTGRAY
148			};
149#endif
150
151			for (anchor = p_next = obuf;
152			     (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
153			{
154				p = p_next;
155				if (p[1] == '[')  /* "ESC-[" sequence */
156				{
157					if (p > anchor)
158					{
159						/*
160						 * If some chars seen since
161						 * the last escape sequence,
162						 * write them out to the screen.
163						 */
164						WIN32textout(anchor, p-anchor);
165						anchor = p;
166					}
167					p += 2;  /* Skip the "ESC-[" */
168					if (is_ansi_end(*p))
169					{
170						/*
171						 * Handle null escape sequence
172						 * "ESC[m", which restores
173						 * the normal color.
174						 */
175						p++;
176						anchor = p_next = p;
177						WIN32setcolors(nm_fg_color, nm_bg_color);
178						continue;
179					}
180					p_next = p;
181
182					/*
183					 * Select foreground/background colors
184					 * based on the escape sequence.
185					 */
186					fg = nm_fg_color;
187					bg = nm_bg_color;
188					while (!is_ansi_end(*p))
189					{
190						char *q;
191						long code = strtol(p, &q, 10);
192
193						if (*q == '\0')
194						{
195							/*
196							 * Incomplete sequence.
197							 * Leave it unprocessed
198							 * in the buffer.
199							 */
200							int slop = q - anchor;
201							/* {{ strcpy args overlap! }} */
202							strcpy(obuf, anchor);
203							ob = &obuf[slop];
204							return;
205						}
206
207						if (q == p ||
208						    code > 49 || code < 0 ||
209						    (!is_ansi_end(*q) && *q != ';'))
210						{
211							p_next = q;
212							break;
213						}
214						if (*q == ';')
215							q++;
216
217						switch (code)
218						{
219						default:
220						/* case 0: all attrs off */
221							fg = nm_fg_color;
222							bg = nm_bg_color;
223							at = 0;
224							break;
225						case 1:	/* bold on */
226							at |= 1;
227							break;
228						case 3:	/* italic on */
229						case 7: /* inverse on */
230							at |= 2;
231							break;
232						case 4:	/* underline on */
233							at |= 4;
234							break;
235						case 5: /* slow blink on */
236						case 6: /* fast blink on */
237							at |= 8;
238							break;
239						case 8:	/* concealed on */
240							fg = (bg & 7) | 8;
241							break;
242						case 22: /* bold off */
243							at &= ~1;
244							break;
245						case 23: /* italic off */
246						case 27: /* inverse off */
247							at &= ~2;
248							break;
249						case 24: /* underline off */
250							at &= ~4;
251							break;
252						case 30: case 31: case 32:
253						case 33: case 34: case 35:
254						case 36: case 37:
255							fg = (fg & 8) | (screen_color[code - 30]);
256							break;
257						case 39: /* default fg */
258							fg = nm_fg_color;
259							break;
260						case 40: case 41: case 42:
261						case 43: case 44: case 45:
262						case 46: case 47:
263							bg = (bg & 8) | (screen_color[code - 40]);
264							break;
265						case 49: /* default fg */
266							bg = nm_bg_color;
267							break;
268						}
269						p = q;
270					}
271					if (!is_ansi_end(*p) || p == p_next)
272						break;
273					if (at & 1)
274					{
275							fg = bo_fg_color;
276							bg = bo_bg_color;
277					} else if (at & 2)
278					{
279							fg = so_fg_color;
280							bg = so_bg_color;
281					} else if (at & 4)
282					{
283							fg = ul_fg_color;
284							bg = ul_bg_color;
285					} else if (at & 8)
286					{
287							fg = bl_fg_color;
288							bg = bl_bg_color;
289					}
290					fg &= 0xf;
291					bg &= 0xf;
292					WIN32setcolors(fg, bg);
293					p_next = anchor = p + 1;
294				} else
295					p_next++;
296			}
297
298			/* Output what's left in the buffer.  */
299			WIN32textout(anchor, ob - anchor);
300		}
301		ob = obuf;
302		return;
303	}
304#endif
305#endif
306	fd = (any_display) ? 1 : 2;
307	if (write(fd, obuf, n) != n)
308		screen_trashed = 1;
309	ob = obuf;
310}
311
312/*
313 * Output a character.
314 */
315	public int
316putchr(c)
317	int c;
318{
319#if 0 /* fake UTF-8 output for testing */
320	extern int utf_mode;
321	if (utf_mode)
322	{
323		static char ubuf[MAX_UTF_CHAR_LEN];
324		static int ubuf_len = 0;
325		static int ubuf_index = 0;
326		if (ubuf_len == 0)
327		{
328			ubuf_len = utf_len(c);
329			ubuf_index = 0;
330		}
331		ubuf[ubuf_index++] = c;
332		if (ubuf_index < ubuf_len)
333			return c;
334		c = get_wchar(ubuf) & 0xFF;
335		ubuf_len = 0;
336	}
337#endif
338	if (need_clr)
339	{
340		need_clr = 0;
341		clear_bot();
342	}
343#if MSDOS_COMPILER
344	if (c == '\n' && is_tty)
345	{
346		/* remove_top(1); */
347		putchr('\r');
348	}
349#else
350#ifdef _OSK
351	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
352		putchr(0x0A);
353#endif
354#endif
355	/*
356	 * Some versions of flush() write to *ob, so we must flush
357	 * when we are still one char from the end of obuf.
358	 */
359	if (ob >= &obuf[sizeof(obuf)-1])
360		flush();
361	*ob++ = c;
362	at_prompt = 0;
363	return (c);
364}
365
366/*
367 * Output a string.
368 */
369	public void
370putstr(s)
371	register char *s;
372{
373	while (*s != '\0')
374		putchr(*s++);
375}
376
377
378/*
379 * Convert an integral type to a string.
380 */
381#define TYPE_TO_A_FUNC(funcname, type) \
382void funcname(num, buf) \
383	type num; \
384	char *buf; \
385{ \
386	int neg = (num < 0); \
387	char tbuf[INT_STRLEN_BOUND(num)+2]; \
388	register char *s = tbuf + sizeof(tbuf); \
389	if (neg) num = -num; \
390	*--s = '\0'; \
391	do { \
392		*--s = (num % 10) + '0'; \
393	} while ((num /= 10) != 0); \
394	if (neg) *--s = '-'; \
395	strcpy(buf, s); \
396}
397
398TYPE_TO_A_FUNC(postoa, POSITION)
399TYPE_TO_A_FUNC(linenumtoa, LINENUM)
400TYPE_TO_A_FUNC(inttoa, int)
401
402/*
403 * Output an integer in a given radix.
404 */
405	static int
406iprint_int(num)
407	int num;
408{
409	char buf[INT_STRLEN_BOUND(num)];
410
411	inttoa(num, buf);
412	putstr(buf);
413	return (strlen(buf));
414}
415
416/*
417 * Output a line number in a given radix.
418 */
419	static int
420iprint_linenum(num)
421	LINENUM num;
422{
423	char buf[INT_STRLEN_BOUND(num)];
424
425	linenumtoa(num, buf);
426	putstr(buf);
427	return (strlen(buf));
428}
429
430/*
431 * This function implements printf-like functionality
432 * using a more portable argument list mechanism than printf's.
433 */
434	static int
435less_printf(fmt, parg)
436	register char *fmt;
437	PARG *parg;
438{
439	register char *s;
440	register int col;
441
442	col = 0;
443	while (*fmt != '\0')
444	{
445		if (*fmt != '%')
446		{
447			putchr(*fmt++);
448			col++;
449		} else
450		{
451			++fmt;
452			switch (*fmt++)
453			{
454			case 's':
455				s = parg->p_string;
456				parg++;
457				while (*s != '\0')
458				{
459					putchr(*s++);
460					col++;
461				}
462				break;
463			case 'd':
464				col += iprint_int(parg->p_int);
465				parg++;
466				break;
467			case 'n':
468				col += iprint_linenum(parg->p_linenum);
469				parg++;
470				break;
471			}
472		}
473	}
474	return (col);
475}
476
477/*
478 * Get a RETURN.
479 * If some other non-trivial char is pressed, unget it, so it will
480 * become the next command.
481 */
482	public void
483get_return()
484{
485	int c;
486
487#if ONLY_RETURN
488	while ((c = getchr()) != '\n' && c != '\r')
489		bell();
490#else
491	c = getchr();
492	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
493		ungetcc(c);
494#endif
495}
496
497/*
498 * Output a message in the lower left corner of the screen
499 * and wait for carriage return.
500 */
501	public void
502error(fmt, parg)
503	char *fmt;
504	PARG *parg;
505{
506	int col = 0;
507	static char return_to_continue[] = "  (press RETURN)";
508
509	errmsgs++;
510
511	if (any_display && is_tty)
512	{
513		if (!oldbot)
514			squish_check();
515		at_exit();
516		clear_bot();
517		at_enter(AT_STANDOUT);
518		col += so_s_width;
519	}
520
521	col += less_printf(fmt, parg);
522
523	if (!(any_display && is_tty))
524	{
525		putchr('\n');
526		return;
527	}
528
529	putstr(return_to_continue);
530	at_exit();
531	col += sizeof(return_to_continue) + so_e_width;
532
533	get_return();
534	lower_left();
535
536	if (col >= sc_width)
537		/*
538		 * Printing the message has probably scrolled the screen.
539		 * {{ Unless the terminal doesn't have auto margins,
540		 *    in which case we just hammered on the right margin. }}
541		 */
542		screen_trashed = 1;
543
544	flush();
545}
546
547static char intr_to_abort[] = "... (interrupt to abort)";
548
549/*
550 * Output a message in the lower left corner of the screen
551 * and don't wait for carriage return.
552 * Usually used to warn that we are beginning a potentially
553 * time-consuming operation.
554 */
555	public void
556ierror(fmt, parg)
557	char *fmt;
558	PARG *parg;
559{
560	at_exit();
561	clear_bot();
562	at_enter(AT_STANDOUT);
563	(void) less_printf(fmt, parg);
564	putstr(intr_to_abort);
565	at_exit();
566	flush();
567	need_clr = 1;
568}
569
570/*
571 * Output a message in the lower left corner of the screen
572 * and return a single-character response.
573 */
574	public int
575query(fmt, parg)
576	char *fmt;
577	PARG *parg;
578{
579	register int c;
580	int col = 0;
581
582	if (any_display && is_tty)
583		clear_bot();
584
585	(void) less_printf(fmt, parg);
586	c = getchr();
587
588	if (!(any_display && is_tty))
589	{
590		putchr('\n');
591		return (c);
592	}
593
594	lower_left();
595	if (col >= sc_width)
596		screen_trashed = 1;
597	flush();
598
599	return (c);
600}
601