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 ((s = lgetenv("LINES")) != NULL) 847 sc_height = atoi(s); 848 else if (sys_height > 0) 849 sc_height = sys_height; 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 ((s = lgetenv("COLUMNS")) != NULL) 858 sc_width = atoi(s); 859 else if (sys_width > 0) 860 sc_width = sys_width; 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 #if !MSDOS_COMPILER 1711 if (!(quit_if_one_screen && one_screen)) 1712 { 1713 if (!no_init) 1714 ltputs(sc_init, sc_height, putchr); 1715 if (!no_keypad) 1716 ltputs(sc_s_keypad, sc_height, putchr); 1717 if (mousecap) 1718 init_mouse(); 1719 } 1720 init_done = 1; 1721 if (top_scroll) 1722 { 1723 int i; 1724 1725 /* 1726 * This is nice to terminals with no alternate screen, 1727 * but with saved scrolled-off-the-top lines. This way, 1728 * no previous line is lost, but we start with a whole 1729 * screen to ourself. 1730 */ 1731 for (i = 1; i < sc_height; i++) 1732 putchr('\n'); 1733 } else 1734 line_left(); 1735 #else 1736 #if MSDOS_COMPILER==WIN32C 1737 if (!(quit_if_one_screen && one_screen)) 1738 { 1739 if (!no_init) 1740 win32_init_term(); 1741 if (mousecap) 1742 init_mouse(); 1743 1744 } 1745 win32_init_vt_term(); 1746 #endif 1747 init_done = 1; 1748 initcolor(); 1749 flush(); 1750 #endif 1751 } 1752 1753 /* 1754 * Deinitialize terminal 1755 */ 1756 public void 1757 deinit(VOID_PARAM) 1758 { 1759 if (!init_done) 1760 return; 1761 #if !MSDOS_COMPILER 1762 if (!(quit_if_one_screen && one_screen)) 1763 { 1764 if (mousecap) 1765 deinit_mouse(); 1766 if (!no_keypad) 1767 ltputs(sc_e_keypad, sc_height, putchr); 1768 if (!no_init) 1769 ltputs(sc_deinit, sc_height, putchr); 1770 } 1771 #else 1772 /* Restore system colors. */ 1773 SETCOLORS(sy_fg_color, sy_bg_color); 1774 #if MSDOS_COMPILER==WIN32C 1775 win32_deinit_vt_term(); 1776 if (!(quit_if_one_screen && one_screen)) 1777 { 1778 if (mousecap) 1779 deinit_mouse(); 1780 if (!no_init) 1781 win32_deinit_term(); 1782 } 1783 #else 1784 /* Need clreol to make SETCOLORS take effect. */ 1785 clreol(); 1786 #endif 1787 #endif 1788 init_done = 0; 1789 } 1790 1791 /* 1792 * Are we interactive (ie. writing to an initialized tty)? 1793 */ 1794 public int 1795 interactive(VOID_PARAM) 1796 { 1797 return (is_tty && init_done); 1798 } 1799 1800 static void 1801 assert_interactive(VOID_PARAM) 1802 { 1803 if (interactive()) return; 1804 /* abort(); */ 1805 } 1806 1807 /* 1808 * Home cursor (move to upper left corner of screen). 1809 */ 1810 public void 1811 home(VOID_PARAM) 1812 { 1813 assert_interactive(); 1814 #if !MSDOS_COMPILER 1815 ltputs(sc_home, 1, putchr); 1816 #else 1817 flush(); 1818 _settextposition(1,1); 1819 #endif 1820 } 1821 1822 /* 1823 * Add a blank line (called with cursor at home). 1824 * Should scroll the display down. 1825 */ 1826 public void 1827 add_line(VOID_PARAM) 1828 { 1829 assert_interactive(); 1830 #if !MSDOS_COMPILER 1831 ltputs(sc_addline, sc_height, putchr); 1832 #else 1833 flush(); 1834 #if MSDOS_COMPILER==MSOFTC 1835 _scrolltextwindow(_GSCROLLDOWN); 1836 _settextposition(1,1); 1837 #else 1838 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1839 movetext(1,1, sc_width,sc_height-1, 1,2); 1840 gotoxy(1,1); 1841 clreol(); 1842 #else 1843 #if MSDOS_COMPILER==WIN32C 1844 { 1845 CHAR_INFO fillchar; 1846 SMALL_RECT rcSrc, rcClip; 1847 COORD new_org; 1848 CONSOLE_SCREEN_BUFFER_INFO csbi; 1849 1850 GetConsoleScreenBufferInfo(con_out,&csbi); 1851 1852 /* The clip rectangle is the entire visible screen. */ 1853 rcClip.Left = csbi.srWindow.Left; 1854 rcClip.Top = csbi.srWindow.Top; 1855 rcClip.Right = csbi.srWindow.Right; 1856 rcClip.Bottom = csbi.srWindow.Bottom; 1857 1858 /* The source rectangle is the visible screen minus the last line. */ 1859 rcSrc = rcClip; 1860 rcSrc.Bottom--; 1861 1862 /* Move the top left corner of the source window down one row. */ 1863 new_org.X = rcSrc.Left; 1864 new_org.Y = rcSrc.Top + 1; 1865 1866 /* Fill the right character and attributes. */ 1867 fillchar.Char.AsciiChar = ' '; 1868 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1869 fillchar.Attributes = curr_attr; 1870 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1871 _settextposition(1,1); 1872 } 1873 #endif 1874 #endif 1875 #endif 1876 #endif 1877 } 1878 1879 #if 0 1880 /* 1881 * Remove the n topmost lines and scroll everything below it in the 1882 * window upward. This is needed to stop leaking the topmost line 1883 * into the scrollback buffer when we go down-one-line (in WIN32). 1884 */ 1885 public void 1886 remove_top(n) 1887 int n; 1888 { 1889 #if MSDOS_COMPILER==WIN32C 1890 SMALL_RECT rcSrc, rcClip; 1891 CHAR_INFO fillchar; 1892 COORD new_org; 1893 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 1894 1895 if (n >= sc_height - 1) 1896 { 1897 clear(); 1898 home(); 1899 return; 1900 } 1901 1902 flush(); 1903 1904 GetConsoleScreenBufferInfo(con_out, &csbi); 1905 1906 /* Get the extent of all-visible-rows-but-the-last. */ 1907 rcSrc.Left = csbi.srWindow.Left; 1908 rcSrc.Top = csbi.srWindow.Top + n; 1909 rcSrc.Right = csbi.srWindow.Right; 1910 rcSrc.Bottom = csbi.srWindow.Bottom; 1911 1912 /* Get the clip rectangle. */ 1913 rcClip.Left = rcSrc.Left; 1914 rcClip.Top = csbi.srWindow.Top; 1915 rcClip.Right = rcSrc.Right; 1916 rcClip.Bottom = rcSrc.Bottom ; 1917 1918 /* Move the source window up n rows. */ 1919 new_org.X = rcSrc.Left; 1920 new_org.Y = rcSrc.Top - n; 1921 1922 /* Fill the right character and attributes. */ 1923 fillchar.Char.AsciiChar = ' '; 1924 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1925 fillchar.Attributes = curr_attr; 1926 1927 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1928 1929 /* Position cursor on first blank line. */ 1930 goto_line(sc_height - n - 1); 1931 #endif 1932 } 1933 #endif 1934 1935 #if MSDOS_COMPILER==WIN32C 1936 /* 1937 * Clear the screen. 1938 */ 1939 static void 1940 win32_clear(VOID_PARAM) 1941 { 1942 /* 1943 * This will clear only the currently visible rows of the NT 1944 * console buffer, which means none of the precious scrollback 1945 * rows are touched making for faster scrolling. Note that, if 1946 * the window has fewer columns than the console buffer (i.e. 1947 * there is a horizontal scrollbar as well), the entire width 1948 * of the visible rows will be cleared. 1949 */ 1950 COORD topleft; 1951 DWORD nchars; 1952 DWORD winsz; 1953 CONSOLE_SCREEN_BUFFER_INFO csbi; 1954 1955 /* get the number of cells in the current buffer */ 1956 GetConsoleScreenBufferInfo(con_out, &csbi); 1957 winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 1958 topleft.X = 0; 1959 topleft.Y = csbi.srWindow.Top; 1960 1961 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1962 FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); 1963 FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); 1964 } 1965 1966 /* 1967 * Remove the n topmost lines and scroll everything below it in the 1968 * window upward. 1969 */ 1970 public void 1971 win32_scroll_up(n) 1972 int n; 1973 { 1974 SMALL_RECT rcSrc, rcClip; 1975 CHAR_INFO fillchar; 1976 COORD topleft; 1977 COORD new_org; 1978 DWORD nchars; 1979 DWORD size; 1980 CONSOLE_SCREEN_BUFFER_INFO csbi; 1981 1982 if (n <= 0) 1983 return; 1984 1985 if (n >= sc_height - 1) 1986 { 1987 win32_clear(); 1988 _settextposition(1,1); 1989 return; 1990 } 1991 1992 /* Get the extent of what will remain visible after scrolling. */ 1993 GetConsoleScreenBufferInfo(con_out, &csbi); 1994 rcSrc.Left = csbi.srWindow.Left; 1995 rcSrc.Top = csbi.srWindow.Top + n; 1996 rcSrc.Right = csbi.srWindow.Right; 1997 rcSrc.Bottom = csbi.srWindow.Bottom; 1998 1999 /* Get the clip rectangle. */ 2000 rcClip.Left = rcSrc.Left; 2001 rcClip.Top = csbi.srWindow.Top; 2002 rcClip.Right = rcSrc.Right; 2003 rcClip.Bottom = rcSrc.Bottom ; 2004 2005 /* Move the source text to the top of the screen. */ 2006 new_org.X = rcSrc.Left; 2007 new_org.Y = rcClip.Top; 2008 2009 /* Fill the right character and attributes. */ 2010 fillchar.Char.AsciiChar = ' '; 2011 fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); 2012 2013 /* Scroll the window. */ 2014 SetConsoleTextAttribute(con_out, fillchar.Attributes); 2015 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 2016 2017 /* Clear remaining lines at bottom. */ 2018 topleft.X = csbi.dwCursorPosition.X; 2019 topleft.Y = rcSrc.Bottom - n; 2020 size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); 2021 FillConsoleOutputCharacter(con_out, ' ', size, topleft, 2022 &nchars); 2023 FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, 2024 &nchars); 2025 SetConsoleTextAttribute(con_out, curr_attr); 2026 2027 /* Move cursor n lines up from where it was. */ 2028 csbi.dwCursorPosition.Y -= n; 2029 SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); 2030 } 2031 #endif 2032 2033 /* 2034 * Move cursor to lower left corner of screen. 2035 */ 2036 public void 2037 lower_left(VOID_PARAM) 2038 { 2039 assert_interactive(); 2040 #if !MSDOS_COMPILER 2041 ltputs(sc_lower_left, 1, putchr); 2042 #else 2043 flush(); 2044 _settextposition(sc_height, 1); 2045 #endif 2046 } 2047 2048 /* 2049 * Move cursor to left position of current line. 2050 */ 2051 public void 2052 line_left(VOID_PARAM) 2053 { 2054 assert_interactive(); 2055 #if !MSDOS_COMPILER 2056 ltputs(sc_return, 1, putchr); 2057 #else 2058 { 2059 int row; 2060 flush(); 2061 #if MSDOS_COMPILER==WIN32C 2062 { 2063 CONSOLE_SCREEN_BUFFER_INFO scr; 2064 GetConsoleScreenBufferInfo(con_out, &scr); 2065 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2066 } 2067 #else 2068 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2069 row = wherey(); 2070 #else 2071 { 2072 struct rccoord tpos = _gettextposition(); 2073 row = tpos.row; 2074 } 2075 #endif 2076 #endif 2077 _settextposition(row, 1); 2078 } 2079 #endif 2080 } 2081 2082 /* 2083 * Check if the console size has changed and reset internals 2084 * (in lieu of SIGWINCH for WIN32). 2085 */ 2086 public void 2087 check_winch(VOID_PARAM) 2088 { 2089 #if MSDOS_COMPILER==WIN32C 2090 CONSOLE_SCREEN_BUFFER_INFO scr; 2091 COORD size; 2092 2093 if (con_out == INVALID_HANDLE_VALUE) 2094 return; 2095 2096 flush(); 2097 GetConsoleScreenBufferInfo(con_out, &scr); 2098 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 2099 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 2100 if (size.Y != sc_height || size.X != sc_width) 2101 { 2102 sc_height = size.Y; 2103 sc_width = size.X; 2104 if (!no_init && con_out_ours == con_out) 2105 SetConsoleScreenBufferSize(con_out, size); 2106 pos_init(); 2107 wscroll = (sc_height + 1) / 2; 2108 screen_trashed = 1; 2109 } 2110 #endif 2111 } 2112 2113 /* 2114 * Goto a specific line on the screen. 2115 */ 2116 public void 2117 goto_line(sindex) 2118 int sindex; 2119 { 2120 assert_interactive(); 2121 #if !MSDOS_COMPILER 2122 ltputs(tgoto(sc_move, 0, sindex), 1, putchr); 2123 #else 2124 flush(); 2125 _settextposition(sindex+1, 1); 2126 #endif 2127 } 2128 2129 #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC 2130 /* 2131 * Create an alternate screen which is all white. 2132 * This screen is used to create a "flash" effect, by displaying it 2133 * briefly and then switching back to the normal screen. 2134 * {{ Yuck! There must be a better way to get a visual bell. }} 2135 */ 2136 static void 2137 create_flash(VOID_PARAM) 2138 { 2139 #if MSDOS_COMPILER==MSOFTC 2140 struct videoconfig w; 2141 char *blanks; 2142 int row, col; 2143 2144 _getvideoconfig(&w); 2145 videopages = w.numvideopages; 2146 if (videopages < 2) 2147 { 2148 at_enter(AT_STANDOUT); 2149 at_exit(); 2150 } else 2151 { 2152 _setactivepage(1); 2153 at_enter(AT_STANDOUT); 2154 blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); 2155 for (col = 0; col < w.numtextcols; col++) 2156 blanks[col] = ' '; 2157 for (row = w.numtextrows; row > 0; row--) 2158 _outmem(blanks, w.numtextcols); 2159 _setactivepage(0); 2160 _setvisualpage(0); 2161 free(blanks); 2162 at_exit(); 2163 } 2164 #else 2165 #if MSDOS_COMPILER==BORLANDC 2166 int n; 2167 2168 whitescreen = (unsigned short *) 2169 malloc(sc_width * sc_height * sizeof(short)); 2170 if (whitescreen == NULL) 2171 return; 2172 for (n = 0; n < sc_width * sc_height; n++) 2173 whitescreen[n] = 0x7020; 2174 #endif 2175 #endif 2176 flash_created = 1; 2177 } 2178 #endif /* MSDOS_COMPILER */ 2179 2180 /* 2181 * Output the "visual bell", if there is one. 2182 */ 2183 public void 2184 vbell(VOID_PARAM) 2185 { 2186 #if !MSDOS_COMPILER 2187 if (*sc_visual_bell == '\0') 2188 return; 2189 ltputs(sc_visual_bell, sc_height, putchr); 2190 #else 2191 #if MSDOS_COMPILER==DJGPPC 2192 ScreenVisualBell(); 2193 #else 2194 #if MSDOS_COMPILER==MSOFTC 2195 /* 2196 * Create a flash screen on the second video page. 2197 * Switch to that page, then switch back. 2198 */ 2199 if (!flash_created) 2200 create_flash(); 2201 if (videopages < 2) 2202 return; 2203 _setvisualpage(1); 2204 delay(100); 2205 _setvisualpage(0); 2206 #else 2207 #if MSDOS_COMPILER==BORLANDC 2208 unsigned short *currscreen; 2209 2210 /* 2211 * Get a copy of the current screen. 2212 * Display the flash screen. 2213 * Then restore the old screen. 2214 */ 2215 if (!flash_created) 2216 create_flash(); 2217 if (whitescreen == NULL) 2218 return; 2219 currscreen = (unsigned short *) 2220 malloc(sc_width * sc_height * sizeof(short)); 2221 if (currscreen == NULL) return; 2222 gettext(1, 1, sc_width, sc_height, currscreen); 2223 puttext(1, 1, sc_width, sc_height, whitescreen); 2224 delay(100); 2225 puttext(1, 1, sc_width, sc_height, currscreen); 2226 free(currscreen); 2227 #else 2228 #if MSDOS_COMPILER==WIN32C 2229 /* paint screen with an inverse color */ 2230 clear(); 2231 2232 /* leave it displayed for 100 msec. */ 2233 Sleep(100); 2234 2235 /* restore with a redraw */ 2236 repaint(); 2237 #endif 2238 #endif 2239 #endif 2240 #endif 2241 #endif 2242 } 2243 2244 /* 2245 * Make a noise. 2246 */ 2247 static void 2248 beep(VOID_PARAM) 2249 { 2250 #if !MSDOS_COMPILER 2251 putchr(CONTROL('G')); 2252 #else 2253 #if MSDOS_COMPILER==WIN32C 2254 MessageBeep(0); 2255 #else 2256 write(1, "\7", 1); 2257 #endif 2258 #endif 2259 } 2260 2261 /* 2262 * Ring the terminal bell. 2263 */ 2264 public void 2265 bell(VOID_PARAM) 2266 { 2267 if (quiet == VERY_QUIET) 2268 vbell(); 2269 else 2270 beep(); 2271 } 2272 2273 /* 2274 * Clear the screen. 2275 */ 2276 public void 2277 clear(VOID_PARAM) 2278 { 2279 assert_interactive(); 2280 #if !MSDOS_COMPILER 2281 ltputs(sc_clear, sc_height, putchr); 2282 #else 2283 flush(); 2284 #if MSDOS_COMPILER==WIN32C 2285 win32_clear(); 2286 #else 2287 _clearscreen(_GCLEARSCREEN); 2288 #endif 2289 #endif 2290 } 2291 2292 /* 2293 * Clear from the cursor to the end of the cursor's line. 2294 * {{ This must not move the cursor. }} 2295 */ 2296 public void 2297 clear_eol(VOID_PARAM) 2298 { 2299 /* assert_interactive();*/ 2300 #if !MSDOS_COMPILER 2301 ltputs(sc_eol_clear, 1, putchr); 2302 #else 2303 #if MSDOS_COMPILER==MSOFTC 2304 short top, left; 2305 short bot, right; 2306 struct rccoord tpos; 2307 2308 flush(); 2309 /* 2310 * Save current state. 2311 */ 2312 tpos = _gettextposition(); 2313 _gettextwindow(&top, &left, &bot, &right); 2314 /* 2315 * Set a temporary window to the current line, 2316 * from the cursor's position to the right edge of the screen. 2317 * Then clear that window. 2318 */ 2319 _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); 2320 _clearscreen(_GWINDOW); 2321 /* 2322 * Restore state. 2323 */ 2324 _settextwindow(top, left, bot, right); 2325 _settextposition(tpos.row, tpos.col); 2326 #else 2327 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2328 flush(); 2329 clreol(); 2330 #else 2331 #if MSDOS_COMPILER==WIN32C 2332 DWORD nchars; 2333 COORD cpos; 2334 CONSOLE_SCREEN_BUFFER_INFO scr; 2335 2336 flush(); 2337 memset(&scr, 0, sizeof(scr)); 2338 GetConsoleScreenBufferInfo(con_out, &scr); 2339 cpos.X = scr.dwCursorPosition.X; 2340 cpos.Y = scr.dwCursorPosition.Y; 2341 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2342 FillConsoleOutputAttribute(con_out, curr_attr, 2343 scr.dwSize.X - cpos.X, cpos, &nchars); 2344 FillConsoleOutputCharacter(con_out, ' ', 2345 scr.dwSize.X - cpos.X, cpos, &nchars); 2346 #endif 2347 #endif 2348 #endif 2349 #endif 2350 } 2351 2352 /* 2353 * Clear the current line. 2354 * Clear the screen if there's off-screen memory below the display. 2355 */ 2356 static void 2357 clear_eol_bot(VOID_PARAM) 2358 { 2359 assert_interactive(); 2360 #if MSDOS_COMPILER 2361 clear_eol(); 2362 #else 2363 if (below_mem) 2364 ltputs(sc_eos_clear, 1, putchr); 2365 else 2366 ltputs(sc_eol_clear, 1, putchr); 2367 #endif 2368 } 2369 2370 /* 2371 * Clear the bottom line of the display. 2372 * Leave the cursor at the beginning of the bottom line. 2373 */ 2374 public void 2375 clear_bot(VOID_PARAM) 2376 { 2377 /* 2378 * If we're in a non-normal attribute mode, temporarily exit 2379 * the mode while we do the clear. Some terminals fill the 2380 * cleared area with the current attribute. 2381 */ 2382 if (oldbot) 2383 lower_left(); 2384 else 2385 line_left(); 2386 2387 if (attrmode == AT_NORMAL) 2388 clear_eol_bot(); 2389 else 2390 { 2391 int saved_attrmode = attrmode; 2392 2393 at_exit(); 2394 clear_eol_bot(); 2395 at_enter(saved_attrmode); 2396 } 2397 } 2398 2399 /* 2400 * Color string may be "x[y]" where x and y are 4-bit color chars, 2401 * or "N[.M]" where N and M are decimal integers> 2402 * Any of x,y,N,M may also be "-" to mean "unchanged". 2403 */ 2404 2405 /* 2406 * Parse a 4-bit color char. 2407 */ 2408 static int 2409 parse_color4(ch) 2410 char ch; 2411 { 2412 switch (ch) 2413 { 2414 case 'k': return 0; 2415 case 'r': return CV_RED; 2416 case 'g': return CV_GREEN; 2417 case 'y': return CV_RED|CV_GREEN; 2418 case 'b': return CV_BLUE; 2419 case 'm': return CV_RED|CV_BLUE; 2420 case 'c': return CV_GREEN|CV_BLUE; 2421 case 'w': return CV_RED|CV_GREEN|CV_BLUE; 2422 case 'K': return 0|CV_BRIGHT; 2423 case 'R': return CV_RED|CV_BRIGHT; 2424 case 'G': return CV_GREEN|CV_BRIGHT; 2425 case 'Y': return CV_RED|CV_GREEN|CV_BRIGHT; 2426 case 'B': return CV_BLUE|CV_BRIGHT; 2427 case 'M': return CV_RED|CV_BLUE|CV_BRIGHT; 2428 case 'C': return CV_GREEN|CV_BLUE|CV_BRIGHT; 2429 case 'W': return CV_RED|CV_GREEN|CV_BLUE|CV_BRIGHT; 2430 case '-': return CV_NOCHANGE; 2431 default: return CV_ERROR; 2432 } 2433 } 2434 2435 /* 2436 * Parse a color as a decimal integer. 2437 */ 2438 static int 2439 parse_color6(ps) 2440 char **ps; 2441 { 2442 if (**ps == '-') 2443 { 2444 (*ps)++; 2445 return CV_NOCHANGE; 2446 } else 2447 { 2448 char *ops = *ps; 2449 int color = lstrtoi(ops, ps); 2450 if (*ps == ops) 2451 return CV_ERROR; 2452 return color; 2453 } 2454 } 2455 2456 /* 2457 * Parse a color pair and return the foreground/background values. 2458 * Return type of color specifier: 2459 * CV_4BIT: fg/bg values are OR of CV_{RGB} bits. 2460 * CV_6BIT: fg/bg values are integers entered by user. 2461 */ 2462 public COLOR_TYPE 2463 parse_color(str, p_fg, p_bg) 2464 char *str; 2465 int *p_fg; 2466 int *p_bg; 2467 { 2468 int fg; 2469 int bg; 2470 COLOR_TYPE type = CT_NULL; 2471 2472 if (str == NULL || *str == '\0') 2473 return CT_NULL; 2474 if (*str == '+') 2475 str++; /* ignore leading + */ 2476 2477 fg = parse_color4(str[0]); 2478 bg = parse_color4((strlen(str) < 2) ? '-' : str[1]); 2479 if (fg != CV_ERROR && bg != CV_ERROR) 2480 type = CT_4BIT; 2481 else 2482 { 2483 fg = parse_color6(&str); 2484 bg = (fg != CV_ERROR && *str++ == '.') ? parse_color6(&str) : CV_NOCHANGE; 2485 if (fg != CV_ERROR && bg != CV_ERROR) 2486 type = CT_6BIT; 2487 } 2488 if (p_fg != NULL) *p_fg = fg; 2489 if (p_bg != NULL) *p_bg = bg; 2490 return type; 2491 } 2492 2493 #if !MSDOS_COMPILER 2494 2495 static int 2496 sgr_color(color) 2497 int color; 2498 { 2499 switch (color) 2500 { 2501 case 0: return 30; 2502 case CV_RED: return 31; 2503 case CV_GREEN: return 32; 2504 case CV_RED|CV_GREEN: return 33; 2505 case CV_BLUE: return 34; 2506 case CV_RED|CV_BLUE: return 35; 2507 case CV_GREEN|CV_BLUE: return 36; 2508 case CV_RED|CV_GREEN|CV_BLUE: return 37; 2509 2510 case CV_BRIGHT: return 90; 2511 case CV_RED|CV_BRIGHT: return 91; 2512 case CV_GREEN|CV_BRIGHT: return 92; 2513 case CV_RED|CV_GREEN|CV_BRIGHT: return 93; 2514 case CV_BLUE|CV_BRIGHT: return 94; 2515 case CV_RED|CV_BLUE|CV_BRIGHT: return 95; 2516 case CV_GREEN|CV_BLUE|CV_BRIGHT: return 96; 2517 case CV_RED|CV_GREEN|CV_BLUE|CV_BRIGHT: return 97; 2518 2519 default: return color; 2520 } 2521 } 2522 2523 static void 2524 tput_fmt(fmt, color, f_putc) 2525 char *fmt; 2526 int color; 2527 int (*f_putc)(int); 2528 { 2529 char buf[16]; 2530 if (color == attrcolor) 2531 return; 2532 SNPRINTF1(buf, sizeof(buf), fmt, color); 2533 ltputs(buf, 1, f_putc); 2534 attrcolor = color; 2535 } 2536 2537 static void 2538 tput_color(str, f_putc) 2539 char *str; 2540 int (*f_putc)(int); 2541 { 2542 int fg; 2543 int bg; 2544 2545 if (str != NULL && strcmp(str, "*") == 0) 2546 { 2547 /* Special case: reset to normal */ 2548 tput_fmt(ESCS"[m", -1, f_putc); 2549 return; 2550 } 2551 switch (parse_color(str, &fg, &bg)) 2552 { 2553 case CT_4BIT: 2554 if (fg >= 0) 2555 tput_fmt(ESCS"[%dm", sgr_color(fg), f_putc); 2556 if (bg >= 0) 2557 tput_fmt(ESCS"[%dm", sgr_color(bg)+10, f_putc); 2558 break; 2559 case CT_6BIT: 2560 if (fg >= 0) 2561 tput_fmt(ESCS"[38;5;%dm", fg, f_putc); 2562 if (bg >= 0) 2563 tput_fmt(ESCS"[48;5;%dm", bg, f_putc); 2564 break; 2565 default: 2566 break; 2567 } 2568 } 2569 2570 static void 2571 tput_inmode(mode_str, attr, attr_bit, f_putc) 2572 char *mode_str; 2573 int attr; 2574 int attr_bit; 2575 int (*f_putc)(int); 2576 { 2577 char *color_str; 2578 if ((attr & attr_bit) == 0) 2579 return; 2580 color_str = get_color_map(attr_bit); 2581 if (color_str == NULL || *color_str == '\0' || *color_str == '+') 2582 { 2583 ltputs(mode_str, 1, f_putc); 2584 if (color_str == NULL || *color_str++ != '+') 2585 return; 2586 } 2587 /* Color overrides mode string */ 2588 tput_color(color_str, f_putc); 2589 } 2590 2591 static void 2592 tput_outmode(mode_str, attr_bit, f_putc) 2593 char *mode_str; 2594 int attr_bit; 2595 int (*f_putc)(int); 2596 { 2597 if ((attrmode & attr_bit) == 0) 2598 return; 2599 ltputs(mode_str, 1, f_putc); 2600 } 2601 2602 #else /* MSDOS_COMPILER */ 2603 2604 #if MSDOS_COMPILER==WIN32C 2605 static int 2606 WIN32put_fmt(fmt, color) 2607 char *fmt; 2608 int color; 2609 { 2610 char buf[16]; 2611 int len = SNPRINTF1(buf, sizeof(buf), fmt, color); 2612 WIN32textout(buf, len); 2613 return TRUE; 2614 } 2615 #endif 2616 2617 static int 2618 win_set_color(attr) 2619 int attr; 2620 { 2621 int fg; 2622 int bg; 2623 int out = FALSE; 2624 char *str = get_color_map(attr); 2625 if (str == NULL || str[0] == '\0') 2626 return FALSE; 2627 switch (parse_color(str, &fg, &bg)) 2628 { 2629 case CT_4BIT: 2630 if (fg >= 0 && bg >= 0) 2631 { 2632 SETCOLORS(fg, bg); 2633 out = TRUE; 2634 } else if (fg >= 0) 2635 { 2636 SET_FG_COLOR(fg); 2637 out = TRUE; 2638 } else if (bg >= 0) 2639 { 2640 SET_BG_COLOR(bg); 2641 out = TRUE; 2642 } 2643 break; 2644 #if MSDOS_COMPILER==WIN32C 2645 case CT_6BIT: 2646 if (vt_enabled) 2647 { 2648 if (fg > 0) 2649 out = WIN32put_fmt(ESCS"[38;5;%dm", fg); 2650 if (bg > 0) 2651 out = WIN32put_fmt(ESCS"[48;5;%dm", bg); 2652 } 2653 break; 2654 #endif 2655 default: 2656 break; 2657 } 2658 return out; 2659 } 2660 2661 #endif /* MSDOS_COMPILER */ 2662 2663 public void 2664 at_enter(attr) 2665 int attr; 2666 { 2667 attr = apply_at_specials(attr); 2668 #if !MSDOS_COMPILER 2669 /* The one with the most priority is last. */ 2670 tput_inmode(sc_u_in, attr, AT_UNDERLINE, putchr); 2671 tput_inmode(sc_b_in, attr, AT_BOLD, putchr); 2672 tput_inmode(sc_bl_in, attr, AT_BLINK, putchr); 2673 /* Don't use standout and color at the same time. */ 2674 if (use_color && (attr & AT_COLOR)) 2675 tput_color(get_color_map(attr), putchr); 2676 else 2677 tput_inmode(sc_s_in, attr, AT_STANDOUT, putchr); 2678 #else 2679 flush(); 2680 /* The one with the most priority is first. */ 2681 if ((attr & AT_COLOR) && use_color) 2682 { 2683 win_set_color(attr); 2684 } else if (attr & AT_STANDOUT) 2685 { 2686 SETCOLORS(so_fg_color, so_bg_color); 2687 } else if (attr & AT_BLINK) 2688 { 2689 SETCOLORS(bl_fg_color, bl_bg_color); 2690 } else if (attr & AT_BOLD) 2691 { 2692 SETCOLORS(bo_fg_color, bo_bg_color); 2693 } else if (attr & AT_UNDERLINE) 2694 { 2695 SETCOLORS(ul_fg_color, ul_bg_color); 2696 } 2697 #endif 2698 attrmode = attr; 2699 } 2700 2701 public void 2702 at_exit(VOID_PARAM) 2703 { 2704 #if !MSDOS_COMPILER 2705 /* Undo things in the reverse order we did them. */ 2706 tput_color("*", putchr); 2707 tput_outmode(sc_s_out, AT_STANDOUT, putchr); 2708 tput_outmode(sc_bl_out, AT_BLINK, putchr); 2709 tput_outmode(sc_b_out, AT_BOLD, putchr); 2710 tput_outmode(sc_u_out, AT_UNDERLINE, putchr); 2711 #else 2712 flush(); 2713 SETCOLORS(nm_fg_color, nm_bg_color); 2714 #endif 2715 attrmode = AT_NORMAL; 2716 } 2717 2718 public void 2719 at_switch(attr) 2720 int attr; 2721 { 2722 int new_attrmode = apply_at_specials(attr); 2723 int ignore_modes = AT_ANSI; 2724 2725 if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) 2726 { 2727 at_exit(); 2728 at_enter(attr); 2729 } 2730 } 2731 2732 public int 2733 is_at_equiv(attr1, attr2) 2734 int attr1; 2735 int attr2; 2736 { 2737 attr1 = apply_at_specials(attr1); 2738 attr2 = apply_at_specials(attr2); 2739 2740 return (attr1 == attr2); 2741 } 2742 2743 public int 2744 apply_at_specials(attr) 2745 int attr; 2746 { 2747 if (attr & AT_BINARY) 2748 attr |= binattr; 2749 if (attr & AT_HILITE) 2750 attr |= AT_STANDOUT; 2751 attr &= ~(AT_BINARY|AT_HILITE); 2752 2753 return attr; 2754 } 2755 2756 /* 2757 * Output a plain backspace, without erasing the previous char. 2758 */ 2759 public void 2760 putbs(VOID_PARAM) 2761 { 2762 if (termcap_debug) 2763 putstr("<bs>"); 2764 else 2765 { 2766 #if !MSDOS_COMPILER 2767 ltputs(sc_backspace, 1, putchr); 2768 #else 2769 int row, col; 2770 2771 flush(); 2772 { 2773 #if MSDOS_COMPILER==MSOFTC 2774 struct rccoord tpos; 2775 tpos = _gettextposition(); 2776 row = tpos.row; 2777 col = tpos.col; 2778 #else 2779 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2780 row = wherey(); 2781 col = wherex(); 2782 #else 2783 #if MSDOS_COMPILER==WIN32C 2784 CONSOLE_SCREEN_BUFFER_INFO scr; 2785 GetConsoleScreenBufferInfo(con_out, &scr); 2786 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2787 col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; 2788 #endif 2789 #endif 2790 #endif 2791 } 2792 if (col <= 1) 2793 return; 2794 _settextposition(row, col-1); 2795 #endif /* MSDOS_COMPILER */ 2796 } 2797 } 2798 2799 #if MSDOS_COMPILER==WIN32C 2800 /* 2801 * Determine whether an input character is waiting to be read. 2802 */ 2803 public int 2804 win32_kbhit(VOID_PARAM) 2805 { 2806 INPUT_RECORD ip; 2807 DWORD read; 2808 2809 if (keyCount > 0) 2810 return (TRUE); 2811 2812 currentKey.ascii = 0; 2813 currentKey.scan = 0; 2814 2815 if (x11mouseCount > 0) 2816 { 2817 currentKey.ascii = x11mousebuf[x11mousePos++]; 2818 --x11mouseCount; 2819 keyCount = 1; 2820 return (TRUE); 2821 } 2822 2823 /* 2824 * Wait for a real key-down event, but 2825 * ignore SHIFT and CONTROL key events. 2826 */ 2827 do 2828 { 2829 PeekConsoleInput(tty, &ip, 1, &read); 2830 if (read == 0) 2831 return (FALSE); 2832 ReadConsoleInput(tty, &ip, 1, &read); 2833 /* generate an X11 mouse sequence from the mouse event */ 2834 if (mousecap && ip.EventType == MOUSE_EVENT && 2835 ip.Event.MouseEvent.dwEventFlags != MOUSE_MOVED) 2836 { 2837 x11mousebuf[3] = X11MOUSE_OFFSET + ip.Event.MouseEvent.dwMousePosition.X + 1; 2838 x11mousebuf[4] = X11MOUSE_OFFSET + ip.Event.MouseEvent.dwMousePosition.Y + 1; 2839 switch (ip.Event.MouseEvent.dwEventFlags) 2840 { 2841 case 0: /* press or release */ 2842 if (ip.Event.MouseEvent.dwButtonState == 0) 2843 x11mousebuf[2] = X11MOUSE_OFFSET + X11MOUSE_BUTTON_REL; 2844 else if (ip.Event.MouseEvent.dwButtonState & (FROM_LEFT_3RD_BUTTON_PRESSED | FROM_LEFT_4TH_BUTTON_PRESSED)) 2845 continue; 2846 else 2847 x11mousebuf[2] = X11MOUSE_OFFSET + X11MOUSE_BUTTON1 + ((int)ip.Event.MouseEvent.dwButtonState << 1); 2848 break; 2849 case MOUSE_WHEELED: 2850 x11mousebuf[2] = X11MOUSE_OFFSET + (((int)ip.Event.MouseEvent.dwButtonState < 0) ? X11MOUSE_WHEEL_DOWN : X11MOUSE_WHEEL_UP); 2851 break; 2852 default: 2853 continue; 2854 } 2855 x11mousePos = 0; 2856 x11mouseCount = 5; 2857 currentKey.ascii = ESC; 2858 keyCount = 1; 2859 return (TRUE); 2860 } 2861 } while (ip.EventType != KEY_EVENT || 2862 ip.Event.KeyEvent.bKeyDown != TRUE || 2863 ip.Event.KeyEvent.wVirtualScanCode == 0 || 2864 ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || 2865 ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || 2866 ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU); 2867 2868 currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; 2869 currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; 2870 keyCount = ip.Event.KeyEvent.wRepeatCount; 2871 2872 if (ip.Event.KeyEvent.dwControlKeyState & 2873 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 2874 { 2875 switch (currentKey.scan) 2876 { 2877 case PCK_ALT_E: /* letter 'E' */ 2878 currentKey.ascii = 0; 2879 break; 2880 } 2881 } else if (ip.Event.KeyEvent.dwControlKeyState & 2882 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 2883 { 2884 switch (currentKey.scan) 2885 { 2886 case PCK_RIGHT: /* right arrow */ 2887 currentKey.scan = PCK_CTL_RIGHT; 2888 break; 2889 case PCK_LEFT: /* left arrow */ 2890 currentKey.scan = PCK_CTL_LEFT; 2891 break; 2892 case PCK_DELETE: /* delete */ 2893 currentKey.scan = PCK_CTL_DELETE; 2894 break; 2895 } 2896 } else if (ip.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) 2897 { 2898 switch (currentKey.scan) 2899 { 2900 case PCK_SHIFT_TAB: /* tab */ 2901 currentKey.ascii = 0; 2902 break; 2903 } 2904 } 2905 2906 return (TRUE); 2907 } 2908 2909 /* 2910 * Read a character from the keyboard. 2911 */ 2912 public char 2913 WIN32getch(VOID_PARAM) 2914 { 2915 int ascii; 2916 2917 if (pending_scancode) 2918 { 2919 pending_scancode = 0; 2920 return ((char)(currentKey.scan & 0x00FF)); 2921 } 2922 2923 do { 2924 while (win32_kbhit() == FALSE) 2925 { 2926 Sleep(20); 2927 if (ABORT_SIGS()) 2928 return ('\003'); 2929 continue; 2930 } 2931 keyCount --; 2932 ascii = currentKey.ascii; 2933 /* 2934 * On PC's, the extended keys return a 2 byte sequence beginning 2935 * with '00', so if the ascii code is 00, the next byte will be 2936 * the lsb of the scan code. 2937 */ 2938 pending_scancode = (ascii == 0x00); 2939 } while (pending_scancode && 2940 (currentKey.scan == PCK_CAPS_LOCK || currentKey.scan == PCK_NUM_LOCK)); 2941 2942 return ((char)ascii); 2943 } 2944 #endif 2945 2946 #if MSDOS_COMPILER 2947 /* 2948 */ 2949 public void 2950 WIN32setcolors(fg, bg) 2951 int fg; 2952 int bg; 2953 { 2954 SETCOLORS(fg, bg); 2955 } 2956 2957 /* 2958 */ 2959 public void 2960 WIN32textout(text, len) 2961 char *text; 2962 int len; 2963 { 2964 #if MSDOS_COMPILER==WIN32C 2965 DWORD written; 2966 if (utf_mode == 2) 2967 { 2968 /* 2969 * We've got UTF-8 text in a non-UTF-8 console. Convert it to 2970 * wide and use WriteConsoleW. 2971 */ 2972 WCHAR wtext[1024]; 2973 len = MultiByteToWideChar(CP_UTF8, 0, text, len, wtext, 2974 sizeof(wtext)/sizeof(*wtext)); 2975 WriteConsoleW(con_out, wtext, len, &written, NULL); 2976 } else 2977 WriteConsole(con_out, text, len, &written, NULL); 2978 #else 2979 char c = text[len]; 2980 text[len] = '\0'; 2981 cputs(text); 2982 text[len] = c; 2983 #endif 2984 } 2985 #endif 2986 2987