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 #if MSDOS_COMPILER==WIN32C 276 fg |= FOREGROUND_INTENSITY; 277 bg |= BACKGROUND_INTENSITY; 278 #else 279 fg = bo_fg_color; 280 bg = bo_bg_color; 281 #endif 282 } else if (at & 2) 283 { 284 fg = so_fg_color; 285 bg = so_bg_color; 286 } else if (at & 4) 287 { 288 fg = ul_fg_color; 289 bg = ul_bg_color; 290 } else if (at & 8) 291 { 292 fg = bl_fg_color; 293 bg = bl_bg_color; 294 } 295 fg &= 0xf; 296 bg &= 0xf; 297 WIN32setcolors(fg, bg); 298 p_next = anchor = p + 1; 299 } else 300 p_next++; 301 } 302 303 /* Output what's left in the buffer. */ 304 WIN32textout(anchor, ob - anchor); 305 } 306 ob = obuf; 307 return; 308 } 309 #endif 310 #endif 311 fd = (any_display) ? 1 : 2; 312 if (write(fd, obuf, n) != n) 313 screen_trashed = 1; 314 ob = obuf; 315 } 316 317 /* 318 * Output a character. 319 */ 320 public int 321 putchr(c) 322 int c; 323 { 324 #if 0 /* fake UTF-8 output for testing */ 325 extern int utf_mode; 326 if (utf_mode) 327 { 328 static char ubuf[MAX_UTF_CHAR_LEN]; 329 static int ubuf_len = 0; 330 static int ubuf_index = 0; 331 if (ubuf_len == 0) 332 { 333 ubuf_len = utf_len(c); 334 ubuf_index = 0; 335 } 336 ubuf[ubuf_index++] = c; 337 if (ubuf_index < ubuf_len) 338 return c; 339 c = get_wchar(ubuf) & 0xFF; 340 ubuf_len = 0; 341 } 342 #endif 343 if (need_clr) 344 { 345 need_clr = 0; 346 clear_bot(); 347 } 348 #if MSDOS_COMPILER 349 if (c == '\n' && is_tty) 350 { 351 /* remove_top(1); */ 352 putchr('\r'); 353 } 354 #else 355 #ifdef _OSK 356 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 357 putchr(0x0A); 358 #endif 359 #endif 360 /* 361 * Some versions of flush() write to *ob, so we must flush 362 * when we are still one char from the end of obuf. 363 */ 364 if (ob >= &obuf[sizeof(obuf)-1]) 365 flush(); 366 *ob++ = c; 367 at_prompt = 0; 368 return (c); 369 } 370 371 /* 372 * Output a string. 373 */ 374 public void 375 putstr(s) 376 register char *s; 377 { 378 while (*s != '\0') 379 putchr(*s++); 380 } 381 382 383 /* 384 * Convert an integral type to a string. 385 */ 386 #define TYPE_TO_A_FUNC(funcname, type) \ 387 void funcname(num, buf) \ 388 type num; \ 389 char *buf; \ 390 { \ 391 int neg = (num < 0); \ 392 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 393 register char *s = tbuf + sizeof(tbuf); \ 394 if (neg) num = -num; \ 395 *--s = '\0'; \ 396 do { \ 397 *--s = (num % 10) + '0'; \ 398 } while ((num /= 10) != 0); \ 399 if (neg) *--s = '-'; \ 400 strcpy(buf, s); \ 401 } 402 403 TYPE_TO_A_FUNC(postoa, POSITION) 404 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 405 TYPE_TO_A_FUNC(inttoa, int) 406 407 /* 408 * Output an integer in a given radix. 409 */ 410 static int 411 iprint_int(num) 412 int num; 413 { 414 char buf[INT_STRLEN_BOUND(num)]; 415 416 inttoa(num, buf); 417 putstr(buf); 418 return (strlen(buf)); 419 } 420 421 /* 422 * Output a line number in a given radix. 423 */ 424 static int 425 iprint_linenum(num) 426 LINENUM num; 427 { 428 char buf[INT_STRLEN_BOUND(num)]; 429 430 linenumtoa(num, buf); 431 putstr(buf); 432 return (strlen(buf)); 433 } 434 435 /* 436 * This function implements printf-like functionality 437 * using a more portable argument list mechanism than printf's. 438 */ 439 static int 440 less_printf(fmt, parg) 441 register char *fmt; 442 PARG *parg; 443 { 444 register char *s; 445 register int col; 446 447 col = 0; 448 while (*fmt != '\0') 449 { 450 if (*fmt != '%') 451 { 452 putchr(*fmt++); 453 col++; 454 } else 455 { 456 ++fmt; 457 switch (*fmt++) 458 { 459 case 's': 460 s = parg->p_string; 461 parg++; 462 while (*s != '\0') 463 { 464 putchr(*s++); 465 col++; 466 } 467 break; 468 case 'd': 469 col += iprint_int(parg->p_int); 470 parg++; 471 break; 472 case 'n': 473 col += iprint_linenum(parg->p_linenum); 474 parg++; 475 break; 476 } 477 } 478 } 479 return (col); 480 } 481 482 /* 483 * Get a RETURN. 484 * If some other non-trivial char is pressed, unget it, so it will 485 * become the next command. 486 */ 487 public void 488 get_return() 489 { 490 int c; 491 492 #if ONLY_RETURN 493 while ((c = getchr()) != '\n' && c != '\r') 494 bell(); 495 #else 496 c = getchr(); 497 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 498 ungetcc(c); 499 #endif 500 } 501 502 /* 503 * Output a message in the lower left corner of the screen 504 * and wait for carriage return. 505 */ 506 public void 507 error(fmt, parg) 508 char *fmt; 509 PARG *parg; 510 { 511 int col = 0; 512 static char return_to_continue[] = " (press RETURN)"; 513 514 errmsgs++; 515 516 if (any_display && is_tty) 517 { 518 if (!oldbot) 519 squish_check(); 520 at_exit(); 521 clear_bot(); 522 at_enter(AT_STANDOUT); 523 col += so_s_width; 524 } 525 526 col += less_printf(fmt, parg); 527 528 if (!(any_display && is_tty)) 529 { 530 putchr('\n'); 531 return; 532 } 533 534 putstr(return_to_continue); 535 at_exit(); 536 col += sizeof(return_to_continue) + so_e_width; 537 538 get_return(); 539 lower_left(); 540 clear_eol(); 541 542 if (col >= sc_width) 543 /* 544 * Printing the message has probably scrolled the screen. 545 * {{ Unless the terminal doesn't have auto margins, 546 * in which case we just hammered on the right margin. }} 547 */ 548 screen_trashed = 1; 549 550 flush(); 551 } 552 553 static char intr_to_abort[] = "... (interrupt to abort)"; 554 555 /* 556 * Output a message in the lower left corner of the screen 557 * and don't wait for carriage return. 558 * Usually used to warn that we are beginning a potentially 559 * time-consuming operation. 560 */ 561 public void 562 ierror(fmt, parg) 563 char *fmt; 564 PARG *parg; 565 { 566 at_exit(); 567 clear_bot(); 568 at_enter(AT_STANDOUT); 569 (void) less_printf(fmt, parg); 570 putstr(intr_to_abort); 571 at_exit(); 572 flush(); 573 need_clr = 1; 574 } 575 576 /* 577 * Output a message in the lower left corner of the screen 578 * and return a single-character response. 579 */ 580 public int 581 query(fmt, parg) 582 char *fmt; 583 PARG *parg; 584 { 585 register int c; 586 int col = 0; 587 588 if (any_display && is_tty) 589 clear_bot(); 590 591 (void) less_printf(fmt, parg); 592 c = getchr(); 593 594 if (!(any_display && is_tty)) 595 { 596 putchr('\n'); 597 return (c); 598 } 599 600 lower_left(); 601 if (col >= sc_width) 602 screen_trashed = 1; 603 flush(); 604 605 return (c); 606 } 607