xref: /minix/external/bsd/less/dist/output.c (revision dda632a2)
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 
23 public int errmsgs;	/* Count of messages displayed by error() */
24 public int need_clr;
25 public int final_attr;
26 public int at_prompt;
27 
28 extern int sigs;
29 extern int sc_width;
30 extern int so_s_width, so_e_width;
31 extern int screen_trashed;
32 extern int any_display;
33 extern int is_tty;
34 extern int oldbot;
35 
36 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
37 extern int ctldisp;
38 extern int nm_fg_color, nm_bg_color;
39 extern int bo_fg_color, bo_bg_color;
40 extern int ul_fg_color, ul_bg_color;
41 extern int so_fg_color, so_bg_color;
42 extern 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
49 put_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 
79 static char obuf[OUTBUF_SIZE];
80 static 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
99 flush()
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
318 putchr(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
372 putstr(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) \
384 void 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 
400 TYPE_TO_A_FUNC(postoa, POSITION)
401 TYPE_TO_A_FUNC(linenumtoa, LINENUM)
402 TYPE_TO_A_FUNC(inttoa, int)
403 
404 /*
405  * Output an integer in a given radix.
406  */
407 	static int
408 iprint_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
422 iprint_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
437 less_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
485 get_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
504 error(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 
550 static 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
559 ierror(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
578 query(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