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 int a; 39 40 if (ABORT_SIGS()) { 41 /* 42 * Don't output if a signal is pending. 43 */ 44 screen_trashed = 1; 45 return; 46 } 47 48 for (i = 0; (c = gline(i, &a)) != '\0'; i++) { 49 at_switch(a); 50 if (c == '\b') 51 putbs(); 52 else 53 (void) putchr(c); 54 } 55 56 at_exit(); 57 } 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 n = (intptr_t)ob - (intptr_t)obuf; 86 if (n == 0) 87 return; 88 89 fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO; 90 nwritten = write(fd, obuf, n); 91 if (nwritten != n) { 92 if (nwritten == -1 && !ignore_errors) 93 quit(QUIT_ERROR); 94 screen_trashed = 1; 95 } 96 ob = obuf; 97 } 98 99 /* 100 * Output a character. 101 */ 102 int 103 putchr(int c) 104 { 105 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 if (ob >= &obuf[sizeof (obuf)-1]) 114 flush(0); 115 *ob++ = (char)c; 116 return (c); 117 } 118 119 /* 120 * Output a string. 121 */ 122 void 123 putstr(const char *s) 124 { 125 while (*s != '\0') 126 (void) putchr(*s++); 127 } 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 while (*fmt != '\0') { 192 if (*fmt != '%') { 193 (void) putchr(*fmt++); 194 col++; 195 } 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 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 c = getchr(); 231 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 232 ungetcc(c); 233 } 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 errmsgs++; 246 247 if (any_display && is_tty) { 248 if (!oldbot) 249 squish_check(); 250 at_exit(); 251 clear_bot(); 252 at_enter(AT_STANDOUT); 253 col += so_s_width; 254 } 255 256 col += less_printf(fmt, parg); 257 258 if (!(any_display && is_tty)) { 259 (void) putchr('\n'); 260 return; 261 } 262 263 putstr(return_to_continue); 264 at_exit(); 265 col += sizeof (return_to_continue) + so_e_width; 266 267 get_return(); 268 lower_left(); 269 clear_eol(); 270 271 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 flush(0); 280 } 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 } 331