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