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