1 /* 2 * Copyright (C) 1984-2011 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * High level routines dealing with the output to the screen. 14 */ 15 16 #include "less.h" 17 #if MSDOS_COMPILER==WIN32C 18 #include "windows.h" 19 #endif 20 21 public int errmsgs; /* Count of messages displayed by error() */ 22 public int need_clr; 23 public int final_attr; 24 public int at_prompt; 25 26 extern volatile sig_atomic_t sigs; 27 extern int sc_width; 28 extern int so_s_width, so_e_width; 29 extern int screen_trashed; 30 extern int any_display; 31 extern int is_tty; 32 extern int oldbot; 33 34 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 35 extern int ctldisp; 36 extern int nm_fg_color, nm_bg_color; 37 extern int bo_fg_color, bo_bg_color; 38 extern int ul_fg_color, ul_bg_color; 39 extern int so_fg_color, so_bg_color; 40 extern int bl_fg_color, bl_bg_color; 41 #endif 42 43 /* 44 * Display the line which is in the line buffer. 45 */ 46 public void 47 put_line() 48 { 49 register int c; 50 register int i; 51 int a; 52 53 if (ABORT_SIGS()) 54 { 55 /* 56 * Don't output if a signal is pending. 57 */ 58 screen_trashed = 1; 59 return; 60 } 61 62 final_attr = AT_NORMAL; 63 64 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 65 { 66 at_switch(a); 67 final_attr = a; 68 if (c == '\b') 69 putbs(); 70 else 71 putchr(c); 72 } 73 74 at_exit(); 75 } 76 77 static char obuf[OUTBUF_SIZE]; 78 static char *ob = obuf; 79 80 /* 81 * Flush buffered output. 82 * 83 * If we haven't displayed any file data yet, 84 * output messages on error output (file descriptor 2), 85 * otherwise output on standard output (file descriptor 1). 86 * 87 * This has the desirable effect of producing all 88 * error messages on error output if standard output 89 * is directed to a file. It also does the same if 90 * we never produce any real output; for example, if 91 * the input file(s) cannot be opened. If we do 92 * eventually produce output, code in edit() makes 93 * sure these messages can be seen before they are 94 * overwritten or scrolled away. 95 */ 96 public void 97 flush() 98 { 99 register int n; 100 register int fd; 101 ssize_t nwritten; 102 103 n = ob - obuf; 104 if (n == 0) 105 return; 106 107 #if MSDOS_COMPILER==MSOFTC 108 if (is_tty && any_display) 109 { 110 *ob = '\0'; 111 _outtext(obuf); 112 ob = obuf; 113 return; 114 } 115 #else 116 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 117 if (is_tty && any_display) 118 { 119 *ob = '\0'; 120 if (ctldisp != OPT_ONPLUS) 121 WIN32textout(obuf, ob - obuf); 122 else 123 { 124 /* 125 * Look for SGR escape sequences, and convert them 126 * to color commands. Replace bold, underline, 127 * and italic escapes into colors specified via 128 * the -D command-line option. 129 */ 130 char *anchor, *p, *p_next; 131 unsigned char fg, bg; 132 static unsigned char at; 133 #if MSDOS_COMPILER==WIN32C 134 /* Screen colors used by 3x and 4x SGR commands. */ 135 static unsigned char screen_color[] = { 136 0, /* BLACK */ 137 FOREGROUND_RED, 138 FOREGROUND_GREEN, 139 FOREGROUND_RED|FOREGROUND_GREEN, 140 FOREGROUND_BLUE, 141 FOREGROUND_BLUE|FOREGROUND_RED, 142 FOREGROUND_BLUE|FOREGROUND_GREEN, 143 FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 144 }; 145 #else 146 static enum COLORS screen_color[] = { 147 BLACK, RED, GREEN, BROWN, 148 BLUE, MAGENTA, CYAN, LIGHTGRAY 149 }; 150 #endif 151 152 for (anchor = p_next = obuf; 153 (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 154 { 155 p = p_next; 156 if (p[1] == '[') /* "ESC-[" sequence */ 157 { 158 if (p > anchor) 159 { 160 /* 161 * If some chars seen since 162 * the last escape sequence, 163 * write them out to the screen. 164 */ 165 WIN32textout(anchor, p-anchor); 166 anchor = p; 167 } 168 p += 2; /* Skip the "ESC-[" */ 169 if (is_ansi_end(*p)) 170 { 171 /* 172 * Handle null escape sequence 173 * "ESC[m", which restores 174 * the normal color. 175 */ 176 p++; 177 anchor = p_next = p; 178 WIN32setcolors(nm_fg_color, nm_bg_color); 179 continue; 180 } 181 p_next = p; 182 183 /* 184 * Select foreground/background colors 185 * based on the escape sequence. 186 */ 187 fg = nm_fg_color; 188 bg = nm_bg_color; 189 while (!is_ansi_end(*p)) 190 { 191 char *q; 192 long code = strtol(p, &q, 10); 193 194 if (*q == '\0') 195 { 196 /* 197 * Incomplete sequence. 198 * Leave it unprocessed 199 * in the buffer. 200 */ 201 int slop = q - anchor; 202 /* {{ strlcpy args overlap! }} */ 203 strlcpy(obuf, anchor, 204 sizeof(obuf)); 205 ob = &obuf[slop]; 206 return; 207 } 208 209 if (q == p || 210 code > 49 || code < 0 || 211 (!is_ansi_end(*q) && *q != ';')) 212 { 213 p_next = q; 214 break; 215 } 216 if (*q == ';') 217 q++; 218 219 switch (code) 220 { 221 default: 222 /* case 0: all attrs off */ 223 fg = nm_fg_color; 224 bg = nm_bg_color; 225 at = 0; 226 break; 227 case 1: /* bold on */ 228 at |= 1; 229 break; 230 case 3: /* italic on */ 231 case 7: /* inverse on */ 232 at |= 2; 233 break; 234 case 4: /* underline on */ 235 at |= 4; 236 break; 237 case 5: /* slow blink on */ 238 case 6: /* fast blink on */ 239 at |= 8; 240 break; 241 case 8: /* concealed on */ 242 fg = (bg & 7) | 8; 243 break; 244 case 22: /* bold off */ 245 at &= ~1; 246 break; 247 case 23: /* italic off */ 248 case 27: /* inverse off */ 249 at &= ~2; 250 break; 251 case 24: /* underline off */ 252 at &= ~4; 253 break; 254 case 30: case 31: case 32: 255 case 33: case 34: case 35: 256 case 36: case 37: 257 fg = (fg & 8) | (screen_color[code - 30]); 258 break; 259 case 39: /* default fg */ 260 fg = nm_fg_color; 261 break; 262 case 40: case 41: case 42: 263 case 43: case 44: case 45: 264 case 46: case 47: 265 bg = (bg & 8) | (screen_color[code - 40]); 266 break; 267 case 49: /* default fg */ 268 bg = nm_bg_color; 269 break; 270 } 271 p = q; 272 } 273 if (!is_ansi_end(*p) || p == p_next) 274 break; 275 if (at & 1) 276 { 277 fg = bo_fg_color; 278 bg = bo_bg_color; 279 } else if (at & 2) 280 { 281 fg = so_fg_color; 282 bg = so_bg_color; 283 } else if (at & 4) 284 { 285 fg = ul_fg_color; 286 bg = ul_bg_color; 287 } else if (at & 8) 288 { 289 fg = bl_fg_color; 290 bg = bl_bg_color; 291 } 292 fg &= 0xf; 293 bg &= 0xf; 294 WIN32setcolors(fg, bg); 295 p_next = anchor = p + 1; 296 } else 297 p_next++; 298 } 299 300 /* Output what's left in the buffer. */ 301 WIN32textout(anchor, ob - anchor); 302 } 303 ob = obuf; 304 return; 305 } 306 #endif 307 #endif 308 fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO; 309 nwritten = write(fd, obuf, n); 310 if (nwritten != n) { 311 if (nwritten == -1) 312 quit(QUIT_ERROR); 313 screen_trashed = 1; 314 } 315 ob = obuf; 316 } 317 318 /* 319 * Output a character. 320 */ 321 public int 322 putchr(c) 323 int c; 324 { 325 #if 0 /* fake UTF-8 output for testing */ 326 extern int utf_mode; 327 if (utf_mode) 328 { 329 static char ubuf[MAX_UTF_CHAR_LEN]; 330 static int ubuf_len = 0; 331 static int ubuf_index = 0; 332 if (ubuf_len == 0) 333 { 334 ubuf_len = utf_len(c); 335 ubuf_index = 0; 336 } 337 ubuf[ubuf_index++] = c; 338 if (ubuf_index < ubuf_len) 339 return c; 340 c = get_wchar(ubuf) & 0xFF; 341 ubuf_len = 0; 342 } 343 #endif 344 if (need_clr) 345 { 346 need_clr = 0; 347 clear_bot(); 348 } 349 #if MSDOS_COMPILER 350 if (c == '\n' && is_tty) 351 { 352 /* remove_top(1); */ 353 putchr('\r'); 354 } 355 #else 356 #ifdef _OSK 357 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 358 putchr(0x0A); 359 #endif 360 #endif 361 /* 362 * Some versions of flush() write to *ob, so we must flush 363 * when we are still one char from the end of obuf. 364 */ 365 if (ob >= &obuf[sizeof(obuf)-1]) 366 flush(); 367 *ob++ = c; 368 at_prompt = 0; 369 return (c); 370 } 371 372 /* 373 * Output a string. 374 */ 375 public void 376 putstr(s) 377 register char *s; 378 { 379 while (*s != '\0') 380 putchr(*s++); 381 } 382 383 384 /* 385 * Convert an integral type to a string. 386 */ 387 #define TYPE_TO_A_FUNC(funcname, type) \ 388 void funcname(num, buf, len) \ 389 type num; \ 390 char *buf; \ 391 size_t len; \ 392 { \ 393 int neg = (num < 0); \ 394 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 395 register char *s = tbuf + sizeof(tbuf); \ 396 if (neg) num = -num; \ 397 *--s = '\0'; \ 398 do { \ 399 *--s = (num % 10) + '0'; \ 400 } while ((num /= 10) != 0); \ 401 if (neg) *--s = '-'; \ 402 strlcpy(buf, s, len); \ 403 } 404 405 TYPE_TO_A_FUNC(postoa, POSITION) 406 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 407 TYPE_TO_A_FUNC(inttoa, int) 408 409 /* 410 * Output an integer in a given radix. 411 */ 412 static int 413 iprint_int(num) 414 int num; 415 { 416 char buf[INT_STRLEN_BOUND(num)]; 417 418 inttoa(num, buf, sizeof(buf)); 419 putstr(buf); 420 return (strlen(buf)); 421 } 422 423 /* 424 * Output a line number in a given radix. 425 */ 426 static int 427 iprint_linenum(num) 428 LINENUM num; 429 { 430 char buf[INT_STRLEN_BOUND(num)]; 431 432 linenumtoa(num, buf, sizeof(buf)); 433 putstr(buf); 434 return (strlen(buf)); 435 } 436 437 /* 438 * This function implements printf-like functionality 439 * using a more portable argument list mechanism than printf's. 440 */ 441 static int 442 less_printf(fmt, parg) 443 register char *fmt; 444 PARG *parg; 445 { 446 register char *s; 447 register int col; 448 449 col = 0; 450 while (*fmt != '\0') 451 { 452 if (*fmt != '%') 453 { 454 putchr(*fmt++); 455 col++; 456 } else 457 { 458 ++fmt; 459 switch (*fmt++) 460 { 461 case 's': 462 s = parg->p_string; 463 parg++; 464 while (*s != '\0') 465 { 466 putchr(*s++); 467 col++; 468 } 469 break; 470 case 'd': 471 col += iprint_int(parg->p_int); 472 parg++; 473 break; 474 case 'n': 475 col += iprint_linenum(parg->p_linenum); 476 parg++; 477 break; 478 } 479 } 480 } 481 return (col); 482 } 483 484 /* 485 * Get a RETURN. 486 * If some other non-trivial char is pressed, unget it, so it will 487 * become the next command. 488 */ 489 public void 490 get_return() 491 { 492 int c; 493 494 #if ONLY_RETURN 495 while ((c = getchr()) != '\n' && c != '\r') 496 bell(); 497 #else 498 c = getchr(); 499 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 500 ungetcc(c); 501 #endif 502 } 503 504 /* 505 * Output a message in the lower left corner of the screen 506 * and wait for carriage return. 507 */ 508 public void 509 error(fmt, parg) 510 char *fmt; 511 PARG *parg; 512 { 513 int col = 0; 514 static char return_to_continue[] = " (press RETURN)"; 515 516 errmsgs++; 517 518 if (any_display && is_tty) 519 { 520 if (!oldbot) 521 squish_check(); 522 at_exit(); 523 clear_bot(); 524 at_enter(AT_STANDOUT); 525 col += so_s_width; 526 } 527 528 col += less_printf(fmt, parg); 529 530 if (!(any_display && is_tty)) 531 { 532 putchr('\n'); 533 return; 534 } 535 536 putstr(return_to_continue); 537 at_exit(); 538 col += sizeof(return_to_continue) + so_e_width; 539 540 get_return(); 541 lower_left(); 542 clear_eol(); 543 544 if (col >= sc_width) 545 /* 546 * Printing the message has probably scrolled the screen. 547 * {{ Unless the terminal doesn't have auto margins, 548 * in which case we just hammered on the right margin. }} 549 */ 550 screen_trashed = 1; 551 552 flush(); 553 } 554 555 static char intr_to_abort[] = "... (interrupt to abort)"; 556 557 /* 558 * Output a message in the lower left corner of the screen 559 * and don't wait for carriage return. 560 * Usually used to warn that we are beginning a potentially 561 * time-consuming operation. 562 */ 563 public void 564 ierror(fmt, parg) 565 char *fmt; 566 PARG *parg; 567 { 568 at_exit(); 569 clear_bot(); 570 at_enter(AT_STANDOUT); 571 (void) less_printf(fmt, parg); 572 putstr(intr_to_abort); 573 at_exit(); 574 flush(); 575 need_clr = 1; 576 } 577 578 /* 579 * Output a message in the lower left corner of the screen 580 * and return a single-character response. 581 */ 582 public int 583 query(fmt, parg) 584 char *fmt; 585 PARG *parg; 586 { 587 register int c; 588 int col = 0; 589 590 if (any_display && is_tty) 591 clear_bot(); 592 593 (void) less_printf(fmt, parg); 594 c = getchr(); 595 596 if (!(any_display && is_tty)) 597 { 598 putchr('\n'); 599 return (c); 600 } 601 602 lower_left(); 603 if (col >= sc_width) 604 screen_trashed = 1; 605 flush(); 606 607 return (c); 608 } 609