xref: /freebsd/contrib/less/output.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*
2  * Copyright (C) 1984-2000  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 
21 public int errmsgs;	/* Count of messages displayed by error() */
22 public int need_clr;
23 public int final_attr;
24 
25 extern int sigs;
26 extern int sc_width;
27 extern int so_s_width, so_e_width;
28 extern int screen_trashed;
29 extern int any_display;
30 extern int is_tty;
31 
32 /*
33  * Display the line which is in the line buffer.
34  */
35 	public void
36 put_line()
37 {
38 	register int c;
39 	register int i;
40 	int a;
41 	int curr_attr;
42 
43 	if (ABORT_SIGS())
44 	{
45 		/*
46 		 * Don't output if a signal is pending.
47 		 */
48 		screen_trashed = 1;
49 		return;
50 	}
51 
52 	curr_attr = AT_NORMAL;
53 
54 	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
55 	{
56 		if (a != curr_attr)
57 		{
58 			/*
59 			 * Changing attributes.
60 			 * Display the exit sequence for the old attribute
61 			 * and the enter sequence for the new one.
62 			 */
63 			switch (curr_attr)
64 			{
65 			case AT_UNDERLINE:	ul_exit();	break;
66 			case AT_BOLD:		bo_exit();	break;
67 			case AT_BLINK:		bl_exit();	break;
68 			case AT_STANDOUT:	so_exit();	break;
69 			}
70 			switch (a)
71 			{
72 			case AT_UNDERLINE:	ul_enter();	break;
73 			case AT_BOLD:		bo_enter();	break;
74 			case AT_BLINK:		bl_enter();	break;
75 			case AT_STANDOUT:	so_enter();	break;
76 			}
77 			curr_attr = a;
78 		}
79 		if (curr_attr == AT_INVIS)
80 			continue;
81 		if (c == '\b')
82 			putbs();
83 		else
84 			putchr(c);
85 	}
86 
87 	switch (curr_attr)
88 	{
89 	case AT_UNDERLINE:	ul_exit();	break;
90 	case AT_BOLD:		bo_exit();	break;
91 	case AT_BLINK:		bl_exit();	break;
92 	case AT_STANDOUT:	so_exit();	break;
93 	}
94 	final_attr = curr_attr;
95 }
96 
97 static char obuf[OUTBUF_SIZE];
98 static char *ob = obuf;
99 
100 /*
101  * Flush buffered output.
102  *
103  * If we haven't displayed any file data yet,
104  * output messages on error output (file descriptor 2),
105  * otherwise output on standard output (file descriptor 1).
106  *
107  * This has the desirable effect of producing all
108  * error messages on error output if standard output
109  * is directed to a file.  It also does the same if
110  * we never produce any real output; for example, if
111  * the input file(s) cannot be opened.  If we do
112  * eventually produce output, code in edit() makes
113  * sure these messages can be seen before they are
114  * overwritten or scrolled away.
115  */
116 	public void
117 flush()
118 {
119 	register int n;
120 	register int fd;
121 
122 	n = ob - obuf;
123 	if (n == 0)
124 		return;
125 #if MSDOS_COMPILER==WIN32C
126 	if (is_tty && any_display)
127 	{
128 		char *p;
129 		char *op;
130 		DWORD nwritten = 0;
131 		CONSOLE_SCREEN_BUFFER_INFO scr;
132 		DWORD nchars;
133 		COORD cpos;
134 		WORD nm_attr;
135 		int olen;
136 		extern HANDLE con_out;
137 		extern int nm_fg_color;
138 		extern int nm_bg_color;
139 #define	MAKEATTR(fg,bg)		((WORD)((fg)|((bg)<<4)))
140 
141 		*ob = '\0';
142 		olen = ob - obuf;
143 		/*
144 		 * To avoid color problems, if we're scrolling the screen,
145 		 * we write only up to the char that causes the scroll,
146 		 * (a newline or a char in the last column), then fill
147 		 * the bottom line with the "normal" attribute, then
148 		 * write the rest.
149 		 * When Windows scrolls, it takes the attributes for the
150 		 * new line from the first char of the (previously)
151 		 * bottom line.
152 		 *
153 		 * {{ This still doesn't work correctly in all cases! }}
154 		 */
155 		nm_attr = MAKEATTR(nm_fg_color, nm_bg_color);
156 		for (op = obuf;  *op != '\0';  )
157 		{
158 			GetConsoleScreenBufferInfo(con_out, &scr);
159 			/* Find the next newline. */
160 			p = strchr(op, '\n');
161 			if (p == NULL &&
162 			    scr.dwCursorPosition.X + olen >= sc_width)
163 			{
164 				/*
165 				 * No newline, but writing in the
166 				 * last column causes scrolling.
167 				 */
168 				p = op + sc_width - scr.dwCursorPosition.X - 1;
169 			}
170 			if (scr.dwCursorPosition.Y != scr.srWindow.Bottom ||
171 			    p == NULL)
172 			{
173 				/* Write the entire buffer. */
174 				WriteConsole(con_out, op, olen,
175 						&nwritten, NULL);
176 				op += olen;
177 			} else
178 			{
179 				/* Write only up to the scrolling char. */
180 				WriteConsole(con_out, op, p - op + 1,
181 						&nwritten, NULL);
182 				cpos.X = 0;
183 				cpos.Y = scr.dwCursorPosition.Y;
184 				FillConsoleOutputAttribute(con_out, nm_attr,
185 						sc_width, cpos, &nchars);
186 				olen -= p - op + 1;
187 				op = p + 1;
188 			}
189 		}
190 		ob = obuf;
191 		return;
192 	}
193 #else
194 #if MSDOS_COMPILER==MSOFTC
195 	if (is_tty && any_display)
196 	{
197 		*ob = '\0';
198 		_outtext(obuf);
199 		ob = obuf;
200 		return;
201 	}
202 #else
203 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
204 	if (is_tty && any_display)
205 	{
206 		*ob = '\0';
207 		cputs(obuf);
208 		ob = obuf;
209 		return;
210 	}
211 #endif
212 #endif
213 #endif
214 	fd = (any_display) ? 1 : 2;
215 	if (write(fd, obuf, n) != n)
216 		screen_trashed = 1;
217 	ob = obuf;
218 }
219 
220 /*
221  * Output a character.
222  */
223 	public int
224 putchr(c)
225 	int c;
226 {
227 	if (need_clr)
228 	{
229 		need_clr = 0;
230 		clear_bot();
231 	}
232 #if MSDOS_COMPILER
233 	if (c == '\n' && is_tty)
234 	{
235 		/* remove_top(1); */
236 		putchr('\r');
237 	}
238 #else
239 #ifdef _OSK
240 	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
241 		putchr(0x0A);
242 #endif
243 #endif
244 	/*
245 	 * Some versions of flush() write to *ob, so we must flush
246 	 * when we are still one char from the end of obuf.
247 	 */
248 	if (ob >= &obuf[sizeof(obuf)-1])
249 		flush();
250 	*ob++ = c;
251 	return (c);
252 }
253 
254 /*
255  * Output a string.
256  */
257 	public void
258 putstr(s)
259 	register char *s;
260 {
261 	while (*s != '\0')
262 		putchr(*s++);
263 }
264 
265 
266 /*
267  * Output an integer in a given radix.
268  */
269 	static int
270 iprintnum(num, radix)
271 	int num;
272 	int radix;
273 {
274 	register char *s;
275 	int r;
276 	int neg;
277 	char buf[10];
278 
279 	neg = (num < 0);
280 	if (neg)
281 		num = -num;
282 
283 	s = buf;
284 	do
285 	{
286 		*s++ = (num % radix) + '0';
287 	} while ((num /= radix) != 0);
288 
289 	if (neg)
290 		*s++ = '-';
291 	r = s - buf;
292 
293 	while (s > buf)
294 		putchr(*--s);
295 	return (r);
296 }
297 
298 /*
299  * This function implements printf-like functionality
300  * using a more portable argument list mechanism than printf's.
301  */
302 	static int
303 less_printf(fmt, parg)
304 	register char *fmt;
305 	PARG *parg;
306 {
307 	register char *s;
308 	register int n;
309 	register int col;
310 
311 	col = 0;
312 	while (*fmt != '\0')
313 	{
314 		if (*fmt != '%')
315 		{
316 			putchr(*fmt++);
317 			col++;
318 		} else
319 		{
320 			++fmt;
321 			switch (*fmt++) {
322 			case 's':
323 				s = parg->p_string;
324 				parg++;
325 				while (*s != '\0')
326 				{
327 					putchr(*s++);
328 					col++;
329 				}
330 				break;
331 			case 'd':
332 				n = parg->p_int;
333 				parg++;
334 				col += iprintnum(n, 10);
335 				break;
336 			}
337 		}
338 	}
339 	return (col);
340 }
341 
342 /*
343  * Get a RETURN.
344  * If some other non-trivial char is pressed, unget it, so it will
345  * become the next command.
346  */
347 	public void
348 get_return()
349 {
350 	int c;
351 
352 #if ONLY_RETURN
353 	while ((c = getchr()) != '\n' && c != '\r')
354 		bell();
355 #else
356 	c = getchr();
357 	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
358 		ungetcc(c);
359 #endif
360 }
361 
362 /*
363  * Output a message in the lower left corner of the screen
364  * and wait for carriage return.
365  */
366 	public void
367 error(fmt, parg)
368 	char *fmt;
369 	PARG *parg;
370 {
371 	int col = 0;
372 	static char return_to_continue[] = "  (press RETURN)";
373 
374 	errmsgs++;
375 
376 	if (any_display && is_tty)
377 	{
378 		clear_bot();
379 		so_enter();
380 		col += so_s_width;
381 	}
382 
383 	col += less_printf(fmt, parg);
384 
385 	if (!(any_display && is_tty))
386 	{
387 		putchr('\n');
388 		return;
389 	}
390 
391 	putstr(return_to_continue);
392 	so_exit();
393 	col += sizeof(return_to_continue) + so_e_width;
394 
395 	get_return();
396 	lower_left();
397 
398 	if (col >= sc_width)
399 		/*
400 		 * Printing the message has probably scrolled the screen.
401 		 * {{ Unless the terminal doesn't have auto margins,
402 		 *    in which case we just hammered on the right margin. }}
403 		 */
404 		screen_trashed = 1;
405 
406 	flush();
407 }
408 
409 static char intr_to_abort[] = "... (interrupt to abort)";
410 
411 /*
412  * Output a message in the lower left corner of the screen
413  * and don't wait for carriage return.
414  * Usually used to warn that we are beginning a potentially
415  * time-consuming operation.
416  */
417 	public void
418 ierror(fmt, parg)
419 	char *fmt;
420 	PARG *parg;
421 {
422 	clear_bot();
423 	so_enter();
424 	(void) less_printf(fmt, parg);
425 	putstr(intr_to_abort);
426 	so_exit();
427 	flush();
428 	need_clr = 1;
429 }
430 
431 /*
432  * Output a message in the lower left corner of the screen
433  * and return a single-character response.
434  */
435 	public int
436 query(fmt, parg)
437 	char *fmt;
438 	PARG *parg;
439 {
440 	register int c;
441 	int col = 0;
442 
443 	if (any_display && is_tty)
444 		clear_bot();
445 
446 	(void) less_printf(fmt, parg);
447 	c = getchr();
448 
449 	if (!(any_display && is_tty))
450 	{
451 		putchr('\n');
452 		return (c);
453 	}
454 
455 	lower_left();
456 	if (col >= sc_width)
457 		screen_trashed = 1;
458 	flush();
459 
460 	return (c);
461 }
462