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