1 /*
2  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice in the documentation and/or other materials provided with
12  *    the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 
28 /*
29  * High level routines dealing with the output to the screen.
30  */
31 
32 #include "less.h"
33 
34 public int errmsgs;	/* Count of messages displayed by error() */
35 public int need_clr;
36 
37 extern int sigs;
38 extern int sc_width;
39 extern int so_s_width, so_e_width;
40 extern int screen_trashed;
41 extern int any_display;
42 
43 /*
44  * Display the line which is in the line buffer.
45  */
46 	public void
put_line()47 put_line()
48 {
49 	register int c;
50 	register int i;
51 	int a;
52 	int curr_attr;
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 	curr_attr = AT_NORMAL;
64 
65 	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
66 	{
67 		if (a != curr_attr)
68 		{
69 			/*
70 			 * Changing attributes.
71 			 * Display the exit sequence for the old attribute
72 			 * and the enter sequence for the new one.
73 			 */
74 			switch (curr_attr)
75 			{
76 			case AT_UNDERLINE:	ul_exit();	break;
77 			case AT_BOLD:		bo_exit();	break;
78 			case AT_BLINK:		bl_exit();	break;
79 			case AT_STANDOUT:	so_exit();	break;
80 			}
81 			switch (a)
82 			{
83 			case AT_UNDERLINE:	ul_enter();	break;
84 			case AT_BOLD:		bo_enter();	break;
85 			case AT_BLINK:		bl_enter();	break;
86 			case AT_STANDOUT:	so_enter();	break;
87 			}
88 			curr_attr = a;
89 		}
90 		if (curr_attr == AT_INVIS)
91 			continue;
92 		if (c == '\b')
93 			putbs();
94 		else
95 			putchr(c);
96 	}
97 
98 	switch (curr_attr)
99 	{
100 	case AT_UNDERLINE:	ul_exit();	break;
101 	case AT_BOLD:		bo_exit();	break;
102 	case AT_BLINK:		bl_exit();	break;
103 	case AT_STANDOUT:	so_exit();	break;
104 	}
105 }
106 
107 static char obuf[1024];
108 static char *ob = obuf;
109 
110 /*
111  * Flush buffered output.
112  *
113  * If we haven't displayed any file data yet,
114  * output messages on error output (file descriptor 2),
115  * otherwise output on standard output (file descriptor 1).
116  *
117  * This has the desirable effect of producing all
118  * error messages on error output if standard output
119  * is directed to a file.  It also does the same if
120  * we never produce any real output; for example, if
121  * the input file(s) cannot be opened.  If we do
122  * eventually produce output, code in edit() makes
123  * sure these messages can be seen before they are
124  * overwritten or scrolled away.
125  */
126 	public void
flush()127 flush()
128 {
129 	register int n;
130 	register int fd;
131 
132 #if MSOFTC
133 	*ob = '\0';
134 	_outtext(obuf);
135 	ob = obuf;
136 #else
137 	n = ob - obuf;
138 	if (n == 0)
139 		return;
140 	fd = (any_display) ? 1 : 2;
141 	if (write(fd, obuf, n) != n)
142 		screen_trashed = 1;
143 	ob = obuf;
144 #endif
145 }
146 
147 /*
148  * Output a character.
149  */
150 	public int
putchr(c)151 putchr(c)
152 	int c;
153 {
154 	if (ob >= &obuf[sizeof(obuf)])
155 		flush();
156 	if (need_clr)
157 	{
158 		need_clr = 0;
159 		clear_bot();
160 	}
161 #if MSOFTC
162 	if (c == '\n')
163 		putchr('\r');
164 #endif
165 	*ob++ = c;
166 	return (c);
167 }
168 
169 /*
170  * Output a string.
171  */
172 	public void
putstr(s)173 putstr(s)
174 	register char *s;
175 {
176 	while (*s != '\0')
177 		putchr(*s++);
178 }
179 
180 
181 /*
182  * Output an integer in a given radix.
183  */
184 	static int
iprintnum(num,radix)185 iprintnum(num, radix)
186 	int num;
187 	int radix;
188 {
189 	register char *s;
190 	int r;
191 	int neg;
192 	char buf[10];
193 
194 	if (neg = (num < 0))
195 		num = -num;
196 
197 	s = buf;
198 	do
199 	{
200 		*s++ = (num % radix) + '0';
201 	} while ((num /= radix) != 0);
202 
203 	if (neg)
204 		*s++ = '-';
205 	r = s - buf;
206 
207 	while (s > buf)
208 		putchr(*--s);
209 	return (r);
210 }
211 
212 /*
213  * This function implements printf-like functionality
214  * using a more portable argument list mechanism than printf's.
215  */
216 	static int
iprintf(fmt,parg)217 iprintf(fmt, parg)
218 	register char *fmt;
219 	PARG *parg;
220 {
221 	register char *s;
222 	register int n;
223 	register int col;
224 
225 	col = 0;
226 	while (*fmt != '\0')
227 	{
228 		if (*fmt != '%')
229 		{
230 			putchr(*fmt++);
231 			col++;
232 		} else
233 		{
234 			++fmt;
235 			switch (*fmt++) {
236 			case 's':
237 				s = parg->p_string;
238 				parg++;
239 				while (*s != '\0')
240 				{
241 					putchr(*s++);
242 					col++;
243 				}
244 				break;
245 			case 'd':
246 				n = parg->p_int;
247 				parg++;
248 				col += iprintnum(n, 10);
249 				break;
250 			}
251 		}
252 	}
253 	return (col);
254 }
255 
256 /*
257  * Output a message in the lower left corner of the screen
258  * and wait for carriage return.
259  */
260 	public void
error(fmt,parg)261 error(fmt, parg)
262 	char *fmt;
263 	PARG *parg;
264 {
265 	int c;
266 	int col = 0;
267 	static char return_to_continue[] = "  (press RETURN)";
268 
269 	errmsgs++;
270 
271 	if (any_display)
272 	{
273 		clear_bot();
274 		so_enter();
275 		col += so_s_width;
276 	}
277 
278 	col += iprintf(fmt, parg);
279 
280 	if (!any_display)
281 	{
282 		putchr('\n');
283 		return;
284 	}
285 
286 	putstr(return_to_continue);
287 	so_exit();
288 	col += sizeof(return_to_continue) + so_e_width;
289 
290 #if ONLY_RETURN
291 	while ((c = getchr()) != '\n' && c != '\r')
292 		bell();
293 #else
294 	c = getchr();
295 	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
296 		ungetcc(c);
297 #endif
298 	lower_left();
299 
300 	if (col >= sc_width)
301 		/*
302 		 * Printing the message has probably scrolled the screen.
303 		 * {{ Unless the terminal doesn't have auto margins,
304 		 *    in which case we just hammered on the right margin. }}
305 		 */
306 		screen_trashed = 1;
307 
308 	flush();
309 }
310 
311 static char intr_to_abort[] = "... (interrupt to abort)";
312 
313 /*
314  * Output a message in the lower left corner of the screen
315  * and don't wait for carriage return.
316  * Usually used to warn that we are beginning a potentially
317  * time-consuming operation.
318  */
319 	public void
ierror(fmt,parg)320 ierror(fmt, parg)
321 	char *fmt;
322 	PARG *parg;
323 {
324 	clear_bot();
325 	so_enter();
326 	(void) iprintf(fmt, parg);
327 	putstr(intr_to_abort);
328 	so_exit();
329 	flush();
330 	need_clr = 1;
331 }
332 
333 /*
334  * Output a message in the lower left corner of the screen
335  * and return a single-character response.
336  */
337 	public int
query(fmt,parg)338 query(fmt, parg)
339 	char *fmt;
340 	PARG *parg;
341 {
342 	register int c;
343 	int col = 0;
344 
345 	if (any_display)
346 		clear_bot();
347 
348 	(void) iprintf(fmt, parg);
349 	c = getchr();
350 
351 	if (!any_display)
352 	{
353 		putchr('\n');
354 		return (c);
355 	}
356 
357 	lower_left();
358 	if (col >= sc_width)
359 		screen_trashed = 1;
360 	flush();
361 
362 	return (c);
363 }
364