1 /* 2 * Copyright (C) 1984-2026 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 * Routines which deal with the characteristics of the terminal. 13 * Uses termcap to be as terminal-independent as possible. 14 */ 15 16 #include "less.h" 17 #include "cmd.h" 18 19 #if MSDOS_COMPILER 20 #include "pckeys.h" 21 #if MSDOS_COMPILER==MSOFTC 22 #include <graph.h> 23 #else 24 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 25 #include <conio.h> 26 #if MSDOS_COMPILER==DJGPPC 27 #include <pc.h> 28 extern int fd0; 29 #endif 30 #else 31 #if MSDOS_COMPILER==WIN32C 32 #include <windows.h> 33 #endif 34 #endif 35 #endif 36 #include <time.h> 37 38 #ifndef FOREGROUND_BLUE 39 #define FOREGROUND_BLUE 0x0001 40 #endif 41 #ifndef FOREGROUND_GREEN 42 #define FOREGROUND_GREEN 0x0002 43 #endif 44 #ifndef FOREGROUND_RED 45 #define FOREGROUND_RED 0x0004 46 #endif 47 #ifndef FOREGROUND_INTENSITY 48 #define FOREGROUND_INTENSITY 0x0008 49 #endif 50 51 #else 52 53 #if HAVE_SYS_IOCTL_H 54 #include <sys/ioctl.h> 55 #endif 56 57 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 58 #include <termios.h> 59 #else 60 #if HAVE_TERMIO_H 61 #include <termio.h> 62 #else 63 #if HAVE_SGSTAT_H 64 #include <sgstat.h> 65 #else 66 #include <sgtty.h> 67 #endif 68 #endif 69 #endif 70 71 #if HAVE_TERMINFO && !USE_TERMCAP 72 #define USE_TERMINFO 1 73 #endif 74 75 #if USE_TERMINFO 76 #include <curses.h> 77 #include <term.h> 78 #else 79 #if HAVE_NCURSESW_TERMCAP_H 80 #include <ncursesw/termcap.h> 81 #else 82 #if HAVE_NCURSES_TERMCAP_H 83 #include <ncurses/termcap.h> 84 #else 85 #if HAVE_TERMCAP_H 86 #include <termcap.h> 87 #endif 88 #endif 89 #endif 90 #endif 91 #ifdef _OSK 92 #include <signal.h> 93 #endif 94 #if OS2 95 #include <sys/signal.h> 96 #include "pckeys.h" 97 #endif 98 #if HAVE_SYS_STREAM_H 99 #include <sys/stream.h> 100 #endif 101 #if HAVE_SYS_PTEM_H 102 #include <sys/ptem.h> 103 #endif 104 105 #endif /* MSDOS_COMPILER */ 106 107 /* 108 * Check for broken termios package that forces you to manually 109 * set the line discipline. 110 */ 111 #ifdef __ultrix__ 112 #define MUST_SET_LINE_DISCIPLINE 1 113 #else 114 #define MUST_SET_LINE_DISCIPLINE 0 115 #endif 116 117 #if OS2 118 #define DEFAULT_TERM "ansi" 119 static char *windowid; 120 #else 121 #define DEFAULT_TERM "unknown" 122 #endif 123 124 #if MSDOS_COMPILER==MSOFTC 125 static int videopages; 126 static long msec_loops; 127 static int flash_created = 0; 128 #define SET_FG_COLOR(fg) _settextcolor(fg) 129 #define SET_BG_COLOR(bg) _setbkcolor(bg) 130 #define SETCOLORS(fg,bg) { SET_FG_COLOR(fg); SET_BG_COLOR(bg); } 131 #endif 132 133 #if MSDOS_COMPILER==BORLANDC 134 static unsigned short *whitescreen; 135 static int flash_created = 0; 136 #endif 137 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 138 #define _settextposition(y,x) gotoxy(x,y) 139 #define _clearscreen(m) clrscr() 140 #define _outtext(s) cputs(s) 141 #define SET_FG_COLOR(fg) textcolor(fg) 142 #define SET_BG_COLOR(bg) textbackground(bg) 143 #define SETCOLORS(fg,bg) { SET_FG_COLOR(fg); SET_BG_COLOR(bg); } 144 extern int sc_height; 145 #endif 146 147 #if MSDOS_COMPILER==WIN32C 148 #define UTF8_MAX_LENGTH 4 149 150 static WORD curr_attr; 151 152 static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ 153 static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ 154 HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ 155 156 extern int utf_mode; 157 extern lbool quitting; 158 static void win32_init_term(); 159 static void win32_deinit_term(); 160 161 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 162 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 163 #endif 164 165 #define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) 166 #define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) 167 #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) 168 #define APPLY_COLORS() { if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ 169 error("SETCOLORS failed", NULL_PARG); } 170 #define SET_FG_COLOR(fg) { curr_attr &= ~0x0f; curr_attr |= (fg); APPLY_COLORS(); } 171 #define SET_BG_COLOR(bg) { curr_attr &= ~0xf0; curr_attr |= ((bg)<<4); APPLY_COLORS(); } 172 #define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); APPLY_COLORS(); } 173 #endif 174 175 #if MSDOS_COMPILER 176 public int nm_fg_color = CV_ERROR; /* Color of normal text */ 177 public int nm_bg_color = CV_ERROR; 178 public int nm_attr = 0; 179 public int bo_fg_color = CV_ERROR; /* Color of bold text */ 180 public int bo_bg_color = CV_ERROR; 181 public int bo_attr = 0; 182 public int ul_fg_color = CV_ERROR; /* Color of underlined text */ 183 public int ul_bg_color = CV_ERROR; 184 public int ul_attr = 0; 185 public int so_fg_color = CV_ERROR; /* Color of standout text */ 186 public int so_bg_color = CV_ERROR; 187 public int so_attr = 0; 188 public int bl_fg_color = CV_ERROR; /* Color of blinking text */ 189 public int bl_bg_color = CV_ERROR; 190 public int bl_attr = 0; 191 static int sy_fg_color; /* Color of system text (before less) */ 192 static int sy_bg_color; 193 public int sgr_mode; /* Honor ANSI sequences rather than using above */ 194 #if MSDOS_COMPILER==WIN32C 195 static DWORD init_console_output_mode; 196 extern DWORD init_console_input_mode; 197 extern DWORD curr_console_input_mode; 198 extern DWORD base_console_input_mode; 199 extern DWORD mouse_console_input_mode; 200 public int vt_enabled = -1; /* Is virtual terminal processing available? */ 201 #endif 202 #else 203 204 /* 205 * These two variables are sometimes defined in, 206 * and needed by, the termcap library. 207 */ 208 #if USE_TERMINFO 209 #undef HAVE_OSPEED 210 #else 211 #if MUST_DEFINE_OSPEED 212 extern short ospeed; /* Terminal output baud rate */ 213 extern char PC; /* Pad character */ 214 #endif 215 #ifdef _OSK 216 short ospeed; 217 char PC_, *UP, *BC; 218 #endif 219 #endif 220 221 /* 222 * Strings passed to tputs() to do various terminal functions. 223 */ 224 static constant char 225 #if HAVE_OSPEED 226 *sc_pad, /* Pad string */ 227 #endif 228 *sc_home, /* Cursor home */ 229 *sc_addline, /* Add line, scroll down following lines */ 230 *sc_lower_left, /* Cursor to last line, first column */ 231 *sc_return, /* Cursor to beginning of current line */ 232 *sc_move, /* General cursor positioning */ 233 *sc_clear, /* Clear screen */ 234 *sc_eol_clear, /* Clear to end of line */ 235 *sc_eos_clear, /* Clear to end of screen */ 236 *sc_s_in, /* Enter standout (highlighted) mode */ 237 *sc_s_out, /* Exit standout mode */ 238 *sc_u_in, /* Enter underline mode */ 239 *sc_u_out, /* Exit underline mode */ 240 *sc_b_in, /* Enter bold mode */ 241 *sc_b_out, /* Exit bold mode */ 242 *sc_bl_in, /* Enter blink mode */ 243 *sc_bl_out, /* Exit blink mode */ 244 *sc_visual_bell, /* Visual bell (flash screen) sequence */ 245 *sc_backspace, /* Backspace cursor */ 246 *sc_s_keypad, /* Start keypad mode */ 247 *sc_e_keypad, /* End keypad mode */ 248 *sc_s_mousecap, /* Start mouse capture mode */ 249 *sc_e_mousecap, /* End mouse capture mode */ 250 *sc_s_bracketed_paste, /* Start bracketed paste mode */ 251 *sc_e_bracketed_paste, /* End bracketed paste mode */ 252 *sc_suspend, /* Suspend screen updates */ 253 *sc_resume, /* Resume screen updates */ 254 *sc_init, /* Startup terminal initialization */ 255 *sc_deinit; /* Exit terminal de-initialization */ 256 257 static int attrcolor = -1; 258 #endif 259 260 /* term_init has been called; terminal is ready for use by less */ 261 static lbool term_init_done = FALSE; 262 public lbool term_init_ever = FALSE; 263 264 public int auto_wrap; /* Terminal does \r\n when write past margin */ 265 public int defer_wrap; /* After printing char in last column, doesn't wrap until next char */ 266 public int erase_char; /* The user's erase char */ 267 public int erase2_char; /* The user's other erase char */ 268 public int kill_char; /* The user's line-kill char */ 269 public int werase_char; /* The user's word-erase char */ 270 public int sc_width, sc_height; /* Height & width of screen */ 271 public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ 272 public int ul_s_width, ul_e_width; /* Printing width of underline seq */ 273 public int so_s_width, so_e_width; /* Printing width of standout seq */ 274 public int bl_s_width, bl_e_width; /* Printing width of blink seq */ 275 public int above_mem, below_mem; /* Memory retained above/below screen */ 276 public int can_goto_line; /* Can move cursor to any line */ 277 public int clear_bg; /* Clear fills with background color */ 278 public lbool missing_cap = FALSE; /* Some capability is missing */ 279 public constant char *kent = NULL; /* Keypad ENTER sequence */ 280 public lbool term_addrs = FALSE; /* "ti" has been sent to terminal */ 281 public lbool full_screen = TRUE; /* We're using all lines of terminal */ 282 283 static int attrmode = AT_NORMAL; 284 static int termcap_debug = -1; 285 static int no_alt_screen; /* sc_init does not switch to alt screen */ 286 extern int binattr; 287 extern int one_screen; 288 extern int shell_lines; 289 290 #if !MSDOS_COMPILER 291 static constant char *cheaper(constant char *t1, constant char *t2, constant char *def); 292 static void tmodes(constant char *inti, constant char *outti, constant char *intc, constant char *outtc, constant char **instr, 293 constant char **outstr, constant char *def_instr, constant char *def_outstr, char **spp); 294 #endif 295 296 extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 297 extern int no_vbell; 298 extern int no_back_scroll; 299 extern int no_init; 300 extern int no_keypad; 301 extern int sigs; 302 extern int top_scroll; 303 extern int quit_if_one_screen; 304 extern int oldbot; 305 extern int mousecap; 306 extern int is_tty; 307 extern int use_color; 308 extern int no_paste; 309 extern int wscroll; 310 #if HILITE_SEARCH 311 extern int hilite_search; 312 #endif 313 #if MSDOS_COMPILER==WIN32C 314 extern HANDLE tty; 315 #else 316 extern int tty; 317 #endif 318 319 #if (HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS) || defined(TCGETA) 320 /* 321 * Set termio flags for use by less. 322 */ 323 static void set_termio_flags( 324 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 325 struct termios *s 326 #else 327 struct termio *s 328 #endif 329 ) 330 { 331 s->c_lflag &= ~(0 332 #ifdef ICANON 333 | ICANON 334 #endif 335 #ifdef ECHO 336 | ECHO 337 #endif 338 #ifdef ECHOE 339 | ECHOE 340 #endif 341 #ifdef ECHOK 342 | ECHOK 343 #endif 344 #ifdef ECHONL 345 | ECHONL 346 #endif 347 ); 348 349 s->c_oflag |= (0 350 #ifdef OPOST 351 | OPOST 352 #endif 353 #ifdef ONLCR 354 | ONLCR 355 #endif 356 ); 357 358 s->c_oflag &= ~(0 359 #ifdef ONOEOT 360 | ONOEOT 361 #endif 362 #ifdef OCRNL 363 | OCRNL 364 #endif 365 #ifdef ONOCR 366 | ONOCR 367 #endif 368 #ifdef ONLRET 369 | ONLRET 370 #endif 371 ); 372 373 s->c_iflag &= ~(0 374 #ifdef ICRNL 375 | ICRNL 376 #endif 377 #ifdef INLCR 378 | INLCR 379 #endif 380 ); 381 } 382 #endif 383 384 /* 385 * Change terminal to "raw mode", or restore to "normal" mode. 386 * "Raw mode" means 387 * 1. An outstanding read will complete on receipt of a single keystroke. 388 * 2. Input is not echoed. 389 * 3. On output, \n is mapped to \r\n. 390 * 4. \t is NOT expanded into spaces. 391 * 5. Signal-causing characters such as ctrl-C (interrupt), 392 * etc. are NOT disabled. 393 * 6. Input \r is not mapped to \n, nor vice versa. 394 */ 395 public void raw_mode(int on) 396 { 397 static int curr_on = 0; 398 399 if (on == curr_on) 400 return; 401 erase2_char = '\b'; /* in case OS doesn't know about erase2 */ 402 #if LESSTEST 403 if (is_lesstest()) 404 { 405 /* {{ For consistent conditions when running tests. }} */ 406 erase_char = '\b'; 407 kill_char = CONTROL('U'); 408 werase_char = CONTROL('W'); 409 } else 410 #endif /*LESSTEST*/ 411 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 412 { 413 struct termios s; 414 static struct termios save_term; 415 static int saved_term = 0; 416 417 if (on) 418 { 419 /* 420 * Get terminal modes. 421 */ 422 if (tcgetattr(tty, &s) < 0) 423 { 424 erase_char = '\b'; 425 kill_char = CONTROL('U'); 426 werase_char = CONTROL('W'); 427 } else 428 { 429 /* 430 * Save modes and set certain variables dependent on modes. 431 */ 432 if (!saved_term) 433 { 434 save_term = s; 435 saved_term = 1; 436 } 437 #if HAVE_OSPEED 438 switch (cfgetospeed(&s)) 439 { 440 #ifdef B0 441 case B0: ospeed = 0; break; 442 #endif 443 #ifdef B50 444 case B50: ospeed = 1; break; 445 #endif 446 #ifdef B75 447 case B75: ospeed = 2; break; 448 #endif 449 #ifdef B110 450 case B110: ospeed = 3; break; 451 #endif 452 #ifdef B134 453 case B134: ospeed = 4; break; 454 #endif 455 #ifdef B150 456 case B150: ospeed = 5; break; 457 #endif 458 #ifdef B200 459 case B200: ospeed = 6; break; 460 #endif 461 #ifdef B300 462 case B300: ospeed = 7; break; 463 #endif 464 #ifdef B600 465 case B600: ospeed = 8; break; 466 #endif 467 #ifdef B1200 468 case B1200: ospeed = 9; break; 469 #endif 470 #ifdef B1800 471 case B1800: ospeed = 10; break; 472 #endif 473 #ifdef B2400 474 case B2400: ospeed = 11; break; 475 #endif 476 #ifdef B4800 477 case B4800: ospeed = 12; break; 478 #endif 479 #ifdef B9600 480 case B9600: ospeed = 13; break; 481 #endif 482 #ifdef EXTA 483 case EXTA: ospeed = 14; break; 484 #endif 485 #ifdef EXTB 486 case EXTB: ospeed = 15; break; 487 #endif 488 #ifdef B57600 489 case B57600: ospeed = 16; break; 490 #endif 491 #ifdef B115200 492 case B115200: ospeed = 17; break; 493 #endif 494 default: ; 495 } 496 #endif 497 erase_char = s.c_cc[VERASE]; 498 #ifdef VERASE2 499 erase2_char = s.c_cc[VERASE2]; 500 #endif 501 kill_char = s.c_cc[VKILL]; 502 #ifdef VWERASE 503 werase_char = s.c_cc[VWERASE]; 504 #else 505 werase_char = CONTROL('W'); 506 #endif 507 508 /* 509 * Set the modes to the way we want them. 510 */ 511 set_termio_flags(&s); 512 s.c_cc[VMIN] = 1; 513 s.c_cc[VTIME] = 0; 514 #ifdef VLNEXT 515 s.c_cc[VLNEXT] = 0; 516 #endif 517 #ifdef VDSUSP 518 s.c_cc[VDSUSP] = 0; 519 #endif 520 #ifdef VSTOP 521 s.c_cc[VSTOP] = 0; 522 #endif 523 #ifdef VSTART 524 s.c_cc[VSTART] = 0; 525 #endif 526 #ifdef VDISCARD 527 s.c_cc[VDISCARD] = 0; 528 #endif 529 #if MUST_SET_LINE_DISCIPLINE 530 /* 531 * System's termios is broken; need to explicitly 532 * request TERMIODISC line discipline. 533 */ 534 s.c_line = TERMIODISC; 535 #endif 536 } 537 } else 538 { 539 /* 540 * Restore saved modes. 541 */ 542 s = save_term; 543 } 544 #if HAVE_FSYNC 545 fsync(tty); 546 #endif 547 tcsetattr(tty, TCSADRAIN, &s); 548 #if MUST_SET_LINE_DISCIPLINE 549 if (!on) 550 { 551 /* 552 * Broken termios *ignores* any line discipline 553 * except TERMIODISC. A different old line discipline 554 * is therefore not restored, yet. Restore the old 555 * line discipline by hand. 556 */ 557 ioctl(tty, TIOCSETD, &save_term.c_line); 558 } 559 #endif 560 } 561 #else 562 #ifdef TCGETA 563 { 564 struct termio s; 565 static struct termio save_term; 566 static int saved_term = 0; 567 568 if (on) 569 { 570 /* 571 * Get terminal modes. 572 */ 573 ioctl(tty, TCGETA, &s); 574 575 /* 576 * Save modes and set certain variables dependent on modes. 577 */ 578 if (!saved_term) 579 { 580 save_term = s; 581 saved_term = 1; 582 } 583 #if HAVE_OSPEED 584 ospeed = s.c_cflag & CBAUD; 585 #endif 586 erase_char = s.c_cc[VERASE]; 587 kill_char = s.c_cc[VKILL]; 588 #ifdef VWERASE 589 werase_char = s.c_cc[VWERASE]; 590 #else 591 werase_char = CONTROL('W'); 592 #endif 593 594 /* 595 * Set the modes to the way we want them. 596 */ 597 set_termio_flags(&s); 598 s.c_cc[VMIN] = 1; 599 s.c_cc[VTIME] = 0; 600 #ifdef VSTOP 601 s.c_cc[VSTOP] = 0; 602 #endif 603 #ifdef VSTART 604 s.c_cc[VSTART] = 0; 605 #endif 606 } else 607 { 608 /* 609 * Restore saved modes. 610 */ 611 s = save_term; 612 } 613 ioctl(tty, TCSETAW, &s); 614 } 615 #else 616 #ifdef TIOCGETP 617 { 618 struct sgttyb s; 619 static struct sgttyb save_term; 620 static int saved_term = 0; 621 622 if (on) 623 { 624 /* 625 * Get terminal modes. 626 */ 627 ioctl(tty, TIOCGETP, &s); 628 629 /* 630 * Save modes and set certain variables dependent on modes. 631 */ 632 if (!saved_term) 633 { 634 save_term = s; 635 saved_term = 1; 636 } 637 #if HAVE_OSPEED 638 ospeed = s.sg_ospeed; 639 #endif 640 erase_char = s.sg_erase; 641 kill_char = s.sg_kill; 642 werase_char = CONTROL('W'); 643 644 /* 645 * Set the modes to the way we want them. 646 */ 647 s.sg_flags |= CBREAK; 648 s.sg_flags &= ~(ECHO); 649 } else 650 { 651 /* 652 * Restore saved modes. 653 */ 654 s = save_term; 655 } 656 ioctl(tty, TIOCSETN, &s); 657 } 658 #else 659 #ifdef _OSK 660 { 661 struct sgbuf s; 662 static struct sgbuf save_term; 663 static int saved_term = 0; 664 665 if (on) 666 { 667 /* 668 * Get terminal modes. 669 */ 670 _gs_opt(tty, &s); 671 672 /* 673 * Save modes and set certain variables dependent on modes. 674 */ 675 if (!saved_term) 676 { 677 save_term = s; 678 saved_term = 1; 679 } 680 erase_char = s.sg_bspch; 681 kill_char = s.sg_dlnch; 682 werase_char = CONTROL('W'); 683 684 /* 685 * Set the modes to the way we want them. 686 */ 687 s.sg_echo = 0; 688 s.sg_eofch = 0; 689 s.sg_pause = 0; 690 s.sg_psch = 0; 691 } else 692 { 693 /* 694 * Restore saved modes. 695 */ 696 s = save_term; 697 } 698 _ss_opt(tty, &s); 699 } 700 #else 701 /* MS-DOS, Windows, or OS2 */ 702 #if OS2 703 /* OS2 */ 704 LSIGNAL(SIGINT, SIG_IGN); 705 #endif 706 erase_char = '\b'; 707 #if MSDOS_COMPILER==DJGPPC 708 kill_char = CONTROL('U'); 709 /* 710 * So that when we shell out or run another program, its 711 * stdin is in cooked mode. We do not switch stdin to binary 712 * mode if fd0 is zero, since that means we were called before 713 * tty was reopened in open_getchr, in which case we would be 714 * changing the original stdin device outside less. 715 */ 716 if (fd0 != 0) 717 setmode(0, on ? O_BINARY : O_TEXT); 718 #else 719 kill_char = ESC; 720 #endif 721 werase_char = CONTROL('W'); 722 #endif 723 #endif 724 #endif 725 #endif 726 curr_on = on; 727 } 728 729 #if !MSDOS_COMPILER 730 731 /* 732 * Some glue to prevent calling termcap functions if tgetent() failed. 733 */ 734 static int hardcopy; 735 736 static constant char * ltget_env(constant char *tiname, constant char *tcname) 737 { 738 char envname[64]; 739 constant char *s; 740 741 if (termcap_debug) 742 { 743 struct env { struct env *next; char *name; char *value; }; 744 constant char *capname = tcname != NULL ? tcname : tiname; 745 static struct env *envs = NULL; 746 struct env *p; 747 for (p = envs; p != NULL; p = p->next) 748 if (strcmp(p->name, capname) == 0) 749 return p->value; 750 p = (struct env *) ecalloc(1, sizeof(struct env)); 751 p->name = save(capname); 752 p->value = (char *) ecalloc(strlen(capname)+3, sizeof(char)); 753 sprintf(p->value, "<%s>", capname); 754 p->next = envs; 755 envs = p; 756 return p->value; 757 } 758 if (tiname != NULL) 759 { 760 SNPRINTF1(envname, sizeof(envname), "LESS_TERMINFO_%s", tiname); 761 s = lgetenv(envname); 762 if (!isnullenv(s)) 763 return s; 764 } 765 if (tcname != NULL) 766 { 767 SNPRINTF1(envname, sizeof(envname), "LESS_TERMCAP_%s", tcname); 768 s = lgetenv(envname); 769 if (!isnullenv(s)) 770 return s; 771 } 772 return NULL; 773 } 774 775 static int ltgetflag(constant char *tiname, constant char *tcname) 776 { 777 constant char *s; 778 779 if ((s = ltget_env(tiname, tcname)) != NULL) 780 return (*s != '\0' && *s != '0'); 781 if (hardcopy) 782 return (0); 783 #if USE_TERMINFO 784 return tiname == NULL ? 0 : tigetflag(tiname); 785 #else 786 return tcname == NULL ? 0 : tgetflag(tcname); 787 #endif 788 } 789 790 static int ltgetnum(constant char *tiname, constant char *tcname) 791 { 792 constant char *s; 793 794 if ((s = ltget_env(tiname, tcname)) != NULL) 795 return (atoi(s)); 796 if (hardcopy) 797 return (-1); 798 #if USE_TERMINFO 799 return tiname == NULL ? -1 : tigetnum(tiname); 800 #else 801 return tcname == NULL ? -1 : tgetnum(tcname); 802 #endif 803 } 804 805 static constant char * ltgetstr(constant char *tiname, constant char *tcname, char **pp) 806 { 807 constant char *s; 808 809 if ((s = ltget_env(tiname, tcname)) != NULL) 810 return (s); 811 if (hardcopy) 812 return (NULL); 813 #if USE_TERMINFO 814 if (tiname == NULL) 815 return (NULL); 816 s = tigetstr(tiname); 817 if (s == (constant char *)-1) 818 s = NULL; 819 return (s); 820 #else 821 if (tcname == NULL) 822 return (NULL); 823 return tgetstr(tcname, pp); 824 #endif 825 } 826 #endif /* MSDOS_COMPILER */ 827 828 /* 829 * Get size of the output screen. 830 */ 831 public void scrsize(void) 832 { 833 constant char *s; 834 int sys_height; 835 int sys_width; 836 #if !MSDOS_COMPILER 837 int n; 838 #endif 839 840 #define DEF_SC_WIDTH 80 841 #if MSDOS_COMPILER 842 #define DEF_SC_HEIGHT 25 843 #else 844 #define DEF_SC_HEIGHT 24 845 #endif 846 847 sys_width = sys_height = 0; 848 849 #if LESSTEST 850 if (0) /* can't use is_lesstest(): ttyin_name may not be set by scan_option yet */ 851 #endif /*LESSTEST*/ 852 { 853 #if MSDOS_COMPILER==MSOFTC 854 { 855 struct videoconfig w; 856 _getvideoconfig(&w); 857 sys_height = w.numtextrows; 858 sys_width = w.numtextcols; 859 } 860 #else 861 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 862 { 863 struct text_info w; 864 gettextinfo(&w); 865 sys_height = w.screenheight; 866 sys_width = w.screenwidth; 867 } 868 #else 869 #if MSDOS_COMPILER==WIN32C 870 { 871 CONSOLE_SCREEN_BUFFER_INFO scr; 872 GetConsoleScreenBufferInfo(con_out, &scr); 873 sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1; 874 sys_width = scr.srWindow.Right - scr.srWindow.Left + 1; 875 } 876 #else 877 #if OS2 878 { 879 int s[2]; 880 _scrsize(s); 881 sys_width = s[0]; 882 sys_height = s[1]; 883 /* 884 * When using terminal emulators for XFree86/OS2, the 885 * _scrsize function does not work well. 886 * Call the scrsize.exe program to get the window size. 887 */ 888 windowid = getenv("WINDOWID"); 889 if (windowid != NULL) 890 { 891 FILE *fd = popen("scrsize", "rt"); 892 if (fd != NULL) 893 { 894 int w, h; 895 fscanf(fd, "%i %i", &w, &h); 896 if (w > 0 && h > 0) 897 { 898 sys_width = w; 899 sys_height = h; 900 } 901 pclose(fd); 902 } 903 } 904 } 905 #else 906 #ifdef TIOCGWINSZ 907 { 908 struct winsize w; 909 if (ioctl(2, TIOCGWINSZ, &w) == 0 || ioctl(1, TIOCGWINSZ, &w) == 0) 910 { 911 if (w.ws_row > 0) 912 sys_height = w.ws_row; 913 if (w.ws_col > 0) 914 sys_width = w.ws_col; 915 } 916 } 917 #else 918 #ifdef WIOCGETD 919 { 920 struct uwdata w; 921 if (ioctl(2, WIOCGETD, &w) == 0 || ioctl(1, WIOCGETD, &w) == 0) 922 { 923 if (w.uw_height > 0) 924 sys_height = w.uw_height / w.uw_vs; 925 if (w.uw_width > 0) 926 sys_width = w.uw_width / w.uw_hs; 927 } 928 } 929 #endif 930 #endif 931 #endif 932 #endif 933 #endif 934 #endif 935 } 936 937 if (sys_height > 0) 938 sc_height = sys_height; 939 else if ((s = lgetenv("LINES")) != NULL) 940 sc_height = atoi(s); 941 #if !MSDOS_COMPILER 942 else if ((n = ltgetnum("lines", "li")) > 0) 943 sc_height = n; 944 #endif 945 if ((s = lgetenv("LESS_LINES")) != NULL) 946 { 947 int height = atoi(s); 948 sc_height = (height < 0) ? sc_height + height : height; 949 full_screen = FALSE; 950 } 951 if (sc_height <= 0) 952 sc_height = DEF_SC_HEIGHT; 953 954 if (sys_width > 0) 955 sc_width = sys_width; 956 else if ((s = lgetenv("COLUMNS")) != NULL) 957 sc_width = atoi(s); 958 #if !MSDOS_COMPILER 959 else if ((n = ltgetnum("cols", "co")) > 0) 960 sc_width = n; 961 #endif 962 if ((s = lgetenv("LESS_COLUMNS")) != NULL) 963 { 964 int width = atoi(s); 965 sc_width = (width < 0) ? sc_width + width : width; 966 } 967 if (sc_width <= 0) 968 sc_width = DEF_SC_WIDTH; 969 screen_size_changed(); 970 } 971 972 /* 973 * Recalculate things that depend on the screen size. 974 */ 975 public void screen_size_changed(void) 976 { 977 constant char *env = lgetenv("LESS_SHELL_LINES"); 978 shell_lines = isnullenv(env) ? 1 : atoi(env); 979 if (shell_lines >= sc_height) 980 shell_lines = sc_height - 1; 981 wscroll = (sc_height + 1) / 2; 982 calc_jump_sline(); 983 calc_shift_count(); 984 calc_match_shift(); 985 } 986 987 #if MSDOS_COMPILER==MSOFTC 988 /* 989 * Figure out how many empty loops it takes to delay a millisecond. 990 */ 991 static void get_clock(void) 992 { 993 clock_t start; 994 995 /* 996 * Get synchronized at the start of a tick. 997 */ 998 start = clock(); 999 while (clock() == start) 1000 ; 1001 /* 1002 * Now count loops till the next tick. 1003 */ 1004 start = clock(); 1005 msec_loops = 0; 1006 while (clock() == start) 1007 msec_loops++; 1008 /* 1009 * Convert from (loops per clock) to (loops per millisecond). 1010 */ 1011 msec_loops *= CLOCKS_PER_SEC; 1012 msec_loops /= 1000; 1013 } 1014 1015 /* 1016 * Delay for a specified number of milliseconds. 1017 */ 1018 static void delay(int msec) 1019 { 1020 long i; 1021 1022 while (msec-- > 0) 1023 { 1024 for (i = 0; i < msec_loops; i++) 1025 (void) clock(); 1026 } 1027 } 1028 #endif 1029 1030 /* 1031 * Return the characters actually input by a "special" key. 1032 */ 1033 public constant char * special_key_str(int key) 1034 { 1035 static char tbuf[40]; 1036 constant char *s; 1037 #if MSDOS_COMPILER || OS2 1038 static char k_right[] = { '\340', PCK_RIGHT, 0 }; 1039 static char k_left[] = { '\340', PCK_LEFT, 0 }; 1040 static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 }; 1041 static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 }; 1042 static char k_shift_right[] = { '\340', PCK_SHIFT_RIGHT, 0 }; 1043 static char k_shift_left[] = { '\340', PCK_SHIFT_LEFT, 0 }; 1044 static char k_insert[] = { '\340', PCK_INSERT, 0 }; 1045 static char k_delete[] = { '\340', PCK_DELETE, 0 }; 1046 static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 }; 1047 static char k_ctl_backspace[] = { '\177', 0 }; 1048 static char k_backspace[] = { '\b', 0 }; 1049 static char k_home[] = { '\340', PCK_HOME, 0 }; 1050 static char k_end[] = { '\340', PCK_END, 0 }; 1051 static char k_up[] = { '\340', PCK_UP, 0 }; 1052 static char k_down[] = { '\340', PCK_DOWN, 0 }; 1053 static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 }; 1054 static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 }; 1055 static char k_pageup[] = { '\340', PCK_PAGEUP, 0 }; 1056 static char k_ctl_home[] = { '\340', PCK_CTL_HOME, 0 }; 1057 static char k_ctl_end[] = { '\340', PCK_CTL_END, 0 }; 1058 static char k_shift_home[] = { '\340', PCK_SHIFT_HOME, 0 }; 1059 static char k_shift_end[] = { '\340', PCK_SHIFT_END, 0 }; 1060 static char k_f1[] = { '\340', PCK_F1, 0 }; 1061 #endif 1062 #if !MSDOS_COMPILER 1063 char *sp = tbuf; 1064 #endif 1065 1066 switch (key) 1067 { 1068 #if OS2 1069 /* 1070 * If windowid is not NULL, assume less is executed in 1071 * the XFree86 environment. 1072 */ 1073 case SK_RIGHT_ARROW: 1074 s = windowid ? ltgetstr("kcuf1", "kr", &sp) : k_right; 1075 break; 1076 case SK_LEFT_ARROW: 1077 s = windowid ? ltgetstr("kcub1", "kl", &sp) : k_left; 1078 break; 1079 case SK_UP_ARROW: 1080 s = windowid ? ltgetstr("kcuu1", "ku", &sp) : k_up; 1081 break; 1082 case SK_DOWN_ARROW: 1083 s = windowid ? ltgetstr("kcud1", "kd", &sp) : k_down; 1084 break; 1085 case SK_PAGE_UP: 1086 s = windowid ? ltgetstr("kpp", "kP", &sp) : k_pageup; 1087 break; 1088 case SK_PAGE_DOWN: 1089 s = windowid ? ltgetstr("knp", "kN", &sp) : k_pagedown; 1090 break; 1091 case SK_HOME: 1092 s = windowid ? ltgetstr("khome", "kh", &sp) : k_home; 1093 break; 1094 case SK_END: 1095 s = windowid ? ltgetstr("kend", "@7", &sp) : k_end; 1096 break; 1097 case SK_F1: 1098 s = windowid ? ltgetstr("kf1", "k1", &sp) : k_f1; 1099 break; 1100 case SK_DELETE: 1101 s = windowid ? ltgetstr("kdch1", "kD", &sp) : k_delete; 1102 if (s == NULL) 1103 { 1104 tbuf[0] = '\177'; 1105 tbuf[1] = '\0'; 1106 s = tbuf; 1107 } 1108 break; 1109 #endif 1110 #if MSDOS_COMPILER 1111 case SK_RIGHT_ARROW: 1112 s = k_right; 1113 break; 1114 case SK_LEFT_ARROW: 1115 s = k_left; 1116 break; 1117 case SK_UP_ARROW: 1118 s = k_up; 1119 break; 1120 case SK_DOWN_ARROW: 1121 s = k_down; 1122 break; 1123 case SK_PAGE_UP: 1124 s = k_pageup; 1125 break; 1126 case SK_PAGE_DOWN: 1127 s = k_pagedown; 1128 break; 1129 case SK_HOME: 1130 s = k_home; 1131 break; 1132 case SK_END: 1133 s = k_end; 1134 break; 1135 case SK_DELETE: 1136 s = k_delete; 1137 break; 1138 #endif 1139 #if MSDOS_COMPILER || OS2 1140 case SK_INSERT: 1141 s = k_insert; 1142 break; 1143 case SK_CTL_LEFT_ARROW: 1144 s = k_ctl_left; 1145 break; 1146 case SK_CTL_RIGHT_ARROW: 1147 s = k_ctl_right; 1148 break; 1149 case SK_SHIFT_LEFT_ARROW: 1150 s = k_shift_left; 1151 break; 1152 case SK_SHIFT_RIGHT_ARROW: 1153 s = k_shift_right; 1154 break; 1155 case SK_CTL_BACKSPACE: 1156 s = k_ctl_backspace; 1157 break; 1158 case SK_CTL_DELETE: 1159 s = k_ctl_delete; 1160 break; 1161 case SK_BACKSPACE: 1162 s = k_backspace; 1163 break; 1164 case SK_F1: 1165 s = k_f1; 1166 break; 1167 case SK_BACKTAB: 1168 s = k_backtab; 1169 break; 1170 case SK_SHIFT_HOME: 1171 s = k_shift_home; 1172 break; 1173 case SK_CTL_HOME: 1174 s = k_ctl_home; 1175 break; 1176 case SK_SHIFT_END: 1177 s = k_shift_end; 1178 break; 1179 case SK_CTL_END: 1180 s = k_ctl_end; 1181 break; 1182 #else 1183 case SK_RIGHT_ARROW: 1184 s = ltgetstr("kcuf1", "kr", &sp); 1185 break; 1186 case SK_LEFT_ARROW: 1187 s = ltgetstr("kcub1", "kl", &sp); 1188 break; 1189 case SK_UP_ARROW: 1190 s = ltgetstr("kcuu1", "ku", &sp); 1191 break; 1192 case SK_DOWN_ARROW: 1193 s = ltgetstr("kcud1", "kd", &sp); 1194 break; 1195 case SK_PAGE_UP: 1196 s = ltgetstr("kpp", "kP", &sp); 1197 break; 1198 case SK_PAGE_DOWN: 1199 s = ltgetstr("knp", "kN", &sp); 1200 break; 1201 case SK_HOME: 1202 s = ltgetstr("khome", "kh", &sp); 1203 break; 1204 case SK_SHIFT_HOME: 1205 s = ltgetstr("kHOM", "#2", &sp); 1206 break; 1207 case SK_CTL_HOME: 1208 s = ltgetstr("kHOM5", NULL, &sp); 1209 break; 1210 case SK_END: 1211 s = ltgetstr("kend", "@7", &sp); 1212 break; 1213 case SK_SHIFT_END: 1214 s = ltgetstr("kEND", "#7", &sp); 1215 break; 1216 case SK_CTL_END: 1217 s = ltgetstr("kEND5", NULL, &sp); 1218 break; 1219 case SK_INSERT: 1220 s = ltgetstr("kich1", "kI", &sp); 1221 break; 1222 case SK_BACKTAB: 1223 s = ltgetstr("kcbt", "kB", &sp); 1224 break; 1225 case SK_SHIFT_RIGHT_ARROW: 1226 s = ltgetstr("kRIT", NULL, &sp); 1227 break; 1228 case SK_SHIFT_LEFT_ARROW: 1229 s = ltgetstr("kLFT", NULL, &sp); 1230 break; 1231 case SK_CTL_RIGHT_ARROW: 1232 s = ltgetstr("kRIT5", NULL, &sp); 1233 break; 1234 case SK_CTL_LEFT_ARROW: 1235 s = ltgetstr("kLFT5", NULL, &sp); 1236 break; 1237 case SK_F1: 1238 s = ltgetstr("kf1", "k1", &sp); 1239 break; 1240 case SK_PAD_UL: 1241 s = ltgetstr("ka1", "K1", &sp); 1242 break; 1243 case SK_PAD_U: 1244 s = ltgetstr("ka2", NULL, &sp); 1245 break; 1246 case SK_PAD_UR: 1247 s = ltgetstr("ka3", "K3", &sp); 1248 break; 1249 case SK_PAD_L: 1250 s = ltgetstr("kb1", NULL, &sp); 1251 break; 1252 case SK_PAD_CENTER: 1253 s = ltgetstr("kb2", "K2", &sp); 1254 break; 1255 case SK_PAD_R: 1256 s = ltgetstr("kb3", NULL, &sp); 1257 break; 1258 case SK_PAD_DL: 1259 s = ltgetstr("kc1", "K4", &sp); 1260 break; 1261 case SK_PAD_D: 1262 s = ltgetstr("kc2", NULL, &sp); 1263 break; 1264 case SK_PAD_DR: 1265 s = ltgetstr("kc3", "K5", &sp); 1266 break; 1267 case SK_PAD_STAR: 1268 s = ltgetstr("kpMUL", NULL, &sp); 1269 break; 1270 case SK_PAD_SLASH: 1271 s = ltgetstr("kpDIV", NULL, &sp); 1272 break; 1273 case SK_PAD_DASH: 1274 s = ltgetstr("kpSUB", NULL, &sp); 1275 break; 1276 case SK_PAD_PLUS: 1277 s = ltgetstr("kpADD", NULL, &sp); 1278 break; 1279 case SK_PAD_DOT: 1280 s = ltgetstr("kpDOT", NULL, &sp); 1281 break; 1282 case SK_PAD_COMMA: 1283 s = ltgetstr("kpCMA", NULL, &sp); 1284 break; 1285 case SK_PAD_ZERO: 1286 s = ltgetstr("kpZRO", NULL, &sp); 1287 break; 1288 case SK_DELETE: 1289 s = ltgetstr("kdch1", "kD", &sp); 1290 if (s == NULL) 1291 { 1292 tbuf[0] = '\177'; 1293 tbuf[1] = '\0'; 1294 s = tbuf; 1295 } 1296 break; 1297 case SK_BACKSPACE: 1298 s = ltgetstr("kbs", "kb", &sp); 1299 if (s == NULL) 1300 { 1301 tbuf[0] = '\b'; 1302 tbuf[1] = '\0'; 1303 s = tbuf; 1304 } 1305 break; 1306 #endif 1307 case SK_CONTROL_K: 1308 tbuf[0] = CONTROL('K'); 1309 tbuf[1] = '\0'; 1310 s = tbuf; 1311 break; 1312 default: 1313 return (NULL); 1314 } 1315 if (s != NULL && *s == '\0') 1316 s = NULL; 1317 return (s); 1318 } 1319 1320 #if !MSDOS_COMPILER 1321 static constant char *ltgoto(constant char *cap, int col, int line) 1322 { 1323 #if USE_TERMINFO 1324 return tparm(cap, line, col); 1325 #else 1326 return tgoto(cap, col, line); 1327 #endif 1328 } 1329 #endif /* MSDOS_COMPILER */ 1330 1331 #if MSDOS_COMPILER 1332 public void init_win_colors(void) 1333 { 1334 if (nm_fg_color == CV_ERROR || nm_fg_color == CV_NOCHANGE) nm_fg_color = sy_fg_color; 1335 if (nm_bg_color == CV_ERROR || nm_bg_color == CV_NOCHANGE) nm_bg_color = sy_bg_color; 1336 if (bo_fg_color == CV_NOCHANGE) bo_fg_color = sy_fg_color; else if (bo_fg_color == CV_ERROR) bo_fg_color = sy_fg_color | 8; 1337 if (bo_bg_color == CV_NOCHANGE) bo_bg_color = sy_bg_color; else if (bo_bg_color == CV_ERROR) bo_bg_color = sy_bg_color; 1338 if (ul_fg_color == CV_NOCHANGE) ul_fg_color = sy_fg_color; else if (ul_fg_color == CV_ERROR) ul_fg_color = (sy_bg_color == 3 || sy_bg_color == 11) ? 0 : 11; 1339 if (ul_bg_color == CV_NOCHANGE) ul_bg_color = sy_bg_color; else if (ul_bg_color == CV_ERROR) ul_bg_color = sy_bg_color; 1340 if (so_fg_color == CV_NOCHANGE) so_fg_color = sy_fg_color; else if (so_fg_color == CV_ERROR) so_fg_color = sy_bg_color; 1341 if (so_bg_color == CV_NOCHANGE) so_bg_color = sy_bg_color; else if (so_bg_color == CV_ERROR) so_bg_color = sy_fg_color; 1342 if (bl_fg_color == CV_NOCHANGE) bl_fg_color = sy_fg_color; else if (bl_fg_color == CV_ERROR) bl_fg_color = ul_bg_color; 1343 if (bl_bg_color == CV_NOCHANGE) bl_bg_color = sy_bg_color; else if (bl_bg_color == CV_ERROR) bl_bg_color = ul_fg_color; 1344 nm_fg_color |= nm_attr; 1345 bo_fg_color |= bo_attr; 1346 ul_fg_color |= ul_attr; 1347 so_fg_color |= so_attr; 1348 bl_fg_color |= bl_attr; 1349 } 1350 #endif /* MSDOS_COMPILER */ 1351 1352 /* 1353 * Get terminal capabilities via termcap. 1354 */ 1355 public void get_term() 1356 { 1357 termcap_debug = !isnullenv(lgetenv("LESS_TERMCAP_DEBUG")); 1358 #if MSDOS_COMPILER 1359 auto_wrap = 1; 1360 defer_wrap = 0; 1361 can_goto_line = 1; 1362 clear_bg = 1; 1363 /* 1364 * Set up default colors. 1365 * The xx_s_width and xx_e_width vars are already initialized to 0. 1366 */ 1367 #if MSDOS_COMPILER==MSOFTC 1368 sy_bg_color = _getbkcolor(); 1369 sy_fg_color = _gettextcolor(); 1370 get_clock(); 1371 #else 1372 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1373 { 1374 struct text_info w; 1375 gettextinfo(&w); 1376 sy_bg_color = (w.attribute >> 4) & 0x0F; 1377 sy_fg_color = (w.attribute >> 0) & 0x0F; 1378 } 1379 #else 1380 #if MSDOS_COMPILER==WIN32C 1381 { 1382 CONSOLE_SCREEN_BUFFER_INFO scr; 1383 1384 con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE); 1385 /* 1386 * Always open stdin in binary. Note this *must* be done 1387 * before any file operations have been done on fd0. 1388 */ 1389 SET_BINARY(0); 1390 GetConsoleMode(con_out, &init_console_output_mode); 1391 GetConsoleScreenBufferInfo(con_out, &scr); 1392 curr_attr = scr.wAttributes; 1393 sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */ 1394 sy_fg_color = curr_attr & FG_COLORS; 1395 } 1396 #endif 1397 #endif 1398 #endif 1399 init_win_colors(); 1400 1401 /* 1402 * Get size of the screen. 1403 */ 1404 scrsize(); 1405 pos_init(); 1406 1407 #else /* !MSDOS_COMPILER */ 1408 { 1409 constant char *t1; 1410 constant char *t2; 1411 constant char *term; 1412 char *sp; 1413 /* 1414 * Some termcap libraries assume termbuf is static 1415 * (accessible after tgetent returns). 1416 */ 1417 #if !USE_TERMINFO 1418 static char termbuf[TERMBUF_SIZE]; 1419 #endif 1420 static char sbuf[TERMSBUF_SIZE]; 1421 1422 #if OS2 1423 /* 1424 * Make sure the termcap database is available. 1425 */ 1426 constant char *cp = lgetenv("TERMCAP"); 1427 if (isnullenv(cp)) 1428 { 1429 char *termcap; 1430 if ((sp = homefile("termcap.dat")) != NULL) 1431 { 1432 termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); 1433 sprintf(termcap, "TERMCAP=%s", sp); 1434 free(sp); 1435 putenv(termcap); 1436 } 1437 } 1438 #endif 1439 /* 1440 * Find out what kind of terminal this is. 1441 */ 1442 if ((term = lgetenv("TERM")) == NULL && (term = getenv("TERM")) == NULL) 1443 term = DEFAULT_TERM; 1444 hardcopy = 0; 1445 #if USE_TERMINFO 1446 if (cur_term != NULL) 1447 del_curterm(cur_term); 1448 if (setupterm(term, -1, NULL) != OK) 1449 hardcopy = 1; 1450 #else 1451 if (tgetent(termbuf, term) != TGETENT_OK) 1452 hardcopy = 1; 1453 #endif 1454 if (!hardcopy && ltgetflag("hc", "hc")) 1455 hardcopy = 1; 1456 1457 /* 1458 * Get size of the screen. 1459 */ 1460 scrsize(); 1461 pos_init(); 1462 1463 auto_wrap = ltgetflag("am", "am"); 1464 defer_wrap = ltgetflag("xenl", "xn"); 1465 above_mem = ltgetflag("da", "da"); 1466 below_mem = ltgetflag("db", "db"); 1467 clear_bg = ltgetflag("bce", "ut"); 1468 no_alt_screen = ltgetflag("nrrmc", "NR"); 1469 1470 /* 1471 * Assumes termcap variable "sg" is the printing width of: 1472 * the standout sequence, the end standout sequence, 1473 * the underline sequence, the end underline sequence, 1474 * the boldface sequence, and the end boldface sequence. 1475 */ 1476 if ((so_s_width = ltgetnum("xmc", "sg")) < 0) 1477 so_s_width = 0; 1478 so_e_width = so_s_width; 1479 1480 bo_s_width = bo_e_width = so_s_width; 1481 ul_s_width = ul_e_width = so_s_width; 1482 bl_s_width = bl_e_width = so_s_width; 1483 1484 #if HILITE_SEARCH 1485 if (so_s_width > 0 || so_e_width > 0) 1486 /* 1487 * Disable highlighting by default on magic cookie terminals. 1488 * Turning on highlighting might change the displayed width 1489 * of a line, causing the display to get messed up. 1490 * The user can turn it back on with -g, 1491 * but she won't like the results. 1492 */ 1493 hilite_search = 0; 1494 #endif 1495 1496 /* 1497 * Get various string-valued capabilities. 1498 */ 1499 sp = sbuf; 1500 1501 #if HAVE_OSPEED 1502 sc_pad = ltgetstr("pad", "pc", &sp); 1503 if (sc_pad != NULL) 1504 PC = *sc_pad; 1505 #endif 1506 1507 sc_s_keypad = ltgetstr("smkx", "ks", &sp); 1508 if (sc_s_keypad == NULL) 1509 sc_s_keypad = ""; 1510 sc_e_keypad = ltgetstr("rmkx", "ke", &sp); 1511 if (sc_e_keypad == NULL) 1512 sc_e_keypad = ""; 1513 kent = ltgetstr("kent", "@8", &sp); 1514 1515 sc_s_mousecap = ltgetstr("MOUSE_START", "MOUSE_START", &sp); 1516 if (sc_s_mousecap == NULL) 1517 sc_s_mousecap = ESCS "[?1000h" ESCS "[?1002h" ESCS "[?1006h"; 1518 sc_e_mousecap = ltgetstr("MOUSE_END", "MOUSE_END", &sp); 1519 if (sc_e_mousecap == NULL) 1520 sc_e_mousecap = ESCS "[?1006l" ESCS "[?1002l" ESCS "[?1000l"; 1521 1522 sc_s_bracketed_paste = ltgetstr("BRACKETED_PASTE_START", "BRACKETED_PASTE_START", &sp); 1523 if (sc_s_bracketed_paste == NULL) 1524 sc_s_bracketed_paste = ESCS"[?2004h"; 1525 sc_e_bracketed_paste = ltgetstr("BRACKETED_PASTE_END", "BRACKETED_PASTE_END", &sp); 1526 if (sc_e_bracketed_paste == NULL) 1527 sc_e_bracketed_paste = ESCS"[?2004l"; 1528 1529 sc_suspend = ltgetstr("SUSPEND", "SUSPEND", &sp); 1530 if (sc_suspend == NULL) 1531 sc_suspend = ""; 1532 sc_resume = ltgetstr("RESUME", "RESUME", &sp); 1533 if (sc_resume == NULL) 1534 sc_resume = ""; 1535 1536 sc_init = ltgetstr("smcup", "ti", &sp); 1537 if (sc_init == NULL) 1538 sc_init = ""; 1539 1540 sc_deinit= ltgetstr("rmcup", "te", &sp); 1541 if (sc_deinit == NULL) 1542 sc_deinit = ""; 1543 1544 sc_eol_clear = ltgetstr("el", "ce", &sp); 1545 if (sc_eol_clear == NULL || *sc_eol_clear == '\0') 1546 { 1547 missing_cap = TRUE; 1548 sc_eol_clear = ""; 1549 } 1550 1551 sc_eos_clear = ltgetstr("ed", "cd", &sp); 1552 if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0')) 1553 { 1554 missing_cap = TRUE; 1555 sc_eos_clear = ""; 1556 } 1557 1558 sc_clear = ltgetstr("clear", "cl", &sp); 1559 if (sc_clear == NULL || *sc_clear == '\0') 1560 { 1561 missing_cap = TRUE; 1562 sc_clear = "\n\n"; 1563 } 1564 1565 sc_move = ltgetstr("cup", "cm", &sp); 1566 if (sc_move == NULL || *sc_move == '\0') 1567 { 1568 /* 1569 * This is not an error here, because we don't 1570 * always need sc_move. 1571 * We need it only if we don't have home or lower-left. 1572 */ 1573 sc_move = ""; 1574 can_goto_line = 0; 1575 } else 1576 can_goto_line = 1; 1577 1578 tmodes("smso", "rmso", "so", "se", &sc_s_in, &sc_s_out, "", "", &sp); 1579 tmodes("smul", "rmul", "us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp); 1580 tmodes("bold", "sgr0", "md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp); 1581 tmodes("blink", "sgr0", "mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp); 1582 1583 sc_visual_bell = ltgetstr("flash", "vb", &sp); 1584 if (sc_visual_bell == NULL) 1585 sc_visual_bell = ""; 1586 1587 if (ltgetflag(NULL, "bs")) 1588 sc_backspace = "\b"; 1589 else 1590 { 1591 sc_backspace = ltgetstr("OTbc", "bc", &sp); 1592 if (sc_backspace == NULL || *sc_backspace == '\0') 1593 sc_backspace = "\b"; 1594 } 1595 1596 /* 1597 * Choose between using "ho" and "cm" ("home" and "cursor move") 1598 * to move the cursor to the upper left corner of the screen. 1599 */ 1600 t1 = ltgetstr("home", "ho", &sp); 1601 if (t1 == NULL) 1602 t1 = ""; 1603 if (*sc_move == '\0') 1604 t2 = ""; 1605 else 1606 { 1607 strcpy(sp, ltgoto(sc_move, 0, 0)); 1608 t2 = sp; 1609 sp += strlen(sp) + 1; 1610 } 1611 sc_home = cheaper(t1, t2, "|\b^"); 1612 1613 /* 1614 * Choose between using "ll" and "cm" ("lower left" and "cursor move") 1615 * to move the cursor to the lower left corner of the screen. 1616 */ 1617 t1 = ltgetstr("ll", "ll", &sp); 1618 if (t1 == NULL || !full_screen) 1619 t1 = ""; 1620 if (*sc_move == '\0') 1621 t2 = ""; 1622 else 1623 { 1624 strcpy(sp, ltgoto(sc_move, 0, sc_height-1)); 1625 t2 = sp; 1626 sp += strlen(sp) + 1; 1627 } 1628 sc_lower_left = cheaper(t1, t2, "\r"); 1629 1630 /* 1631 * Get carriage return string. 1632 */ 1633 sc_return = ltgetstr("cr", "cr", &sp); 1634 if (sc_return == NULL) 1635 sc_return = "\r"; 1636 1637 /* 1638 * Choose between using "al" or "sr" ("add line" or "scroll reverse") 1639 * to add a line at the top of the screen. 1640 */ 1641 t1 = ltgetstr("ill", "al", &sp); 1642 if (t1 == NULL) 1643 t1 = ""; 1644 t2 = ltgetstr("ri", "sr", &sp); 1645 if (t2 == NULL) 1646 t2 = ""; 1647 if (*t1 == '\0' && *t2 == '\0') 1648 sc_addline = ""; 1649 else if (above_mem) 1650 sc_addline = t1; 1651 else 1652 sc_addline = cheaper(t1, t2, ""); 1653 if (*sc_addline == '\0') 1654 { 1655 /* 1656 * Force repaint on any backward movement. 1657 */ 1658 no_back_scroll = 1; 1659 } 1660 } 1661 #endif /* MSDOS_COMPILER */ 1662 } 1663 1664 #if !MSDOS_COMPILER 1665 /* 1666 * Return the cost of displaying a termcap string. 1667 * We use the trick of calling tputs, but as a char printing function 1668 * we give it inc_costcount, which just increments "costcount". 1669 * This tells us how many chars would be printed by using this string. 1670 */ 1671 static int costcount; 1672 1673 /*ARGSUSED*/ 1674 static int inc_costcount(int c) 1675 { 1676 costcount++; 1677 return (c); 1678 } 1679 1680 static int cost(constant char *t) 1681 { 1682 costcount = 0; 1683 tputs(t, sc_height, inc_costcount); 1684 return (costcount); 1685 } 1686 1687 /* 1688 * Return the "best" of the two given termcap strings. 1689 * The best, if both exist, is the one with the lower 1690 * cost (see cost() function). 1691 */ 1692 static constant char * cheaper(constant char *t1, constant char *t2, constant char *def) 1693 { 1694 if (*t1 == '\0' && *t2 == '\0') 1695 { 1696 missing_cap = TRUE; 1697 return (def); 1698 } 1699 if (*t1 == '\0') 1700 return (t2); 1701 if (*t2 == '\0') 1702 return (t1); 1703 if (cost(t1) < cost(t2)) 1704 return (t1); 1705 return (t2); 1706 } 1707 1708 static void tmodes(constant char *inti, constant char *outti, constant char *intc, constant char *outtc, constant char **instr, constant char **outstr, constant char *def_instr, constant char *def_outstr, char **spp) 1709 { 1710 *instr = ltgetstr(inti, intc, spp); 1711 if (*instr == NULL) 1712 { 1713 /* Use defaults. */ 1714 *instr = def_instr; 1715 *outstr = def_outstr; 1716 return; 1717 } 1718 1719 *outstr = ltgetstr(outti, outtc, spp); 1720 if (*outstr == NULL) 1721 /* No specific out capability; use "me". */ 1722 *outstr = ltgetstr("sgr0", "me", spp); 1723 if (*outstr == NULL) 1724 /* Don't even have "me"; use a null string. */ 1725 *outstr = ""; 1726 } 1727 1728 #endif /* MSDOS_COMPILER */ 1729 1730 1731 /* 1732 * Below are the functions which perform all the 1733 * terminal-specific screen manipulation. 1734 */ 1735 1736 1737 #if MSDOS_COMPILER 1738 1739 #if MSDOS_COMPILER==WIN32C 1740 static void _settextposition(int row, int col) 1741 { 1742 COORD cpos; 1743 CONSOLE_SCREEN_BUFFER_INFO csbi; 1744 1745 GetConsoleScreenBufferInfo(con_out, &csbi); 1746 cpos.X = csbi.srWindow.Left + (col - 1); 1747 cpos.Y = csbi.srWindow.Top + (row - 1); 1748 SetConsoleCursorPosition(con_out, cpos); 1749 } 1750 #endif 1751 1752 /* 1753 * Initialize the screen to the correct color at startup. 1754 */ 1755 static void initcolor(void) 1756 { 1757 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1758 intensevideo(); 1759 #endif 1760 SETCOLORS(nm_fg_color, nm_bg_color); 1761 #if 0 1762 /* 1763 * This clears the screen at startup. This is different from 1764 * the behavior of other versions of less. Disable it for now. 1765 */ 1766 char *blanks; 1767 int row; 1768 int col; 1769 1770 /* 1771 * Create a complete, blank screen using "normal" colors. 1772 */ 1773 SETCOLORS(nm_fg_color, nm_bg_color); 1774 blanks = (char *) ecalloc(width+1, sizeof(char)); 1775 for (col = 0; col < sc_width; col++) 1776 blanks[col] = ' '; 1777 blanks[sc_width] = '\0'; 1778 for (row = 0; row < sc_height; row++) 1779 _outtext(blanks); 1780 free(blanks); 1781 #endif 1782 } 1783 #endif 1784 1785 #if MSDOS_COMPILER==WIN32C 1786 1787 /* 1788 * Enable virtual terminal processing, if available. 1789 */ 1790 static void win32_init_vt_term(void) 1791 { 1792 if (vt_enabled == 0 || (vt_enabled == 1 && con_out == con_out_ours)) 1793 return; /* already initialized */ 1794 1795 /* don't care about the initial mode, and win VT hard-enables am+xn */ 1796 vt_enabled = SetConsoleMode(con_out, ENABLE_PROCESSED_OUTPUT | 1797 ENABLE_VIRTUAL_TERMINAL_PROCESSING | 1798 ENABLE_WRAP_AT_EOL_OUTPUT); 1799 if (vt_enabled) 1800 { 1801 auto_wrap = 1; 1802 defer_wrap = 1; 1803 } 1804 } 1805 1806 static void win32_deinit_vt_term(void) 1807 { 1808 if (vt_enabled == 1 && con_out == con_out_save) 1809 SetConsoleMode(con_out, init_console_output_mode); 1810 } 1811 1812 /* 1813 * Termcap-like init with a private win32 console. 1814 */ 1815 static void win32_init_term(void) 1816 { 1817 CONSOLE_SCREEN_BUFFER_INFO scr; 1818 COORD size; 1819 1820 if (con_out_save == INVALID_HANDLE_VALUE) 1821 return; 1822 1823 GetConsoleScreenBufferInfo(con_out_save, &scr); 1824 1825 if (con_out_ours == INVALID_HANDLE_VALUE) 1826 { 1827 /* 1828 * Create our own screen buffer, so that we 1829 * may restore the original when done. 1830 */ 1831 con_out_ours = CreateConsoleScreenBuffer( 1832 GENERIC_WRITE | GENERIC_READ, 1833 FILE_SHARE_WRITE | FILE_SHARE_READ, 1834 (LPSECURITY_ATTRIBUTES) NULL, 1835 CONSOLE_TEXTMODE_BUFFER, 1836 (LPVOID) NULL); 1837 1838 /* 1839 * We don't care about the initial state. We need processed 1840 * output without anything else (no wrap at EOL, no VT, 1841 * no disabled auto-return). 1842 */ 1843 if (SetConsoleMode(con_out_ours, ENABLE_PROCESSED_OUTPUT)) 1844 auto_wrap = 0; 1845 } 1846 1847 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1848 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1849 SetConsoleScreenBufferSize(con_out_ours, size); 1850 SetConsoleActiveScreenBuffer(con_out_ours); 1851 con_out = con_out_ours; 1852 } 1853 1854 /* 1855 * Restore the startup console. 1856 */ 1857 static void win32_deinit_term(void) 1858 { 1859 if (con_out_save == INVALID_HANDLE_VALUE) 1860 return; 1861 if (quitting) 1862 (void) CloseHandle(con_out_ours); 1863 SetConsoleActiveScreenBuffer(con_out_save); 1864 con_out = con_out_save; 1865 } 1866 1867 #endif 1868 1869 #if !MSDOS_COMPILER 1870 static void do_tputs(constant char *str, int affcnt, int (*f_putc)(int)) 1871 { 1872 #if LESSTEST 1873 if (is_lesstest() && f_putc == putchr) 1874 putstr(str); 1875 else 1876 #endif /*LESSTEST*/ 1877 tputs(str, affcnt, f_putc); 1878 } 1879 1880 /* 1881 * Like tputs but we handle $<...> delay strings here because 1882 * some implementations of tputs don't perform delays correctly. 1883 */ 1884 static void ltputs(constant char *str, int affcnt, int (*f_putc)(int)) 1885 { 1886 while (str != NULL && *str != '\0') 1887 { 1888 constant char *obrac = strstr(str, "$<"); 1889 if (obrac != NULL) 1890 { 1891 char str2[64]; 1892 size_t slen = ptr_diff(obrac, str); 1893 if (slen < sizeof(str2)) 1894 { 1895 int delay; 1896 /* Output first part of string (before "$<"). */ 1897 memcpy(str2, str, slen); 1898 str2[slen] = '\0'; 1899 do_tputs(str2, affcnt, f_putc); 1900 str += slen + 2; 1901 /* Perform the delay. */ 1902 delay = lstrtoic(str, &str, 10); 1903 if (*str == '*') 1904 if (ckd_mul(&delay, delay, affcnt)) 1905 delay = INT_MAX; 1906 flush(); 1907 sleep_ms(delay); 1908 /* Skip past closing ">" at end of delay string. */ 1909 str = strstr(str, ">"); 1910 if (str != NULL) 1911 str++; 1912 continue; 1913 } 1914 } 1915 /* Pass the rest of the string to tputs and we're done. */ 1916 do_tputs(str, affcnt, f_putc); 1917 break; 1918 } 1919 } 1920 #endif /* MSDOS_COMPILER */ 1921 1922 /* 1923 * Configure the terminal so mouse clicks and wheel moves 1924 * produce input to less. 1925 */ 1926 public void init_mouse(void) 1927 { 1928 #if !MSDOS_COMPILER 1929 ltputs(sc_s_mousecap, sc_height, putchr); 1930 #else 1931 #if MSDOS_COMPILER==WIN32C 1932 curr_console_input_mode = mouse_console_input_mode; 1933 SetConsoleMode(tty, curr_console_input_mode); 1934 #endif 1935 #endif 1936 } 1937 1938 /* 1939 * Configure the terminal so mouse clicks and wheel moves 1940 * are handled by the system (so text can be selected, etc). 1941 */ 1942 public void deinit_mouse(void) 1943 { 1944 #if !MSDOS_COMPILER 1945 ltputs(sc_e_mousecap, sc_height, putchr); 1946 #else 1947 #if MSDOS_COMPILER==WIN32C 1948 curr_console_input_mode = base_console_input_mode; 1949 SetConsoleMode(tty, curr_console_input_mode); 1950 #endif 1951 #endif 1952 } 1953 1954 /* 1955 * Suspend screen updates. 1956 */ 1957 public void suspend_screen(void) 1958 { 1959 #if !MSDOS_COMPILER 1960 ltputs(sc_suspend, 1, putchr); 1961 #endif 1962 } 1963 1964 /* 1965 * Resume screen updates. 1966 */ 1967 public void resume_screen(void) 1968 { 1969 #if !MSDOS_COMPILER 1970 ltputs(sc_resume, 1, putchr); 1971 #endif 1972 } 1973 1974 /* 1975 * Initialize terminal 1976 */ 1977 public void term_init(void) 1978 { 1979 if (term_init_done) 1980 return; 1981 term_init_done = term_init_ever = TRUE; 1982 clear_bot_if_needed(); 1983 #if !MSDOS_COMPILER 1984 if (!(quit_if_one_screen && one_screen)) 1985 { 1986 if (!no_init) 1987 { 1988 ltputs(sc_init, sc_height, putchr); 1989 /* 1990 * Some terminals leave the cursor unmoved when switching 1991 * to the alt screen. To avoid having the text appear at 1992 * a seemingly random line on the alt screen, move to 1993 * lower left if we are using an alt screen. 1994 */ 1995 if (*sc_init != '\0' && *sc_deinit != '\0' && !no_alt_screen) 1996 lower_left(); 1997 term_addrs = TRUE; 1998 } 1999 if (!no_keypad) 2000 ltputs(sc_s_keypad, sc_height, putchr); 2001 if (mousecap) 2002 init_mouse(); 2003 if (no_paste) 2004 init_bracketed_paste(); 2005 } 2006 if (top_scroll) 2007 { 2008 int i; 2009 2010 /* 2011 * This is nice to terminals with no alternate screen, 2012 * but with saved scrolled-off-the-top lines. This way, 2013 * no previous line is lost, but we start with a whole 2014 * screen to ourself. 2015 */ 2016 for (i = 1; i < sc_height; i++) 2017 putchr('\n'); 2018 } else 2019 line_left(); 2020 #else 2021 #if MSDOS_COMPILER==WIN32C 2022 if (!(quit_if_one_screen && one_screen)) 2023 { 2024 if (!no_init) 2025 { 2026 win32_init_term(); 2027 term_addrs = TRUE; 2028 } 2029 if (mousecap) 2030 init_mouse(); 2031 2032 } 2033 win32_init_vt_term(); 2034 #endif 2035 initcolor(); 2036 flush(); 2037 #endif 2038 } 2039 2040 /* 2041 * Deinitialize terminal 2042 */ 2043 public void term_deinit(void) 2044 { 2045 if (!term_init_done) 2046 return; 2047 #if !MSDOS_COMPILER 2048 if (!(quit_if_one_screen && one_screen)) 2049 { 2050 if (mousecap) 2051 deinit_mouse(); 2052 if (no_paste) 2053 deinit_bracketed_paste(); 2054 if (!no_keypad) 2055 ltputs(sc_e_keypad, sc_height, putchr); 2056 if (!no_init) 2057 ltputs(sc_deinit, sc_height, putchr); 2058 } 2059 #else 2060 /* Restore system colors. */ 2061 SETCOLORS(sy_fg_color, sy_bg_color); 2062 #if MSDOS_COMPILER==WIN32C 2063 win32_deinit_vt_term(); 2064 if (!(quit_if_one_screen && one_screen)) 2065 { 2066 if (mousecap) 2067 deinit_mouse(); 2068 if (!no_init) 2069 win32_deinit_term(); 2070 } 2071 #else 2072 /* Need clreol to make SETCOLORS take effect. */ 2073 clreol(); 2074 #endif 2075 #endif 2076 term_init_done = FALSE; 2077 } 2078 2079 /* 2080 * Are we interactive (ie. writing to an initialized tty)? 2081 */ 2082 public lbool interactive(void) 2083 { 2084 return (is_tty && term_init_done); 2085 } 2086 2087 static void assert_interactive(void) 2088 { 2089 } 2090 2091 /* 2092 * Home cursor (move to upper left corner of screen). 2093 */ 2094 public void home(void) 2095 { 2096 assert_interactive(); 2097 #if !MSDOS_COMPILER 2098 ltputs(sc_home, 1, putchr); 2099 #else 2100 flush(); 2101 _settextposition(1,1); 2102 #endif 2103 } 2104 2105 #if LESSTEST 2106 public void dump_screen(void) 2107 { 2108 char dump_cmd[32]; 2109 SNPRINTF1(dump_cmd, sizeof(dump_cmd), ESCS"0;0;%dR", sc_width * sc_height); 2110 ltputs(dump_cmd, sc_height, putchr); 2111 flush(); 2112 } 2113 #endif /*LESSTEST*/ 2114 2115 /* 2116 * Add a blank line (called with cursor at home). 2117 * Should scroll the display down. 2118 */ 2119 public void add_line(void) 2120 { 2121 assert_interactive(); 2122 #if !MSDOS_COMPILER 2123 ltputs(sc_addline, sc_height, putchr); 2124 #else 2125 flush(); 2126 #if MSDOS_COMPILER==MSOFTC 2127 _scrolltextwindow(_GSCROLLDOWN); 2128 _settextposition(1,1); 2129 #else 2130 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2131 movetext(1,1, sc_width,sc_height-1, 1,2); 2132 gotoxy(1,1); 2133 clreol(); 2134 #else 2135 #if MSDOS_COMPILER==WIN32C 2136 { 2137 CHAR_INFO fillchar; 2138 SMALL_RECT rcSrc, rcClip; 2139 COORD new_org; 2140 CONSOLE_SCREEN_BUFFER_INFO csbi; 2141 2142 GetConsoleScreenBufferInfo(con_out,&csbi); 2143 2144 /* The clip rectangle is the entire visible screen. */ 2145 rcClip.Left = csbi.srWindow.Left; 2146 rcClip.Top = csbi.srWindow.Top; 2147 rcClip.Right = csbi.srWindow.Right; 2148 rcClip.Bottom = csbi.srWindow.Bottom; 2149 2150 /* The source rectangle is the visible screen minus the last line. */ 2151 rcSrc = rcClip; 2152 rcSrc.Bottom--; 2153 2154 /* Move the top left corner of the source window down one row. */ 2155 new_org.X = rcSrc.Left; 2156 new_org.Y = rcSrc.Top + 1; 2157 2158 /* Fill the right character and attributes. */ 2159 fillchar.Char.AsciiChar = ' '; 2160 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2161 fillchar.Attributes = curr_attr; 2162 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 2163 _settextposition(1,1); 2164 } 2165 #endif 2166 #endif 2167 #endif 2168 #endif 2169 } 2170 2171 #if 0 2172 /* 2173 * Remove the n topmost lines and scroll everything below it in the 2174 * window upward. This is needed to stop leaking the topmost line 2175 * into the scrollback buffer when we go down-one-line (in WIN32). 2176 */ 2177 public void remove_top(int n) 2178 { 2179 #if MSDOS_COMPILER==WIN32C 2180 SMALL_RECT rcSrc, rcClip; 2181 CHAR_INFO fillchar; 2182 COORD new_org; 2183 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 2184 2185 if (n >= sc_height - 1) 2186 { 2187 lclear(); 2188 home(); 2189 return; 2190 } 2191 2192 flush(); 2193 2194 GetConsoleScreenBufferInfo(con_out, &csbi); 2195 2196 /* Get the extent of all-visible-rows-but-the-last. */ 2197 rcSrc.Left = csbi.srWindow.Left; 2198 rcSrc.Top = csbi.srWindow.Top + n; 2199 rcSrc.Right = csbi.srWindow.Right; 2200 rcSrc.Bottom = csbi.srWindow.Bottom; 2201 2202 /* Get the clip rectangle. */ 2203 rcClip.Left = rcSrc.Left; 2204 rcClip.Top = csbi.srWindow.Top; 2205 rcClip.Right = rcSrc.Right; 2206 rcClip.Bottom = rcSrc.Bottom ; 2207 2208 /* Move the source window up n rows. */ 2209 new_org.X = rcSrc.Left; 2210 new_org.Y = rcSrc.Top - n; 2211 2212 /* Fill the right character and attributes. */ 2213 fillchar.Char.AsciiChar = ' '; 2214 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2215 fillchar.Attributes = curr_attr; 2216 2217 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 2218 2219 /* Position cursor on first blank line. */ 2220 goto_line(sc_height - n - 1); 2221 #endif 2222 } 2223 #endif 2224 2225 #if MSDOS_COMPILER==WIN32C 2226 /* 2227 * Clear the screen. 2228 */ 2229 static void win32_clear(void) 2230 { 2231 /* 2232 * This will clear only the currently visible rows of the NT 2233 * console buffer, which means none of the precious scrollback 2234 * rows are touched making for faster scrolling. Note that, if 2235 * the window has fewer columns than the console buffer (i.e. 2236 * there is a horizontal scrollbar as well), the entire width 2237 * of the visible rows will be cleared. 2238 */ 2239 COORD topleft; 2240 DWORD nchars; 2241 DWORD winsz; 2242 CONSOLE_SCREEN_BUFFER_INFO csbi; 2243 2244 /* get the number of cells in the current buffer */ 2245 GetConsoleScreenBufferInfo(con_out, &csbi); 2246 winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 2247 topleft.X = 0; 2248 topleft.Y = csbi.srWindow.Top; 2249 2250 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2251 FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); 2252 FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); 2253 } 2254 2255 /* 2256 * Remove the n topmost lines and scroll everything below it in the 2257 * window upward. 2258 */ 2259 public void win32_scroll_up(int n) 2260 { 2261 SMALL_RECT rcSrc, rcClip; 2262 CHAR_INFO fillchar; 2263 COORD topleft; 2264 COORD new_org; 2265 DWORD nchars; 2266 DWORD size; 2267 CONSOLE_SCREEN_BUFFER_INFO csbi; 2268 2269 if (n <= 0) 2270 return; 2271 2272 if (n >= sc_height - 1) 2273 { 2274 win32_clear(); 2275 _settextposition(1,1); 2276 return; 2277 } 2278 2279 /* Get the extent of what will remain visible after scrolling. */ 2280 GetConsoleScreenBufferInfo(con_out, &csbi); 2281 rcSrc.Left = csbi.srWindow.Left; 2282 rcSrc.Top = csbi.srWindow.Top + n; 2283 rcSrc.Right = csbi.srWindow.Right; 2284 rcSrc.Bottom = csbi.srWindow.Bottom; 2285 2286 /* Get the clip rectangle. */ 2287 rcClip.Left = rcSrc.Left; 2288 rcClip.Top = csbi.srWindow.Top; 2289 rcClip.Right = rcSrc.Right; 2290 rcClip.Bottom = rcSrc.Bottom ; 2291 2292 /* Move the source text to the top of the screen. */ 2293 new_org.X = rcSrc.Left; 2294 new_org.Y = rcClip.Top; 2295 2296 /* Fill the right character and attributes. */ 2297 fillchar.Char.AsciiChar = ' '; 2298 fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); 2299 2300 /* Scroll the window. */ 2301 SetConsoleTextAttribute(con_out, fillchar.Attributes); 2302 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 2303 2304 /* Clear remaining lines at bottom. */ 2305 topleft.X = csbi.dwCursorPosition.X; 2306 topleft.Y = rcSrc.Bottom - n; 2307 size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); 2308 FillConsoleOutputCharacter(con_out, ' ', size, topleft, 2309 &nchars); 2310 FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, 2311 &nchars); 2312 SetConsoleTextAttribute(con_out, curr_attr); 2313 2314 /* Move cursor n lines up from where it was. */ 2315 csbi.dwCursorPosition.Y -= n; 2316 SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); 2317 } 2318 #endif 2319 2320 /* 2321 * Move cursor to lower left corner of screen. 2322 */ 2323 public void lower_left(void) 2324 { 2325 assert_interactive(); 2326 #if !MSDOS_COMPILER 2327 ltputs(sc_lower_left, 1, putchr); 2328 #else 2329 flush(); 2330 _settextposition(sc_height, 1); 2331 #endif 2332 } 2333 2334 /* 2335 * Move cursor to left position of current line. 2336 */ 2337 public void line_left(void) 2338 { 2339 assert_interactive(); 2340 #if !MSDOS_COMPILER 2341 ltputs(sc_return, 1, putchr); 2342 #else 2343 { 2344 int row; 2345 flush(); 2346 #if MSDOS_COMPILER==WIN32C 2347 { 2348 CONSOLE_SCREEN_BUFFER_INFO scr; 2349 GetConsoleScreenBufferInfo(con_out, &scr); 2350 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2351 } 2352 #else 2353 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2354 row = wherey(); 2355 #else 2356 { 2357 struct rccoord tpos = _gettextposition(); 2358 row = tpos.row; 2359 } 2360 #endif 2361 #endif 2362 _settextposition(row, 1); 2363 } 2364 #endif 2365 } 2366 2367 /* 2368 * Check if the console size has changed and reset internals 2369 * (in lieu of SIGWINCH for WIN32). 2370 */ 2371 public void check_winch(void) 2372 { 2373 #if MSDOS_COMPILER==WIN32C 2374 CONSOLE_SCREEN_BUFFER_INFO scr; 2375 COORD size; 2376 2377 if (con_out == INVALID_HANDLE_VALUE) 2378 return; 2379 2380 flush(); 2381 GetConsoleScreenBufferInfo(con_out, &scr); 2382 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 2383 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 2384 if (size.Y != sc_height || size.X != sc_width) 2385 { 2386 sc_height = size.Y; 2387 sc_width = size.X; 2388 if (!no_init && con_out_ours == con_out) 2389 SetConsoleScreenBufferSize(con_out, size); 2390 pos_init(); 2391 screen_size_changed(); 2392 screen_trashed(); 2393 } 2394 #endif 2395 } 2396 2397 /* 2398 * Goto a specific line on the screen. 2399 */ 2400 public void goto_line(int sindex) 2401 { 2402 assert_interactive(); 2403 #if !MSDOS_COMPILER 2404 ltputs(ltgoto(sc_move, 0, sindex), 1, putchr); 2405 #else 2406 flush(); 2407 _settextposition(sindex+1, 1); 2408 #endif 2409 } 2410 2411 #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC 2412 /* 2413 * Create an alternate screen which is all white. 2414 * This screen is used to create a "flash" effect, by displaying it 2415 * briefly and then switching back to the normal screen. 2416 * {{ Yuck! There must be a better way to get a visual bell. }} 2417 */ 2418 static void create_flash(void) 2419 { 2420 #if MSDOS_COMPILER==MSOFTC 2421 struct videoconfig w; 2422 char *blanks; 2423 int row, col; 2424 2425 _getvideoconfig(&w); 2426 videopages = w.numvideopages; 2427 if (videopages < 2) 2428 { 2429 at_enter(AT_STANDOUT); 2430 at_exit(); 2431 } else 2432 { 2433 _setactivepage(1); 2434 at_enter(AT_STANDOUT); 2435 blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); 2436 for (col = 0; col < w.numtextcols; col++) 2437 blanks[col] = ' '; 2438 for (row = w.numtextrows; row > 0; row--) 2439 _outmem(blanks, w.numtextcols); 2440 _setactivepage(0); 2441 _setvisualpage(0); 2442 free(blanks); 2443 at_exit(); 2444 } 2445 #else 2446 #if MSDOS_COMPILER==BORLANDC 2447 int n; 2448 2449 whitescreen = (unsigned short *) 2450 malloc(sc_width * sc_height * sizeof(short)); 2451 if (whitescreen == NULL) 2452 return; 2453 for (n = 0; n < sc_width * sc_height; n++) 2454 whitescreen[n] = 0x7020; 2455 #endif 2456 #endif 2457 flash_created = 1; 2458 } 2459 #endif /* MSDOS_COMPILER */ 2460 2461 /* 2462 * Output the "visual bell", if there is one. 2463 */ 2464 public void vbell(void) 2465 { 2466 if (no_vbell) 2467 return; 2468 #if !MSDOS_COMPILER 2469 if (*sc_visual_bell == '\0') 2470 return; 2471 ltputs(sc_visual_bell, sc_height, putchr); 2472 #else 2473 #if MSDOS_COMPILER==DJGPPC 2474 ScreenVisualBell(); 2475 #else 2476 #if MSDOS_COMPILER==MSOFTC 2477 /* 2478 * Create a flash screen on the second video page. 2479 * Switch to that page, then switch back. 2480 */ 2481 if (!flash_created) 2482 create_flash(); 2483 if (videopages < 2) 2484 return; 2485 _setvisualpage(1); 2486 delay(100); 2487 _setvisualpage(0); 2488 #else 2489 #if MSDOS_COMPILER==BORLANDC 2490 unsigned short *currscreen; 2491 2492 /* 2493 * Get a copy of the current screen. 2494 * Display the flash screen. 2495 * Then restore the old screen. 2496 */ 2497 if (!flash_created) 2498 create_flash(); 2499 if (whitescreen == NULL) 2500 return; 2501 currscreen = (unsigned short *) 2502 malloc(sc_width * sc_height * sizeof(short)); 2503 if (currscreen == NULL) return; 2504 gettext(1, 1, sc_width, sc_height, currscreen); 2505 puttext(1, 1, sc_width, sc_height, whitescreen); 2506 delay(100); 2507 puttext(1, 1, sc_width, sc_height, currscreen); 2508 free(currscreen); 2509 #else 2510 #if MSDOS_COMPILER==WIN32C 2511 /* paint screen with an inverse color */ 2512 lclear(); 2513 2514 /* leave it displayed for 100 msec. */ 2515 Sleep(100); 2516 2517 /* restore with a redraw */ 2518 repaint(); 2519 #endif 2520 #endif 2521 #endif 2522 #endif 2523 #endif 2524 } 2525 2526 /* 2527 * Make a noise. 2528 */ 2529 static void lbeep(void) 2530 { 2531 #if !MSDOS_COMPILER 2532 putchr(CONTROL('G')); 2533 #else 2534 #if MSDOS_COMPILER==WIN32C 2535 MessageBeep(0); 2536 #else 2537 write(1, "\7", 1); 2538 #endif 2539 #endif 2540 } 2541 2542 /* 2543 * Ring the terminal bell. 2544 */ 2545 public void lbell(void) 2546 { 2547 if (quiet == VERY_QUIET) 2548 vbell(); 2549 else 2550 lbeep(); 2551 } 2552 2553 /* 2554 * Clear the screen. 2555 */ 2556 public void lclear(void) 2557 { 2558 assert_interactive(); 2559 suspend_screen(); 2560 #if !MSDOS_COMPILER 2561 ltputs(sc_clear, sc_height, putchr); 2562 #else 2563 flush(); 2564 #if MSDOS_COMPILER==WIN32C 2565 win32_clear(); 2566 #else 2567 _clearscreen(_GCLEARSCREEN); 2568 #endif 2569 #endif 2570 } 2571 2572 /* 2573 * Clear from the cursor to the end of the cursor's line. 2574 * {{ This must not move the cursor. }} 2575 */ 2576 public void clear_eol(void) 2577 { 2578 /* assert_interactive();*/ 2579 #if !MSDOS_COMPILER 2580 ltputs(sc_eol_clear, 1, putchr); 2581 #else 2582 #if MSDOS_COMPILER==MSOFTC 2583 short top, left; 2584 short bot, right; 2585 struct rccoord tpos; 2586 2587 flush(); 2588 /* 2589 * Save current state. 2590 */ 2591 tpos = _gettextposition(); 2592 _gettextwindow(&top, &left, &bot, &right); 2593 /* 2594 * Set a temporary window to the current line, 2595 * from the cursor's position to the right edge of the screen. 2596 * Then clear that window. 2597 */ 2598 _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); 2599 _clearscreen(_GWINDOW); 2600 /* 2601 * Restore state. 2602 */ 2603 _settextwindow(top, left, bot, right); 2604 _settextposition(tpos.row, tpos.col); 2605 #else 2606 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2607 flush(); 2608 clreol(); 2609 #else 2610 #if MSDOS_COMPILER==WIN32C 2611 DWORD nchars; 2612 COORD cpos; 2613 CONSOLE_SCREEN_BUFFER_INFO scr; 2614 2615 flush(); 2616 memset(&scr, 0, sizeof(scr)); 2617 GetConsoleScreenBufferInfo(con_out, &scr); 2618 cpos.X = scr.dwCursorPosition.X; 2619 cpos.Y = scr.dwCursorPosition.Y; 2620 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2621 FillConsoleOutputAttribute(con_out, curr_attr, 2622 scr.dwSize.X - cpos.X, cpos, &nchars); 2623 FillConsoleOutputCharacter(con_out, ' ', 2624 scr.dwSize.X - cpos.X, cpos, &nchars); 2625 #endif 2626 #endif 2627 #endif 2628 #endif 2629 } 2630 2631 /* 2632 * Clear the current line. 2633 * Clear the screen if there's off-screen memory below the display. 2634 */ 2635 static void clear_eol_bot(void) 2636 { 2637 assert_interactive(); 2638 #if MSDOS_COMPILER 2639 clear_eol(); 2640 #else 2641 if (below_mem) 2642 ltputs(sc_eos_clear, 1, putchr); 2643 else 2644 ltputs(sc_eol_clear, 1, putchr); 2645 #endif 2646 } 2647 2648 /* 2649 * Clear the bottom line of the display. 2650 * Leave the cursor at the beginning of the bottom line. 2651 */ 2652 public void clear_bot(void) 2653 { 2654 /* 2655 * If we're in a non-normal attribute mode, temporarily exit 2656 * the mode while we do the clear. Some terminals fill the 2657 * cleared area with the current attribute. 2658 */ 2659 if (oldbot) 2660 lower_left(); 2661 else 2662 line_left(); 2663 2664 if (attrmode == AT_NORMAL) 2665 clear_eol_bot(); 2666 else 2667 { 2668 int saved_attrmode = attrmode; 2669 2670 at_exit(); 2671 clear_eol_bot(); 2672 at_enter(saved_attrmode); 2673 } 2674 } 2675 2676 /* 2677 * Enable or disable bracketed paste mode. 2678 * When enabled, the terminal sends an "open bracket" sequence 2679 * before pasted content and "close bracket" after it. 2680 */ 2681 public void init_bracketed_paste(void) 2682 { 2683 #if !MSDOS_COMPILER 2684 ltputs(sc_s_bracketed_paste, 1, putchr); 2685 #endif 2686 } 2687 2688 public void deinit_bracketed_paste(void) 2689 { 2690 #if !MSDOS_COMPILER 2691 ltputs(sc_e_bracketed_paste, 1, putchr); 2692 #endif 2693 } 2694 2695 /* 2696 * Color string may be "x[y]" where x and y are 4-bit color chars, 2697 * or "N[.M]" where N and M are decimal integers> 2698 * Any of x,y,N,M may also be "-" to mean "unchanged". 2699 */ 2700 2701 /* 2702 * Parse a 4-bit color char. 2703 */ 2704 static int parse_color4(char ch) 2705 { 2706 switch (ch) 2707 { 2708 case 'k': return 0; 2709 case 'r': return CV_RED; 2710 case 'g': return CV_GREEN; 2711 case 'y': return CV_RED|CV_GREEN; 2712 case 'b': return CV_BLUE; 2713 case 'm': return CV_RED|CV_BLUE; 2714 case 'c': return CV_GREEN|CV_BLUE; 2715 case 'w': return CV_RED|CV_GREEN|CV_BLUE; 2716 case 'K': return 0|CV_BRIGHT; 2717 case 'R': return CV_RED|CV_BRIGHT; 2718 case 'G': return CV_GREEN|CV_BRIGHT; 2719 case 'Y': return CV_RED|CV_GREEN|CV_BRIGHT; 2720 case 'B': return CV_BLUE|CV_BRIGHT; 2721 case 'M': return CV_RED|CV_BLUE|CV_BRIGHT; 2722 case 'C': return CV_GREEN|CV_BLUE|CV_BRIGHT; 2723 case 'W': return CV_RED|CV_GREEN|CV_BLUE|CV_BRIGHT; 2724 case '-': return CV_NOCHANGE; 2725 default: return CV_ERROR; 2726 } 2727 } 2728 2729 /* 2730 * Parse a color as a decimal integer. 2731 */ 2732 static int parse_color6(constant char **ps) 2733 { 2734 if (**ps == '-') 2735 { 2736 (*ps)++; 2737 return CV_NOCHANGE; 2738 } else 2739 { 2740 constant char *os = *ps; 2741 int color = lstrtoic(os, ps, 10); 2742 if (color < 0 || *ps == os) 2743 return CV_ERROR; 2744 return color; 2745 } 2746 } 2747 2748 /* 2749 * Parse a color pair and return the foreground/background/attribute values. 2750 * Return type of color specifier: 2751 * CV_4BIT: fg/bg values are OR of CV_{RGB} bits. 2752 * CV_6BIT: fg/bg values are integers entered by user. 2753 */ 2754 public COLOR_TYPE parse_color(constant char *str, mutable int *p_fg, mutable int *p_bg, mutable CHAR_ATTR *p_cattr) 2755 { 2756 int fg; 2757 int bg = CV_ERROR; 2758 CHAR_ATTR cattr = CATTR_NULL; 2759 COLOR_TYPE type = CT_NULL; 2760 2761 if (str == NULL || *str == '\0') 2762 return CT_NULL; 2763 if (*str == '+') 2764 str++; /* ignore leading + */ 2765 2766 fg = parse_color4(*str); 2767 if (fg != CV_ERROR) 2768 { 2769 if (str[1] == '\0' || strchr("*~_&dsul", str[1]) != NULL) 2770 { 2771 bg = CV_NOCHANGE; 2772 str++; /* skip the fg char */ 2773 } else 2774 { 2775 bg = parse_color4(str[1]); 2776 if (bg != CV_ERROR) 2777 str += 2; /* skip both fg and bg chars */ 2778 } 2779 } 2780 if (fg != CV_ERROR && bg != CV_ERROR) 2781 type = CT_4BIT; 2782 else 2783 { 2784 fg = (*str == '.') ? CV_NOCHANGE : parse_color6(&str); 2785 if (fg != CV_ERROR) 2786 { 2787 if (*str != '.') 2788 bg = CV_NOCHANGE; 2789 else 2790 { 2791 str++; /* skip the dot */ 2792 bg = parse_color6(&str); 2793 } 2794 } 2795 if (fg != CV_ERROR && bg != CV_ERROR) 2796 type = CT_6BIT; 2797 } 2798 if (type != CT_NULL) 2799 { 2800 for (;; str++) 2801 { 2802 if (*str == '*' || *str == 'd') 2803 cattr |= CATTR_BOLD; 2804 else if (*str == '~' || *str == 's') 2805 cattr |= CATTR_STANDOUT; 2806 else if (*str == '_' || *str == 'u') 2807 cattr |= CATTR_UNDERLINE; 2808 else if (*str == '&' || *str == 'l') /* can't use 'k' because of conflict with "black" */ 2809 cattr |= CATTR_BLINK; 2810 else 2811 break; 2812 } 2813 if (p_fg != NULL) *p_fg = fg; 2814 if (p_bg != NULL) *p_bg = bg; 2815 if (p_cattr != NULL) *p_cattr = cattr; 2816 } 2817 return type; 2818 } 2819 2820 #if !MSDOS_COMPILER 2821 2822 static int sgr_color(int color) 2823 { 2824 switch (color) 2825 { 2826 case 0: return 30; 2827 case CV_RED: return 31; 2828 case CV_GREEN: return 32; 2829 case CV_RED|CV_GREEN: return 33; 2830 case CV_BLUE: return 34; 2831 case CV_RED|CV_BLUE: return 35; 2832 case CV_GREEN|CV_BLUE: return 36; 2833 case CV_RED|CV_GREEN|CV_BLUE: return 37; 2834 2835 case CV_BRIGHT: return 90; 2836 case CV_RED|CV_BRIGHT: return 91; 2837 case CV_GREEN|CV_BRIGHT: return 92; 2838 case CV_RED|CV_GREEN|CV_BRIGHT: return 93; 2839 case CV_BLUE|CV_BRIGHT: return 94; 2840 case CV_RED|CV_BLUE|CV_BRIGHT: return 95; 2841 case CV_GREEN|CV_BLUE|CV_BRIGHT: return 96; 2842 case CV_RED|CV_GREEN|CV_BLUE|CV_BRIGHT: return 97; 2843 2844 default: return color; 2845 } 2846 } 2847 2848 static void tput_fmt(constant char *fmt, int color, int (*f_putc)(int)) 2849 { 2850 char buf[INT_STRLEN_BOUND(int)+16]; 2851 if (color == attrcolor) 2852 return; 2853 SNPRINTF1(buf, sizeof(buf), fmt, color); 2854 ltputs(buf, 1, f_putc); 2855 attrcolor = color; 2856 } 2857 2858 static void tput_char_cattr(CHAR_ATTR cattr, int (*f_putc)(int)) 2859 { 2860 if (cattr & CATTR_UNDERLINE) 2861 ltputs(sc_u_in, 1, f_putc); 2862 if (cattr & CATTR_BOLD) 2863 ltputs(sc_b_in, 1, f_putc); 2864 if (cattr & CATTR_BLINK) 2865 ltputs(sc_bl_in, 1, f_putc); 2866 if (cattr & CATTR_STANDOUT) 2867 ltputs(sc_s_in, 1, f_putc); 2868 } 2869 2870 static void tput_color(constant char *str, int (*f_putc)(int)) 2871 { 2872 int fg; 2873 int bg; 2874 CHAR_ATTR cattr; 2875 2876 if (str != NULL && strcmp(str, "*") == 0) 2877 { 2878 /* Special case: reset to normal */ 2879 tput_fmt(ESCS"[m", -1, f_putc); 2880 return; 2881 } 2882 switch (parse_color(str, &fg, &bg, &cattr)) 2883 { 2884 case CT_4BIT: 2885 if (fg >= 0) 2886 tput_fmt(ESCS"[%dm", sgr_color(fg), f_putc); 2887 if (bg >= 0) 2888 tput_fmt(ESCS"[%dm", sgr_color(bg)+10, f_putc); 2889 tput_char_cattr(cattr, f_putc); 2890 break; 2891 case CT_6BIT: 2892 if (fg >= 0) 2893 tput_fmt(ESCS"[38;5;%dm", fg, f_putc); 2894 if (bg >= 0) 2895 tput_fmt(ESCS"[48;5;%dm", bg, f_putc); 2896 tput_char_cattr(cattr, f_putc); 2897 break; 2898 default: 2899 break; 2900 } 2901 } 2902 2903 static void tput_inmode(constant char *mode_str, int attr, int attr_bit, int (*f_putc)(int)) 2904 { 2905 constant char *color_str; 2906 if ((attr & attr_bit) == 0) 2907 return; 2908 color_str = get_color_map(attr_bit); 2909 if (color_str == NULL || *color_str == '\0' || *color_str == '+') 2910 { 2911 ltputs(mode_str, 1, f_putc); 2912 if (color_str == NULL || *color_str++ != '+') 2913 return; 2914 } 2915 /* Color overrides mode string */ 2916 tput_color(color_str, f_putc); 2917 } 2918 2919 static void tput_outmode(constant char *mode_str, int attr_bit, int (*f_putc)(int)) 2920 { 2921 if ((attrmode & attr_bit) == 0) 2922 return; 2923 ltputs(mode_str, 1, f_putc); 2924 } 2925 2926 #else /* MSDOS_COMPILER */ 2927 2928 #if MSDOS_COMPILER==WIN32C 2929 static lbool WIN32put_fmt(constant char *fmt, int color) 2930 { 2931 char buf[INT_STRLEN_BOUND(int)+16]; 2932 int len = (size_t) SNPRINTF1(buf, sizeof(buf), fmt, color); 2933 if (len > 0) 2934 WIN32textout(buf, (size_t) len); 2935 return TRUE; 2936 } 2937 2938 static void win_set_cattr(CHAR_ATTR cattr) 2939 { 2940 if (cattr & CATTR_UNDERLINE) 2941 WIN32textout(ESCS"[4m", 4); 2942 if (cattr & CATTR_BOLD) 2943 WIN32textout(ESCS"[1m", 4); 2944 if (cattr & CATTR_BLINK) 2945 WIN32textout(ESCS"[5m", 4); 2946 if (cattr & CATTR_STANDOUT) 2947 WIN32textout(ESCS"[7m", 4); 2948 } 2949 #endif 2950 2951 static lbool win_set_color(int attr) 2952 { 2953 int fg; 2954 int bg; 2955 CHAR_ATTR cattr; 2956 lbool out = FALSE; 2957 constant char *str = get_color_map(attr); 2958 if (str == NULL || str[0] == '\0') 2959 return FALSE; 2960 switch (parse_color(str, &fg, &bg, &cattr)) 2961 { 2962 case CT_4BIT: 2963 if (fg >= 0 && bg >= 0) 2964 { 2965 SETCOLORS(fg, bg); 2966 out = TRUE; 2967 } else if (fg >= 0) 2968 { 2969 SET_FG_COLOR(fg); 2970 out = TRUE; 2971 } else if (bg >= 0) 2972 { 2973 SET_BG_COLOR(bg); 2974 out = TRUE; 2975 } 2976 #if MSDOS_COMPILER==WIN32C 2977 if (vt_enabled) 2978 win_set_cattr(cattr); 2979 #endif 2980 break; 2981 #if MSDOS_COMPILER==WIN32C 2982 case CT_6BIT: 2983 if (vt_enabled) 2984 { 2985 if (fg > 0) 2986 out = WIN32put_fmt(ESCS"[38;5;%dm", fg); 2987 if (bg > 0) 2988 out = WIN32put_fmt(ESCS"[48;5;%dm", bg); 2989 win_set_cattr(cattr); 2990 } 2991 break; 2992 #endif 2993 default: 2994 break; 2995 } 2996 return out; 2997 } 2998 2999 #endif /* MSDOS_COMPILER */ 3000 3001 public void at_enter(int attr) 3002 { 3003 attr = apply_at_specials(attr); 3004 #if !MSDOS_COMPILER 3005 /* The one with the most priority is last. */ 3006 tput_inmode(sc_u_in, attr, AT_UNDERLINE, putchr); 3007 tput_inmode(sc_b_in, attr, AT_BOLD, putchr); 3008 tput_inmode(sc_bl_in, attr, AT_BLINK, putchr); 3009 /* Don't use standout and color at the same time. */ 3010 if (use_color && (attr & AT_COLOR)) 3011 tput_color(get_color_map(attr), putchr); 3012 else 3013 tput_inmode(sc_s_in, attr, AT_STANDOUT, putchr); 3014 #else 3015 flush(); 3016 /* The one with the most priority is first. */ 3017 if ((attr & AT_COLOR) && use_color) 3018 { 3019 win_set_color(attr); 3020 } else if (attr & AT_STANDOUT) 3021 { 3022 SETCOLORS(so_fg_color, so_bg_color); 3023 } else if (attr & AT_BLINK) 3024 { 3025 SETCOLORS(bl_fg_color, bl_bg_color); 3026 } else if (attr & AT_BOLD) 3027 { 3028 SETCOLORS(bo_fg_color, bo_bg_color); 3029 } else if (attr & AT_UNDERLINE) 3030 { 3031 SETCOLORS(ul_fg_color, ul_bg_color); 3032 } 3033 #endif 3034 attrmode = attr; 3035 } 3036 3037 public void at_exit(void) 3038 { 3039 #if !MSDOS_COMPILER 3040 /* Undo things in the reverse order we did them. */ 3041 tput_color("*", putchr); 3042 tput_outmode(sc_s_out, AT_STANDOUT, putchr); 3043 tput_outmode(sc_bl_out, AT_BLINK, putchr); 3044 tput_outmode(sc_b_out, AT_BOLD, putchr); 3045 tput_outmode(sc_u_out, AT_UNDERLINE, putchr); 3046 #else 3047 flush(); 3048 SETCOLORS(nm_fg_color, nm_bg_color); 3049 #endif 3050 attrmode = AT_NORMAL; 3051 } 3052 3053 public void at_switch(int attr) 3054 { 3055 int new_attrmode = apply_at_specials(attr); 3056 int ignore_modes = AT_ANSI; 3057 3058 if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) 3059 { 3060 at_exit(); 3061 at_enter(attr); 3062 } 3063 } 3064 3065 public lbool is_at_equiv(int attr1, int attr2) 3066 { 3067 attr1 = apply_at_specials(attr1); 3068 attr2 = apply_at_specials(attr2); 3069 3070 return (attr1 == attr2); 3071 } 3072 3073 public int apply_at_specials(int attr) 3074 { 3075 if (attr & AT_BINARY) 3076 attr |= binattr; 3077 if (attr & AT_HILITE) 3078 attr |= AT_STANDOUT; 3079 attr &= ~(AT_BINARY|AT_HILITE); 3080 3081 return attr; 3082 } 3083 3084 /* 3085 * Output a plain backspace, without erasing the previous char. 3086 */ 3087 public void putbs(void) 3088 { 3089 if (termcap_debug) 3090 putstr("<bs>"); 3091 else 3092 { 3093 #if !MSDOS_COMPILER 3094 ltputs(sc_backspace, 1, putchr); 3095 #else 3096 int row, col; 3097 3098 flush(); 3099 { 3100 #if MSDOS_COMPILER==MSOFTC 3101 struct rccoord tpos; 3102 tpos = _gettextposition(); 3103 row = tpos.row; 3104 col = tpos.col; 3105 #else 3106 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 3107 row = wherey(); 3108 col = wherex(); 3109 #else 3110 #if MSDOS_COMPILER==WIN32C 3111 CONSOLE_SCREEN_BUFFER_INFO scr; 3112 GetConsoleScreenBufferInfo(con_out, &scr); 3113 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 3114 col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; 3115 #endif 3116 #endif 3117 #endif 3118 } 3119 if (col <= 1) 3120 return; 3121 _settextposition(row, col-1); 3122 #endif /* MSDOS_COMPILER */ 3123 } 3124 } 3125 3126 #if MSDOS_COMPILER==WIN32C 3127 3128 #define WIN32_MAX_REPEAT 3 3129 #define LAST_DOWN_COUNT 8 3130 static LWCHAR last_downs[LAST_DOWN_COUNT] = { 0 }; 3131 static int last_down_index = 0; 3132 static LWCHAR hi_surr = 0; 3133 3134 typedef struct XINPUT_RECORD { 3135 INPUT_RECORD ir; 3136 LWCHAR ichar; /* because ir...UnicodeChar is only 16 bits */ 3137 } XINPUT_RECORD; 3138 3139 typedef struct WIN32_CHAR { 3140 struct WIN32_CHAR *wc_next; 3141 int wc_ch; 3142 } WIN32_CHAR; 3143 3144 static WIN32_CHAR *win32_queue = NULL; 3145 3146 /* 3147 * Is the win32_queue nonempty? 3148 */ 3149 static lbool win32_queued_char(void) 3150 { 3151 return (win32_queue != NULL); 3152 } 3153 3154 /* 3155 * Push a char onto the back of the win32_queue. 3156 */ 3157 static void win32_enqueue(int ch) 3158 { 3159 WIN32_CHAR *wch = (WIN32_CHAR *) ecalloc(1, sizeof(WIN32_CHAR)); 3160 wch->wc_ch = ch; 3161 wch->wc_next = NULL; 3162 if (win32_queue == NULL) 3163 win32_queue = wch; 3164 else 3165 { 3166 WIN32_CHAR *pch; 3167 for (pch = win32_queue; pch->wc_next != NULL; pch = pch->wc_next) 3168 continue; 3169 pch->wc_next = wch; 3170 } 3171 } 3172 3173 /* 3174 * Push a char onto the front of the win32_queue. 3175 * Makes the next call to WIN32getch return ch. 3176 */ 3177 public void WIN32ungetch(int ch) 3178 { 3179 WIN32_CHAR *wch = (WIN32_CHAR *) ecalloc(1, sizeof(WIN32_CHAR)); 3180 wch->wc_ch = ch; 3181 wch->wc_next = win32_queue; 3182 win32_queue = wch; 3183 } 3184 3185 /* 3186 * Get a char from the front of the win32_queue. 3187 */ 3188 static int win32_get_queue(void) 3189 { 3190 WIN32_CHAR *wch = win32_queue; 3191 int ch = wch->wc_ch; 3192 win32_queue = wch->wc_next; 3193 free(wch); 3194 return ch; 3195 } 3196 3197 /* 3198 * Handle a mouse input event. 3199 */ 3200 static lbool win32_mouse_event(XINPUT_RECORD *xip) 3201 { 3202 char b; 3203 3204 if (!mousecap || xip->ir.EventType != MOUSE_EVENT) 3205 return (FALSE); 3206 3207 /* Generate an X11 mouse sequence from the mouse event. */ 3208 /* TODO: switch to the 1006 protocol to allow specific-button-up reports */ 3209 switch (xip->ir.Event.MouseEvent.dwEventFlags) 3210 { 3211 case 0: /* press or release */ 3212 if (xip->ir.Event.MouseEvent.dwButtonState == 0) 3213 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON_REL; 3214 else if (xip->ir.Event.MouseEvent.dwButtonState == 1) /* leftmost */ 3215 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON1; 3216 else if (xip->ir.Event.MouseEvent.dwButtonState == 2) /* rightmost */ 3217 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON3; 3218 else if (xip->ir.Event.MouseEvent.dwButtonState == 4) /* middle ("next-to-leftmost") */ 3219 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON2; 3220 else /* don't bother to figure out what changed */ 3221 return (FALSE); 3222 break; 3223 case MOUSE_WHEELED: 3224 b = X11MOUSE_OFFSET + (((int)xip->ir.Event.MouseEvent.dwButtonState < 0) ? X11MOUSE_WHEEL_DOWN : X11MOUSE_WHEEL_UP); 3225 break; 3226 case MOUSE_MOVED: 3227 if (xip->ir.Event.MouseEvent.dwButtonState != 1) 3228 return (FALSE); 3229 /* Drag with left button down. */ 3230 b = X11MOUSE_OFFSET + X11MOUSE_DRAG; 3231 break; 3232 default: 3233 return (FALSE); 3234 } 3235 /* {{ TODO: change to X11 1006 format. }} */ 3236 win32_enqueue(ESC); 3237 win32_enqueue('['); 3238 win32_enqueue('M'); 3239 win32_enqueue(b); 3240 win32_enqueue(X11MOUSE_OFFSET + xip->ir.Event.MouseEvent.dwMousePosition.X + 1); 3241 win32_enqueue(X11MOUSE_OFFSET + xip->ir.Event.MouseEvent.dwMousePosition.Y + 1); 3242 return (TRUE); 3243 } 3244 3245 static void set_last_down(LWCHAR ch) 3246 { 3247 if (ch == 0) return; 3248 last_downs[last_down_index] = ch; 3249 if (++last_down_index >= LAST_DOWN_COUNT) 3250 last_down_index = 0; 3251 } 3252 3253 static LWCHAR *find_last_down(LWCHAR ch) 3254 { 3255 int i; 3256 for (i = 0; i < LAST_DOWN_COUNT; ++i) 3257 if (last_downs[i] == ch) 3258 return &last_downs[i]; 3259 return NULL; 3260 } 3261 3262 /* 3263 * Get an input char from an INPUT_RECORD and store in xip->ichar. 3264 * Handles surrogate chars, and KeyUp without previous corresponding KeyDown. 3265 */ 3266 static lbool win32_get_ichar(XINPUT_RECORD *xip) 3267 { 3268 LWCHAR ch = xip->ir.Event.KeyEvent.uChar.UnicodeChar; 3269 xip->ichar = ch; 3270 if (!is_ascii_char(ch)) 3271 { 3272 int is_down = xip->ir.Event.KeyEvent.bKeyDown; 3273 LWCHAR *last_down = find_last_down(ch); 3274 if (last_down == NULL) { /* key was up */ 3275 if (is_down) { /* key was up, now is down */ 3276 set_last_down(ch); 3277 } else { /* key up without previous down: pretend this is a down. */ 3278 xip->ir.Event.KeyEvent.bKeyDown = 1; 3279 } 3280 } else if (!is_down) { /* key was down, now is up */ 3281 *last_down = 0; /* use this last_down only once */ 3282 } 3283 3284 if (ch >= 0xD800 && ch < 0xDC00) { /* high surrogate */ 3285 hi_surr = 0x10000 + ((ch - 0xD800) << 10); 3286 return (FALSE); /* get next input, which should be the low surrogate */ 3287 } 3288 if (ch >= 0xDC00 && ch < 0xE000) { /* low surrogate */ 3289 xip->ichar = hi_surr + (ch - 0xDC00); 3290 hi_surr = 0; 3291 } 3292 } 3293 return (TRUE); 3294 } 3295 3296 /* 3297 * Handle a scan code (non-ASCII) key input. 3298 */ 3299 static lbool win32_scan_code(XINPUT_RECORD *xip) 3300 { 3301 int scan = -1; 3302 if (xip->ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 3303 { 3304 switch (xip->ir.Event.KeyEvent.wVirtualScanCode) 3305 { 3306 case PCK_RIGHT: /* right arrow */ 3307 scan = PCK_CTL_RIGHT; 3308 break; 3309 case PCK_LEFT: /* left arrow */ 3310 scan = PCK_CTL_LEFT; 3311 break; 3312 case PCK_DELETE: /* delete */ 3313 scan = PCK_CTL_DELETE; 3314 break; 3315 case PCK_HOME: 3316 scan = PCK_CTL_HOME; 3317 break; 3318 case PCK_END: 3319 scan = PCK_CTL_END; 3320 break; 3321 } 3322 } else if (xip->ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) 3323 { 3324 if (xip->ichar == '\t') 3325 scan = PCK_SHIFT_TAB; 3326 else 3327 { 3328 switch (xip->ir.Event.KeyEvent.wVirtualScanCode) 3329 { 3330 case PCK_RIGHT: 3331 scan = PCK_SHIFT_RIGHT; 3332 break; 3333 case PCK_LEFT: 3334 scan = PCK_SHIFT_LEFT; 3335 break; 3336 case PCK_HOME: 3337 scan = PCK_SHIFT_HOME; 3338 break; 3339 case PCK_END: 3340 scan = PCK_SHIFT_END; 3341 break; 3342 } 3343 } 3344 } 3345 if (scan < 0 && xip->ichar == 0) 3346 scan = xip->ir.Event.KeyEvent.wVirtualScanCode; 3347 if (scan < 0) 3348 return (FALSE); 3349 /* 3350 * An extended key returns a 2 byte sequence consisting of 3351 * a zero byte followed by the scan code. 3352 */ 3353 win32_enqueue('\0'); 3354 win32_enqueue(scan); 3355 return (TRUE); 3356 } 3357 3358 /* 3359 * Handle a key input event. 3360 */ 3361 static lbool win32_key_event(XINPUT_RECORD *xip) 3362 { 3363 int repeat; 3364 char utf8[UTF8_MAX_LENGTH]; 3365 char *up; 3366 3367 if (xip->ir.EventType != KEY_EVENT || 3368 ((xip->ir.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_CTRL_PRESSED)) == (RIGHT_ALT_PRESSED|LEFT_CTRL_PRESSED) && xip->ir.Event.KeyEvent.uChar.UnicodeChar == 0) || 3369 (xip->ir.Event.KeyEvent.wVirtualScanCode == 0 && xip->ir.Event.KeyEvent.uChar.UnicodeChar == 0) || 3370 xip->ir.Event.KeyEvent.wVirtualScanCode == PCK_CAPS_LOCK || 3371 xip->ir.Event.KeyEvent.wVirtualScanCode == PCK_NUM_LOCK || 3372 (xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU && xip->ir.Event.KeyEvent.uChar.UnicodeChar == 0) || 3373 xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_KANJI || 3374 xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || 3375 xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL) 3376 return (FALSE); 3377 3378 if (!win32_get_ichar(xip)) 3379 return (FALSE); 3380 if (!xip->ir.Event.KeyEvent.bKeyDown) 3381 return (FALSE); 3382 3383 if (win32_scan_code(xip)) 3384 return (TRUE); 3385 3386 repeat = xip->ir.Event.KeyEvent.wRepeatCount; 3387 if (repeat > WIN32_MAX_REPEAT) 3388 repeat = WIN32_MAX_REPEAT; 3389 up = utf8; 3390 put_wchar(&up, xip->ichar); 3391 for (; repeat > 0; --repeat) 3392 { 3393 constant char *p; 3394 for (p = utf8; p < up; ++p) 3395 win32_enqueue(*p & 0xFF); 3396 } 3397 return (TRUE); 3398 } 3399 3400 /* 3401 * Handle a window input event. 3402 */ 3403 static lbool win32_window_event(XINPUT_RECORD *xip) 3404 { 3405 if (xip->ir.EventType != WINDOW_BUFFER_SIZE_EVENT) 3406 return (FALSE); 3407 win32_enqueue(READ_AGAIN); 3408 return (TRUE); 3409 } 3410 3411 /* 3412 * Determine whether an input character is waiting to be read. 3413 */ 3414 public lbool win32_kbhit2(lbool no_queued) 3415 { 3416 XINPUT_RECORD xip; 3417 3418 if (!no_queued && win32_queued_char()) 3419 return (TRUE); 3420 3421 for (;;) 3422 { 3423 DWORD nread; 3424 DWORD console_input_mode; 3425 /* 3426 * When an input pipe closes, cmd may reset the console mode, 3427 * so set the mode every time we read input. 3428 */ 3429 if (GetConsoleMode(tty, &console_input_mode) && console_input_mode != curr_console_input_mode) 3430 SetConsoleMode(tty, curr_console_input_mode); 3431 PeekConsoleInputW(tty, &xip.ir, 1, &nread); 3432 if (nread == 0) 3433 return (FALSE); 3434 ReadConsoleInputW(tty, &xip.ir, 1, &nread); 3435 if (nread == 0) 3436 return (FALSE); 3437 if (win32_mouse_event(&xip) || win32_key_event(&xip) || win32_window_event(&xip)) 3438 break; 3439 } 3440 return (TRUE); 3441 } 3442 3443 public lbool win32_kbhit(void) 3444 { 3445 return win32_kbhit2(FALSE); 3446 } 3447 3448 /* 3449 * Read a character from the keyboard. 3450 */ 3451 public int WIN32getch(void) 3452 { 3453 while (!win32_kbhit()) 3454 { 3455 Sleep(20); 3456 if (ABORT_SIGS()) 3457 return ('\003'); 3458 } 3459 return (win32_get_queue()); 3460 } 3461 3462 public void win32_getch_clear(void) 3463 { 3464 while (win32_kbhit()) 3465 (void) WIN32getch(); 3466 } 3467 3468 #endif /* MSDOS_COMPILER==WIN32C */ 3469 3470 #if MSDOS_COMPILER 3471 /* 3472 */ 3473 public void WIN32setcolors(int fg, int bg) 3474 { 3475 SETCOLORS(fg, bg); 3476 } 3477 3478 /* 3479 */ 3480 public void WIN32textout(constant char *text, size_t len) 3481 { 3482 #if MSDOS_COMPILER==WIN32C 3483 DWORD written; 3484 if (utf_mode == 2) 3485 { 3486 /* 3487 * We've got UTF-8 text in a non-UTF-8 console. Convert it to 3488 * wide and use WriteConsoleW. 3489 * Biggest input len is OUTBUF_SIZE of obuf from win_flush, 3490 * which is also the biggest output count if it's ASCII. 3491 * "static" wtext is not a state - only avoid 16K on stack. 3492 */ 3493 static WCHAR wtext[OUTBUF_SIZE]; 3494 len = MultiByteToWideChar(CP_UTF8, 0, text, len, wtext, countof(wtext)); 3495 WriteConsoleW(con_out, wtext, len, &written, NULL); 3496 } else 3497 WriteConsole(con_out, text, (DWORD) len, &written, NULL); 3498 #else 3499 char buf[2048]; 3500 if (len >= sizeof(buf)) 3501 len = sizeof(buf) - 1; 3502 memcpy(buf, text, len); 3503 buf[len] = 0; 3504 cputs(buf); 3505 #endif 3506 } 3507 #endif 3508