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