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> 28extern 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" 102static char *windowid; 103#else 104#define DEFAULT_TERM "unknown" 105#endif 106 107#if MSDOS_COMPILER==MSOFTC 108static int videopages; 109static long msec_loops; 110static 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 117static unsigned short *whitescreen; 118static 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); } 127extern int sc_height; 128#endif 129 130#if MSDOS_COMPILER==WIN32C 131struct keyRecord 132{ 133 int ascii; 134 int scan; 135} currentKey; 136 137static int keyCount = 0; 138static WORD curr_attr; 139static int pending_scancode = 0; 140static char x11mousebuf[] = "[M???"; /* Mouse report, after ESC */ 141static int x11mousePos, x11mouseCount; 142 143static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ 144static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ 145HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ 146 147extern int utf_mode; 148extern int quitting; 149static void win32_init_term(); 150static 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 167public int nm_fg_color; /* Color of normal text */ 168public int nm_bg_color; 169public int bo_fg_color; /* Color of bold text */ 170public int bo_bg_color; 171public int ul_fg_color; /* Color of underlined text */ 172public int ul_bg_color; 173public int so_fg_color; /* Color of standout text */ 174public int so_bg_color; 175public int bl_fg_color; /* Color of blinking text */ 176public int bl_bg_color; 177static int sy_fg_color; /* Color of system text (before less) */ 178static int sy_bg_color; 179public int sgr_mode; /* Honor ANSI sequences rather than using above */ 180#if MSDOS_COMPILER==WIN32C 181static DWORD init_output_mode; /* The initial console output mode */ 182public 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 */ 189static 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 216static int attrcolor = -1; 217#endif 218 219static int init_done = 0; 220 221public int auto_wrap; /* Terminal does \r\n when write past margin */ 222public int ignaw; /* Terminal ignores \n immediately after wrap */ 223public int erase_char; /* The user's erase char */ 224public int erase2_char; /* The user's other erase char */ 225public int kill_char; /* The user's line-kill char */ 226public int werase_char; /* The user's word-erase char */ 227public int sc_width, sc_height; /* Height & width of screen */ 228public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ 229public int ul_s_width, ul_e_width; /* Printing width of underline seq */ 230public int so_s_width, so_e_width; /* Printing width of standout seq */ 231public int bl_s_width, bl_e_width; /* Printing width of blink seq */ 232public int above_mem, below_mem; /* Memory retained above/below screen */ 233public int can_goto_line; /* Can move cursor to any line */ 234public int clear_bg; /* Clear fills with background color */ 235public int missing_cap = 0; /* Some capability is missing */ 236public char *kent = NULL; /* Keypad ENTER sequence */ 237 238static int attrmode = AT_NORMAL; 239static int termcap_debug = -1; 240extern int binattr; 241extern int one_screen; 242#if LESSTEST 243extern char *ttyin_name; 244#endif /*LESSTEST*/ 245 246#if !MSDOS_COMPILER 247static char *cheaper LESSPARAMS((char *t1, char *t2, char *def)); 248static 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 257extern short ospeed; /* Terminal output baud rate */ 258extern char PC; /* Pad character */ 259#endif 260#ifdef _OSK 261short ospeed; 262char PC_, *UP, *BC; 263#endif 264 265extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 266extern int no_back_scroll; 267extern int swindow; 268extern int no_init; 269extern int no_keypad; 270extern int sigs; 271extern int wscroll; 272extern int screen_trashed; 273extern int top_scroll; 274extern int quit_if_one_screen; 275extern int oldbot; 276extern int mousecap; 277extern int is_tty; 278extern int use_color; 279#if HILITE_SEARCH 280extern int hilite_search; 281#endif 282#if MSDOS_COMPILER==WIN32C 283extern HANDLE tty; 284extern 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 290extern int tty; 291#endif 292 293extern char *tgetstr(); 294extern 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 309raw_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 */ 671static int hardcopy; 672 673 static char * 674ltget_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 700ltgetflag(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 713ltgetnum(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 * 726ltgetstr(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 744scrsize(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 874get_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 902delay(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 * 919special_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 1088get_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 */ 1395static int costcount; 1396 1397/*ARGSUSED*/ 1398 static int 1399inc_costcount(c) 1400 int c; 1401{ 1402 costcount++; 1403 return (c); 1404} 1405 1406 static int 1407cost(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 * 1421cheaper(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 1440tmodes(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 1496initcolor(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 1532win32_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 1550win32_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 1560win32_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 1595win32_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 1609do_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 1627ltputs(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 1674init_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 1692deinit_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 1708init(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 1757deinit(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 1795interactive(VOID_PARAM) 1796{ 1797 return (is_tty && init_done); 1798} 1799 1800 static void 1801assert_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 1811home(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 1827add_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 1886remove_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 1940win32_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 1971win32_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 2037lower_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 2052line_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 2087check_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 2117goto_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 2137create_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 2184vbell(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 2248beep(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 2265bell(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 2277clear(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 2297clear_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 2357clear_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 2375clear_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 2409parse_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 2439parse_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 2463parse_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 2496sgr_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 2524tput_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 2538tput_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 2571tput_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 2592tput_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 2606WIN32put_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 2618win_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 2664at_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 2702at_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 2719at_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 2733is_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 2744apply_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 2760putbs(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 2804win32_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 2913WIN32getch(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 2950WIN32setcolors(fg, bg) 2951 int fg; 2952 int bg; 2953{ 2954 SETCOLORS(fg, bg); 2955} 2956 2957/* 2958 */ 2959 public void 2960WIN32textout(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