1 /* 2 * Copyright (C) 1984-2002 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 about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * High level routines dealing with the output to the screen. 14 */ 15 16 #include "less.h" 17 #if MSDOS_COMPILER==WIN32C 18 #include "windows.h" 19 #endif 20 21 public int errmsgs; /* Count of messages displayed by error() */ 22 public int need_clr; 23 public int final_attr; 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 32 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 33 extern int ctldisp; 34 extern int nm_fg_color, nm_bg_color; 35 extern int bo_fg_color, bo_bg_color; 36 extern int ul_fg_color, ul_bg_color; 37 extern int so_fg_color, so_bg_color; 38 extern int bl_fg_color, bl_bg_color; 39 #endif 40 41 /* 42 * Display the line which is in the line buffer. 43 */ 44 public void 45 put_line() 46 { 47 register int c; 48 register int i; 49 int a; 50 int curr_attr; 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 curr_attr = AT_NORMAL; 62 63 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 64 { 65 if (a != curr_attr) 66 { 67 /* 68 * Changing attributes. 69 * Display the exit sequence for the old attribute 70 * and the enter sequence for the new one. 71 */ 72 switch (curr_attr) 73 { 74 case AT_UNDERLINE: ul_exit(); break; 75 case AT_BOLD: bo_exit(); break; 76 case AT_BLINK: bl_exit(); break; 77 case AT_STANDOUT: so_exit(); break; 78 } 79 switch (a) 80 { 81 case AT_UNDERLINE: ul_enter(); break; 82 case AT_BOLD: bo_enter(); break; 83 case AT_BLINK: bl_enter(); break; 84 case AT_STANDOUT: so_enter(); break; 85 } 86 curr_attr = a; 87 } 88 if (curr_attr == AT_INVIS) 89 continue; 90 if (c == '\b') 91 putbs(); 92 else 93 putchr(c); 94 } 95 96 switch (curr_attr) 97 { 98 case AT_UNDERLINE: ul_exit(); break; 99 case AT_BOLD: bo_exit(); break; 100 case AT_BLINK: bl_exit(); break; 101 case AT_STANDOUT: so_exit(); break; 102 } 103 final_attr = curr_attr; 104 } 105 106 static char obuf[OUTBUF_SIZE]; 107 static char *ob = obuf; 108 109 /* 110 * Flush buffered output. 111 * 112 * If we haven't displayed any file data yet, 113 * output messages on error output (file descriptor 2), 114 * otherwise output on standard output (file descriptor 1). 115 * 116 * This has the desirable effect of producing all 117 * error messages on error output if standard output 118 * is directed to a file. It also does the same if 119 * we never produce any real output; for example, if 120 * the input file(s) cannot be opened. If we do 121 * eventually produce output, code in edit() makes 122 * sure these messages can be seen before they are 123 * overwritten or scrolled away. 124 */ 125 public void 126 flush() 127 { 128 register int n; 129 register int fd; 130 131 n = ob - obuf; 132 if (n == 0) 133 return; 134 #if MSDOS_COMPILER==WIN32C 135 if (is_tty && any_display) 136 { 137 char *op; 138 DWORD nwritten = 0; 139 CONSOLE_SCREEN_BUFFER_INFO scr; 140 int row; 141 int col; 142 int olen; 143 extern HANDLE con_out; 144 145 olen = ob - obuf; 146 /* 147 * There is a bug in Win32 WriteConsole() if we're 148 * writing in the last cell with a different color. 149 * To avoid color problems in the bottom line, 150 * we scroll the screen manually, before writing. 151 */ 152 GetConsoleScreenBufferInfo(con_out, &scr); 153 col = scr.dwCursorPosition.X; 154 row = scr.dwCursorPosition.Y; 155 for (op = obuf; op < obuf + olen; op++) 156 { 157 if (*op == '\n') 158 { 159 col = 0; 160 row++; 161 } else if (*op == '\r') 162 { 163 col = 0; 164 } else 165 { 166 col++; 167 if (col >= sc_width) 168 { 169 col = 0; 170 row++; 171 } 172 } 173 } 174 if (row > scr.srWindow.Bottom) 175 win32_scroll_up(row - scr.srWindow.Bottom); 176 WriteConsole(con_out, obuf, olen, &nwritten, NULL); 177 ob = obuf; 178 return; 179 } 180 #else 181 #if MSDOS_COMPILER==MSOFTC 182 if (is_tty && any_display) 183 { 184 *ob = '\0'; 185 _outtext(obuf); 186 ob = obuf; 187 return; 188 } 189 #else 190 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 191 if (is_tty && any_display) 192 { 193 *ob = '\0'; 194 if (ctldisp != OPT_ONPLUS) 195 cputs(obuf); 196 else 197 { 198 /* 199 * Look for SGR escape sequences, and convert them 200 * to color commands. Replace bold, underline, 201 * and italic escapes into colors specified via 202 * the -D command-line option. 203 */ 204 char *anchor, *p, *p_next; 205 int buflen = ob - obuf; 206 unsigned char fg, bg, norm_attr; 207 /* 208 * Only dark colors mentioned here, so that 209 * bold has visible effect. 210 */ 211 static enum COLORS screen_color[] = { 212 BLACK, RED, GREEN, BROWN, 213 BLUE, MAGENTA, CYAN, LIGHTGRAY 214 }; 215 216 /* Normal text colors are used as baseline. */ 217 bg = nm_bg_color & 0xf; 218 fg = nm_fg_color & 0xf; 219 norm_attr = (bg << 4) | fg; 220 for (anchor = p_next = obuf; 221 (p_next = memchr (p_next, ESC, 222 buflen - (p_next - obuf))) 223 != NULL; ) 224 { 225 p = p_next; 226 227 /* 228 * Handle the null escape sequence 229 * (ESC-[m), which is used to restore 230 * the original color. 231 */ 232 if (p[1] == '[' && is_ansi_end(p[2])) 233 { 234 textattr(norm_attr); 235 p += 3; 236 anchor = p_next = p; 237 continue; 238 } 239 240 if (p[1] == '[') /* "Esc-[" sequence */ 241 { 242 /* 243 * If some chars seen since 244 * the last escape sequence, 245 * write it out to the screen 246 * using current text attributes. 247 */ 248 if (p > anchor) 249 { 250 *p = '\0'; 251 cputs (anchor); 252 *p = ESC; 253 anchor = p; 254 } 255 p += 2; 256 p_next = p; 257 while (!is_ansi_end(*p)) 258 { 259 char *q; 260 long code = strtol(p, &q, 10); 261 262 if (!*q) 263 { 264 /* 265 * Incomplete sequence. 266 * Leave it unprocessed 267 * in the buffer. 268 */ 269 int slop = q - anchor; 270 strlcpy(obuf, anchor, 271 sizeof(obuf)); 272 ob = &obuf[slop]; 273 return; 274 } 275 276 if (q == p 277 || code > 49 || code < 0 278 || (!is_ansi_end(*q) 279 && *q != ';')) 280 { 281 p_next = q; 282 break; 283 } 284 if (*q == ';') 285 q++; 286 287 switch (code) 288 { 289 case 1: /* bold on */ 290 fg = bo_fg_color; 291 bg = bo_bg_color; 292 break; 293 case 3: /* italic on */ 294 fg = so_fg_color; 295 bg = so_bg_color; 296 break; 297 case 4: /* underline on */ 298 fg = ul_fg_color; 299 bg = ul_bg_color; 300 break; 301 case 8: /* concealed on */ 302 fg = (bg & 7) | 8; 303 break; 304 case 0: /* all attrs off */ 305 case 22:/* bold off */ 306 case 23:/* italic off */ 307 case 24:/* underline off */ 308 fg = nm_fg_color; 309 bg = nm_bg_color; 310 break; 311 case 30: case 31: case 32: 312 case 33: case 34: case 35: 313 case 36: case 37: 314 fg = (fg & 8) | (screen_color[code - 30]); 315 break; 316 case 39: /* default fg */ 317 fg = nm_fg_color; 318 break; 319 case 40: case 41: case 42: 320 case 43: case 44: case 45: 321 case 46: case 47: 322 bg = (bg & 8) | (screen_color[code - 40]); 323 break; 324 case 49: /* default fg */ 325 bg = nm_bg_color; 326 break; 327 } 328 p = q; 329 } 330 if (is_ansi_end(*p) && p > p_next) 331 { 332 bg &= 15; 333 fg &= 15; 334 textattr ((bg << 4)| fg); 335 p_next = anchor = p + 1; 336 } else 337 break; 338 } else 339 p_next++; 340 } 341 342 /* Output what's left in the buffer. */ 343 cputs (anchor); 344 } 345 ob = obuf; 346 return; 347 } 348 #endif 349 #endif 350 #endif 351 fd = (any_display) ? 1 : 2; 352 if (write(fd, obuf, n) != n) 353 screen_trashed = 1; 354 ob = obuf; 355 } 356 357 /* 358 * Output a character. 359 */ 360 public int 361 putchr(c) 362 int c; 363 { 364 if (need_clr) 365 { 366 need_clr = 0; 367 clear_bot(); 368 } 369 #if MSDOS_COMPILER 370 if (c == '\n' && is_tty) 371 { 372 /* remove_top(1); */ 373 putchr('\r'); 374 } 375 #else 376 #ifdef _OSK 377 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 378 putchr(0x0A); 379 #endif 380 #endif 381 /* 382 * Some versions of flush() write to *ob, so we must flush 383 * when we are still one char from the end of obuf. 384 */ 385 if (ob >= &obuf[sizeof(obuf)-1]) 386 flush(); 387 *ob++ = c; 388 return (c); 389 } 390 391 /* 392 * Output a string. 393 */ 394 public void 395 putstr(s) 396 register char *s; 397 { 398 while (*s != '\0') 399 putchr(*s++); 400 } 401 402 403 /* 404 * Convert an integral type to a string. 405 */ 406 #define TYPE_TO_A_FUNC(funcname, type) \ 407 void funcname(num, buf, len) \ 408 type num; \ 409 char *buf; \ 410 size_t len; \ 411 { \ 412 int neg = (num < 0); \ 413 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 414 register char *s = tbuf + sizeof(tbuf); \ 415 if (neg) num = -num; \ 416 *--s = '\0'; \ 417 do { \ 418 *--s = (num % 10) + '0'; \ 419 } while ((num /= 10) != 0); \ 420 if (neg) *--s = '-'; \ 421 strlcpy(buf, s, len); \ 422 } 423 424 TYPE_TO_A_FUNC(postoa, POSITION) 425 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 426 TYPE_TO_A_FUNC(inttoa, int) 427 428 /* 429 * Output an integer in a given radix. 430 */ 431 static int 432 iprint_int(num) 433 int num; 434 { 435 char buf[INT_STRLEN_BOUND(num)]; 436 437 inttoa(num, buf, sizeof(buf)); 438 putstr(buf); 439 return (strlen(buf)); 440 } 441 442 /* 443 * Output a line number in a given radix. 444 */ 445 static int 446 iprint_linenum(num) 447 LINENUM num; 448 { 449 char buf[INT_STRLEN_BOUND(num)]; 450 451 linenumtoa(num, buf, sizeof(buf)); 452 putstr(buf); 453 return (strlen(buf)); 454 } 455 456 /* 457 * This function implements printf-like functionality 458 * using a more portable argument list mechanism than printf's. 459 */ 460 static int 461 less_printf(fmt, parg) 462 register char *fmt; 463 PARG *parg; 464 { 465 register char *s; 466 register int col; 467 468 col = 0; 469 while (*fmt != '\0') 470 { 471 if (*fmt != '%') 472 { 473 putchr(*fmt++); 474 col++; 475 } else 476 { 477 ++fmt; 478 switch (*fmt++) 479 { 480 case 's': 481 s = parg->p_string; 482 parg++; 483 while (*s != '\0') 484 { 485 putchr(*s++); 486 col++; 487 } 488 break; 489 case 'd': 490 col += iprint_int(parg->p_int); 491 parg++; 492 break; 493 case 'n': 494 col += iprint_linenum(parg->p_linenum); 495 parg++; 496 break; 497 } 498 } 499 } 500 return (col); 501 } 502 503 /* 504 * Get a RETURN. 505 * If some other non-trivial char is pressed, unget it, so it will 506 * become the next command. 507 */ 508 public void 509 get_return() 510 { 511 int c; 512 513 #if ONLY_RETURN 514 while ((c = getchr()) != '\n' && c != '\r') 515 bell(); 516 #else 517 c = getchr(); 518 if (c == 'q') 519 quit(QUIT_OK); 520 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 521 ungetcc(c); 522 #endif 523 } 524 525 /* 526 * Output a message in the lower left corner of the screen 527 * and wait for carriage return. 528 */ 529 public void 530 error(fmt, parg) 531 char *fmt; 532 PARG *parg; 533 { 534 int col = 0; 535 static char return_to_continue[] = " (press RETURN)"; 536 537 errmsgs++; 538 539 if (any_display && is_tty) 540 { 541 clear_bot(); 542 so_enter(); 543 col += so_s_width; 544 } 545 546 col += less_printf(fmt, parg); 547 548 if (!(any_display && is_tty)) 549 { 550 putchr('\n'); 551 return; 552 } 553 554 putstr(return_to_continue); 555 so_exit(); 556 col += sizeof(return_to_continue) + so_e_width; 557 558 get_return(); 559 lower_left(); 560 561 if (col >= sc_width) 562 /* 563 * Printing the message has probably scrolled the screen. 564 * {{ Unless the terminal doesn't have auto margins, 565 * in which case we just hammered on the right margin. }} 566 */ 567 screen_trashed = 1; 568 569 flush(); 570 } 571 572 static char intr_to_abort[] = "... (interrupt to abort)"; 573 574 /* 575 * Output a message in the lower left corner of the screen 576 * and don't wait for carriage return. 577 * Usually used to warn that we are beginning a potentially 578 * time-consuming operation. 579 */ 580 public void 581 ierror(fmt, parg) 582 char *fmt; 583 PARG *parg; 584 { 585 clear_bot(); 586 so_enter(); 587 (void) less_printf(fmt, parg); 588 putstr(intr_to_abort); 589 so_exit(); 590 flush(); 591 need_clr = 1; 592 } 593 594 /* 595 * Output a message in the lower left corner of the screen 596 * and return a single-character response. 597 */ 598 public int 599 query(fmt, parg) 600 char *fmt; 601 PARG *parg; 602 { 603 register int c; 604 int col = 0; 605 606 if (any_display && is_tty) 607 clear_bot(); 608 609 (void) less_printf(fmt, parg); 610 c = getchr(); 611 612 if (!(any_display && is_tty)) 613 { 614 putchr('\n'); 615 return (c); 616 } 617 618 lower_left(); 619 if (col >= sc_width) 620 screen_trashed = 1; 621 flush(); 622 623 return (c); 624 } 625