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