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