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