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