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