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