xref: /freebsd/contrib/less/output.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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 *op;
129 		DWORD nwritten = 0;
130 		CONSOLE_SCREEN_BUFFER_INFO scr;
131 		int row;
132 		int col;
133 		int olen;
134 		extern HANDLE con_out;
135 
136 		olen = ob - obuf;
137 		/*
138 		 * There is a bug in Win32 WriteConsole() if we're
139 		 * writing in the last cell with a different color.
140 		 * To avoid color problems in the bottom line,
141 		 * we scroll the screen manually, before writing.
142 		 */
143 		GetConsoleScreenBufferInfo(con_out, &scr);
144 		col = scr.dwCursorPosition.X;
145 		row = scr.dwCursorPosition.Y;
146 		for (op = obuf;  op < obuf + olen;  op++)
147 		{
148 			if (*op == '\n')
149 			{
150 				col = 0;
151 				row++;
152 			} else
153 			{
154 				col++;
155 				if (col >= sc_width)
156 				{
157 					col = 0;
158 					row++;
159 				}
160 			}
161 		}
162 		if (row > scr.srWindow.Bottom)
163 			win32_scroll_up(row - scr.srWindow.Bottom);
164 		WriteConsole(con_out, obuf, olen, &nwritten, NULL);
165 		ob = obuf;
166 		return;
167 	}
168 #else
169 #if MSDOS_COMPILER==MSOFTC
170 	if (is_tty && any_display)
171 	{
172 		*ob = '\0';
173 		_outtext(obuf);
174 		ob = obuf;
175 		return;
176 	}
177 #else
178 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
179 	if (is_tty && any_display)
180 	{
181 		*ob = '\0';
182 		cputs(obuf);
183 		ob = obuf;
184 		return;
185 	}
186 #endif
187 #endif
188 #endif
189 	fd = (any_display) ? 1 : 2;
190 	if (write(fd, obuf, n) != n)
191 		screen_trashed = 1;
192 	ob = obuf;
193 }
194 
195 /*
196  * Output a character.
197  */
198 	public int
199 putchr(c)
200 	int c;
201 {
202 	if (need_clr)
203 	{
204 		need_clr = 0;
205 		clear_bot();
206 	}
207 #if MSDOS_COMPILER
208 	if (c == '\n' && is_tty)
209 	{
210 		/* remove_top(1); */
211 		putchr('\r');
212 	}
213 #else
214 #ifdef _OSK
215 	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
216 		putchr(0x0A);
217 #endif
218 #endif
219 	/*
220 	 * Some versions of flush() write to *ob, so we must flush
221 	 * when we are still one char from the end of obuf.
222 	 */
223 	if (ob >= &obuf[sizeof(obuf)-1])
224 		flush();
225 	*ob++ = c;
226 	return (c);
227 }
228 
229 /*
230  * Output a string.
231  */
232 	public void
233 putstr(s)
234 	register char *s;
235 {
236 	while (*s != '\0')
237 		putchr(*s++);
238 }
239 
240 
241 /*
242  * Output an integer in a given radix.
243  */
244 	static int
245 iprintnum(num, radix)
246 	int num;
247 	int radix;
248 {
249 	register char *s;
250 	int r;
251 	int neg;
252 	char buf[INT_STRLEN_BOUND(num)];
253 
254 	neg = (num < 0);
255 	if (neg)
256 		num = -num;
257 
258 	s = buf;
259 	do
260 	{
261 		*s++ = (num % radix) + '0';
262 	} while ((num /= radix) != 0);
263 
264 	if (neg)
265 		*s++ = '-';
266 	r = s - buf;
267 
268 	while (s > buf)
269 		putchr(*--s);
270 	return (r);
271 }
272 
273 /*
274  * This function implements printf-like functionality
275  * using a more portable argument list mechanism than printf's.
276  */
277 	static int
278 less_printf(fmt, parg)
279 	register char *fmt;
280 	PARG *parg;
281 {
282 	register char *s;
283 	register int n;
284 	register int col;
285 
286 	col = 0;
287 	while (*fmt != '\0')
288 	{
289 		if (*fmt != '%')
290 		{
291 			putchr(*fmt++);
292 			col++;
293 		} else
294 		{
295 			++fmt;
296 			switch (*fmt++) {
297 			case 's':
298 				s = parg->p_string;
299 				parg++;
300 				while (*s != '\0')
301 				{
302 					putchr(*s++);
303 					col++;
304 				}
305 				break;
306 			case 'd':
307 				n = parg->p_int;
308 				parg++;
309 				col += iprintnum(n, 10);
310 				break;
311 			}
312 		}
313 	}
314 	return (col);
315 }
316 
317 /*
318  * Get a RETURN.
319  * If some other non-trivial char is pressed, unget it, so it will
320  * become the next command.
321  */
322 	public void
323 get_return()
324 {
325 	int c;
326 
327 #if ONLY_RETURN
328 	while ((c = getchr()) != '\n' && c != '\r')
329 		bell();
330 #else
331 	c = getchr();
332 	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
333 		ungetcc(c);
334 #endif
335 }
336 
337 /*
338  * Output a message in the lower left corner of the screen
339  * and wait for carriage return.
340  */
341 	public void
342 error(fmt, parg)
343 	char *fmt;
344 	PARG *parg;
345 {
346 	int col = 0;
347 	static char return_to_continue[] = "  (press RETURN)";
348 
349 	errmsgs++;
350 
351 	if (any_display && is_tty)
352 	{
353 		clear_bot();
354 		so_enter();
355 		col += so_s_width;
356 	}
357 
358 	col += less_printf(fmt, parg);
359 
360 	if (!(any_display && is_tty))
361 	{
362 		putchr('\n');
363 		return;
364 	}
365 
366 	putstr(return_to_continue);
367 	so_exit();
368 	col += sizeof(return_to_continue) + so_e_width;
369 
370 	get_return();
371 	lower_left();
372 
373 	if (col >= sc_width)
374 		/*
375 		 * Printing the message has probably scrolled the screen.
376 		 * {{ Unless the terminal doesn't have auto margins,
377 		 *    in which case we just hammered on the right margin. }}
378 		 */
379 		screen_trashed = 1;
380 
381 	flush();
382 }
383 
384 static char intr_to_abort[] = "... (interrupt to abort)";
385 
386 /*
387  * Output a message in the lower left corner of the screen
388  * and don't wait for carriage return.
389  * Usually used to warn that we are beginning a potentially
390  * time-consuming operation.
391  */
392 	public void
393 ierror(fmt, parg)
394 	char *fmt;
395 	PARG *parg;
396 {
397 	clear_bot();
398 	so_enter();
399 	(void) less_printf(fmt, parg);
400 	putstr(intr_to_abort);
401 	so_exit();
402 	flush();
403 	need_clr = 1;
404 }
405 
406 /*
407  * Output a message in the lower left corner of the screen
408  * and return a single-character response.
409  */
410 	public int
411 query(fmt, parg)
412 	char *fmt;
413 	PARG *parg;
414 {
415 	register int c;
416 	int col = 0;
417 
418 	if (any_display && is_tty)
419 		clear_bot();
420 
421 	(void) less_printf(fmt, parg);
422 	c = getchr();
423 
424 	if (!(any_display && is_tty))
425 	{
426 		putchr('\n');
427 		return (c);
428 	}
429 
430 	lower_left();
431 	if (col >= sc_width)
432 		screen_trashed = 1;
433 	flush();
434 
435 	return (c);
436 }
437