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