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