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