1 /* 2 * Copyright (C) 1984-2012 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 volatile sig_atomic_t 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 #endif 41 42 /* 43 * Display the line which is in the line buffer. 44 */ 45 public void 46 put_line() 47 { 48 register int c; 49 register int i; 50 int a; 51 52 if (ABORT_SIGS()) 53 { 54 /* 55 * Don't output if a signal is pending. 56 */ 57 screen_trashed = 1; 58 return; 59 } 60 61 final_attr = AT_NORMAL; 62 63 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 64 { 65 at_switch(a); 66 final_attr = a; 67 if (c == '\b') 68 putbs(); 69 else 70 putchr(c); 71 } 72 73 at_exit(); 74 } 75 76 static char obuf[OUTBUF_SIZE]; 77 static char *ob = obuf; 78 79 /* 80 * Flush buffered output. 81 * 82 * If we haven't displayed any file data yet, 83 * output messages on error output (file descriptor 2), 84 * otherwise output on standard output (file descriptor 1). 85 * 86 * This has the desirable effect of producing all 87 * error messages on error output if standard output 88 * is directed to a file. It also does the same if 89 * we never produce any real output; for example, if 90 * the input file(s) cannot be opened. If we do 91 * eventually produce output, code in edit() makes 92 * sure these messages can be seen before they are 93 * overwritten or scrolled away. 94 */ 95 public void 96 flush() 97 { 98 register int n; 99 register int fd; 100 ssize_t nwritten; 101 102 n = 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 unsigned char fg, bg; 131 static unsigned char at; 132 #if MSDOS_COMPILER==WIN32C 133 /* Screen colors used by 3x and 4x SGR commands. */ 134 static unsigned char screen_color[] = { 135 0, /* BLACK */ 136 FOREGROUND_RED, 137 FOREGROUND_GREEN, 138 FOREGROUND_RED|FOREGROUND_GREEN, 139 FOREGROUND_BLUE, 140 FOREGROUND_BLUE|FOREGROUND_RED, 141 FOREGROUND_BLUE|FOREGROUND_GREEN, 142 FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 143 }; 144 #else 145 static enum COLORS screen_color[] = { 146 BLACK, RED, GREEN, BROWN, 147 BLUE, MAGENTA, CYAN, LIGHTGRAY 148 }; 149 #endif 150 151 for (anchor = p_next = obuf; 152 (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 153 { 154 p = p_next; 155 if (p[1] == '[') /* "ESC-[" sequence */ 156 { 157 if (p > anchor) 158 { 159 /* 160 * If some chars seen since 161 * the last escape sequence, 162 * write them out to the screen. 163 */ 164 WIN32textout(anchor, p-anchor); 165 anchor = p; 166 } 167 p += 2; /* Skip the "ESC-[" */ 168 if (is_ansi_end(*p)) 169 { 170 /* 171 * Handle null escape sequence 172 * "ESC[m", which restores 173 * the normal color. 174 */ 175 p++; 176 anchor = p_next = p; 177 at = 0; 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 /* 278 * If \e[1m use defined bold 279 * color, else set intensity. 280 */ 281 if (p[-2] == '[') 282 { 283 #if MSDOS_COMPILER==WIN32C 284 fg |= FOREGROUND_INTENSITY; 285 bg |= BACKGROUND_INTENSITY; 286 #else 287 fg = bo_fg_color; 288 bg = bo_bg_color; 289 #endif 290 } else 291 fg |= 8; 292 } else if (at & 2) 293 { 294 fg = so_fg_color; 295 bg = so_bg_color; 296 } else if (at & 4) 297 { 298 fg = ul_fg_color; 299 bg = ul_bg_color; 300 } else if (at & 8) 301 { 302 fg = bl_fg_color; 303 bg = bl_bg_color; 304 } 305 fg &= 0xf; 306 bg &= 0xf; 307 WIN32setcolors(fg, bg); 308 p_next = anchor = p + 1; 309 } else 310 p_next++; 311 } 312 313 /* Output what's left in the buffer. */ 314 WIN32textout(anchor, ob - anchor); 315 } 316 ob = obuf; 317 return; 318 } 319 #endif 320 #endif 321 fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO; 322 nwritten = write(fd, obuf, n); 323 if (nwritten != n) { 324 if (nwritten == -1) 325 quit(QUIT_ERROR); 326 screen_trashed = 1; 327 } 328 ob = obuf; 329 } 330 331 /* 332 * Output a character. 333 */ 334 public int 335 putchr(c) 336 int c; 337 { 338 #if 0 /* fake UTF-8 output for testing */ 339 extern int utf_mode; 340 if (utf_mode) 341 { 342 static char ubuf[MAX_UTF_CHAR_LEN]; 343 static int ubuf_len = 0; 344 static int ubuf_index = 0; 345 if (ubuf_len == 0) 346 { 347 ubuf_len = utf_len(c); 348 ubuf_index = 0; 349 } 350 ubuf[ubuf_index++] = c; 351 if (ubuf_index < ubuf_len) 352 return c; 353 c = get_wchar(ubuf) & 0xFF; 354 ubuf_len = 0; 355 } 356 #endif 357 if (need_clr) 358 { 359 need_clr = 0; 360 clear_bot(); 361 } 362 #if MSDOS_COMPILER 363 if (c == '\n' && is_tty) 364 { 365 /* remove_top(1); */ 366 putchr('\r'); 367 } 368 #else 369 #ifdef _OSK 370 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 371 putchr(0x0A); 372 #endif 373 #endif 374 /* 375 * Some versions of flush() write to *ob, so we must flush 376 * when we are still one char from the end of obuf. 377 */ 378 if (ob >= &obuf[sizeof(obuf)-1]) 379 flush(); 380 *ob++ = c; 381 at_prompt = 0; 382 return (c); 383 } 384 385 /* 386 * Output a string. 387 */ 388 public void 389 putstr(s) 390 register char *s; 391 { 392 while (*s != '\0') 393 putchr(*s++); 394 } 395 396 397 /* 398 * Convert an integral type to a string. 399 */ 400 #define TYPE_TO_A_FUNC(funcname, type) \ 401 void funcname(num, buf, len) \ 402 type num; \ 403 char *buf; \ 404 size_t len; \ 405 { \ 406 int neg = (num < 0); \ 407 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 408 register char *s = tbuf + sizeof(tbuf); \ 409 if (neg) num = -num; \ 410 *--s = '\0'; \ 411 do { \ 412 *--s = (num % 10) + '0'; \ 413 } while ((num /= 10) != 0); \ 414 if (neg) *--s = '-'; \ 415 strlcpy(buf, s, len); \ 416 } 417 418 TYPE_TO_A_FUNC(postoa, POSITION) 419 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 420 TYPE_TO_A_FUNC(inttoa, int) 421 422 /* 423 * Output an integer in a given radix. 424 */ 425 static int 426 iprint_int(num) 427 int num; 428 { 429 char buf[INT_STRLEN_BOUND(num)]; 430 431 inttoa(num, buf, sizeof(buf)); 432 putstr(buf); 433 return (strlen(buf)); 434 } 435 436 /* 437 * Output a line number in a given radix. 438 */ 439 static int 440 iprint_linenum(num) 441 LINENUM num; 442 { 443 char buf[INT_STRLEN_BOUND(num)]; 444 445 linenumtoa(num, buf, sizeof(buf)); 446 putstr(buf); 447 return (strlen(buf)); 448 } 449 450 /* 451 * This function implements printf-like functionality 452 * using a more portable argument list mechanism than printf's. 453 */ 454 static int 455 less_printf(fmt, parg) 456 register char *fmt; 457 PARG *parg; 458 { 459 register char *s; 460 register int col; 461 462 col = 0; 463 while (*fmt != '\0') 464 { 465 if (*fmt != '%') 466 { 467 putchr(*fmt++); 468 col++; 469 } else 470 { 471 ++fmt; 472 switch (*fmt++) 473 { 474 case 's': 475 s = parg->p_string; 476 parg++; 477 while (*s != '\0') 478 { 479 putchr(*s++); 480 col++; 481 } 482 break; 483 case 'd': 484 col += iprint_int(parg->p_int); 485 parg++; 486 break; 487 case 'n': 488 col += iprint_linenum(parg->p_linenum); 489 parg++; 490 break; 491 } 492 } 493 } 494 return (col); 495 } 496 497 /* 498 * Get a RETURN. 499 * If some other non-trivial char is pressed, unget it, so it will 500 * become the next command. 501 */ 502 public void 503 get_return() 504 { 505 int c; 506 507 #if ONLY_RETURN 508 while ((c = getchr()) != '\n' && c != '\r') 509 bell(); 510 #else 511 c = getchr(); 512 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 513 ungetcc(c); 514 #endif 515 } 516 517 /* 518 * Output a message in the lower left corner of the screen 519 * and wait for carriage return. 520 */ 521 public void 522 error(fmt, parg) 523 char *fmt; 524 PARG *parg; 525 { 526 int col = 0; 527 static char return_to_continue[] = " (press RETURN)"; 528 529 errmsgs++; 530 531 if (any_display && is_tty) 532 { 533 if (!oldbot) 534 squish_check(); 535 at_exit(); 536 clear_bot(); 537 at_enter(AT_STANDOUT); 538 col += so_s_width; 539 } 540 541 col += less_printf(fmt, parg); 542 543 if (!(any_display && is_tty)) 544 { 545 putchr('\n'); 546 return; 547 } 548 549 putstr(return_to_continue); 550 at_exit(); 551 col += sizeof(return_to_continue) + so_e_width; 552 553 get_return(); 554 lower_left(); 555 clear_eol(); 556 557 if (col >= sc_width) 558 /* 559 * Printing the message has probably scrolled the screen. 560 * {{ Unless the terminal doesn't have auto margins, 561 * in which case we just hammered on the right margin. }} 562 */ 563 screen_trashed = 1; 564 565 flush(); 566 } 567 568 static char intr_to_abort[] = "... (interrupt to abort)"; 569 570 /* 571 * Output a message in the lower left corner of the screen 572 * and don't wait for carriage return. 573 * Usually used to warn that we are beginning a potentially 574 * time-consuming operation. 575 */ 576 public void 577 ierror(fmt, parg) 578 char *fmt; 579 PARG *parg; 580 { 581 at_exit(); 582 clear_bot(); 583 at_enter(AT_STANDOUT); 584 (void) less_printf(fmt, parg); 585 putstr(intr_to_abort); 586 at_exit(); 587 flush(); 588 need_clr = 1; 589 } 590 591 /* 592 * Output a message in the lower left corner of the screen 593 * and return a single-character response. 594 */ 595 public int 596 query(fmt, parg) 597 char *fmt; 598 PARG *parg; 599 { 600 register int c; 601 int col = 0; 602 603 if (any_display && is_tty) 604 clear_bot(); 605 606 (void) less_printf(fmt, parg); 607 c = getchr(); 608 609 if (!(any_display && is_tty)) 610 { 611 putchr('\n'); 612 return (c); 613 } 614 615 lower_left(); 616 if (col >= sc_width) 617 screen_trashed = 1; 618 flush(); 619 620 return (c); 621 } 622