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