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