xref: /openbsd/usr.bin/less/output.c (revision d65139b4)
1 /*
2  * Copyright (C) 1984-2012  Mark Nudelman
3  * Modified for use with illumos by Garrett D'Amore.
4  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information, see the README file.
10  */
11 
12 /*
13  * High level routines dealing with the output to the screen.
14  */
15 
16 #include "less.h"
17 
18 int errmsgs;	/* Count of messages displayed by error() */
19 
20 extern int sc_width;
21 extern int so_s_width, so_e_width;
22 extern int screen_trashed;
23 extern int any_display;
24 extern int is_tty;
25 extern int oldbot;
26 
27 static int need_clr;
28 
29 /*
30  * Display the line which is in the line buffer.
31  */
32 void
put_line(void)33 put_line(void)
34 {
35 	int c;
36 	int i;
37 	int a;
38 
39 	if (abort_sigs()) {
40 		/*
41 		 * Don't output if a signal is pending.
42 		 */
43 		screen_trashed = 1;
44 		return;
45 	}
46 
47 	for (i = 0; (c = gline(i, &a)) != '\0'; i++) {
48 		at_switch(a);
49 		if (c == '\b')
50 			putbs();
51 		else
52 			(void) putchr(c);
53 	}
54 
55 	at_exit();
56 }
57 
58 static char obuf[OUTBUF_SIZE];
59 static char *ob = obuf;
60 
61 /*
62  * Flush buffered output.
63  *
64  * If we haven't displayed any file data yet,
65  * output messages on error output (file descriptor 2),
66  * otherwise output on standard output (file descriptor 1).
67  *
68  * This has the desirable effect of producing all
69  * error messages on error output if standard output
70  * is directed to a file.  It also does the same if
71  * we never produce any real output; for example, if
72  * the input file(s) cannot be opened.  If we do
73  * eventually produce output, code in edit() makes
74  * sure these messages can be seen before they are
75  * overwritten or scrolled away.
76  */
77 void
flush(int ignore_errors)78 flush(int ignore_errors)
79 {
80 	int n;
81 	int fd;
82 	ssize_t nwritten;
83 
84 	n = (intptr_t)ob - (intptr_t)obuf;
85 	if (n == 0)
86 		return;
87 
88 	fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO;
89 	nwritten = write(fd, obuf, n);
90 	if (nwritten != n) {
91 		if (nwritten == -1 && !ignore_errors)
92 			quit(QUIT_ERROR);
93 		screen_trashed = 1;
94 	}
95 	ob = obuf;
96 }
97 
98 /*
99  * Output a character.
100  */
101 int
putchr(int c)102 putchr(int c)
103 {
104 	if (need_clr) {
105 		need_clr = 0;
106 		clear_bot();
107 	}
108 	/*
109 	 * Some versions of flush() write to *ob, so we must flush
110 	 * when we are still one char from the end of obuf.
111 	 */
112 	if (ob >= &obuf[sizeof (obuf)-1])
113 		flush(0);
114 	*ob++ = (char)c;
115 	return (c);
116 }
117 
118 /*
119  * Output a string.
120  */
121 void
putstr(const char * s)122 putstr(const char *s)
123 {
124 	while (*s != '\0')
125 		(void) putchr(*s++);
126 }
127 
128 
129 /*
130  * Convert an integral type to a string.
131  */
132 #define	TYPE_TO_A_FUNC(funcname, type)		\
133 void						\
134 funcname(type num, char *buf, size_t len)	\
135 {						\
136 	int neg = (num < 0);			\
137 	char tbuf[23];	\
138 	char *s = tbuf + sizeof (tbuf);		\
139 	if (neg)				\
140 		num = -num;			\
141 	*--s = '\0';				\
142 	do {					\
143 		*--s = (num % 10) + '0';	\
144 	} while ((num /= 10) != 0);		\
145 	if (neg)				\
146 		 *--s = '-';			\
147 	(void) strlcpy(buf, s, len);		\
148 }
149 
TYPE_TO_A_FUNC(postoa,off_t)150 TYPE_TO_A_FUNC(postoa, off_t)
151 TYPE_TO_A_FUNC(inttoa, int)
152 
153 /*
154  * Output an integer in a given radix.
155  */
156 static int
157 iprint_int(int num)
158 {
159 	char buf[11];
160 
161 	inttoa(num, buf, sizeof (buf));
162 	putstr(buf);
163 	return (strlen(buf));
164 }
165 
166 /*
167  * Output a line number in a given radix.
168  */
169 static int
iprint_linenum(off_t num)170 iprint_linenum(off_t num)
171 {
172 	char buf[21];
173 
174 	postoa(num, buf, sizeof(buf));
175 	putstr(buf);
176 	return (strlen(buf));
177 }
178 
179 /*
180  * This function implements printf-like functionality
181  * using a more portable argument list mechanism than printf's.
182  */
183 static int
less_printf(const char * fmt,PARG * parg)184 less_printf(const char *fmt, PARG *parg)
185 {
186 	char *s;
187 	int col;
188 
189 	col = 0;
190 	while (*fmt != '\0') {
191 		if (*fmt != '%') {
192 			(void) putchr(*fmt++);
193 			col++;
194 		} else {
195 			++fmt;
196 			switch (*fmt++) {
197 			case 's':
198 				s = parg->p_string;
199 				parg++;
200 				while (*s != '\0') {
201 					(void) putchr(*s++);
202 					col++;
203 				}
204 				break;
205 			case 'd':
206 				col += iprint_int(parg->p_int);
207 				parg++;
208 				break;
209 			case 'n':
210 				col += iprint_linenum(parg->p_linenum);
211 				parg++;
212 				break;
213 			}
214 		}
215 	}
216 	return (col);
217 }
218 
219 /*
220  * Get a RETURN.
221  * If some other non-trivial char is pressed, unget it, so it will
222  * become the next command.
223  */
224 void
get_return(void)225 get_return(void)
226 {
227 	int c;
228 
229 	c = getchr();
230 	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
231 		ungetcc(c);
232 }
233 
234 /*
235  * Output a message in the lower left corner of the screen
236  * and wait for carriage return.
237  */
238 void
error(const char * fmt,PARG * parg)239 error(const char *fmt, PARG *parg)
240 {
241 	int col = 0;
242 	static char return_to_continue[] = "  (press RETURN)";
243 
244 	errmsgs++;
245 
246 	if (any_display && is_tty) {
247 		if (!oldbot)
248 			squish_check();
249 		at_exit();
250 		clear_bot();
251 		at_enter(AT_STANDOUT);
252 		col += so_s_width;
253 	}
254 
255 	col += less_printf(fmt, parg);
256 
257 	if (!(any_display && is_tty)) {
258 		(void) putchr('\n');
259 		return;
260 	}
261 
262 	putstr(return_to_continue);
263 	at_exit();
264 	col += sizeof (return_to_continue) + so_e_width;
265 
266 	get_return();
267 	lower_left();
268 	clear_eol();
269 
270 	if (col >= sc_width)
271 		/*
272 		 * Printing the message has probably scrolled the screen.
273 		 * {{ Unless the terminal doesn't have auto margins,
274 		 *    in which case we just hammered on the right margin. }}
275 		 */
276 		screen_trashed = 1;
277 
278 	flush(0);
279 }
280 
281 static char intr_to_abort[] = "... (interrupt to abort)";
282 
283 /*
284  * Output a message in the lower left corner of the screen
285  * and don't wait for carriage return.
286  * Usually used to warn that we are beginning a potentially
287  * time-consuming operation.
288  */
289 void
ierror(const char * fmt,PARG * parg)290 ierror(const char *fmt, PARG *parg)
291 {
292 	at_exit();
293 	clear_bot();
294 	at_enter(AT_STANDOUT);
295 	(void) less_printf(fmt, parg);
296 	putstr(intr_to_abort);
297 	at_exit();
298 	flush(0);
299 	need_clr = 1;
300 }
301 
302 /*
303  * Output a message in the lower left corner of the screen
304  * and return a single-character response.
305  */
306 int
query(const char * fmt,PARG * parg)307 query(const char *fmt, PARG *parg)
308 {
309 	int c;
310 	int col = 0;
311 
312 	if (any_display && is_tty)
313 		clear_bot();
314 
315 	(void) less_printf(fmt, parg);
316 	c = getchr();
317 
318 	if (!(any_display && is_tty)) {
319 		(void) putchr('\n');
320 		return (c);
321 	}
322 
323 	lower_left();
324 	if (col >= sc_width)
325 		screen_trashed = 1;
326 	flush(0);
327 
328 	return (c);
329 }
330