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 strcpy(obuf, anchor); 271 ob = &obuf[slop]; 272 return; 273 } 274 275 if (q == p 276 || code > 49 || code < 0 277 || (!is_ansi_end(*q) 278 && *q != ';')) 279 { 280 p_next = q; 281 break; 282 } 283 if (*q == ';') 284 q++; 285 286 switch (code) 287 { 288 case 1: /* bold on */ 289 fg = bo_fg_color; 290 bg = bo_bg_color; 291 break; 292 case 3: /* italic on */ 293 fg = so_fg_color; 294 bg = so_bg_color; 295 break; 296 case 4: /* underline on */ 297 fg = ul_fg_color; 298 bg = ul_bg_color; 299 break; 300 case 8: /* concealed on */ 301 fg = (bg & 7) | 8; 302 break; 303 case 0: /* all attrs off */ 304 case 22:/* bold off */ 305 case 23:/* italic off */ 306 case 24:/* underline off */ 307 fg = nm_fg_color; 308 bg = nm_bg_color; 309 break; 310 case 30: case 31: case 32: 311 case 33: case 34: case 35: 312 case 36: case 37: 313 fg = (fg & 8) | (screen_color[code - 30]); 314 break; 315 case 39: /* default fg */ 316 fg = nm_fg_color; 317 break; 318 case 40: case 41: case 42: 319 case 43: case 44: case 45: 320 case 46: case 47: 321 bg = (bg & 8) | (screen_color[code - 40]); 322 break; 323 case 49: /* default fg */ 324 bg = nm_bg_color; 325 break; 326 } 327 p = q; 328 } 329 if (is_ansi_end(*p) && p > p_next) 330 { 331 bg &= 15; 332 fg &= 15; 333 textattr ((bg << 4)| fg); 334 p_next = anchor = p + 1; 335 } else 336 break; 337 } else 338 p_next++; 339 } 340 341 /* Output what's left in the buffer. */ 342 cputs (anchor); 343 } 344 ob = obuf; 345 return; 346 } 347 #endif 348 #endif 349 #endif 350 fd = (any_display) ? 1 : 2; 351 if (write(fd, obuf, n) != n) 352 screen_trashed = 1; 353 ob = obuf; 354 } 355 356 /* 357 * Output a character. 358 */ 359 public int 360 putchr(c) 361 int c; 362 { 363 if (need_clr) 364 { 365 need_clr = 0; 366 clear_bot(); 367 } 368 #if MSDOS_COMPILER 369 if (c == '\n' && is_tty) 370 { 371 /* remove_top(1); */ 372 putchr('\r'); 373 } 374 #else 375 #ifdef _OSK 376 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 377 putchr(0x0A); 378 #endif 379 #endif 380 /* 381 * Some versions of flush() write to *ob, so we must flush 382 * when we are still one char from the end of obuf. 383 */ 384 if (ob >= &obuf[sizeof(obuf)-1]) 385 flush(); 386 *ob++ = c; 387 return (c); 388 } 389 390 /* 391 * Output a string. 392 */ 393 public void 394 putstr(s) 395 register char *s; 396 { 397 while (*s != '\0') 398 putchr(*s++); 399 } 400 401 402 /* 403 * Convert an integral type to a string. 404 */ 405 #define TYPE_TO_A_FUNC(funcname, type) \ 406 void funcname(num, buf) \ 407 type num; \ 408 char *buf; \ 409 { \ 410 int neg = (num < 0); \ 411 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 412 register char *s = tbuf + sizeof(tbuf); \ 413 if (neg) num = -num; \ 414 *--s = '\0'; \ 415 do { \ 416 *--s = (num % 10) + '0'; \ 417 } while ((num /= 10) != 0); \ 418 if (neg) *--s = '-'; \ 419 strcpy(buf, s); \ 420 } 421 422 TYPE_TO_A_FUNC(postoa, POSITION) 423 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 424 TYPE_TO_A_FUNC(inttoa, int) 425 426 /* 427 * Output an integer in a given radix. 428 */ 429 static int 430 iprint_int(num) 431 int num; 432 { 433 char buf[INT_STRLEN_BOUND(num)]; 434 435 inttoa(num, buf); 436 putstr(buf); 437 return (strlen(buf)); 438 } 439 440 /* 441 * Output a line number in a given radix. 442 */ 443 static int 444 iprint_linenum(num) 445 LINENUM num; 446 { 447 char buf[INT_STRLEN_BOUND(num)]; 448 449 linenumtoa(num, buf); 450 putstr(buf); 451 return (strlen(buf)); 452 } 453 454 /* 455 * This function implements printf-like functionality 456 * using a more portable argument list mechanism than printf's. 457 */ 458 static int 459 less_printf(fmt, parg) 460 register char *fmt; 461 PARG *parg; 462 { 463 register char *s; 464 register int col; 465 466 col = 0; 467 while (*fmt != '\0') 468 { 469 if (*fmt != '%') 470 { 471 putchr(*fmt++); 472 col++; 473 } else 474 { 475 ++fmt; 476 switch (*fmt++) 477 { 478 case 's': 479 s = parg->p_string; 480 parg++; 481 while (*s != '\0') 482 { 483 putchr(*s++); 484 col++; 485 } 486 break; 487 case 'd': 488 col += iprint_int(parg->p_int); 489 parg++; 490 break; 491 case 'n': 492 col += iprint_linenum(parg->p_linenum); 493 parg++; 494 break; 495 } 496 } 497 } 498 return (col); 499 } 500 501 /* 502 * Get a RETURN. 503 * If some other non-trivial char is pressed, unget it, so it will 504 * become the next command. 505 */ 506 public void 507 get_return() 508 { 509 int c; 510 511 #if ONLY_RETURN 512 while ((c = getchr()) != '\n' && c != '\r') 513 bell(); 514 #else 515 c = getchr(); 516 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 517 ungetcc(c); 518 #endif 519 } 520 521 /* 522 * Output a message in the lower left corner of the screen 523 * and wait for carriage return. 524 */ 525 public void 526 error(fmt, parg) 527 char *fmt; 528 PARG *parg; 529 { 530 int col = 0; 531 static char return_to_continue[] = " (press RETURN)"; 532 533 errmsgs++; 534 535 if (any_display && is_tty) 536 { 537 clear_bot(); 538 so_enter(); 539 col += so_s_width; 540 } 541 542 col += less_printf(fmt, parg); 543 544 if (!(any_display && is_tty)) 545 { 546 putchr('\n'); 547 return; 548 } 549 550 putstr(return_to_continue); 551 so_exit(); 552 col += sizeof(return_to_continue) + so_e_width; 553 554 get_return(); 555 lower_left(); 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 clear_bot(); 582 so_enter(); 583 (void) less_printf(fmt, parg); 584 putstr(intr_to_abort); 585 so_exit(); 586 flush(); 587 need_clr = 1; 588 } 589 590 /* 591 * Output a message in the lower left corner of the screen 592 * and return a single-character response. 593 */ 594 public int 595 query(fmt, parg) 596 char *fmt; 597 PARG *parg; 598 { 599 register int c; 600 int col = 0; 601 602 if (any_display && is_tty) 603 clear_bot(); 604 605 (void) less_printf(fmt, parg); 606 c = getchr(); 607 608 if (!(any_display && is_tty)) 609 { 610 putchr('\n'); 611 return (c); 612 } 613 614 lower_left(); 615 if (col >= sc_width) 616 screen_trashed = 1; 617 flush(); 618 619 return (c); 620 } 621