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