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 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 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 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 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 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 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 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 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 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 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 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