output.c revision 60786
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
21public int errmsgs;	/* Count of messages displayed by error() */
22public int need_clr;
23public int final_attr;
24
25extern int sigs;
26extern int sc_width;
27extern int so_s_width, so_e_width;
28extern int screen_trashed;
29extern int any_display;
30extern int is_tty;
31
32/*
33 * Display the line which is in the line buffer.
34 */
35	public void
36put_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
97static char obuf[OUTBUF_SIZE];
98static 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
117flush()
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
224putchr(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
258putstr(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
270iprintnum(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
303less_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
348get_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
367error(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
409static 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
418ierror(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
436query(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