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