1/*
2 * Copyright (C) 1984-2007  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#if MSDOS_COMPILER==WIN32C
132			/* Screen colors used by 3x and 4x SGR commands. */
133			static unsigned char screen_color[] = {
134				0, /* BLACK */
135				FOREGROUND_RED,
136				FOREGROUND_GREEN,
137				FOREGROUND_RED|FOREGROUND_GREEN,
138				FOREGROUND_BLUE,
139				FOREGROUND_BLUE|FOREGROUND_RED,
140				FOREGROUND_BLUE|FOREGROUND_GREEN,
141				FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
142			};
143#else
144			static enum COLORS screen_color[] = {
145				BLACK, RED, GREEN, BROWN,
146				BLUE, MAGENTA, CYAN, LIGHTGRAY
147			};
148#endif
149
150			for (anchor = p_next = obuf;
151			     (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
152			{
153				p = p_next;
154				if (p[1] == '[')  /* "ESC-[" sequence */
155				{
156					if (p > anchor)
157					{
158						/*
159						 * If some chars seen since
160						 * the last escape sequence,
161						 * write them out to the screen.
162						 */
163						WIN32textout(anchor, p-anchor);
164						anchor = p;
165					}
166					p += 2;  /* Skip the "ESC-[" */
167					if (is_ansi_end(*p))
168					{
169						/*
170						 * Handle null escape sequence
171						 * "ESC[m", which restores
172						 * the normal color.
173						 */
174						p++;
175						anchor = p_next = p;
176						WIN32setcolors(nm_fg_color, nm_bg_color);
177						continue;
178					}
179					p_next = p;
180
181					/*
182					 * Select foreground/background colors
183					 * based on the escape sequence.
184					 */
185					fg = nm_fg_color;
186					bg = nm_bg_color;
187					while (!is_ansi_end(*p))
188					{
189						char *q;
190						long code = strtol(p, &q, 10);
191
192						if (*q == '\0')
193						{
194							/*
195							 * Incomplete sequence.
196							 * Leave it unprocessed
197							 * in the buffer.
198							 */
199							int slop = q - anchor;
200							/* {{ strcpy args overlap! }} */
201							strcpy(obuf, anchor);
202							ob = &obuf[slop];
203							return;
204						}
205
206						if (q == p ||
207						    code > 49 || code < 0 ||
208						    (!is_ansi_end(*q) && *q != ';'))
209						{
210							p_next = q;
211							break;
212						}
213						if (*q == ';')
214							q++;
215
216						switch (code)
217						{
218						default:
219						/* case 0:  all attrs off */
220						/* case 22: bold off */
221						/* case 23: italic off */
222						/* case 24: underline off */
223						/* case 27: inverse off */
224							fg = nm_fg_color;
225							bg = nm_bg_color;
226							break;
227						case 1:	/* bold on */
228							fg = bo_fg_color;
229							bg = bo_bg_color;
230							break;
231						case 3:	/* italic on */
232						case 7: /* inverse on */
233							fg = so_fg_color;
234							bg = so_bg_color;
235							break;
236						case 4:	/* underline on */
237							fg = ul_fg_color;
238							bg = ul_bg_color;
239							break;
240						case 5: /* slow blink on */
241						case 6: /* fast blink on */
242							fg = bl_fg_color;
243							bg = bl_bg_color;
244							break;
245						case 8:	/* concealed on */
246							fg = (bg & 7) | 8;
247							break;
248						case 30: case 31: case 32:
249						case 33: case 34: case 35:
250						case 36: case 37:
251							fg = (fg & 8) | (screen_color[code - 30]);
252							break;
253						case 39: /* default fg */
254							fg = nm_fg_color;
255							break;
256						case 40: case 41: case 42:
257						case 43: case 44: case 45:
258						case 46: case 47:
259							bg = (bg & 8) | (screen_color[code - 40]);
260							break;
261						case 49: /* default fg */
262							bg = nm_bg_color;
263							break;
264						}
265						p = q;
266					}
267					if (!is_ansi_end(*p) || p == p_next)
268						break;
269					fg &= 0xf;
270					bg &= 0xf;
271					WIN32setcolors(fg, bg);
272					p_next = anchor = p + 1;
273				} else
274					p_next++;
275			}
276
277			/* Output what's left in the buffer.  */
278			WIN32textout(anchor, ob - anchor);
279		}
280		ob = obuf;
281		return;
282	}
283#endif
284#endif
285	fd = (any_display) ? 1 : 2;
286	if (write(fd, obuf, n) != n)
287		screen_trashed = 1;
288	ob = obuf;
289}
290
291/*
292 * Output a character.
293 */
294	public int
295putchr(c)
296	int c;
297{
298#if 0 /* fake UTF-8 output for testing */
299	extern int utf_mode;
300	if (utf_mode)
301	{
302		static char ubuf[MAX_UTF_CHAR_LEN];
303		static int ubuf_len = 0;
304		static int ubuf_index = 0;
305		if (ubuf_len == 0)
306		{
307			ubuf_len = utf_len(c);
308			ubuf_index = 0;
309		}
310		ubuf[ubuf_index++] = c;
311		if (ubuf_index < ubuf_len)
312			return c;
313		c = get_wchar(ubuf) & 0xFF;
314		ubuf_len = 0;
315	}
316#endif
317	if (need_clr)
318	{
319		need_clr = 0;
320		clear_bot();
321	}
322#if MSDOS_COMPILER
323	if (c == '\n' && is_tty)
324	{
325		/* remove_top(1); */
326		putchr('\r');
327	}
328#else
329#ifdef _OSK
330	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
331		putchr(0x0A);
332#endif
333#endif
334	/*
335	 * Some versions of flush() write to *ob, so we must flush
336	 * when we are still one char from the end of obuf.
337	 */
338	if (ob >= &obuf[sizeof(obuf)-1])
339		flush();
340	*ob++ = c;
341	at_prompt = 0;
342	return (c);
343}
344
345/*
346 * Output a string.
347 */
348	public void
349putstr(s)
350	register char *s;
351{
352	while (*s != '\0')
353		putchr(*s++);
354}
355
356
357/*
358 * Convert an integral type to a string.
359 */
360#define TYPE_TO_A_FUNC(funcname, type) \
361void funcname(num, buf) \
362	type num; \
363	char *buf; \
364{ \
365	int neg = (num < 0); \
366	char tbuf[INT_STRLEN_BOUND(num)+2]; \
367	register char *s = tbuf + sizeof(tbuf); \
368	if (neg) num = -num; \
369	*--s = '\0'; \
370	do { \
371		*--s = (num % 10) + '0'; \
372	} while ((num /= 10) != 0); \
373	if (neg) *--s = '-'; \
374	strcpy(buf, s); \
375}
376
377TYPE_TO_A_FUNC(postoa, POSITION)
378TYPE_TO_A_FUNC(linenumtoa, LINENUM)
379TYPE_TO_A_FUNC(inttoa, int)
380
381/*
382 * Output an integer in a given radix.
383 */
384	static int
385iprint_int(num)
386	int num;
387{
388	char buf[INT_STRLEN_BOUND(num)];
389
390	inttoa(num, buf);
391	putstr(buf);
392	return (strlen(buf));
393}
394
395/*
396 * Output a line number in a given radix.
397 */
398	static int
399iprint_linenum(num)
400	LINENUM num;
401{
402	char buf[INT_STRLEN_BOUND(num)];
403
404	linenumtoa(num, buf);
405	putstr(buf);
406	return (strlen(buf));
407}
408
409/*
410 * This function implements printf-like functionality
411 * using a more portable argument list mechanism than printf's.
412 */
413	static int
414less_printf(fmt, parg)
415	register char *fmt;
416	PARG *parg;
417{
418	register char *s;
419	register int col;
420
421	col = 0;
422	while (*fmt != '\0')
423	{
424		if (*fmt != '%')
425		{
426			putchr(*fmt++);
427			col++;
428		} else
429		{
430			++fmt;
431			switch (*fmt++)
432			{
433			case 's':
434				s = parg->p_string;
435				parg++;
436				while (*s != '\0')
437				{
438					putchr(*s++);
439					col++;
440				}
441				break;
442			case 'd':
443				col += iprint_int(parg->p_int);
444				parg++;
445				break;
446			case 'n':
447				col += iprint_linenum(parg->p_linenum);
448				parg++;
449				break;
450			}
451		}
452	}
453	return (col);
454}
455
456/*
457 * Get a RETURN.
458 * If some other non-trivial char is pressed, unget it, so it will
459 * become the next command.
460 */
461	public void
462get_return()
463{
464	int c;
465
466#if ONLY_RETURN
467	while ((c = getchr()) != '\n' && c != '\r')
468		bell();
469#else
470	c = getchr();
471	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
472		ungetcc(c);
473#endif
474}
475
476/*
477 * Output a message in the lower left corner of the screen
478 * and wait for carriage return.
479 */
480	public void
481error(fmt, parg)
482	char *fmt;
483	PARG *parg;
484{
485	int col = 0;
486	static char return_to_continue[] = "  (press RETURN)";
487
488	errmsgs++;
489
490	if (any_display && is_tty)
491	{
492		if (!oldbot)
493			squish_check();
494		at_exit();
495		clear_bot();
496		at_enter(AT_STANDOUT);
497		col += so_s_width;
498	}
499
500	col += less_printf(fmt, parg);
501
502	if (!(any_display && is_tty))
503	{
504		putchr('\n');
505		return;
506	}
507
508	putstr(return_to_continue);
509	at_exit();
510	col += sizeof(return_to_continue) + so_e_width;
511
512	get_return();
513	lower_left();
514
515	if (col >= sc_width)
516		/*
517		 * Printing the message has probably scrolled the screen.
518		 * {{ Unless the terminal doesn't have auto margins,
519		 *    in which case we just hammered on the right margin. }}
520		 */
521		screen_trashed = 1;
522
523	flush();
524}
525
526static char intr_to_abort[] = "... (interrupt to abort)";
527
528/*
529 * Output a message in the lower left corner of the screen
530 * and don't wait for carriage return.
531 * Usually used to warn that we are beginning a potentially
532 * time-consuming operation.
533 */
534	public void
535ierror(fmt, parg)
536	char *fmt;
537	PARG *parg;
538{
539	at_exit();
540	clear_bot();
541	at_enter(AT_STANDOUT);
542	(void) less_printf(fmt, parg);
543	putstr(intr_to_abort);
544	at_exit();
545	flush();
546	need_clr = 1;
547}
548
549/*
550 * Output a message in the lower left corner of the screen
551 * and return a single-character response.
552 */
553	public int
554query(fmt, parg)
555	char *fmt;
556	PARG *parg;
557{
558	register int c;
559	int col = 0;
560
561	if (any_display && is_tty)
562		clear_bot();
563
564	(void) less_printf(fmt, parg);
565	c = getchr();
566
567	if (!(any_display && is_tty))
568	{
569		putchr('\n');
570		return (c);
571	}
572
573	lower_left();
574	if (col >= sc_width)
575		screen_trashed = 1;
576	flush();
577
578	return (c);
579}
580