screen.c revision 128348
1/* $FreeBSD: head/contrib/less/screen.c 128348 2004-04-17 07:24:09Z tjr $ */ 2/* 3 * Copyright (C) 1984-2002 Mark Nudelman 4 * 5 * You may distribute under the terms of either the GNU General Public 6 * License or the Less License, as specified in the README file. 7 * 8 * For more information about less, or for information on how to 9 * contact the author, see the README file. 10 */ 11 12 13/* 14 * Routines which deal with the characteristics of the terminal. 15 * Uses termcap to be as terminal-independent as possible. 16 */ 17 18#include "less.h" 19#include "cmd.h" 20 21#if MSDOS_COMPILER 22#include "pckeys.h" 23#if MSDOS_COMPILER==MSOFTC 24#include <graph.h> 25#else 26#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 27#include <conio.h> 28#if MSDOS_COMPILER==DJGPPC 29#include <pc.h> 30extern int fd0; 31#endif 32#else 33#if MSDOS_COMPILER==WIN32C 34#include <windows.h> 35#endif 36#endif 37#endif 38#include <time.h> 39 40#else 41 42#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 43#include <termios.h> 44#if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ) 45#include <sys/ioctl.h> 46#endif 47#else 48#if HAVE_TERMIO_H 49#include <termio.h> 50#else 51#if HAVE_SGSTAT_H 52#include <sgstat.h> 53#else 54#include <sgtty.h> 55#endif 56#if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD)) 57#include <sys/ioctl.h> 58#endif 59#endif 60#endif 61 62#if HAVE_TERMCAP_H 63#include <termcap.h> 64#endif 65#ifdef _OSK 66#include <signal.h> 67#endif 68#if OS2 69#include <sys/signal.h> 70#include "pckeys.h" 71#endif 72#if HAVE_SYS_STREAM_H 73#include <sys/stream.h> 74#endif 75#if HAVE_SYS_PTEM_H 76#include <sys/ptem.h> 77#endif 78 79#endif /* MSDOS_COMPILER */ 80 81/* 82 * Check for broken termios package that forces you to manually 83 * set the line discipline. 84 */ 85#ifdef __ultrix__ 86#define MUST_SET_LINE_DISCIPLINE 1 87#else 88#define MUST_SET_LINE_DISCIPLINE 0 89#endif 90 91#if OS2 92#define DEFAULT_TERM "ansi" 93static char *windowid; 94#else 95#define DEFAULT_TERM "unknown" 96#endif 97 98#if MSDOS_COMPILER==MSOFTC 99static int videopages; 100static long msec_loops; 101static int flash_created = 0; 102#define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); } 103#endif 104 105#if MSDOS_COMPILER==BORLANDC 106static unsigned short *whitescreen; 107static int flash_created = 0; 108#endif 109#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 110#define _settextposition(y,x) gotoxy(x,y) 111#define _clearscreen(m) clrscr() 112#define _outtext(s) cputs(s) 113#define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); } 114extern int sc_height; 115#endif 116 117#if MSDOS_COMPILER==WIN32C 118struct keyRecord 119{ 120 int ascii; 121 int scan; 122} currentKey; 123 124static int keyCount = 0; 125static WORD curr_attr; 126static int pending_scancode = 0; 127static WORD *whitescreen; 128 129static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ 130static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ 131HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ 132 133extern int quitting; 134static void win32_init_term(); 135static void win32_deinit_term(); 136 137#define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) 138#define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) 139#define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) 140#define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \ 141 if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ 142 error("SETCOLORS failed"); } 143#endif 144 145#if MSDOS_COMPILER 146public int nm_fg_color; /* Color of normal text */ 147public int nm_bg_color; 148public int bo_fg_color; /* Color of bold text */ 149public int bo_bg_color; 150public int ul_fg_color; /* Color of underlined text */ 151public int ul_bg_color; 152public int so_fg_color; /* Color of standout text */ 153public int so_bg_color; 154public int bl_fg_color; /* Color of blinking text */ 155public int bl_bg_color; 156static int sy_fg_color; /* Color of system text (before less) */ 157static int sy_bg_color; 158 159#else 160 161/* 162 * Strings passed to tputs() to do various terminal functions. 163 */ 164static char 165 *sc_pad, /* Pad string */ 166 *sc_home, /* Cursor home */ 167 *sc_addline, /* Add line, scroll down following lines */ 168 *sc_lower_left, /* Cursor to last line, first column */ 169 *sc_move, /* General cursor positioning */ 170 *sc_clear, /* Clear screen */ 171 *sc_eol_clear, /* Clear to end of line */ 172 *sc_eos_clear, /* Clear to end of screen */ 173 *sc_s_in, /* Enter standout (highlighted) mode */ 174 *sc_s_out, /* Exit standout mode */ 175 *sc_u_in, /* Enter underline mode */ 176 *sc_u_out, /* Exit underline mode */ 177 *sc_b_in, /* Enter bold mode */ 178 *sc_b_out, /* Exit bold mode */ 179 *sc_bl_in, /* Enter blink mode */ 180 *sc_bl_out, /* Exit blink mode */ 181 *sc_visual_bell, /* Visual bell (flash screen) sequence */ 182 *sc_backspace, /* Backspace cursor */ 183 *sc_s_keypad, /* Start keypad mode */ 184 *sc_e_keypad, /* End keypad mode */ 185 *sc_init, /* Startup terminal initialization */ 186 *sc_deinit; /* Exit terminal de-initialization */ 187#endif 188 189static int init_done = 0; 190 191public int auto_wrap; /* Terminal does \r\n when write past margin */ 192public int ignaw; /* Terminal ignores \n immediately after wrap */ 193public int erase_char, kill_char; /* The user's erase and line-kill chars */ 194public int werase_char; /* The user's word-erase char */ 195public int sc_width, sc_height; /* Height & width of screen */ 196public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ 197public int ul_s_width, ul_e_width; /* Printing width of underline seq */ 198public int so_s_width, so_e_width; /* Printing width of standout seq */ 199public int bl_s_width, bl_e_width; /* Printing width of blink seq */ 200public int above_mem, below_mem; /* Memory retained above/below screen */ 201public int can_goto_line; /* Can move cursor to any line */ 202public int clear_bg; /* Clear fills with background color */ 203public int missing_cap = 0; /* Some capability is missing */ 204 205static int attrmode = AT_NORMAL; 206 207#if !MSDOS_COMPILER 208static char *cheaper(); 209static void tmodes(); 210#endif 211 212/* 213 * These two variables are sometimes defined in, 214 * and needed by, the termcap library. 215 */ 216#if MUST_DEFINE_OSPEED 217extern short ospeed; /* Terminal output baud rate */ 218extern char PC; /* Pad character */ 219#endif 220#ifdef _OSK 221short ospeed; 222char PC_, *UP, *BC; 223#endif 224 225extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 226extern int no_back_scroll; 227extern int swindow; 228extern int no_init; 229extern int quit_at_eof; 230extern int more_mode; 231extern int no_keypad; 232extern int sigs; 233extern int wscroll; 234extern int screen_trashed; 235extern int tty; 236#if HILITE_SEARCH 237extern int hilite_search; 238#endif 239 240extern char *tgetstr(); 241extern char *tgoto(); 242 243 244/* 245 * Change terminal to "raw mode", or restore to "normal" mode. 246 * "Raw mode" means 247 * 1. An outstanding read will complete on receipt of a single keystroke. 248 * 2. Input is not echoed. 249 * 3. On output, \n is mapped to \r\n. 250 * 4. \t is NOT expanded into spaces. 251 * 5. Signal-causing characters such as ctrl-C (interrupt), 252 * etc. are NOT disabled. 253 * It doesn't matter whether an input \n is mapped to \r, or vice versa. 254 */ 255 public void 256raw_mode(on) 257 int on; 258{ 259 static int curr_on = 0; 260 261 if (on == curr_on) 262 return; 263#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 264 { 265 struct termios s; 266 static struct termios save_term; 267 static int saved_term = 0; 268 269 if (on) 270 { 271 /* 272 * Get terminal modes. 273 */ 274 tcgetattr(tty, &s); 275 276 /* 277 * Save modes and set certain variables dependent on modes. 278 */ 279 if (!saved_term) 280 { 281 save_term = s; 282 saved_term = 1; 283 } 284#if HAVE_OSPEED 285 switch (cfgetospeed(&s)) 286 { 287#ifdef B0 288 case B0: ospeed = 0; break; 289#endif 290#ifdef B50 291 case B50: ospeed = 1; break; 292#endif 293#ifdef B75 294 case B75: ospeed = 2; break; 295#endif 296#ifdef B110 297 case B110: ospeed = 3; break; 298#endif 299#ifdef B134 300 case B134: ospeed = 4; break; 301#endif 302#ifdef B150 303 case B150: ospeed = 5; break; 304#endif 305#ifdef B200 306 case B200: ospeed = 6; break; 307#endif 308#ifdef B300 309 case B300: ospeed = 7; break; 310#endif 311#ifdef B600 312 case B600: ospeed = 8; break; 313#endif 314#ifdef B1200 315 case B1200: ospeed = 9; break; 316#endif 317#ifdef B1800 318 case B1800: ospeed = 10; break; 319#endif 320#ifdef B2400 321 case B2400: ospeed = 11; break; 322#endif 323#ifdef B4800 324 case B4800: ospeed = 12; break; 325#endif 326#ifdef B9600 327 case B9600: ospeed = 13; break; 328#endif 329#ifdef EXTA 330 case EXTA: ospeed = 14; break; 331#endif 332#ifdef EXTB 333 case EXTB: ospeed = 15; break; 334#endif 335#ifdef B57600 336 case B57600: ospeed = 16; break; 337#endif 338#ifdef B115200 339 case B115200: ospeed = 17; break; 340#endif 341 default: ; 342 } 343#endif 344 erase_char = s.c_cc[VERASE]; 345 kill_char = s.c_cc[VKILL]; 346#ifdef VWERASE 347 werase_char = s.c_cc[VWERASE]; 348#else 349 werase_char = CONTROL('W'); 350#endif 351 352 /* 353 * Set the modes to the way we want them. 354 */ 355 s.c_lflag &= ~(0 356#ifdef ICANON 357 | ICANON 358#endif 359#ifdef ECHO 360 | ECHO 361#endif 362#ifdef ECHOE 363 | ECHOE 364#endif 365#ifdef ECHOK 366 | ECHOK 367#endif 368#if ECHONL 369 | ECHONL 370#endif 371 ); 372 373 s.c_oflag |= (0 374#ifdef OXTABS 375 | OXTABS 376#else 377#ifdef TAB3 378 | TAB3 379#else 380#ifdef XTABS 381 | XTABS 382#endif 383#endif 384#endif 385#ifdef OPOST 386 | OPOST 387#endif 388#ifdef ONLCR 389 | ONLCR 390#endif 391 ); 392 393 s.c_oflag &= ~(0 394#ifdef ONOEOT 395 | ONOEOT 396#endif 397#ifdef OCRNL 398 | OCRNL 399#endif 400#ifdef ONOCR 401 | ONOCR 402#endif 403#ifdef ONLRET 404 | ONLRET 405#endif 406 ); 407 s.c_cc[VMIN] = 1; 408 s.c_cc[VTIME] = 0; 409#ifdef VLNEXT 410 s.c_cc[VLNEXT] = 0; 411#endif 412#ifdef VDSUSP 413 s.c_cc[VDSUSP] = 0; 414#endif 415#if MUST_SET_LINE_DISCIPLINE 416 /* 417 * System's termios is broken; need to explicitly 418 * request TERMIODISC line discipline. 419 */ 420 s.c_line = TERMIODISC; 421#endif 422 } else 423 { 424 /* 425 * Restore saved modes. 426 */ 427 s = save_term; 428 } 429#if HAVE_FSYNC 430 fsync(tty); 431#endif 432 tcsetattr(tty, TCSADRAIN, &s); 433#if MUST_SET_LINE_DISCIPLINE 434 if (!on) 435 { 436 /* 437 * Broken termios *ignores* any line discipline 438 * except TERMIODISC. A different old line discipline 439 * is therefore not restored, yet. Restore the old 440 * line discipline by hand. 441 */ 442 ioctl(tty, TIOCSETD, &save_term.c_line); 443 } 444#endif 445 } 446#else 447#ifdef TCGETA 448 { 449 struct termio s; 450 static struct termio save_term; 451 static int saved_term = 0; 452 453 if (on) 454 { 455 /* 456 * Get terminal modes. 457 */ 458 ioctl(tty, TCGETA, &s); 459 460 /* 461 * Save modes and set certain variables dependent on modes. 462 */ 463 if (!saved_term) 464 { 465 save_term = s; 466 saved_term = 1; 467 } 468#if HAVE_OSPEED 469 ospeed = s.c_cflag & CBAUD; 470#endif 471 erase_char = s.c_cc[VERASE]; 472 kill_char = s.c_cc[VKILL]; 473#ifdef VWERASE 474 werase_char = s.c_cc[VWERASE]; 475#else 476 werase_char = CONTROL('W'); 477#endif 478 479 /* 480 * Set the modes to the way we want them. 481 */ 482 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); 483 s.c_oflag |= (OPOST|ONLCR|TAB3); 484 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); 485 s.c_cc[VMIN] = 1; 486 s.c_cc[VTIME] = 0; 487 } else 488 { 489 /* 490 * Restore saved modes. 491 */ 492 s = save_term; 493 } 494 ioctl(tty, TCSETAW, &s); 495 } 496#else 497#ifdef TIOCGETP 498 { 499 struct sgttyb s; 500 static struct sgttyb save_term; 501 static int saved_term = 0; 502 503 if (on) 504 { 505 /* 506 * Get terminal modes. 507 */ 508 ioctl(tty, TIOCGETP, &s); 509 510 /* 511 * Save modes and set certain variables dependent on modes. 512 */ 513 if (!saved_term) 514 { 515 save_term = s; 516 saved_term = 1; 517 } 518#if HAVE_OSPEED 519 ospeed = s.sg_ospeed; 520#endif 521 erase_char = s.sg_erase; 522 kill_char = s.sg_kill; 523 werase_char = CONTROL('W'); 524 525 /* 526 * Set the modes to the way we want them. 527 */ 528 s.sg_flags |= CBREAK; 529 s.sg_flags &= ~(ECHO|XTABS); 530 } else 531 { 532 /* 533 * Restore saved modes. 534 */ 535 s = save_term; 536 } 537 ioctl(tty, TIOCSETN, &s); 538 } 539#else 540#ifdef _OSK 541 { 542 struct sgbuf s; 543 static struct sgbuf save_term; 544 static int saved_term = 0; 545 546 if (on) 547 { 548 /* 549 * Get terminal modes. 550 */ 551 _gs_opt(tty, &s); 552 553 /* 554 * Save modes and set certain variables dependent on modes. 555 */ 556 if (!saved_term) 557 { 558 save_term = s; 559 saved_term = 1; 560 } 561 erase_char = s.sg_bspch; 562 kill_char = s.sg_dlnch; 563 werase_char = CONTROL('W'); 564 565 /* 566 * Set the modes to the way we want them. 567 */ 568 s.sg_echo = 0; 569 s.sg_eofch = 0; 570 s.sg_pause = 0; 571 s.sg_psch = 0; 572 } else 573 { 574 /* 575 * Restore saved modes. 576 */ 577 s = save_term; 578 } 579 _ss_opt(tty, &s); 580 } 581#else 582 /* MS-DOS, Windows, or OS2 */ 583#if OS2 584 /* OS2 */ 585 LSIGNAL(SIGINT, SIG_IGN); 586#endif 587 erase_char = '\b'; 588#if MSDOS_COMPILER==DJGPPC 589 kill_char = CONTROL('U'); 590 /* 591 * So that when we shell out or run another program, its 592 * stdin is in cooked mode. We do not switch stdin to binary 593 * mode if fd0 is zero, since that means we were called before 594 * tty was reopened in open_getchr, in which case we would be 595 * changing the original stdin device outside less. 596 */ 597 if (fd0 != 0) 598 setmode(0, on ? O_BINARY : O_TEXT); 599#else 600 kill_char = ESC; 601#endif 602 werase_char = CONTROL('W'); 603#endif 604#endif 605#endif 606#endif 607 curr_on = on; 608} 609 610#if !MSDOS_COMPILER 611/* 612 * Some glue to prevent calling termcap functions if tgetent() failed. 613 */ 614static int hardcopy; 615 616 static char * 617ltget_env(capname) 618 char *capname; 619{ 620 char name[16]; 621 622 strcpy(name, "LESS_TERMCAP_"); 623 strcat(name, capname); 624 return (lgetenv(name)); 625} 626 627 static int 628ltgetflag(capname) 629 char *capname; 630{ 631 char *s; 632 633 if ((s = ltget_env(capname)) != NULL) 634 return (*s != '\0' && *s != '0'); 635 if (hardcopy) 636 return (0); 637 return (tgetflag(capname)); 638} 639 640 static int 641ltgetnum(capname) 642 char *capname; 643{ 644 char *s; 645 646 if ((s = ltget_env(capname)) != NULL) 647 return (atoi(s)); 648 if (hardcopy) 649 return (-1); 650 return (tgetnum(capname)); 651} 652 653 static char * 654ltgetstr(capname, pp) 655 char *capname; 656 char **pp; 657{ 658 char *s; 659 660 if ((s = ltget_env(capname)) != NULL) 661 return (s); 662 if (hardcopy) 663 return (NULL); 664 return (tgetstr(capname, pp)); 665} 666#endif /* MSDOS_COMPILER */ 667 668/* 669 * Get size of the output screen. 670 */ 671 public void 672scrsize() 673{ 674 register char *s; 675 int sys_height; 676 int sys_width; 677#if !MSDOS_COMPILER 678 int n; 679#endif 680 681#define DEF_SC_WIDTH 80 682#if MSDOS_COMPILER 683#define DEF_SC_HEIGHT 25 684#else 685#define DEF_SC_HEIGHT 24 686#endif 687 688 689 sys_width = sys_height = 0; 690 691#if MSDOS_COMPILER==MSOFTC 692 { 693 struct videoconfig w; 694 _getvideoconfig(&w); 695 sys_height = w.numtextrows; 696 sys_width = w.numtextcols; 697 } 698#else 699#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 700 { 701 struct text_info w; 702 gettextinfo(&w); 703 sys_height = w.screenheight; 704 sys_width = w.screenwidth; 705 } 706#else 707#if MSDOS_COMPILER==WIN32C 708 { 709 CONSOLE_SCREEN_BUFFER_INFO scr; 710 GetConsoleScreenBufferInfo(con_out, &scr); 711 sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1; 712 sys_width = scr.srWindow.Right - scr.srWindow.Left + 1; 713 } 714#else 715#if OS2 716 { 717 int s[2]; 718 _scrsize(s); 719 sys_width = s[0]; 720 sys_height = s[1]; 721 /* 722 * When using terminal emulators for XFree86/OS2, the 723 * _scrsize function does not work well. 724 * Call the scrsize.exe program to get the window size. 725 */ 726 windowid = getenv("WINDOWID"); 727 if (windowid != NULL) 728 { 729 FILE *fd = popen("scrsize", "rt"); 730 if (fd != NULL) 731 { 732 int w, h; 733 fscanf(fd, "%i %i", &w, &h); 734 if (w > 0 && h > 0) 735 { 736 sys_width = w; 737 sys_height = h; 738 } 739 pclose(fd); 740 } 741 } 742 } 743#else 744#ifdef TIOCGWINSZ 745 { 746 struct winsize w; 747 if (ioctl(2, TIOCGWINSZ, &w) == 0) 748 { 749 if (w.ws_row > 0) 750 sys_height = w.ws_row; 751 if (w.ws_col > 0) 752 sys_width = w.ws_col; 753 } 754 } 755#else 756#ifdef WIOCGETD 757 { 758 struct uwdata w; 759 if (ioctl(2, WIOCGETD, &w) == 0) 760 { 761 if (w.uw_height > 0) 762 sys_height = w.uw_height / w.uw_vs; 763 if (w.uw_width > 0) 764 sys_width = w.uw_width / w.uw_hs; 765 } 766 } 767#endif 768#endif 769#endif 770#endif 771#endif 772#endif 773 774 if (sys_height > 0) 775 sc_height = sys_height; 776 else if ((s = lgetenv("LINES")) != NULL) 777 sc_height = atoi(s); 778#if !MSDOS_COMPILER 779 else if ((n = ltgetnum("li")) > 0) 780 sc_height = n; 781#endif 782 else 783 sc_height = DEF_SC_HEIGHT; 784 785 if (sys_width > 0) 786 sc_width = sys_width; 787 else if ((s = lgetenv("COLUMNS")) != NULL) 788 sc_width = atoi(s); 789#if !MSDOS_COMPILER 790 else if ((n = ltgetnum("co")) > 0) 791 sc_width = n; 792#endif 793 else 794 sc_width = DEF_SC_WIDTH; 795} 796 797#if MSDOS_COMPILER==MSOFTC 798/* 799 * Figure out how many empty loops it takes to delay a millisecond. 800 */ 801 static void 802get_clock() 803{ 804 clock_t start; 805 806 /* 807 * Get synchronized at the start of a tick. 808 */ 809 start = clock(); 810 while (clock() == start) 811 ; 812 /* 813 * Now count loops till the next tick. 814 */ 815 start = clock(); 816 msec_loops = 0; 817 while (clock() == start) 818 msec_loops++; 819 /* 820 * Convert from (loops per clock) to (loops per millisecond). 821 */ 822 msec_loops *= CLOCKS_PER_SEC; 823 msec_loops /= 1000; 824} 825 826/* 827 * Delay for a specified number of milliseconds. 828 */ 829 static void 830dummy_func() 831{ 832 static long delay_dummy = 0; 833 delay_dummy++; 834} 835 836 static void 837delay(msec) 838 int msec; 839{ 840 long i; 841 842 while (msec-- > 0) 843 { 844 for (i = 0; i < msec_loops; i++) 845 { 846 /* 847 * Make it look like we're doing something here, 848 * so the optimizer doesn't remove the whole loop. 849 */ 850 dummy_func(); 851 } 852 } 853} 854#endif 855 856/* 857 * Return the characters actually input by a "special" key. 858 */ 859 public char * 860special_key_str(key) 861 int key; 862{ 863 static char tbuf[40]; 864 char *s; 865#if MSDOS_COMPILER || OS2 866 static char k_right[] = { '\340', PCK_RIGHT, 0 }; 867 static char k_left[] = { '\340', PCK_LEFT, 0 }; 868 static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 }; 869 static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 }; 870 static char k_insert[] = { '\340', PCK_INSERT, 0 }; 871 static char k_delete[] = { '\340', PCK_DELETE, 0 }; 872 static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 }; 873 static char k_ctl_backspace[] = { '\177', 0 }; 874 static char k_home[] = { '\340', PCK_HOME, 0 }; 875 static char k_end[] = { '\340', PCK_END, 0 }; 876 static char k_up[] = { '\340', PCK_UP, 0 }; 877 static char k_down[] = { '\340', PCK_DOWN, 0 }; 878 static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 }; 879 static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 }; 880 static char k_pageup[] = { '\340', PCK_PAGEUP, 0 }; 881 static char k_f1[] = { '\340', PCK_F1, 0 }; 882#endif 883#if !MSDOS_COMPILER 884 char *sp = tbuf; 885#endif 886 887 switch (key) 888 { 889#if OS2 890 /* 891 * If windowid is not NULL, assume less is executed in 892 * the XFree86 environment. 893 */ 894 case SK_RIGHT_ARROW: 895 s = windowid ? ltgetstr("kr", &sp) : k_right; 896 break; 897 case SK_LEFT_ARROW: 898 s = windowid ? ltgetstr("kl", &sp) : k_left; 899 break; 900 case SK_UP_ARROW: 901 s = windowid ? ltgetstr("ku", &sp) : k_up; 902 break; 903 case SK_DOWN_ARROW: 904 s = windowid ? ltgetstr("kd", &sp) : k_down; 905 break; 906 case SK_PAGE_UP: 907 s = windowid ? ltgetstr("kP", &sp) : k_pageup; 908 break; 909 case SK_PAGE_DOWN: 910 s = windowid ? ltgetstr("kN", &sp) : k_pagedown; 911 break; 912 case SK_HOME: 913 s = windowid ? ltgetstr("kh", &sp) : k_home; 914 break; 915 case SK_END: 916 s = windowid ? ltgetstr("@7", &sp) : k_end; 917 break; 918 case SK_DELETE: 919 if (windowid) 920 { 921 s = ltgetstr("kD", &sp); 922 if (s == NULL) 923 { 924 tbuf[0] = '\177'; 925 tbuf[1] = '\0'; 926 s = tbuf; 927 } 928 } else 929 s = k_delete; 930 break; 931#endif 932#if MSDOS_COMPILER 933 case SK_RIGHT_ARROW: 934 s = k_right; 935 break; 936 case SK_LEFT_ARROW: 937 s = k_left; 938 break; 939 case SK_UP_ARROW: 940 s = k_up; 941 break; 942 case SK_DOWN_ARROW: 943 s = k_down; 944 break; 945 case SK_PAGE_UP: 946 s = k_pageup; 947 break; 948 case SK_PAGE_DOWN: 949 s = k_pagedown; 950 break; 951 case SK_HOME: 952 s = k_home; 953 break; 954 case SK_END: 955 s = k_end; 956 break; 957 case SK_DELETE: 958 s = k_delete; 959 break; 960#endif 961#if MSDOS_COMPILER || OS2 962 case SK_INSERT: 963 s = k_insert; 964 break; 965 case SK_CTL_LEFT_ARROW: 966 s = k_ctl_left; 967 break; 968 case SK_CTL_RIGHT_ARROW: 969 s = k_ctl_right; 970 break; 971 case SK_CTL_BACKSPACE: 972 s = k_ctl_backspace; 973 break; 974 case SK_CTL_DELETE: 975 s = k_ctl_delete; 976 break; 977 case SK_F1: 978 s = k_f1; 979 break; 980 case SK_BACKTAB: 981 s = k_backtab; 982 break; 983#else 984 case SK_RIGHT_ARROW: 985 s = ltgetstr("kr", &sp); 986 break; 987 case SK_LEFT_ARROW: 988 s = ltgetstr("kl", &sp); 989 break; 990 case SK_UP_ARROW: 991 s = ltgetstr("ku", &sp); 992 break; 993 case SK_DOWN_ARROW: 994 s = ltgetstr("kd", &sp); 995 break; 996 case SK_PAGE_UP: 997 s = ltgetstr("kP", &sp); 998 break; 999 case SK_PAGE_DOWN: 1000 s = ltgetstr("kN", &sp); 1001 break; 1002 case SK_HOME: 1003 s = ltgetstr("kh", &sp); 1004 break; 1005 case SK_END: 1006 s = ltgetstr("@7", &sp); 1007 break; 1008 case SK_DELETE: 1009 s = ltgetstr("kD", &sp); 1010 if (s == NULL) 1011 { 1012 tbuf[0] = '\177'; 1013 tbuf[1] = '\0'; 1014 s = tbuf; 1015 } 1016 break; 1017#endif 1018 case SK_CONTROL_K: 1019 tbuf[0] = CONTROL('K'); 1020 tbuf[1] = '\0'; 1021 s = tbuf; 1022 break; 1023 default: 1024 return (NULL); 1025 } 1026 return (s); 1027} 1028 1029/* 1030 * Get terminal capabilities via termcap. 1031 */ 1032 public void 1033get_term() 1034{ 1035#if MSDOS_COMPILER 1036 auto_wrap = 1; 1037 ignaw = 0; 1038 can_goto_line = 1; 1039 clear_bg = 1; 1040 /* 1041 * Set up default colors. 1042 * The xx_s_width and xx_e_width vars are already initialized to 0. 1043 */ 1044#if MSDOS_COMPILER==MSOFTC 1045 sy_bg_color = _getbkcolor(); 1046 sy_fg_color = _gettextcolor(); 1047 get_clock(); 1048#else 1049#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1050 { 1051 struct text_info w; 1052 gettextinfo(&w); 1053 sy_bg_color = (w.attribute >> 4) & 0x0F; 1054 sy_fg_color = (w.attribute >> 0) & 0x0F; 1055 } 1056#else 1057#if MSDOS_COMPILER==WIN32C 1058 { 1059 DWORD nread; 1060 CONSOLE_SCREEN_BUFFER_INFO scr; 1061 1062 con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE); 1063 /* 1064 * Always open stdin in binary. Note this *must* be done 1065 * before any file operations have been done on fd0. 1066 */ 1067 SET_BINARY(0); 1068 GetConsoleScreenBufferInfo(con_out, &scr); 1069 ReadConsoleOutputAttribute(con_out, &curr_attr, 1070 1, scr.dwCursorPosition, &nread); 1071 sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */ 1072 sy_fg_color = curr_attr & FG_COLORS; 1073 } 1074#endif 1075#endif 1076#endif 1077 nm_fg_color = sy_fg_color; 1078 nm_bg_color = sy_bg_color; 1079 bo_fg_color = 11; 1080 bo_bg_color = 0; 1081 ul_fg_color = 9; 1082 ul_bg_color = 0; 1083 so_fg_color = 15; 1084 so_bg_color = 9; 1085 bl_fg_color = 15; 1086 bl_bg_color = 0; 1087 1088 /* 1089 * Get size of the screen. 1090 */ 1091 scrsize(); 1092 pos_init(); 1093 1094 1095#else /* !MSDOS_COMPILER */ 1096 1097 char *sp; 1098 register char *t1, *t2; 1099 char *term; 1100 char termbuf[TERMBUF_SIZE]; 1101 1102 static char sbuf[TERMSBUF_SIZE]; 1103 1104#if OS2 1105 /* 1106 * Make sure the termcap database is available. 1107 */ 1108 sp = lgetenv("TERMCAP"); 1109 if (sp == NULL || *sp == '\0') 1110 { 1111 char *termcap; 1112 if ((sp = homefile("termcap.dat")) != NULL) 1113 { 1114 termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); 1115 sprintf(termcap, "TERMCAP=%s", sp); 1116 free(sp); 1117 putenv(termcap); 1118 } 1119 } 1120#endif 1121 /* 1122 * Find out what kind of terminal this is. 1123 */ 1124 if ((term = lgetenv("TERM")) == NULL) 1125 term = DEFAULT_TERM; 1126 hardcopy = 0; 1127 if (tgetent(termbuf, term) <= 0) 1128 hardcopy = 1; 1129 if (ltgetflag("hc")) 1130 hardcopy = 1; 1131 1132 /* 1133 * Get size of the screen. 1134 */ 1135 scrsize(); 1136 pos_init(); 1137 1138 auto_wrap = ltgetflag("am"); 1139 ignaw = ltgetflag("xn"); 1140 above_mem = ltgetflag("da"); 1141 below_mem = ltgetflag("db"); 1142 clear_bg = ltgetflag("ut"); 1143 1144 /* 1145 * Assumes termcap variable "sg" is the printing width of: 1146 * the standout sequence, the end standout sequence, 1147 * the underline sequence, the end underline sequence, 1148 * the boldface sequence, and the end boldface sequence. 1149 */ 1150 if ((so_s_width = ltgetnum("sg")) < 0) 1151 so_s_width = 0; 1152 so_e_width = so_s_width; 1153 1154 bo_s_width = bo_e_width = so_s_width; 1155 ul_s_width = ul_e_width = so_s_width; 1156 bl_s_width = bl_e_width = so_s_width; 1157 1158#if HILITE_SEARCH 1159 if (so_s_width > 0 || so_e_width > 0) 1160 /* 1161 * Disable highlighting by default on magic cookie terminals. 1162 * Turning on highlighting might change the displayed width 1163 * of a line, causing the display to get messed up. 1164 * The user can turn it back on with -g, 1165 * but she won't like the results. 1166 */ 1167 hilite_search = 0; 1168#endif 1169 1170 /* 1171 * Get various string-valued capabilities. 1172 */ 1173 sp = sbuf; 1174 1175#if HAVE_OSPEED 1176 sc_pad = ltgetstr("pc", &sp); 1177 if (sc_pad != NULL) 1178 PC = *sc_pad; 1179#endif 1180 1181 sc_s_keypad = ltgetstr("ks", &sp); 1182 if (sc_s_keypad == NULL) 1183 sc_s_keypad = ""; 1184 sc_e_keypad = ltgetstr("ke", &sp); 1185 if (sc_e_keypad == NULL) 1186 sc_e_keypad = ""; 1187 1188 /* 1189 * This loses for terminals with termcap entries with ti/te strings 1190 * that switch to/from an alternate screen, and we're in quit_at_eof 1191 * (eg, more(1)). 1192 */ 1193 if (!quit_at_eof && !more_mode) { 1194 sc_init = ltgetstr("ti", &sp); 1195 sc_deinit = ltgetstr("te", &sp); 1196 } 1197 1198 if (sc_init == NULL) 1199 sc_init = ""; 1200 1201 if (sc_deinit == NULL) 1202 sc_deinit = ""; 1203 1204 sc_eol_clear = ltgetstr("ce", &sp); 1205 if (sc_eol_clear == NULL || *sc_eol_clear == '\0') 1206 { 1207 missing_cap = 1; 1208 sc_eol_clear = ""; 1209 } 1210 1211 sc_eos_clear = ltgetstr("cd", &sp); 1212 if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0')) 1213 { 1214 missing_cap = 1; 1215 sc_eol_clear = ""; 1216 } 1217 1218 sc_clear = ltgetstr("cl", &sp); 1219 if (sc_clear == NULL || *sc_clear == '\0') 1220 { 1221 missing_cap = 1; 1222 sc_clear = "\n\n"; 1223 } 1224 1225 sc_move = ltgetstr("cm", &sp); 1226 if (sc_move == NULL || *sc_move == '\0') 1227 { 1228 /* 1229 * This is not an error here, because we don't 1230 * always need sc_move. 1231 * We need it only if we don't have home or lower-left. 1232 */ 1233 sc_move = ""; 1234 can_goto_line = 0; 1235 } else 1236 can_goto_line = 1; 1237 1238 tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp); 1239 tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp); 1240 tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp); 1241 tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp); 1242 1243 sc_visual_bell = ltgetstr("vb", &sp); 1244 if (sc_visual_bell == NULL) 1245 sc_visual_bell = ""; 1246 1247 if (ltgetflag("bs")) 1248 sc_backspace = "\b"; 1249 else 1250 { 1251 sc_backspace = ltgetstr("bc", &sp); 1252 if (sc_backspace == NULL || *sc_backspace == '\0') 1253 sc_backspace = "\b"; 1254 } 1255 1256 /* 1257 * Choose between using "ho" and "cm" ("home" and "cursor move") 1258 * to move the cursor to the upper left corner of the screen. 1259 */ 1260 t1 = ltgetstr("ho", &sp); 1261 if (t1 == NULL) 1262 t1 = ""; 1263 if (*sc_move == '\0') 1264 t2 = ""; 1265 else 1266 { 1267 strcpy(sp, tgoto(sc_move, 0, 0)); 1268 t2 = sp; 1269 sp += strlen(sp) + 1; 1270 } 1271 sc_home = cheaper(t1, t2, "|\b^"); 1272 1273 /* 1274 * Choose between using "ll" and "cm" ("lower left" and "cursor move") 1275 * to move the cursor to the lower left corner of the screen. 1276 */ 1277 t1 = ltgetstr("ll", &sp); 1278 if (t1 == NULL) 1279 t1 = ""; 1280 if (*sc_move == '\0') 1281 t2 = ""; 1282 else 1283 { 1284 strcpy(sp, tgoto(sc_move, 0, sc_height-1)); 1285 t2 = sp; 1286 sp += strlen(sp) + 1; 1287 } 1288 sc_lower_left = cheaper(t1, t2, "\r"); 1289 1290 /* 1291 * Choose between using "al" or "sr" ("add line" or "scroll reverse") 1292 * to add a line at the top of the screen. 1293 */ 1294 t1 = ltgetstr("al", &sp); 1295 if (t1 == NULL) 1296 t1 = ""; 1297 t2 = ltgetstr("sr", &sp); 1298 if (t2 == NULL) 1299 t2 = ""; 1300#if OS2 1301 if (*t1 == '\0' && *t2 == '\0') 1302 sc_addline = ""; 1303 else 1304#endif 1305 if (above_mem) 1306 sc_addline = t1; 1307 else 1308 sc_addline = cheaper(t1, t2, ""); 1309 if (*sc_addline == '\0') 1310 { 1311 /* 1312 * Force repaint on any backward movement. 1313 */ 1314 no_back_scroll = 1; 1315 } 1316#endif /* MSDOS_COMPILER */ 1317} 1318 1319#if !MSDOS_COMPILER 1320/* 1321 * Return the cost of displaying a termcap string. 1322 * We use the trick of calling tputs, but as a char printing function 1323 * we give it inc_costcount, which just increments "costcount". 1324 * This tells us how many chars would be printed by using this string. 1325 * {{ Couldn't we just use strlen? }} 1326 */ 1327static int costcount; 1328 1329/*ARGSUSED*/ 1330 static int 1331inc_costcount(c) 1332 int c; 1333{ 1334 costcount++; 1335 return (c); 1336} 1337 1338 static int 1339cost(t) 1340 char *t; 1341{ 1342 costcount = 0; 1343 tputs(t, sc_height, inc_costcount); 1344 return (costcount); 1345} 1346 1347/* 1348 * Return the "best" of the two given termcap strings. 1349 * The best, if both exist, is the one with the lower 1350 * cost (see cost() function). 1351 */ 1352 static char * 1353cheaper(t1, t2, def) 1354 char *t1, *t2; 1355 char *def; 1356{ 1357 if (*t1 == '\0' && *t2 == '\0') 1358 { 1359 missing_cap = 1; 1360 return (def); 1361 } 1362 if (*t1 == '\0') 1363 return (t2); 1364 if (*t2 == '\0') 1365 return (t1); 1366 if (cost(t1) < cost(t2)) 1367 return (t1); 1368 return (t2); 1369} 1370 1371 static void 1372tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp) 1373 char *incap; 1374 char *outcap; 1375 char **instr; 1376 char **outstr; 1377 char *def_instr; 1378 char *def_outstr; 1379 char **spp; 1380{ 1381 *instr = ltgetstr(incap, spp); 1382 if (*instr == NULL) 1383 { 1384 /* Use defaults. */ 1385 *instr = def_instr; 1386 *outstr = def_outstr; 1387 return; 1388 } 1389 1390 *outstr = ltgetstr(outcap, spp); 1391 if (*outstr == NULL) 1392 /* No specific out capability; use "me". */ 1393 *outstr = ltgetstr("me", spp); 1394 if (*outstr == NULL) 1395 /* Don't even have "me"; use a null string. */ 1396 *outstr = ""; 1397} 1398 1399#endif /* MSDOS_COMPILER */ 1400 1401 1402/* 1403 * Below are the functions which perform all the 1404 * terminal-specific screen manipulation. 1405 */ 1406 1407 1408#if MSDOS_COMPILER 1409 1410#if MSDOS_COMPILER==WIN32C 1411 static void 1412_settextposition(int row, int col) 1413{ 1414 COORD cpos; 1415 CONSOLE_SCREEN_BUFFER_INFO csbi; 1416 1417 GetConsoleScreenBufferInfo(con_out, &csbi); 1418 cpos.X = csbi.srWindow.Left + (col - 1); 1419 cpos.Y = csbi.srWindow.Top + (row - 1); 1420 SetConsoleCursorPosition(con_out, cpos); 1421} 1422#endif 1423 1424/* 1425 * Initialize the screen to the correct color at startup. 1426 */ 1427 static void 1428initcolor() 1429{ 1430 SETCOLORS(nm_fg_color, nm_bg_color); 1431#if 0 1432 /* 1433 * This clears the screen at startup. This is different from 1434 * the behavior of other versions of less. Disable it for now. 1435 */ 1436 char *blanks; 1437 int row; 1438 int col; 1439 1440 /* 1441 * Create a complete, blank screen using "normal" colors. 1442 */ 1443 SETCOLORS(nm_fg_color, nm_bg_color); 1444 blanks = (char *) ecalloc(width+1, sizeof(char)); 1445 for (col = 0; col < sc_width; col++) 1446 blanks[col] = ' '; 1447 blanks[sc_width] = '\0'; 1448 for (row = 0; row < sc_height; row++) 1449 _outtext(blanks); 1450 free(blanks); 1451#endif 1452} 1453#endif 1454 1455#if MSDOS_COMPILER==WIN32C 1456 1457/* 1458 * Termcap-like init with a private win32 console. 1459 */ 1460 static void 1461win32_init_term() 1462{ 1463 CONSOLE_SCREEN_BUFFER_INFO scr; 1464 COORD size; 1465 1466 if (con_out_save == INVALID_HANDLE_VALUE) 1467 return; 1468 1469 GetConsoleScreenBufferInfo(con_out_save, &scr); 1470 1471 if (con_out_ours == INVALID_HANDLE_VALUE) 1472 { 1473 /* 1474 * Create our own screen buffer, so that we 1475 * may restore the original when done. 1476 */ 1477 con_out_ours = CreateConsoleScreenBuffer( 1478 GENERIC_WRITE | GENERIC_READ, 1479 FILE_SHARE_WRITE | FILE_SHARE_READ, 1480 (LPSECURITY_ATTRIBUTES) NULL, 1481 CONSOLE_TEXTMODE_BUFFER, 1482 (LPVOID) NULL); 1483 } 1484 1485 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1486 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1487 SetConsoleScreenBufferSize(con_out_ours, size); 1488 SetConsoleActiveScreenBuffer(con_out_ours); 1489 con_out = con_out_ours; 1490} 1491 1492/* 1493 * Restore the startup console. 1494 */ 1495static void 1496win32_deinit_term() 1497{ 1498 if (con_out_save == INVALID_HANDLE_VALUE) 1499 return; 1500 if (quitting) 1501 (void) CloseHandle(con_out_ours); 1502 SetConsoleActiveScreenBuffer(con_out_save); 1503 con_out = con_out_save; 1504} 1505 1506#endif 1507 1508/* 1509 * Initialize terminal 1510 */ 1511 public void 1512init() 1513{ 1514#if !MSDOS_COMPILER 1515 if (!no_init) 1516 tputs(sc_init, sc_height, putchr); 1517 if (!no_keypad) 1518 tputs(sc_s_keypad, sc_height, putchr); 1519#else 1520#if MSDOS_COMPILER==WIN32C 1521 if (!no_init) 1522 win32_init_term(); 1523#endif 1524 initcolor(); 1525 flush(); 1526#endif 1527 init_done = 1; 1528} 1529 1530/* 1531 * Deinitialize terminal 1532 */ 1533 public void 1534deinit() 1535{ 1536 if (!init_done) 1537 return; 1538#if !MSDOS_COMPILER 1539 if (!no_keypad) 1540 tputs(sc_e_keypad, sc_height, putchr); 1541 if (!no_init) 1542 tputs(sc_deinit, sc_height, putchr); 1543#else 1544 /* Restore system colors. */ 1545 SETCOLORS(sy_fg_color, sy_bg_color); 1546#if MSDOS_COMPILER==WIN32C 1547 if (!no_init) 1548 win32_deinit_term(); 1549#else 1550 /* Need clreol to make SETCOLORS take effect. */ 1551 clreol(); 1552#endif 1553#endif 1554 init_done = 0; 1555} 1556 1557/* 1558 * Home cursor (move to upper left corner of screen). 1559 */ 1560 public void 1561home() 1562{ 1563#if !MSDOS_COMPILER 1564 tputs(sc_home, 1, putchr); 1565#else 1566 flush(); 1567 _settextposition(1,1); 1568#endif 1569} 1570 1571/* 1572 * Add a blank line (called with cursor at home). 1573 * Should scroll the display down. 1574 */ 1575 public void 1576add_line() 1577{ 1578#if !MSDOS_COMPILER 1579 tputs(sc_addline, sc_height, putchr); 1580#else 1581 flush(); 1582#if MSDOS_COMPILER==MSOFTC 1583 _scrolltextwindow(_GSCROLLDOWN); 1584 _settextposition(1,1); 1585#else 1586#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1587 movetext(1,1, sc_width,sc_height-1, 1,2); 1588 gotoxy(1,1); 1589 clreol(); 1590#else 1591#if MSDOS_COMPILER==WIN32C 1592 { 1593 CHAR_INFO fillchar; 1594 SMALL_RECT rcSrc, rcClip; 1595 COORD new_org; 1596 CONSOLE_SCREEN_BUFFER_INFO csbi; 1597 1598 GetConsoleScreenBufferInfo(con_out,&csbi); 1599 1600 /* The clip rectangle is the entire visible screen. */ 1601 rcClip.Left = csbi.srWindow.Left; 1602 rcClip.Top = csbi.srWindow.Top; 1603 rcClip.Right = csbi.srWindow.Right; 1604 rcClip.Bottom = csbi.srWindow.Bottom; 1605 1606 /* The source rectangle is the visible screen minus the last line. */ 1607 rcSrc = rcClip; 1608 rcSrc.Bottom--; 1609 1610 /* Move the top left corner of the source window down one row. */ 1611 new_org.X = rcSrc.Left; 1612 new_org.Y = rcSrc.Top + 1; 1613 1614 /* Fill the right character and attributes. */ 1615 fillchar.Char.AsciiChar = ' '; 1616 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1617 fillchar.Attributes = curr_attr; 1618 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1619 _settextposition(1,1); 1620 } 1621#endif 1622#endif 1623#endif 1624#endif 1625} 1626 1627#if 0 1628/* 1629 * Remove the n topmost lines and scroll everything below it in the 1630 * window upward. This is needed to stop leaking the topmost line 1631 * into the scrollback buffer when we go down-one-line (in WIN32). 1632 */ 1633 public void 1634remove_top(n) 1635 int n; 1636{ 1637#if MSDOS_COMPILER==WIN32C 1638 SMALL_RECT rcSrc, rcClip; 1639 CHAR_INFO fillchar; 1640 COORD new_org; 1641 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 1642 1643 if (n >= sc_height - 1) 1644 { 1645 clear(); 1646 home(); 1647 return; 1648 } 1649 1650 flush(); 1651 1652 GetConsoleScreenBufferInfo(con_out, &csbi); 1653 1654 /* Get the extent of all-visible-rows-but-the-last. */ 1655 rcSrc.Left = csbi.srWindow.Left; 1656 rcSrc.Top = csbi.srWindow.Top + n; 1657 rcSrc.Right = csbi.srWindow.Right; 1658 rcSrc.Bottom = csbi.srWindow.Bottom; 1659 1660 /* Get the clip rectangle. */ 1661 rcClip.Left = rcSrc.Left; 1662 rcClip.Top = csbi.srWindow.Top; 1663 rcClip.Right = rcSrc.Right; 1664 rcClip.Bottom = rcSrc.Bottom ; 1665 1666 /* Move the source window up n rows. */ 1667 new_org.X = rcSrc.Left; 1668 new_org.Y = rcSrc.Top - n; 1669 1670 /* Fill the right character and attributes. */ 1671 fillchar.Char.AsciiChar = ' '; 1672 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1673 fillchar.Attributes = curr_attr; 1674 1675 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1676 1677 /* Position cursor on first blank line. */ 1678 goto_line(sc_height - n - 1); 1679#endif 1680} 1681#endif 1682 1683#if MSDOS_COMPILER==WIN32C 1684/* 1685 * Clear the screen. 1686 */ 1687 static void 1688win32_clear() 1689{ 1690 /* 1691 * This will clear only the currently visible rows of the NT 1692 * console buffer, which means none of the precious scrollback 1693 * rows are touched making for faster scrolling. Note that, if 1694 * the window has fewer columns than the console buffer (i.e. 1695 * there is a horizontal scrollbar as well), the entire width 1696 * of the visible rows will be cleared. 1697 */ 1698 COORD topleft; 1699 DWORD nchars; 1700 DWORD winsz; 1701 CONSOLE_SCREEN_BUFFER_INFO csbi; 1702 1703 /* get the number of cells in the current buffer */ 1704 GetConsoleScreenBufferInfo(con_out, &csbi); 1705 winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 1706 topleft.X = 0; 1707 topleft.Y = csbi.srWindow.Top; 1708 1709 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1710 FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); 1711 FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); 1712} 1713 1714/* 1715 * Remove the n topmost lines and scroll everything below it in the 1716 * window upward. 1717 */ 1718 public void 1719win32_scroll_up(n) 1720 int n; 1721{ 1722 SMALL_RECT rcSrc, rcClip; 1723 CHAR_INFO fillchar; 1724 COORD topleft; 1725 COORD new_org; 1726 DWORD nchars; 1727 DWORD size; 1728 CONSOLE_SCREEN_BUFFER_INFO csbi; 1729 1730 if (n <= 0) 1731 return; 1732 1733 if (n >= sc_height - 1) 1734 { 1735 win32_clear(); 1736 _settextposition(1,1); 1737 return; 1738 } 1739 1740 /* Get the extent of what will remain visible after scrolling. */ 1741 GetConsoleScreenBufferInfo(con_out, &csbi); 1742 rcSrc.Left = csbi.srWindow.Left; 1743 rcSrc.Top = csbi.srWindow.Top + n; 1744 rcSrc.Right = csbi.srWindow.Right; 1745 rcSrc.Bottom = csbi.srWindow.Bottom; 1746 1747 /* Get the clip rectangle. */ 1748 rcClip.Left = rcSrc.Left; 1749 rcClip.Top = csbi.srWindow.Top; 1750 rcClip.Right = rcSrc.Right; 1751 rcClip.Bottom = rcSrc.Bottom ; 1752 1753 /* Move the source text to the top of the screen. */ 1754 new_org.X = rcSrc.Left; 1755 new_org.Y = 0; 1756 1757 /* Fill the right character and attributes. */ 1758 fillchar.Char.AsciiChar = ' '; 1759 fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); 1760 1761 /* Scroll the window. */ 1762 SetConsoleTextAttribute(con_out, fillchar.Attributes); 1763 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1764 1765 /* Clear remaining lines at bottom. */ 1766 topleft.X = csbi.dwCursorPosition.X; 1767 topleft.Y = rcSrc.Bottom - n; 1768 size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); 1769 FillConsoleOutputCharacter(con_out, ' ', size, topleft, 1770 &nchars); 1771 FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, 1772 &nchars); 1773 SetConsoleTextAttribute(con_out, curr_attr); 1774 1775 /* Move cursor n lines up from where it was. */ 1776 csbi.dwCursorPosition.Y -= n; 1777 SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); 1778} 1779#endif 1780 1781/* 1782 * Move cursor to lower left corner of screen. 1783 */ 1784 public void 1785lower_left() 1786{ 1787#if !MSDOS_COMPILER 1788 tputs(sc_lower_left, 1, putchr); 1789#else 1790 flush(); 1791 _settextposition(sc_height, 1); 1792#endif 1793} 1794 1795/* 1796 * Check if the console size has changed and reset internals 1797 * (in lieu of SIGWINCH for WIN32). 1798 */ 1799 public void 1800check_winch() 1801{ 1802#if MSDOS_COMPILER==WIN32C 1803 CONSOLE_SCREEN_BUFFER_INFO scr; 1804 COORD size; 1805 1806 if (con_out == INVALID_HANDLE_VALUE) 1807 return; 1808 1809 flush(); 1810 GetConsoleScreenBufferInfo(con_out, &scr); 1811 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1812 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1813 if (size.Y != sc_height || size.X != sc_width) 1814 { 1815 sc_height = size.Y; 1816 sc_width = size.X; 1817 if (!no_init && con_out_ours == con_out) 1818 SetConsoleScreenBufferSize(con_out, size); 1819 pos_init(); 1820 wscroll = (sc_height + 1) / 2; 1821 screen_trashed = 1; 1822 } 1823#endif 1824} 1825 1826/* 1827 * Goto a specific line on the screen. 1828 */ 1829 public void 1830goto_line(slinenum) 1831 int slinenum; 1832{ 1833#if !MSDOS_COMPILER 1834 tputs(tgoto(sc_move, 0, slinenum), 1, putchr); 1835#else 1836 flush(); 1837 _settextposition(slinenum+1, 1); 1838#endif 1839} 1840 1841#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC 1842/* 1843 * Create an alternate screen which is all white. 1844 * This screen is used to create a "flash" effect, by displaying it 1845 * briefly and then switching back to the normal screen. 1846 * {{ Yuck! There must be a better way to get a visual bell. }} 1847 */ 1848 static void 1849create_flash() 1850{ 1851#if MSDOS_COMPILER==MSOFTC 1852 struct videoconfig w; 1853 char *blanks; 1854 int row, col; 1855 1856 _getvideoconfig(&w); 1857 videopages = w.numvideopages; 1858 if (videopages < 2) 1859 { 1860 so_enter(); 1861 so_exit(); 1862 } else 1863 { 1864 _setactivepage(1); 1865 so_enter(); 1866 blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); 1867 for (col = 0; col < w.numtextcols; col++) 1868 blanks[col] = ' '; 1869 for (row = w.numtextrows; row > 0; row--) 1870 _outmem(blanks, w.numtextcols); 1871 _setactivepage(0); 1872 _setvisualpage(0); 1873 free(blanks); 1874 so_exit(); 1875 } 1876#else 1877#if MSDOS_COMPILER==BORLANDC 1878 register int n; 1879 1880 whitescreen = (unsigned short *) 1881 malloc(sc_width * sc_height * sizeof(short)); 1882 if (whitescreen == NULL) 1883 return; 1884 for (n = 0; n < sc_width * sc_height; n++) 1885 whitescreen[n] = 0x7020; 1886#else 1887#if MSDOS_COMPILER==WIN32C 1888 register int n; 1889 1890 whitescreen = (WORD *) 1891 malloc(sc_height * sc_width * sizeof(WORD)); 1892 if (whitescreen == NULL) 1893 return; 1894 /* Invert the standard colors. */ 1895 for (n = 0; n < sc_width * sc_height; n++) 1896 whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color); 1897#endif 1898#endif 1899#endif 1900 flash_created = 1; 1901} 1902#endif /* MSDOS_COMPILER */ 1903 1904/* 1905 * Output the "visual bell", if there is one. 1906 */ 1907 public void 1908vbell() 1909{ 1910#if !MSDOS_COMPILER 1911 if (*sc_visual_bell == '\0') 1912 return; 1913 tputs(sc_visual_bell, sc_height, putchr); 1914#else 1915#if MSDOS_COMPILER==DJGPPC 1916 ScreenVisualBell(); 1917#else 1918#if MSDOS_COMPILER==MSOFTC 1919 /* 1920 * Create a flash screen on the second video page. 1921 * Switch to that page, then switch back. 1922 */ 1923 if (!flash_created) 1924 create_flash(); 1925 if (videopages < 2) 1926 return; 1927 _setvisualpage(1); 1928 delay(100); 1929 _setvisualpage(0); 1930#else 1931#if MSDOS_COMPILER==BORLANDC 1932 unsigned short *currscreen; 1933 1934 /* 1935 * Get a copy of the current screen. 1936 * Display the flash screen. 1937 * Then restore the old screen. 1938 */ 1939 if (!flash_created) 1940 create_flash(); 1941 if (whitescreen == NULL) 1942 return; 1943 currscreen = (unsigned short *) 1944 malloc(sc_width * sc_height * sizeof(short)); 1945 if (currscreen == NULL) return; 1946 gettext(1, 1, sc_width, sc_height, currscreen); 1947 puttext(1, 1, sc_width, sc_height, whitescreen); 1948 delay(100); 1949 puttext(1, 1, sc_width, sc_height, currscreen); 1950 free(currscreen); 1951#else 1952#if MSDOS_COMPILER==WIN32C 1953 /* paint screen with an inverse color */ 1954 clear(); 1955 1956 /* leave it displayed for 100 msec. */ 1957 Sleep(100); 1958 1959 /* restore with a redraw */ 1960 repaint(); 1961#endif 1962#endif 1963#endif 1964#endif 1965#endif 1966} 1967 1968/* 1969 * Make a noise. 1970 */ 1971 static void 1972beep() 1973{ 1974#if !MSDOS_COMPILER 1975 putchr(CONTROL('G')); 1976#else 1977#if MSDOS_COMPILER==WIN32C 1978 MessageBeep(0); 1979#else 1980 write(1, "\7", 1); 1981#endif 1982#endif 1983} 1984 1985/* 1986 * Ring the terminal bell. 1987 */ 1988 public void 1989bell() 1990{ 1991 if (quiet == VERY_QUIET) 1992 vbell(); 1993 else 1994 beep(); 1995} 1996 1997/* 1998 * Clear the screen. 1999 */ 2000 public void 2001clear() 2002{ 2003#if !MSDOS_COMPILER 2004 tputs(sc_clear, sc_height, putchr); 2005#else 2006 flush(); 2007#if MSDOS_COMPILER==WIN32C 2008 win32_clear(); 2009#else 2010 _clearscreen(_GCLEARSCREEN); 2011#endif 2012#endif 2013} 2014 2015/* 2016 * Clear from the cursor to the end of the cursor's line. 2017 * {{ This must not move the cursor. }} 2018 */ 2019 public void 2020clear_eol() 2021{ 2022#if !MSDOS_COMPILER 2023 tputs(sc_eol_clear, 1, putchr); 2024#else 2025#if MSDOS_COMPILER==MSOFTC 2026 short top, left; 2027 short bot, right; 2028 struct rccoord tpos; 2029 2030 flush(); 2031 /* 2032 * Save current state. 2033 */ 2034 tpos = _gettextposition(); 2035 _gettextwindow(&top, &left, &bot, &right); 2036 /* 2037 * Set a temporary window to the current line, 2038 * from the cursor's position to the right edge of the screen. 2039 * Then clear that window. 2040 */ 2041 _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); 2042 _clearscreen(_GWINDOW); 2043 /* 2044 * Restore state. 2045 */ 2046 _settextwindow(top, left, bot, right); 2047 _settextposition(tpos.row, tpos.col); 2048#else 2049#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2050 flush(); 2051 clreol(); 2052#else 2053#if MSDOS_COMPILER==WIN32C 2054 DWORD nchars; 2055 COORD cpos; 2056 CONSOLE_SCREEN_BUFFER_INFO scr; 2057 2058 flush(); 2059 memset(&scr, 0, sizeof(scr)); 2060 GetConsoleScreenBufferInfo(con_out, &scr); 2061 cpos.X = scr.dwCursorPosition.X; 2062 cpos.Y = scr.dwCursorPosition.Y; 2063 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2064 FillConsoleOutputAttribute(con_out, curr_attr, 2065 scr.dwSize.X - cpos.X, cpos, &nchars); 2066 FillConsoleOutputCharacter(con_out, ' ', 2067 scr.dwSize.X - cpos.X, cpos, &nchars); 2068#endif 2069#endif 2070#endif 2071#endif 2072} 2073 2074/* 2075 * Clear the current line. 2076 * Clear the screen if there's off-screen memory below the display. 2077 */ 2078 static void 2079clear_eol_bot() 2080{ 2081#if MSDOS_COMPILER 2082 clear_eol(); 2083#else 2084 if (below_mem) 2085 tputs(sc_eos_clear, 1, putchr); 2086 else 2087 tputs(sc_eol_clear, 1, putchr); 2088#endif 2089} 2090 2091/* 2092 * Clear the bottom line of the display. 2093 * Leave the cursor at the beginning of the bottom line. 2094 */ 2095 public void 2096clear_bot() 2097{ 2098 /* 2099 * If we're in a non-normal attribute mode, temporarily exit 2100 * the mode while we do the clear. Some terminals fill the 2101 * cleared area with the current attribute. 2102 */ 2103 lower_left(); 2104 switch (attrmode) 2105 { 2106 case AT_STANDOUT: 2107 so_exit(); 2108 clear_eol_bot(); 2109 so_enter(); 2110 break; 2111 case AT_UNDERLINE: 2112 ul_exit(); 2113 clear_eol_bot(); 2114 ul_enter(); 2115 break; 2116 case AT_BOLD: 2117 bo_exit(); 2118 clear_eol_bot(); 2119 bo_enter(); 2120 break; 2121 case AT_BLINK: 2122 bl_exit(); 2123 clear_eol_bot(); 2124 bl_enter(); 2125 break; 2126 default: 2127 clear_eol_bot(); 2128 break; 2129 } 2130} 2131 2132/* 2133 * Begin "standout" (bold, underline, or whatever). 2134 */ 2135 public void 2136so_enter() 2137{ 2138#if !MSDOS_COMPILER 2139 tputs(sc_s_in, 1, putchr); 2140#else 2141 flush(); 2142 SETCOLORS(so_fg_color, so_bg_color); 2143#endif 2144 attrmode = AT_STANDOUT; 2145} 2146 2147/* 2148 * End "standout". 2149 */ 2150 public void 2151so_exit() 2152{ 2153#if !MSDOS_COMPILER 2154 tputs(sc_s_out, 1, putchr); 2155#else 2156 flush(); 2157 SETCOLORS(nm_fg_color, nm_bg_color); 2158#endif 2159 attrmode = AT_NORMAL; 2160} 2161 2162/* 2163 * Begin "underline" (hopefully real underlining, 2164 * otherwise whatever the terminal provides). 2165 */ 2166 public void 2167ul_enter() 2168{ 2169#if !MSDOS_COMPILER 2170 tputs(sc_u_in, 1, putchr); 2171#else 2172 flush(); 2173 SETCOLORS(ul_fg_color, ul_bg_color); 2174#endif 2175 attrmode = AT_UNDERLINE; 2176} 2177 2178/* 2179 * End "underline". 2180 */ 2181 public void 2182ul_exit() 2183{ 2184#if !MSDOS_COMPILER 2185 tputs(sc_u_out, 1, putchr); 2186#else 2187 flush(); 2188 SETCOLORS(nm_fg_color, nm_bg_color); 2189#endif 2190 attrmode = AT_NORMAL; 2191} 2192 2193/* 2194 * Begin "bold" 2195 */ 2196 public void 2197bo_enter() 2198{ 2199#if !MSDOS_COMPILER 2200 tputs(sc_b_in, 1, putchr); 2201#else 2202 flush(); 2203 SETCOLORS(bo_fg_color, bo_bg_color); 2204#endif 2205 attrmode = AT_BOLD; 2206} 2207 2208/* 2209 * End "bold". 2210 */ 2211 public void 2212bo_exit() 2213{ 2214#if !MSDOS_COMPILER 2215 tputs(sc_b_out, 1, putchr); 2216#else 2217 flush(); 2218 SETCOLORS(nm_fg_color, nm_bg_color); 2219#endif 2220 attrmode = AT_NORMAL; 2221} 2222 2223/* 2224 * Begin "blink" 2225 */ 2226 public void 2227bl_enter() 2228{ 2229#if !MSDOS_COMPILER 2230 tputs(sc_bl_in, 1, putchr); 2231#else 2232 flush(); 2233 SETCOLORS(bl_fg_color, bl_bg_color); 2234#endif 2235 attrmode = AT_BLINK; 2236} 2237 2238/* 2239 * End "blink". 2240 */ 2241 public void 2242bl_exit() 2243{ 2244#if !MSDOS_COMPILER 2245 tputs(sc_bl_out, 1, putchr); 2246#else 2247 flush(); 2248 SETCOLORS(nm_fg_color, nm_bg_color); 2249#endif 2250 attrmode = AT_NORMAL; 2251} 2252 2253#if 0 /* No longer used */ 2254/* 2255 * Erase the character to the left of the cursor 2256 * and move the cursor left. 2257 */ 2258 public void 2259backspace() 2260{ 2261#if !MSDOS_COMPILER 2262 /* 2263 * Erase the previous character by overstriking with a space. 2264 */ 2265 tputs(sc_backspace, 1, putchr); 2266 putchr(' '); 2267 tputs(sc_backspace, 1, putchr); 2268#else 2269#if MSDOS_COMPILER==MSOFTC 2270 struct rccoord tpos; 2271 2272 flush(); 2273 tpos = _gettextposition(); 2274 if (tpos.col <= 1) 2275 return; 2276 _settextposition(tpos.row, tpos.col-1); 2277 _outtext(" "); 2278 _settextposition(tpos.row, tpos.col-1); 2279#else 2280#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2281 cputs("\b"); 2282#else 2283#if MSDOS_COMPILER==WIN32C 2284 COORD cpos; 2285 DWORD cChars; 2286 CONSOLE_SCREEN_BUFFER_INFO scr; 2287 2288 flush(); 2289 GetConsoleScreenBufferInfo(con_out, &scr); 2290 cpos = scr.dwCursorPosition; 2291 if (cpos.X <= 0) 2292 return; 2293 cpos.X--; 2294 SetConsoleCursorPosition(con_out, cpos); 2295 FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars); 2296 SetConsoleCursorPosition(con_out, cpos); 2297#endif 2298#endif 2299#endif 2300#endif 2301} 2302#endif /* 0 */ 2303 2304/* 2305 * Output a plain backspace, without erasing the previous char. 2306 */ 2307 public void 2308putbs() 2309{ 2310#if !MSDOS_COMPILER 2311 tputs(sc_backspace, 1, putchr); 2312#else 2313 int row, col; 2314 2315 flush(); 2316 { 2317#if MSDOS_COMPILER==MSOFTC 2318 struct rccoord tpos; 2319 tpos = _gettextposition(); 2320 row = tpos.row; 2321 col = tpos.col; 2322#else 2323#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2324 row = wherey(); 2325 col = wherex(); 2326#else 2327#if MSDOS_COMPILER==WIN32C 2328 CONSOLE_SCREEN_BUFFER_INFO scr; 2329 GetConsoleScreenBufferInfo(con_out, &scr); 2330 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2331 col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; 2332#endif 2333#endif 2334#endif 2335 } 2336 if (col <= 1) 2337 return; 2338 _settextposition(row, col-1); 2339#endif /* MSDOS_COMPILER */ 2340} 2341 2342#if MSDOS_COMPILER==WIN32C 2343/* 2344 * Determine whether an input character is waiting to be read. 2345 */ 2346 static int 2347win32_kbhit(tty) 2348 HANDLE tty; 2349{ 2350 INPUT_RECORD ip; 2351 DWORD read; 2352 2353 if (keyCount > 0) 2354 return (TRUE); 2355 2356 currentKey.ascii = 0; 2357 currentKey.scan = 0; 2358 2359 /* 2360 * Wait for a real key-down event, but 2361 * ignore SHIFT and CONTROL key events. 2362 */ 2363 do 2364 { 2365 PeekConsoleInput(tty, &ip, 1, &read); 2366 if (read == 0) 2367 return (FALSE); 2368 ReadConsoleInput(tty, &ip, 1, &read); 2369 } while (ip.EventType != KEY_EVENT || 2370 ip.Event.KeyEvent.bKeyDown != TRUE || 2371 ip.Event.KeyEvent.wVirtualScanCode == 0 || 2372 ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || 2373 ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || 2374 ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU); 2375 2376 currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; 2377 currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; 2378 keyCount = ip.Event.KeyEvent.wRepeatCount; 2379 2380 if (ip.Event.KeyEvent.dwControlKeyState & 2381 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 2382 { 2383 switch (currentKey.scan) 2384 { 2385 case PCK_ALT_E: /* letter 'E' */ 2386 currentKey.ascii = 0; 2387 break; 2388 } 2389 } else if (ip.Event.KeyEvent.dwControlKeyState & 2390 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 2391 { 2392 switch (currentKey.scan) 2393 { 2394 case PCK_RIGHT: /* right arrow */ 2395 currentKey.scan = PCK_CTL_RIGHT; 2396 break; 2397 case PCK_LEFT: /* left arrow */ 2398 currentKey.scan = PCK_CTL_LEFT; 2399 break; 2400 case PCK_DELETE: /* delete */ 2401 currentKey.scan = PCK_CTL_DELETE; 2402 break; 2403 } 2404 } 2405 return (TRUE); 2406} 2407 2408/* 2409 * Read a character from the keyboard. 2410 */ 2411 public char 2412WIN32getch(tty) 2413 int tty; 2414{ 2415 int ascii; 2416 2417 if (pending_scancode) 2418 { 2419 pending_scancode = 0; 2420 return ((char)(currentKey.scan & 0x00FF)); 2421 } 2422 2423 while (win32_kbhit((HANDLE)tty) == FALSE) 2424 { 2425 Sleep(20); 2426 if (ABORT_SIGS()) 2427 return ('\003'); 2428 continue; 2429 } 2430 keyCount --; 2431 ascii = currentKey.ascii; 2432 /* 2433 * On PC's, the extended keys return a 2 byte sequence beginning 2434 * with '00', so if the ascii code is 00, the next byte will be 2435 * the lsb of the scan code. 2436 */ 2437 pending_scancode = (ascii == 0x00); 2438 return ((char)ascii); 2439} 2440#endif 2441