1 /* -*-C-*- */
2 
3 #include "EXTERN.h"
4 #include "perl.h"
5 #include "XSUB.h"
6 #include "ppport.h"
7 
8 #define InputStream PerlIO *
9 
10 /*******************************************************************
11 
12  Copyright (C) 1994,1995,1996,1997 Kenneth Albanowski. Unlimited
13  distribution and/or modification is allowed as long as this copyright
14  notice remains intact.
15 
16  Written by Kenneth Albanowski on Thu Oct  6 11:42:20 EDT 1994
17  Contact at kjahds@kjahds.com or CIS:70705,126
18 
19  Maintained by Jonathan Stowe <jns@gellyfish.com>
20 
21  $Id: ReadKey.xs,v 1.2 2016/07/03 01:07:58 afresh1 Exp $
22 
23  Version 2.21, Sun Jul 28 12:57:56 BST 2002
24     Fix to improve the chances of automated testing succeeding
25 
26  Version 2.20, Tue May 21 07:52:47 BST 2002
27     Patch from Autrijus Tang fixing Win32 Breakage with bleadperl
28 
29  Version 2.19, Thu Mar 21 07:25:31 GMT 2002
30     Added check for definedness of $_[0] in comparisons in ReadKey, ReadLine
31     after reports of warnings.
32 
33  Version 2.18, Sun Feb 10 13:06:57 GMT 2002
34     Altered prototyping style after reports of compile failures on
35     Windows.
36 
37  Version 2.17, Fri Jan 25 06:58:47 GMT 2002
38     The '_' macro for non-ANSI compatibility was removed in 5.7.2
39 
40  Version 2.16, Thu Nov 29 21:19:03 GMT 2001
41     It appears that the genchars.pl bit of the patch didnt apply
42     Applied the new ppport.h from Devel::PPPort
43 
44  Version 2.15, Sun Nov  4 15:02:37 GMT 2001 (jns)
45     Applied the patch in
46     http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2001-01/msg01588.html
47     for PerlIO compatibility.
48 
49  Version 2.14, Sun Mar 28 23:26:13 EST 1999
50     ppport.h 1.007 fixed for 5.005_55.
51 
52  Version 2.13, Wed Mar 24 20:46:06 EST 1999
53  	Adapted to ppport.h 1.006.
54 
55  Version 2.12, Wed Jan  7 10:33:11 EST 1998
56  	Slightly modified test and error reporting for Win32.
57 
58  Version 2.11, Sun Dec 14 00:39:12 EST 1997
59     First attempt at Win32 support.
60 
61  Version 2.10, skipped
62 
63  Version 2.09, Tue Oct  7 13:07:43 EDT 1997
64     Grr. Added explicit detection of sys/poll.h and poll.h.
65 
66  Version 2.08, Mon Oct  6 16:07:44 EDT 1997
67     Changed poll.h to sys/poll.h.
68 
69  Version 2.07, Sun Jan 26 19:11:56 EST 1997
70     Added $VERSION to .pm.
71 
72  Version 2.06, Tue Nov 26 01:47:09 EST 1996
73     Added PERLIO support and removed duplicate declaration in .pm.
74 
75  Version 2.05, Tue Mar 12 19:08:33 EST 1996
76  	Changed poll support so it works. Cleaned up .pm a little.
77 
78  Version 2.04, Tue Oct 10 05:35:48 EDT 1995
79  	Whoops. Changed GetTermSize back so that GSIZE code won't be
80  	compiled if GWINSZ is being used. Also took ts_xxx and ts_yyy
81  	out of GSIZE.
82 
83  Version 2.03, Thu Sep 21 21:53:16 EDT 1995
84 	Fixed up debugging info in Readkey.pm, and changed TermSizeVIO
85 	to use _scrsize(). Hopefully this is GO for both Solaris and OS/2.
86 
87  Version 2.02, Mon Sep 18 22:17:57 EDT 1995
88 	Workaround for Solaris bug wasn't sufficient. Modularlized
89 	GetTermSize into perl code, and added support for the
90 	`resize` executable. Hard coded path for Solaris machines.
91 
92  Version 2.01, Wed Sep 13 22:22:23 EDT 1995
93 	Change error reporting around in getscreensize so that if
94  	an ioctl fails but getenv succeeds, no warning will be
95 	printed. This is an attempt to work around a Solaris bug where
96 	TIOCGWINSZ fails in telnet sessions.
97 
98  Version 2.00, Mon Sep  4 06:37:24 EDT 1995
99 	Added timeouts to select/poll, added USE_STDIO_PTR support
100 	(required for recent perl revisions), and fixed up compilation
101 	under OS/2.
102 
103  Version 1.99, Fri Aug 11 20:18:11 EDT 1995
104 	Add file handles to ReadMode.
105 
106  Version 1.97, Mon Apr 10 21:41:52 EDT 1995
107 	Changed mode 5 to disable UC & delays. Added more ECHO flags.
108         Tested termio[s] & sgtty.
109 	Added termoptions so test.pl can give more info.
110 
111  Version 1.96,
112 	Mucked with filehandle selection in ReadKey.pm.
113 
114  Version 1.95,
115 	Cleaning up for distribution.
116 
117  Version 1.94,
118 	Dealt with get/settermsize sillyness.
119 
120  Version 1.91, Sat Mar 11 23:47:04 EST 1995:
121 	Andy's patches, and a bit of termsize finesse.
122 
123  Version 1.9, Thu Mar  9 14:11:49 EST 1995:
124 	Modifying for portability. Prototypes, singed chars, etc.
125 
126  Version 1.8, Mon Jan  9 23:18:14 EST 1995:
127 	Added use of Configure.pm. No changes to ReadKey.
128 
129  Version 1.7, Fri Dec 16 13:48:14 EST 1994:
130    Getting closer to release. Added new readmode 2. Had to bump up other
131    modes, unfortunately. This is the _last_ time I do that. If I have to
132    bump up the modes again, I'm switching to a different scheme.
133 
134  Version 1.6, Wed Dec 14 17:36:59 EST 1994:
135 	Completly reorganized the control-char support (twice!) so that
136 	it is automatically ported by the preproccessor for termio[s], or
137 	by an included script for sgtty. Logical defaults for sgtty are included
138 	too. Added Sun TermSize support. (Hope I got it right.)
139 
140  Version 1.5, Fri Dec  9 16:07:49 EST 1994:
141 	Added SetTermSize, GetSpeeds, Get/SetControlChars, PerlIO support.
142 
143  Version 1.01, Thu Oct 20 23:32:39 EDT 1994:
144 	Added Select_fd_set_t casts to select() call.
145 
146  Version 1.0: First "real" release. Everything seems cool.
147 
148 
149 *******************************************************************/
150 
151 /***
152 
153  Things to do:
154 
155 	Make sure the GetSpeed function is doing it's best to separate ispeed
156 	from ospeed.
157 
158 	Separate the stty stuff from ReadMode, so that stty -a can be easily
159 	used, among other things.
160 
161 ***/
162 
163 
164 
165 /* Using these defines, you can elide anything you know
166    won't work properly */
167 
168 /* Methods of doing non-blocking reads */
169 
170 /*#define DONT_USE_SELECT*/
171 /*#define DONT_USE_POLL*/
172 /*#define DONT_USE_NODELAY*/
173 
174 
175 /* Terminal I/O packages */
176 
177 /*#define DONT_USE_TERMIOS*/
178 /*#define DONT_USE_TERMIO*/
179 /*#define DONT_USE_SGTTY*/
180 
181 /* IOCTLs that can be used for GetTerminalSize */
182 
183 /*#define DONT_USE_GWINSZ*/
184 /*#define DONT_USE_GSIZE*/
185 
186 /* IOCTLs that can be used for SetTerminalSize */
187 
188 /*#define DONT_USE_SWINSZ*/
189 /*#define DONT_USE_SSIZE*/
190 
191 
192 /* This bit is for OS/2 */
193 
194 #ifdef OS2
195 #       define I_FCNTL
196 #       define HAS_FCNTL
197 
198 #       define O_NODELAY O_NDELAY
199 
200 #       define DONT_USE_SELECT
201 #       define DONT_USE_POLL
202 
203 #       define DONT_USE_TERMIOS
204 #       define DONT_USE_SGTTY
205 #       define I_TERMIO
206 #       define CC_TERMIO
207 
208 /* This flag should be off in the lflags when we enable termio mode */
209 #      define TRK_IDEFAULT     IDEFAULT
210 
211 #       define INCL_SUB
212 #       define INCL_DOS
213 
214 #       include <os2.h>
215 #	include <stdlib.h>
216 
217 #       define VIOMODE
218 #else
219         /* no os2 */
220 #endif
221 
222 /* This bit is for Windows 95/NT */
223 
224 #ifdef WIN32
225 #		define DONT_USE_TERMIO
226 #		define DONT_USE_TERMIOS
227 #		define DONT_USE_SGTTY
228 #		define DONT_USE_POLL
229 #		define DONT_USE_SELECT
230 #		define DONT_USE_NODELAY
231 #		define USE_WIN32
232 #		include <io.h>
233 #		if defined(_get_osfhandle) && (PERL_VERSION == 4) && (PERL_SUBVERSION < 5)
234 #			undef _get_osfhandle
235 #			if defined(_MSC_VER)
236 #				define level _cnt
237 #			endif
238 #		endif
239 #endif
240 
241 /* This bit for NeXT */
242 
243 #ifdef _NEXT_SOURCE
244   /* fcntl with O_NDELAY (FNDELAY, actually) is broken on NeXT */
245 # define DONT_USE_NODELAY
246 #endif
247 
248 #if !defined(DONT_USE_NODELAY)
249 # ifdef HAS_FCNTL
250 #  define Have_nodelay
251 #  ifdef I_FCNTL
252 #   include <fcntl.h>
253 #  endif
254 #  ifdef I_SYS_FILE
255 #   include <sys/file.h>
256 #  endif
257 #  ifdef I_UNISTD
258 #   include <unistd.h>
259 #  endif
260 
261 /* If any other headers are needed for fcntl or O_NODELAY, they need to get
262    included right here */
263 
264 #  if !defined(O_NODELAY)
265 #   if !defined(FNDELAY)
266 #    undef Have_nodelay
267 #   else
268 #    define O_NODELAY FNDELAY
269 #   endif
270 #  else
271 #   define O_NODELAY O_NDELAY
272 #  endif
273 # endif
274 #endif
275 
276 #if !defined(DONT_USE_SELECT)
277 # ifdef HAS_SELECT
278 #  ifdef I_SYS_SELECT
279 #   include <sys/select.h>
280 #  endif
281 
282 /* If any other headers are likely to be needed for select, they need to be
283    included right here */
284 
285 #  define Have_select
286 # endif
287 #endif
288 
289 #if !defined(DONT_USE_POLL)
290 # ifdef HAS_POLL
291 #  ifdef HAVE_POLL_H
292 #   include <poll.h>
293 #   define Have_poll
294 #  endif
295 #  ifdef HAVE_SYS_POLL_H
296 #   include <sys/poll.h>
297 #   define Have_poll
298 #  endif
299 # endif
300 #endif
301 
302 #ifdef DONT_USE_TERMIOS
303 # ifdef I_TERMIOS
304 #  undef I_TERMIOS
305 # endif
306 #endif
307 #ifdef DONT_USE_TERMIO
308 # ifdef I_TERMIO
309 #  undef I_TERMIO
310 # endif
311 #endif
312 #ifdef DONT_USE_SGTTY
313 # ifdef I_SGTTY
314 #  undef I_SGTTY
315 # endif
316 #endif
317 
318 /* Pre-POSIX SVR3 systems sometimes define struct winsize in
319    sys/ptem.h.  However, sys/ptem.h needs a type mblk_t (?) which
320    is defined in <sys/stream.h>.
321    No, Configure (dist3.051) doesn't know how to check for this.
322 */
323 #ifdef I_SYS_STREAM
324 # include <sys/stream.h>
325 #endif
326 #ifdef I_SYS_PTEM
327 # include <sys/ptem.h>
328 #endif
329 
330 #ifdef I_TERMIOS
331 # include <termios.h>
332 #else
333 # ifdef I_TERMIO
334 #  include <termio.h>
335 # else
336 #  ifdef I_SGTTY
337 #   include <sgtty.h>
338 #  endif
339 # endif
340 #endif
341 
342 #ifdef I_TERMIOS
343 # define CC_TERMIOS
344 #else
345 # ifdef I_TERMIO
346 #  define CC_TERMIO
347 # else
348 #  ifdef I_SGTTY
349 #   define CC_SGTTY
350 #  endif
351 # endif
352 #endif
353 
354 #ifndef TRK_IDEFAULT
355 /* This flag should be off in the lflags when we enable termio mode */
356 #      define TRK_IDEFAULT     0
357 #endif
358 
359 /* Fix up the disappearance of the '_' macro in Perl 5.7.2 */
360 
361 #ifndef _
362 #  ifdef CAN_PROTOTYPE
363 #    define _(args) args
364 #  else
365 #    define _(args) ()
366 #  endif
367 #endif
368 
369 #define DisableFlush (1) /* Should flushing mode changes be enabled?
370 		            I think not for now. */
371 
372 
373 #define STDIN PerlIO_stdin()
374 
375 #include "cchars.h"
376 
377 
378 int GetTermSizeVIO _((PerlIO * file,
379 	int * retwidth, int * retheight,
380 	int * xpix, int * ypix));
381 
382 int GetTermSizeGWINSZ _((PerlIO * file,
383 	int * retwidth, int * retheight,
384 	int * xpix, int * ypix));
385 
386 int GetTermSizeGSIZE _((PerlIO * file,
387 	int * retwidth, int * retheight,
388 	int * xpix, int * ypix));
389 
390 int GetTermSizeWin32 _((PerlIO * file,
391 	int * retwidth, int * retheight,
392 	int * xpix, int * ypix));
393 
394 int SetTerminalSize _((PerlIO * file,
395 	int width, int height,
396 	int xpix, int ypix));
397 
398 void ReadMode _((PerlIO * file,int mode));
399 
400 int pollfile _((PerlIO * file, double delay));
401 
402 int setnodelay _((PerlIO * file, int mode));
403 
404 int selectfile _((PerlIO * file, double delay));
405 
406 int Win32PeekChar _((PerlIO * file, double delay, char * key));
407 
408 int getspeed _((PerlIO * file, I32 *in, I32 * out ));
409 
410 
411 #ifdef VIOMODE
412 int GetTermSizeVIO(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
413 {
414 	/*int handle=PerlIO_fileno(file);
415 
416         static VIOMODEINFO *modeinfo = NULL;
417 
418         if (modeinfo == NULL)
419                 modeinfo = (VIOMODEINFO *)malloc(sizeof(VIOMODEINFO));
420 
421         VioGetMode(modeinfo,0);
422         *retheight = modeinfo->row ?: 25;
423         *retwidth = modeinfo->col ?: 80;*/
424 	int buf[2];
425 
426 	_scrsize(&buf[0]);
427 
428 	*retwidth = buf[0]; *retheight = buf[1];
429 
430         *xpix = *ypix = 0;
431         return 0;
432 }
433 #else
434 int GetTermSizeVIO(PerlIO *file,int * retwidth,int *retheight, int *xpix,int *ypix)
435 {
436 	croak("TermSizeVIO is not implemented on this architecture");
437         return 0;
438 }
439 #endif
440 
441 
442 #if defined(TIOCGWINSZ) && !defined(DONT_USE_GWINSZ)
443 int GetTermSizeGWINSZ(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
444 {
445 	int handle=PerlIO_fileno(file);
446 	struct winsize w;
447 
448 	if (ioctl (handle, TIOCGWINSZ, &w) == 0) {
449 		*retwidth=w.ws_col; *retheight=w.ws_row;
450 		*xpix=w.ws_xpixel; *ypix=w.ws_ypixel; return 0;
451 	}
452 	else {
453 		return -1; /* failure */
454 	}
455 
456 }
457 #else
458 int GetTermSizeGWINSZ(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
459 {
460 	croak("TermSizeGWINSZ is not implemented on this architecture");
461         return 0;
462 }
463 #endif
464 
465 #if (!defined(TIOCGWINSZ) || defined(DONT_USE_GWINSZ)) && (defined(TIOCGSIZE) && !defined(DONT_USE_GSIZE))
466 int GetTermSizeGSIZE(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
467 {
468 	int handle=PerlIO_fileno(file);
469 
470 	struct ttysize w;
471 
472 	if (ioctl (handle, TIOCGSIZE, &w) == 0) {
473 		*retwidth=w.ts_cols; *retheight=w.ts_lines;
474 		*xpix=0/*w.ts_xxx*/; *ypix=0/*w.ts_yyy*/; return 0;
475 	}
476 	else {
477 		return -1; /* failure */
478 	}
479 }
480 #else
481 int GetTermSizeGSIZE(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
482 {
483 	croak("TermSizeGSIZE is not implemented on this architecture");
484         return 0;
485 }
486 #endif
487 
488 #ifdef USE_WIN32
489 int GetTermSizeWin32(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
490 {
491 	int handle=PerlIO_fileno(file);
492 	HANDLE whnd = (HANDLE)_get_osfhandle(handle);
493 	CONSOLE_SCREEN_BUFFER_INFO info;
494 
495 	if (GetConsoleScreenBufferInfo(whnd, &info)) {
496 		/* Logic: return maximum possible screen width, but return
497 		   only currently selected height */
498 		if (retwidth)
499 			*retwidth = info.dwMaximumWindowSize.X;
500 			/*info.srWindow.Right - info.srWindow.Left;*/
501 		if (retheight)
502 			*retheight = info.srWindow.Bottom - info.srWindow.Top;
503 		if (xpix)
504 			*xpix = 0;
505 		if (ypix)
506 			*ypix = 0;
507 		return 0;
508 	} else
509 		return -1;
510 }
511 #else
512 int GetTermSizeWin32(PerlIO *file,int *retwidth,int *retheight,int *xpix,int *ypix)
513 {
514 	croak("TermSizeWin32 is not implemented on this architecture");
515         return 0;
516 }
517 #endif /* USE_WIN32 */
518 
519 
520 int termsizeoptions() {
521 	return	0
522 #ifdef VIOMODE
523 		| 1
524 #endif
525 #if defined(TIOCGWINSZ) && !defined(DONT_USE_GWINSZ)
526 		| 2
527 #endif
528 #if defined(TIOCGSIZE) && !defined(DONT_USE_GSIZE)
529 		| 4
530 #endif
531 #if defined(USE_WIN32)
532 		| 8
533 #endif
534 		;
535 }
536 
537 
538 int SetTerminalSize(PerlIO *file,int width,int height,int xpix,int ypix)
539 {
540 	char buffer[10];
541 	int handle=PerlIO_fileno(file);
542 
543 #ifdef VIOMODE
544         return -1;
545 #else
546 
547 #if defined(TIOCSWINSZ) && !defined(DONT_USE_SWINSZ)
548 	struct winsize w;
549 
550 	w.ws_col=width;
551 	w.ws_row=height;
552 	w.ws_xpixel=xpix;
553 	w.ws_ypixel=ypix;
554 	if (ioctl (handle, TIOCSWINSZ, &w) == 0) {
555 		sprintf(buffer,"%d",width); /* Be polite to our children */
556 		my_setenv("COLUMNS",buffer);
557 		sprintf(buffer,"%d",height);
558 		my_setenv("LINES",buffer);
559 		return 0;
560 	}
561 	else {
562 		croak("TIOCSWINSZ ioctl call to set terminal size failed: %s",Strerror(errno));
563 		return -1;
564 	}
565 #else
566 # if defined(TIOCSSIZE) && !defined(DONT_USE_SSIZE)
567 	struct ttysize w;
568 
569 	w.ts_lines=height;
570 	w.ts_cols=width;
571 	w.ts_xxx=xpix;
572 	w.ts_yyy=ypix;
573 	if (ioctl (handle, TIOCSSIZE, &w) == 0) {
574 		sprintf(buffer,"%d",width);
575 		my_setenv("COLUMNS",buffer);
576 		sprintf(buffer,"%d",height);
577 		my_setenv("LINES",buffer);
578 		return 0;
579 	}
580 	else {
581 		croak("TIOCSSIZE ioctl call to set terminal size failed: %s",Strerror(errno));
582 		return -1;
583 	}
584 # else
585 	/*sprintf(buffer,"%d",width)   * Should we could do this and then *
586 	my_setenv("COLUMNS",buffer)    * said we succeeded?               *
587 	sprintf(buffer,"%d",height);
588 	my_setenv("LINES",buffer)*/
589 
590 	return -1; /* Fail */
591 # endif
592 #endif
593 #endif
594 
595 }
596 
597 I32 terminal_speeds[] = {
598 #ifdef B50
599 	50, B50,
600 #endif
601 #ifdef B75
602 	75, B75,
603 #endif
604 #ifdef B110
605 	110, B110,
606 #endif
607 #ifdef B134
608 	134, B134,
609 #endif
610 #ifdef B150
611 	150, B150,
612 #endif
613 #ifdef B200
614 	200, B200,
615 #endif
616 #ifdef B300
617 	300, B300,
618 #endif
619 #ifdef B600
620 	600, B600,
621 #endif
622 #ifdef B1200
623 	1200, B1200,
624 #endif
625 #ifdef B1800
626 	1800, B1800,
627 #endif
628 #ifdef B2400
629 	2400, B2400,
630 #endif
631 #ifdef B4800
632 	4800, B4800,
633 #endif
634 #ifdef B9600
635 	9600, B9600,
636 #endif
637 #ifdef B19200
638 	19200, B19200,
639 #endif
640 #ifdef B38400
641 	38400, B38400,
642 #endif
643 #ifdef B57600
644 	57600, B57600,
645 #endif
646 #ifdef B115200
647 	115200, B115200,
648 #endif
649 #ifdef EXTA
650 	19200, EXTA,
651 #endif
652 #ifdef EXTB
653 	38400, EXTB,
654 #endif
655 #ifdef B0
656 	0,  B0,
657 #endif
658 	-1,-1
659 };
660 
661 int getspeed(PerlIO *file,I32 *in, I32 *out)
662 {
663 	int handle=PerlIO_fileno(file);
664 	int i;
665 #       ifdef I_TERMIOS
666 	/* Posixy stuff */
667 
668 	struct termios buf;
669 	tcgetattr(handle,&buf);
670 
671 	*in = *out = -1;
672 	*in = cfgetispeed(&buf);
673 	*out = cfgetospeed(&buf);
674 	for(i=0;terminal_speeds[i]!=-1;i+=2) {
675 		if(*in == terminal_speeds[i+1])
676 			{ *in = terminal_speeds[i]; break; }
677 	}
678 	for(i=0;terminal_speeds[i]!=-1;i+=2) {
679 		if(*out == terminal_speeds[i+1])
680 			{ *out = terminal_speeds[i]; break; }
681 	}
682 	return 0;
683 
684 #       else
685 #        ifdef I_TERMIO
686 	 /* SysV stuff */
687 	 struct termio buf;
688 
689 	 ioctl(handle,TCGETA,&buf);
690 
691 	*in=*out=-1;
692 	for(i=0;terminal_speeds[i]!=-1;i+=2) {
693 		if((buf.c_cflag & CBAUD) == terminal_speeds[i+1])
694 			{ *in=*out=terminal_speeds[i]; break; }
695 	}
696 	return 0;
697 
698 #        else
699 #         ifdef I_SGTTY
700 	  /* BSD stuff */
701 	  struct sgttyb buf;
702 
703 	  ioctl(handle,TIOCGETP,&buf);
704 
705 	*in=*out=-1;
706 
707 	for(i=0;terminal_speeds[i]!=-1;i+=2)
708 		if(buf.sg_ospeed == terminal_speeds[i+1])
709 			{ *out = terminal_speeds[i]; break; }
710 
711 	for(i=0;terminal_speeds[i]!=-1;i+=2)
712 		if(buf.sg_ispeed == terminal_speeds[i+1])
713 			{ *in = terminal_speeds[i]; break; }
714 
715 	return 0;
716 
717 
718 #         else
719 
720 	   /* No termio, termios or sgtty. I suppose we can try stty,
721 	      but it would be nice if you could get a better OS */
722 
723 	return -1;
724 
725 #         endif
726 #        endif
727 #       endif
728 }
729 
730 #ifdef WIN32
731 struct tbuffer { DWORD Mode; };
732 #else
733 #ifdef I_TERMIOS
734 #define USE_TERMIOS
735 #define tbuffer termios
736 #else
737 #ifdef I_TERMIO
738 #define USE_TERMIO
739 #define tbuffer termio
740 #else
741 #ifdef I_SGTTY
742 #define USE_SGTTY
743 struct tbuffer {
744 	  struct sgttyb buf;
745 #if defined(TIOCGETC)
746 	  struct tchars tchar;
747 #endif
748 #if defined(TIOCGLTC)
749 	  struct ltchars ltchar;
750 #endif
751 #if defined(TIOCLGET)
752 	  int local;
753 #endif
754 };
755 #else
756 #define USE_STTY
757 struct tbuffer {
758 	int dummy;
759 };
760 #endif
761 #endif
762 #endif
763 #endif
764 
765 HV * filehash; /* Used to store the original terminal settings for each handle*/
766 HV * modehash; /* Used to record the current terminal "mode" for each handle*/
767 
768 void ReadMode(PerlIO *file,int mode)
769 {
770 	dTHR;
771 	int handle;
772 	int firsttime;
773 	int oldmode;
774 	struct tbuffer work;
775 	struct tbuffer	savebuf;
776 
777 
778 	handle=PerlIO_fileno(file);
779 
780 	firsttime=!hv_exists(filehash, (char*)&handle, sizeof(int));
781 
782 
783 #	ifdef WIN32
784 
785 	if (!GetConsoleMode((HANDLE)_get_osfhandle(handle), &work.Mode))
786 	    croak("GetConsoleMode failed, LastError=|%d|",GetLastError());
787 
788 #	endif /* WIN32 */
789 
790 #       ifdef USE_TERMIOS
791 	/* Posixy stuff */
792 
793 	tcgetattr(handle,&work);
794 
795 
796 
797 #endif
798 #ifdef USE_TERMIO
799 	 /* SysV stuff */
800 
801 	 ioctl(handle,TCGETA,&work);
802 
803 
804 #endif
805 #ifdef USE_SGTTY
806 	  /* BSD stuff */
807 
808 	  ioctl(handle,TIOCGETP,&work.buf);
809 # 	  if defined(TIOCGETC)
810 	   ioctl(handle,TIOCGETC,&work.tchar);
811 #	  endif
812 #         if defined(TIOCLGET)
813 	   ioctl(handle,TIOCLGET,&work.local);
814 #	  endif
815 #	  if defined(TIOCGLTC)
816 	   ioctl(handle,TIOCGLTC,&work.ltchar);
817 #	  endif
818 
819 
820 #endif
821 
822 
823 	if(firsttime) {
824 		firsttime=0;
825 		memcpy((void*)&savebuf,(void*)&work,sizeof(struct tbuffer));
826 		if(!hv_store(filehash,(char*)&handle,sizeof(int),
827 			newSVpv((char*)&savebuf,sizeof(struct tbuffer)),0))
828 			croak("Unable to stash terminal settings.\n");
829 		if(!hv_store(modehash,(char*)&handle,sizeof(int),newSViv(0),0))
830 			croak("Unable to stash terminal settings.\n");
831 	} else {
832 		SV ** temp;
833 		if(!(temp=hv_fetch(filehash,(char*)&handle,sizeof(int),0)))
834 			croak("Unable to retrieve stashed terminal settings.\n");
835 		memcpy(&savebuf,SvPV(*temp,PL_na),sizeof(struct tbuffer));
836 		if(!(temp=hv_fetch(modehash,(char*)&handle,sizeof(int),0)))
837 			croak("Unable to retrieve stashed terminal mode.\n");
838 		oldmode=SvIV(*temp);
839 	}
840 
841 #ifdef WIN32
842 
843 	switch (mode) {
844 		case 5:
845 			/* Should 5 disable ENABLE_WRAP_AT_EOL_OUTPUT? */
846 		case 4:
847 			work.Mode &= ~(ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_OUTPUT);
848 			work.Mode |= 0;
849 			break;
850 		case 3:
851 			work.Mode &= ~(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
852 			work.Mode |= ENABLE_PROCESSED_INPUT|ENABLE_PROCESSED_OUTPUT;
853 			break;
854 		case 2:
855 			work.Mode &= ~(ENABLE_ECHO_INPUT);
856 			work.Mode |= ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_PROCESSED_OUTPUT;
857 			break;
858 		case 1:
859 			work.Mode &= ~(0);
860 			work.Mode |= ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_PROCESSED_OUTPUT;
861 			break;
862 		case 0:
863 			work = savebuf;
864 			firsttime = 1;
865 			break;
866 	}
867 
868 	if (!SetConsoleMode((HANDLE)_get_osfhandle(handle), work.Mode))
869 	    croak("SetConsoleMode failed, LastError=|%d|",GetLastError());
870 
871 #endif /* WIN32 */
872 
873 
874 #ifdef USE_TERMIOS
875 
876 
877 /* What, me worry about standards? */
878 
879 #       if !defined (VMIN)
880 #		define VMIN VEOF
881 #       endif
882 
883 #	if !defined (VTIME)
884 #		define VTIME VEOL
885 #	endif
886 
887 #	if !defined (IXANY)
888 #		define IXANY (0)
889 #	endif
890 
891 #ifndef IEXTEN
892 #ifdef IDEFAULT
893 #define IEXTEN IDEFAULT
894 #endif
895 #endif
896 
897 /* XXX Is ONLCR in POSIX?.  The value of '4' seems to be the same for
898    both SysV and Sun, so it's probably rather general, and I'm not
899    aware of a POSIX way to do this otherwise.
900 */
901 #ifndef ONLCR
902 # define ONLCR 4
903 #endif
904 
905 #ifndef IMAXBEL
906 #define IMAXBEL 0
907 #endif
908 #ifndef ECHOE
909 #define ECHOE 0
910 #endif
911 #ifndef ECHOK
912 #define ECHOK 0
913 #endif
914 #ifndef ECHONL
915 #define ECHONL 0
916 #endif
917 #ifndef ECHOPRT
918 #define ECHOPRT 0
919 #endif
920 #ifndef FLUSHO
921 #define FLUSHO 0
922 #endif
923 #ifndef PENDIN
924 #define PENDIN 0
925 #endif
926 #ifndef ECHOKE
927 #define ECHOKE 0
928 #endif
929 #ifndef ONLCR
930 #define ONLCR 0
931 #endif
932 #ifndef OCRNL
933 #define OCRNL 0
934 #endif
935 #ifndef ONLRET
936 #define ONLRET 0
937 #endif
938 #ifndef IUCLC
939 #define IUCLC 0
940 #endif
941 #ifndef OPOST
942 #define OPOST 0
943 #endif
944 #ifndef OLCUC
945 #define OLCUC 0
946 #endif
947 #ifndef ECHOCTL
948 #define ECHOCTL 0
949 #endif
950 #ifndef XCASE
951 #define XCASE 0
952 #endif
953 #ifndef BRKINT
954 #define BRKINT 0
955 #endif
956 
957 
958 	if(mode==5) {
959 		/*\
960 		 *  Disable everything except parity if needed.
961 		\*/
962 
963 		/* Hopefully, this should put the tty into unbuffered mode
964 		with signals and control characters (both posixy and normal)
965 		disabled, along with flow control. Echo should be off.
966 		CR/LF is not translated, along with 8-bit/parity */
967 
968 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
969 
970 		work.c_lflag &= ~(ICANON|ISIG|IEXTEN );
971 		work.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL);
972 		work.c_lflag &= ~(ECHOPRT|ECHOKE|FLUSHO|PENDIN|XCASE);
973 		work.c_lflag |= NOFLSH;
974         work.c_iflag &= ~(IXOFF|IXON|IXANY|ICRNL|IMAXBEL|BRKINT);
975 
976 		if(((work.c_iflag & INPCK) != INPCK) ||
977                    ((work.c_cflag & PARENB) != PARENB)) {
978 			work.c_iflag &= ~ISTRIP;
979 			work.c_iflag |= IGNPAR;
980 			work.c_iflag &= ~PARMRK;
981 		}
982 		work.c_oflag &= ~(OPOST |ONLCR|OCRNL|ONLRET);
983 
984 		work.c_cc[VTIME] = 0;
985 		work.c_cc[VMIN] = 1;
986 	}
987 	else if(mode==4) {
988 		/* Hopefully, this should put the tty into unbuffered mode
989 		with signals and control characters (both posixy and normal)
990 		disabled, along with flow control. Echo should be off.
991 		About the only thing left unchanged is 8-bit/parity */
992 
993 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
994 
995 		/*work.c_iflag = savebuf.c_iflag;*/
996 		work.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
997 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL|ECHOCTL|ECHOPRT|ECHOKE);
998         work.c_iflag &= ~(IXON | IXANY | BRKINT);
999 		work.c_oflag = savebuf.c_oflag;
1000 		work.c_cc[VTIME] = 0;
1001 		work.c_cc[VMIN] = 1;
1002 	}
1003 	else if(mode==3)
1004 	{
1005 		/* This should be an unbuffered mode with signals and control
1006 		characters enabled, as should be flow control. Echo should
1007 		still be off */
1008 
1009 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1010 
1011 		work.c_iflag = savebuf.c_iflag;
1012 		work.c_lflag &= ~(ICANON | ECHO);
1013 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL|ECHOCTL|ECHOPRT|ECHOKE);
1014 		work.c_lflag |= ISIG | IEXTEN;
1015 		/*work.c_iflag &= ~(IXON | IXOFF | IXANY);
1016 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);
1017 		work.c_oflag = savebuf.c_oflag;*/
1018 		work.c_cc[VTIME] = 0;
1019 		work.c_cc[VMIN] = 1;
1020 	}
1021 	else if(mode==2)
1022 	{
1023 		/* This should be an unbuffered mode with signals and control
1024 		characters enabled, as should be flow control. Echo should
1025 		still be off */
1026 
1027 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1028 
1029 		work.c_iflag = savebuf.c_iflag;
1030 		work.c_lflag |= ICANON|ISIG|IEXTEN;
1031 		work.c_lflag &= ~ECHO;
1032 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL|ECHOCTL|ECHOPRT|ECHOKE);
1033 		/*work.c_iflag &= ~(IXON |IXOFF|IXANY);
1034 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);
1035 		work.c_oflag = savebuf.c_oflag;
1036 		work.c_cc[VTIME] = savebuf.c_cc[VTIME];
1037 		work.c_cc[VMIN] = savebuf.c_cc[VMIN];*/
1038 	}
1039 	else if(mode==1)
1040 	{
1041 		/* This should be an unbuffered mode with signals and control
1042 		characters enabled, as should be flow control. Echo should
1043 		still be off */
1044 
1045 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1046 
1047 		work.c_iflag = savebuf.c_iflag;
1048 		work.c_lflag |= ICANON|ECHO|ISIG|IEXTEN;
1049 		/*work.c_iflag &= ~(IXON |IXOFF|IXANY);
1050 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);
1051 		work.c_oflag = savebuf.c_oflag;
1052 		work.c_cc[VTIME] = savebuf.c_cc[VTIME];
1053 		work.c_cc[VMIN] = savebuf.c_cc[VMIN];*/
1054 	}
1055 	else if(mode==0){
1056 		/*work.c_lflag &= ~BITMASK;
1057 		work.c_lflag |= savebuf.c_lflag & BITMASK;
1058 		work.c_oflag = savebuf.c_oflag;
1059 		work.c_cc[VTIME] = savebuf.c_cc[VTIME];
1060 		work.c_cc[VMIN] = savebuf.c_cc[VMIN];
1061 		work.c_iflag = savebuf.c_iflag;
1062 		work.c_iflag &= ~(IXON|IXOFF|IXANY);
1063 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);*/
1064 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1065 		/*Copy(&work,&savebuf,1,sizeof(struct tbuffer));*/
1066 
1067 		firsttime=1;
1068 	}
1069 	else
1070 	{
1071 		croak("ReadMode %d is not implemented on this architecture.",mode);
1072 		return;
1073 	}
1074 
1075 
1076 	/* If switching from a "lower power" mode to a higher one, keep the
1077 	data that may be in the queue, as it can easily be type-ahead. On
1078 	switching to a lower mode from a higher one, however, flush the queue
1079 	so that raw keystrokes won't hit an unexpecting program */
1080 
1081 	if(DisableFlush || oldmode<=mode)
1082 		tcsetattr(handle,TCSANOW,&work);
1083 	else
1084 		tcsetattr(handle,TCSAFLUSH,&work);
1085 
1086 	/*tcsetattr(handle,TCSANOW,&work);*/ /* It might be better to FLUSH
1087 					   when changing gears to a lower mode,
1088 					   and only use NOW for higher modes.
1089 					*/
1090 
1091 
1092 #endif
1093 #ifdef USE_TERMIO
1094 
1095 /* What, me worry about standards? */
1096 
1097 #	 if !defined (IXANY)
1098 #                define IXANY (0)
1099 #        endif
1100 
1101 #ifndef ECHOE
1102 #define ECHOE 0
1103 #endif
1104 #ifndef ECHOK
1105 #define ECHOK 0
1106 #endif
1107 #ifndef ECHONL
1108 #define ECHONL 0
1109 #endif
1110 #ifndef XCASE
1111 #define XCASE 0
1112 #endif
1113 #ifndef BRKINT
1114 #define BRKINT 0
1115 #endif
1116 
1117 
1118 
1119 	 if(mode==5) {
1120 		/* This mode should be echo disabled, signals disabled,
1121 		flow control disabled, and unbuffered. CR/LF translation
1122    	 	is off, and 8 bits if possible */
1123 
1124 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1125 
1126 		work.c_lflag &= ~(ECHO | ISIG | ICANON | XCASE);
1127 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL | TRK_IDEFAULT);
1128 		work.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | BRKINT);
1129 		if((work.c_cflag | PARENB)!=PARENB ) {
1130 			work.c_iflag &= ~(ISTRIP|INPCK);
1131 			work.c_iflag |= IGNPAR;
1132 		}
1133 		work.c_oflag &= ~(OPOST|ONLCR);
1134 		work.c_cc[VMIN] = 1;
1135 		work.c_cc[VTIME] = 1;
1136 	 }
1137 	 else if(mode==4) {
1138 		/* This mode should be echo disabled, signals disabled,
1139 		flow control disabled, and unbuffered. Parity is not
1140 		touched. */
1141 
1142 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1143 
1144 		work.c_lflag &= ~(ECHO | ISIG | ICANON);
1145 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL TRK_IDEFAULT);
1146 		work.c_iflag = savebuf.c_iflag;
1147 		work.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT);
1148 		work.c_oflag = savebuf.c_oflag;
1149 		work.c_cc[VMIN] = 1;
1150 		work.c_cc[VTIME] = 1;
1151 	 }
1152 	 else if(mode==3) {
1153 		/* This mode tries to have echo off, signals enabled,
1154 		flow control as per the original setting, and unbuffered. */
1155 
1156 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1157 
1158 		work.c_lflag &= ~(ECHO | ICANON);
1159 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL | TRK_IDEFAULT);
1160 		work.c_lflag |= ISIG;
1161 		work.c_iflag = savebuf.c_iflag;
1162 		work.c_iflag &= ~(IXON | IXOFF | IXANY);
1163 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);
1164 		work.c_oflag = savebuf.c_oflag;
1165 		work.c_cc[VMIN] = 1;
1166 		work.c_cc[VTIME] = 1;
1167 	 }
1168 	 else if(mode==2) {
1169 		/* This mode tries to set echo on, signals on, and buffering
1170 		on, with flow control set to whatever it was originally. */
1171 
1172 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1173 
1174 		work.c_lflag |= (ISIG | ICANON);
1175 		work.c_lflag &= ~ECHO;
1176 		work.c_lflag &= ~(ECHOE | ECHOK | ECHONL | TRK_IDEFAULT);
1177 		work.c_iflag = savebuf.c_iflag;
1178 		work.c_iflag &= ~(IXON | IXOFF | IXANY);
1179 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);
1180 		work.c_oflag = savebuf.c_oflag;
1181 		work.c_cc[VMIN] = savebuf.c_cc[VMIN];
1182 		work.c_cc[VTIME] = savebuf.c_cc[VTIME];
1183 
1184 		/* This assumes turning ECHO and ICANON back on is
1185 		   sufficient to re-enable cooked mode. If this is a
1186 		   problem, complain to me */
1187 
1188 		/* What the heck. We're already saving the entire buf, so
1189 		I'm now going to reset VMIN and VTIME too. Hope this works
1190 		properly */
1191 
1192 	 }
1193 	 else if(mode==1) {
1194 		/* This mode tries to set echo on, signals on, and buffering
1195 		on, with flow control set to whatever it was originally. */
1196 
1197 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1198 
1199 		work.c_lflag |= (ECHO | ISIG | ICANON);
1200       work.c_iflag &= ~TRK_IDEFAULT;
1201 		work.c_iflag = savebuf.c_iflag;
1202 		work.c_iflag &= ~(IXON | IXOFF | IXANY);
1203 		work.c_iflag |= savebuf.c_iflag & (IXON|IXOFF|IXANY);
1204 		work.c_oflag = savebuf.c_oflag;
1205 		work.c_cc[VMIN] = savebuf.c_cc[VMIN];
1206 		work.c_cc[VTIME] = savebuf.c_cc[VTIME];
1207 
1208 		/* This assumes turning ECHO and ICANON back on is
1209 		   sufficient to re-enable cooked mode. If this is a
1210 		   problem, complain to me */
1211 
1212 		/* What the heck. We're already saving the entire buf, so
1213 		I'm now going to reset VMIN and VTIME too. Hope this works
1214 		properly */
1215 	}
1216 	 else if(mode==0) {
1217 		/* Put things back the way they were */
1218 
1219 		/*work.c_lflag = savebuf.c_lflag;
1220 		work.c_iflag = savebuf.c_iflag;
1221 		work.c_oflag = savebuf.c_oflag;
1222 		work.c_cc[VMIN] = savebuf.c_cc[VMIN];
1223 		work.c_cc[VTIME] = savebuf.c_cc[VTIME];*/
1224 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1225 		firsttime=1;
1226 	 }
1227  	 else
1228  	 {
1229 		croak("ReadMode %d is not implemented on this architecture.",mode);
1230 		return;
1231 	 }
1232 
1233 
1234 	 if(DisableFlush || oldmode<=mode)
1235 		ioctl(handle,TCSETA,&work);
1236 	 else
1237 		ioctl(handle,TCSETAF,&work);
1238 
1239 #endif
1240 #ifdef USE_SGTTY
1241 
1242 
1243 	  if(mode==5) {
1244 		/* Unbuffered, echo off, signals off, flow control off */
1245 		/* CR-CR/LF mode off too, and 8-bit path enabled. */
1246 #	 	if defined(TIOCLGET) && defined(LPASS8)
1247 		 if((work.buf.sg_flags & (EVENP|ODDP))==0 ||
1248 		    (work.buf.sg_flags & (EVENP|ODDP))==(EVENP|ODDP))
1249 		 	 work.local |= LPASS8; /* If parity isn't being used, use 8 bits */
1250 #		endif
1251 	  	work.buf.sg_flags &= ~(ECHO|CRMOD);
1252 	  	work.buf.sg_flags |= (RAW|CBREAK);
1253 # 	  	if defined(TIOCGETC)
1254 		 work.tchar.t_intrc = -1;
1255 		 work.tchar.t_quitc = -1;
1256 		 work.tchar.t_startc= -1;
1257 		 work.tchar.t_stopc = -1;
1258 		 work.tchar.t_eofc  = -1;
1259 		 work.tchar.t_brkc  = -1;
1260 #		endif
1261 #		if defined(TIOCGLTC)
1262 		 work.ltchar.t_suspc= -1;
1263 		 work.ltchar.t_dsuspc= -1;
1264 		 work.ltchar.t_rprntc= -1;
1265 		 work.ltchar.t_flushc= -1;
1266 		 work.ltchar.t_werasc= -1;
1267 		 work.ltchar.t_lnextc= -1;
1268 #		endif
1269 	  }
1270 	  else if(mode==4) {
1271 		/* Unbuffered, echo off, signals off, flow control off */
1272 	  	work.buf.sg_flags &= ~(ECHO|RAW);
1273 	  	work.buf.sg_flags |= (CBREAK|CRMOD);
1274 #	 	if defined(TIOCLGET)
1275 		 work.local=savebuf.local;
1276 #		endif
1277 # 	  	if defined(TIOCGETC)
1278 		 work.tchar.t_intrc = -1;
1279 		 work.tchar.t_quitc = -1;
1280 		 work.tchar.t_startc= -1;
1281 		 work.tchar.t_stopc = -1;
1282 		 work.tchar.t_eofc  = -1;
1283 		 work.tchar.t_brkc  = -1;
1284 #		endif
1285 #		if defined(TIOCGLTC)
1286 		 work.ltchar.t_suspc= -1;
1287 		 work.ltchar.t_dsuspc= -1;
1288 		 work.ltchar.t_rprntc= -1;
1289 		 work.ltchar.t_flushc= -1;
1290 		 work.ltchar.t_werasc= -1;
1291 		 work.ltchar.t_lnextc= -1;
1292 #		endif
1293 	  }
1294 	  else if(mode==3) {
1295 		/* Unbuffered, echo off, signals on, flow control on */
1296 		work.buf.sg_flags &= ~(RAW|ECHO);
1297 	  	work.buf.sg_flags |= CBREAK|CRMOD;
1298 #	 	if defined(TIOCLGET)
1299 		 work.local=savebuf.local;
1300 #		endif
1301 #		if defined(TIOCGLTC)
1302 		 work.tchar = savebuf.tchar;
1303 #		endif
1304 #		if defined(TIOCGLTC)
1305 		 work.ltchar = savebuf.ltchar;
1306 #		endif
1307  	  }
1308 	  else if(mode==2) {
1309 		/* Buffered, echo on, signals on, flow control on */
1310 		work.buf.sg_flags &= ~(RAW|CBREAK);
1311 		work.buf.sg_flags |= CRMOD;
1312 		work.buf.sg_flags &= ~ECHO;
1313 #	 	if defined(TIOCLGET)
1314 		 work.local=savebuf.local;
1315 #		endif
1316 #		if defined(TIOCGLTC)
1317 		 work.tchar = savebuf.tchar;
1318 #		endif
1319 #		if defined(TIOCGLTC)
1320 		 work.ltchar = savebuf.ltchar;
1321 #		endif
1322 	  }
1323 	  else if(mode==1) {
1324 		/* Buffered, echo on, signals on, flow control on */
1325 		work.buf.sg_flags &= ~(RAW|CBREAK);
1326 		work.buf.sg_flags |= ECHO|CRMOD;
1327 #	 	if defined(TIOCLGET)
1328 		 work.local=savebuf.local;
1329 #		endif
1330 #		if defined(TIOCGLTC)
1331 		 work.tchar = savebuf.tchar;
1332 #		endif
1333 #		if defined(TIOCGLTC)
1334 		 work.ltchar = savebuf.ltchar;
1335 #		endif
1336 	  }
1337 	  else if(mode==0){
1338 		/* Original settings */
1339 #if 0
1340 		work.buf.sg_flags &= ~(RAW|CBREAK|ECHO|CRMOD);
1341 		work.buf.sg_flags |= savebuf.sg_flags & (RAW|CBREAK|ECHO|CRMOD);
1342 #	 	if defined(TIOCLGET)
1343 		 work.local=savebuf.local;
1344 #		endif
1345 #		if defined(TIOCGLTC)
1346 		 work.tchar = savebuf.tchar;
1347 #		endif
1348 #		if defined(TIOCGLTC)
1349 		 work.ltchar = savebuf.ltchar;
1350 #		endif
1351 #endif
1352 		memcpy((void*)&work,(void*)&savebuf,sizeof(struct tbuffer));
1353 		firsttime=1;
1354 	  }
1355  	  else
1356  	  {
1357 		croak("ReadMode %d is not implemented on this architecture.",mode);
1358 		return;
1359 	  }
1360 #if defined(TIOCLSET)
1361 	  ioctl(handle,TIOCLSET,&work.local);
1362 #endif
1363 #if defined(TIOCSETC)
1364 	  ioctl(handle,TIOCSETC,&work.tchar);
1365 #endif
1366 #	  if defined(TIOCGLTC)
1367 	   ioctl(handle,TIOCSLTC,&work.ltchar);
1368 #	  endif
1369 	  if(DisableFlush || oldmode<=mode)
1370 	  	ioctl(handle,TIOCSETN,&work.buf);
1371 	  else
1372 		ioctl(handle,TIOCSETP,&work.buf);
1373 #endif
1374 #ifdef USE_STTY
1375 
1376 	   /* No termio, termios or sgtty. I suppose we can try stty,
1377 	      but it would be nice if you could get a better OS */
1378 
1379 	   if(mode==5)
1380 		system("/bin/stty  raw -cbreak -isig -echo -ixon -onlcr -icrnl -brkint");
1381 	   else if(mode==4)
1382 		system("/bin/stty -raw  cbreak -isig -echo -ixon  onlcr  icrnl -brkint");
1383 	   else if(mode==3)
1384 		system("/bin/stty -raw  cbreak  isig -echo  ixon  onlcr  icrnl  brkint");
1385 	   else if(mode==2)
1386 		system("/bin/stty -raw -cbreak  isig  echo  ixon  onlcr  icrnl  brkint");
1387 	   else if(mode==1)
1388 		system("/bin/stty -raw -cbreak  isig -echo  ixon  onlcr  icrnl  brkint");
1389 	   else if(mode==0)
1390 		system("/bin/stty -raw -cbreak  isig  echo  ixon  onlcr  icrnl  brkint");
1391 
1392 	   /* Those probably won't work, but they couldn't hurt
1393               at this point */
1394 
1395 #endif
1396 
1397 	/*warn("Mode set to %d.\n",mode);*/
1398 
1399 	if( firsttime ) {
1400 		(void)hv_delete(filehash,(char*)&handle,sizeof(int),0);
1401 		(void)hv_delete(modehash,(char*)&handle,sizeof(int),0);
1402 	} else {
1403 		if(!hv_store(modehash,(char*)&handle,sizeof(int),
1404 			newSViv(mode),0))
1405 			croak("Unable to stash terminal settings.\n");
1406 	}
1407 
1408 }
1409 
1410 #ifdef USE_PERLIO
1411 
1412 /* Make use of a recent addition to Perl, if possible */
1413 # define FCOUNT(f) PerlIO_get_cnt(f)
1414 #else
1415 
1416  /* Make use of a recent addition to Configure, if possible */
1417 # ifdef USE_STDIO_PTR
1418 #  define FCOUNT(f) PerlIO_get_cnt(f)
1419 # else
1420   /* This bit borrowed from pp_sys.c. Complain to Larry if it's broken. */
1421   /* If any of this works PerlIO_get_cnt() will too ... NI-S */
1422 #  if defined(USE_STD_STDIO) || defined(atarist) /* this will work with atariST */
1423 #   define FBASE(f) ((f)->_base)
1424 #   define FSIZE(f) ((f)->_cnt + ((f)->_ptr - (f)->_base))
1425 #   define FPTR(f) ((f)->_ptr)
1426 #   define FCOUNT(f) ((f)->_cnt)
1427 #  else
1428 #   if defined(USE_LINUX_STDIO)
1429 #     define FBASE(f) ((f)->_IO_read_base)
1430 #     define FSIZE(f) ((f)->_IO_read_end - FBASE(f))
1431 #     define FPTR(f) ((f)->_IO_read_ptr)
1432 #     define FCOUNT(f) ((f)->_IO_read_end - FPTR(f))
1433 #   endif
1434 #  endif
1435 # endif
1436 #endif
1437 
1438 /* This is for the best, I'm afraid. */
1439 #if !defined(FCOUNT)
1440 # ifdef Have_select
1441 #  undef Have_select
1442 # endif
1443 # ifdef Have_poll
1444 #  undef Have_poll
1445 # endif
1446 #endif
1447 
1448 /* Note! If your machine has a bolixed up select() call that doesn't
1449 understand this syntax, either fix the checkwaiting call below, or define
1450 DONT_USE_SELECT. */
1451 
1452 #ifdef Have_select
1453 int selectfile(PerlIO *file,double delay)
1454 {
1455 	struct timeval t;
1456 	int handle=PerlIO_fileno(file);
1457 
1458 	/*char buf[32];
1459 	Select_fd_set_t fd=(Select_fd_set_t)&buf[0];*/
1460 
1461 	fd_set fd;
1462 	if (PerlIO_fast_gets(file) && PerlIO_get_cnt(file) > 0)
1463 		return 1;
1464 
1465 	/*t.tv_sec=t.tv_usec=0;*/
1466 
1467         if (delay < 0.0)
1468             delay = 0.0;
1469         t.tv_sec = (long)delay;
1470         delay -= (double)t.tv_sec;
1471         t.tv_usec = (long)(delay * 1000000.0);
1472 
1473 	FD_ZERO(&fd);
1474 	FD_SET(handle,&fd);
1475 	if(select(handle+1,(Select_fd_set_t)&fd,
1476 			   (Select_fd_set_t)0,
1477 			   (Select_fd_set_t)&fd, &t)) return -1;
1478 	else return 0;
1479 }
1480 
1481 #else
1482 int selectfile(PerlIO *file, double delay)
1483 {
1484 	croak("select is not supported on this architecture");
1485 	return 0;
1486 }
1487 #endif
1488 
1489 #ifdef Have_nodelay
1490 int setnodelay(PerlIO *file, int mode)
1491 {
1492 	int handle=PerlIO_fileno(file);
1493 	int flags;
1494 	flags=fcntl(handle,F_GETFL,0);
1495 	if(mode)
1496 		flags|=O_NODELAY;
1497 	else
1498 		flags&=~O_NODELAY;
1499 	fcntl(handle,F_SETFL,flags);
1500 	return 0;
1501 }
1502 
1503 #else
1504 int setnodelay(PerlIO *file, int mode)
1505 {
1506 	croak("setnodelay is not supported on this architecture");
1507 	return 0;
1508 }
1509 #endif
1510 
1511 #ifdef Have_poll
1512 int pollfile(PerlIO *file,double delay)
1513 {
1514 	int handle=PerlIO_fileno(file);
1515 	struct pollfd fds;
1516 	if (PerlIO_fast_gets(f) && PerlIO_get_cnt(f) > 0)
1517 		return 1;
1518 	if(delay<0.0) delay = 0.0;
1519 	fds.fd=handle;
1520 	fds.events=POLLIN;
1521 	fds.revents=0;
1522 	return (poll(&fds,1,(long)(delay * 1000.0))>0);
1523 }
1524 #else
1525 int pollfile(PerlIO *file,double delay)
1526 {
1527 	croak("pollfile is not supported on this architecture");
1528 	return 0;
1529 }
1530 #endif
1531 
1532 #ifdef WIN32
1533 
1534 /*
1535 
1536  This portion of the Win32 code is partially borrowed from a version of PDCurses.
1537 
1538 */
1539 
1540 typedef struct {
1541     int repeatCount;
1542     int vKey;
1543     int vScan;
1544     int ascii;
1545     int control;
1546 } win32_key_event_t;
1547 
1548 #define KEY_PUSH(I, K) { events[I].repeatCount = 1; events[I].ascii = K; }
1549 #define KEY_PUSH3(K1, K2, K3) \
1550     do { \
1551              eventCount = 0;            \
1552              KEY_PUSH(2, K1);           \
1553              KEY_PUSH(1, K2);           \
1554              KEY_PUSH(0, K3);           \
1555              eventCount = 3;            \
1556              goto again;                \
1557     } while (0)
1558 
1559 #define KEY_PUSH4(K1, K2, K3, K4) \
1560     do { \
1561              eventCount = 0;            \
1562              KEY_PUSH(3, K1);           \
1563              KEY_PUSH(2, K2);           \
1564              KEY_PUSH(1, K3);           \
1565              KEY_PUSH(0, K4);           \
1566              eventCount = 4;            \
1567              goto again;                \
1568     } while (0)
1569 
1570 int Win32PeekChar(PerlIO *file,double delay,char *key)
1571 {
1572 	int handle;
1573 	HANDLE whnd;
1574 	INPUT_RECORD record;
1575 	DWORD readRecords;
1576 
1577 #if 0
1578 	static int keyCount = 0;
1579 	static char lastKey = 0;
1580 #endif
1581 
1582 #define MAX_EVENTS 4
1583     static int eventCount = 0;
1584     static win32_key_event_t events[MAX_EVENTS];
1585     int keyCount;
1586 
1587 	file = STDIN;
1588 
1589 	handle = PerlIO_fileno(file);
1590 	whnd = /*GetStdHandle(STD_INPUT_HANDLE)*/(HANDLE)_get_osfhandle(handle);
1591 
1592 
1593 again:
1594 #if 0
1595 	if (keyCount > 0) {
1596 		keyCount--;
1597 		*key = lastKey;
1598 	    return TRUE;
1599 	}
1600 #endif
1601 
1602     /* printf("eventCount: %d\n", eventCount); */
1603     if (eventCount) {
1604         /* printf("key %d; repeatCount %d\n", *key, events[eventCount - 1].repeatCount); */
1605         *key = events[eventCount - 1].ascii;
1606         events[eventCount - 1].repeatCount--;
1607         if (events[eventCount - 1].repeatCount <= 0) {
1608             eventCount--;
1609         }
1610         return TRUE;
1611     }
1612 
1613 	if (delay > 0) {
1614 		if (WaitForSingleObject(whnd, delay * 1000.0) != WAIT_OBJECT_0)
1615 		{
1616 			return FALSE;
1617 		}
1618 	}
1619 
1620 	if (delay != 0) {
1621 		PeekConsoleInput(whnd, &record, 1, &readRecords);
1622 		if (readRecords == 0) {
1623 			return(FALSE);
1624         }
1625 	}
1626 
1627 	ReadConsoleInput(whnd, &record, 1, &readRecords);
1628 	switch(record.EventType)
1629    {
1630     case KEY_EVENT:
1631 		/* printf("\nkeyDown = %d, repeat = %d, vKey = %d, vScan = %d, ASCII = %d, Control = %d\n",
1632 			record.Event.KeyEvent.bKeyDown,
1633 			record.Event.KeyEvent.wRepeatCount,
1634 			record.Event.KeyEvent.wVirtualKeyCode,
1635 			record.Event.KeyEvent.wVirtualScanCode,
1636 			record.Event.KeyEvent.uChar.AsciiChar,
1637 			record.Event.KeyEvent.dwControlKeyState); */
1638 
1639          if (record.Event.KeyEvent.bKeyDown == FALSE)
1640             goto again;                        /* throw away KeyUp events */
1641 
1642          if (record.Event.KeyEvent.wVirtualKeyCode == 38) { /* up */
1643              KEY_PUSH3(27, 91, 65);
1644          }
1645          if (record.Event.KeyEvent.wVirtualKeyCode == 40) { /* down */
1646              KEY_PUSH3(27, 91, 66);
1647          }
1648          if (record.Event.KeyEvent.wVirtualKeyCode == 39) { /* right */
1649              KEY_PUSH3(27, 91, 67);
1650          }
1651          if (record.Event.KeyEvent.wVirtualKeyCode == 37) { /* left */
1652              KEY_PUSH3(27, 91, 68);
1653          }
1654          if (record.Event.KeyEvent.wVirtualKeyCode == 33) { /* page up */
1655              KEY_PUSH3(27, 79, 121);
1656          }
1657          if (record.Event.KeyEvent.wVirtualKeyCode == 34) { /* page down */
1658              KEY_PUSH3(27, 79, 115);
1659          }
1660          if (record.Event.KeyEvent.wVirtualKeyCode == 36) { /* home */
1661              KEY_PUSH4(27, 91, 49, 126);
1662          }
1663          if (record.Event.KeyEvent.wVirtualKeyCode == 35) { /* end */
1664              KEY_PUSH4(27, 91, 52, 126);
1665          }
1666          if (record.Event.KeyEvent.wVirtualKeyCode == 45) { /* insert */
1667              KEY_PUSH4(27, 91, 50, 126);
1668          }
1669          if (record.Event.KeyEvent.wVirtualKeyCode == 46) { /* delete */
1670              KEY_PUSH4(27, 91, 51, 126);
1671          }
1672 
1673          if (record.Event.KeyEvent.wVirtualKeyCode == 16
1674          ||  record.Event.KeyEvent.wVirtualKeyCode == 17
1675          ||  record.Event.KeyEvent.wVirtualKeyCode == 18
1676          ||  record.Event.KeyEvent.wVirtualKeyCode == 20
1677          ||  record.Event.KeyEvent.wVirtualKeyCode == 144
1678          ||  record.Event.KeyEvent.wVirtualKeyCode == 145)
1679             goto again;  /* throw away shift/alt/ctrl key only key events */
1680          keyCount = record.Event.KeyEvent.wRepeatCount;
1681 		 break;
1682     default:
1683          keyCount = 0;
1684          goto again;
1685          break;
1686    }
1687 
1688  *key = record.Event.KeyEvent.uChar.AsciiChar;
1689  keyCount--;
1690 
1691  if (keyCount) {
1692      events[0].repeatCount = keyCount;
1693      events[0].ascii = *key;
1694      eventCount = 1;
1695  }
1696 
1697  return(TRUE);
1698 
1699  /* again:
1700 	return (FALSE);
1701 	*/
1702 
1703 
1704 }
1705 #else
1706 int Win32PeekChar(PerlIO *file, double delay,char *key)
1707 {
1708 	croak("Win32PeekChar is not supported on this architecture");
1709 	return 0;
1710 }
1711 #endif
1712 
1713 
1714 int blockoptions() {
1715 	return	0
1716 #ifdef Have_nodelay
1717 		| 1
1718 #endif
1719 #ifdef Have_poll
1720 		| 2
1721 #endif
1722 #ifdef Have_select
1723 		| 4
1724 #endif
1725 #ifdef USE_WIN32
1726 		| 8
1727 #endif
1728 		;
1729 }
1730 
1731 int termoptions() {
1732 	int i=0;
1733 #ifdef USE_TERMIOS
1734 	i=1;
1735 #endif
1736 #ifdef USE_TERMIO
1737 	i=2;
1738 #endif
1739 #ifdef USE_SGTTY
1740 	i=3;
1741 #endif
1742 #ifdef USE_STTY
1743 	i=4;
1744 #endif
1745 #ifdef USE_WIN32
1746 	i=5;
1747 #endif
1748 	return i;
1749 }
1750 
1751 
1752 
1753 MODULE = Term::ReadKey		PACKAGE = Term::ReadKey
1754 
1755 int
1756 selectfile(file,delay)
1757 	InputStream	file
1758 	double	delay
1759 
1760 # Clever, eh?
1761 void
1762 SetReadMode(mode,file=STDIN)
1763 	int	mode
1764 	InputStream	file
1765 	CODE:
1766 	{
1767 		ReadMode(file,mode);
1768 	}
1769 
1770 int
1771 setnodelay(file,mode)
1772 	InputStream	file
1773 	int	mode
1774 
1775 int
1776 pollfile(file,delay)
1777 	InputStream	file
1778 	double	delay
1779 
1780 SV *
1781 Win32PeekChar(file, delay)
1782 	InputStream	file
1783 	double	delay
1784 	CODE:
1785 	{
1786 		char key;
1787 		if (Win32PeekChar(file, delay, &key))
1788 			RETVAL = newSVpv(&key, 1);
1789 		else
1790 			RETVAL = newSVsv(&PL_sv_undef);
1791 	}
1792 	OUTPUT:
1793 	RETVAL
1794 
1795 int
1796 blockoptions()
1797 
1798 int
1799 termoptions()
1800 
1801 int
1802 termsizeoptions()
1803 
1804 void
1805 GetTermSizeWin32(file=STDIN)
1806 	InputStream	file
1807 	PPCODE:
1808 	{
1809 		int x,y,xpix,ypix;
1810 		if( GetTermSizeWin32(file,&x,&y,&xpix,&ypix)==0)
1811 		{
1812 			EXTEND(sp, 4);
1813 			PUSHs(sv_2mortal(newSViv((IV)x)));
1814 			PUSHs(sv_2mortal(newSViv((IV)y)));
1815 			PUSHs(sv_2mortal(newSViv((IV)xpix)));
1816 			PUSHs(sv_2mortal(newSViv((IV)ypix)));
1817 		}
1818 		else
1819 		{
1820 			ST(0) = sv_newmortal();
1821 		}
1822 	}
1823 
1824 void
1825 GetTermSizeVIO(file=STDIN)
1826 	InputStream	file
1827 	PPCODE:
1828 	{
1829 		int x,y,xpix,ypix;
1830 		if( GetTermSizeVIO(file,&x,&y,&xpix,&ypix)==0)
1831 		{
1832 			EXTEND(sp, 4);
1833 			PUSHs(sv_2mortal(newSViv((IV)x)));
1834 			PUSHs(sv_2mortal(newSViv((IV)y)));
1835 			PUSHs(sv_2mortal(newSViv((IV)xpix)));
1836 			PUSHs(sv_2mortal(newSViv((IV)ypix)));
1837 		}
1838 		else
1839 		{
1840 			ST(0) = sv_newmortal();
1841 		}
1842 	}
1843 
1844 void
1845 GetTermSizeGWINSZ(file=STDIN)
1846 	InputStream	file
1847 	PPCODE:
1848 	{
1849 		int x,y,xpix,ypix;
1850 		if( GetTermSizeGWINSZ(file,&x,&y,&xpix,&ypix)==0)
1851 		{
1852 			EXTEND(sp, 4);
1853 			PUSHs(sv_2mortal(newSViv((IV)x)));
1854 			PUSHs(sv_2mortal(newSViv((IV)y)));
1855 			PUSHs(sv_2mortal(newSViv((IV)xpix)));
1856 			PUSHs(sv_2mortal(newSViv((IV)ypix)));
1857 		}
1858 		else
1859 		{
1860 			ST(0) = sv_newmortal();
1861 		}
1862 	}
1863 
1864 void
1865 GetTermSizeGSIZE(file=STDIN)
1866 	InputStream	file
1867 	PPCODE:
1868 	{
1869 		int x,y,xpix,ypix;
1870 		if( GetTermSizeGSIZE(file,&x,&y,&xpix,&ypix)==0)
1871 		{
1872 			EXTEND(sp, 4);
1873 			PUSHs(sv_2mortal(newSViv((IV)x)));
1874 			PUSHs(sv_2mortal(newSViv((IV)y)));
1875 			PUSHs(sv_2mortal(newSViv((IV)xpix)));
1876 			PUSHs(sv_2mortal(newSViv((IV)ypix)));
1877 		}
1878 		else
1879 		{
1880 			ST(0) = sv_newmortal();
1881 		}
1882 	}
1883 
1884 int
1885 SetTerminalSize(width,height,xpix,ypix,file=STDIN)
1886 	int	width
1887 	int	height
1888 	int	xpix
1889 	int	ypix
1890 	InputStream	file
1891 	CODE:
1892 	{
1893 		RETVAL=SetTerminalSize(file,width,height,xpix,ypix);
1894 	}
1895 	OUTPUT:
1896 		RETVAL
1897 
1898 void
1899 GetSpeed(file=STDIN)
1900 	InputStream	file
1901 	PPCODE:
1902 	{
1903 		I32 in,out;
1904 		if(items!=0) {
1905 			croak("Usage: Term::ReadKey::GetSpeed()");
1906 		}
1907 		if(getspeed(file,&in,&out)) {
1908 			/* Failure */
1909 			ST( 0) = sv_newmortal();
1910 		} else {
1911 			EXTEND(sp, 2);
1912 			PUSHs(sv_2mortal(newSViv((IV)in)));
1913 			PUSHs(sv_2mortal(newSViv((IV)out)));
1914 		}
1915 	}
1916 
1917 
1918 
1919 BOOT:
1920 newXS("Term::ReadKey::GetControlChars", XS_Term__ReadKey_GetControlChars, file);
1921 newXS("Term::ReadKey::SetControlChars", XS_Term__ReadKey_SetControlChars, file);
1922 filehash=newHV();
1923 modehash=newHV();
1924