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