1/*
2 * Copyright (C) 1984-2017  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11/*
12 * Routines dealing with getting input from the keyboard (i.e. from the user).
13 */
14
15#include "less.h"
16#if OS2
17#include "cmd.h"
18#include "pckeys.h"
19#endif
20#if MSDOS_COMPILER==WIN32C
21#include "windows.h"
22extern char WIN32getch();
23static DWORD console_mode;
24#endif
25
26public int tty;
27extern int sigs;
28extern int utf_mode;
29
30/*
31 * Open keyboard for input.
32 */
33	public void
34open_getchr()
35{
36#if MSDOS_COMPILER==WIN32C
37	/* Need this to let child processes inherit our console handle */
38	SECURITY_ATTRIBUTES sa;
39	memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
40	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
41	sa.bInheritHandle = TRUE;
42	tty = (int) CreateFile("CONIN$", GENERIC_READ,
43			FILE_SHARE_READ, &sa,
44			OPEN_EXISTING, 0L, NULL);
45	GetConsoleMode((HANDLE)tty, &console_mode);
46	/* Make sure we get Ctrl+C events. */
47	SetConsoleMode((HANDLE)tty, ENABLE_PROCESSED_INPUT);
48#else
49#if MSDOS_COMPILER
50	extern int fd0;
51	/*
52	 * Open a new handle to CON: in binary mode
53	 * for unbuffered keyboard read.
54	 */
55	 fd0 = dup(0);
56	 close(0);
57	 tty = open("CON", OPEN_READ);
58#if MSDOS_COMPILER==DJGPPC
59	/*
60	 * Setting stdin to binary causes Ctrl-C to not
61	 * raise SIGINT.  We must undo that side-effect.
62	 */
63	(void) __djgpp_set_ctrl_c(1);
64#endif
65#else
66	/*
67	 * Try /dev/tty.
68	 * If that doesn't work, use file descriptor 2,
69	 * which in Unix is usually attached to the screen,
70	 * but also usually lets you read from the keyboard.
71	 */
72#if OS2
73	/* The __open() system call translates "/dev/tty" to "con". */
74	tty = __open("/dev/tty", OPEN_READ);
75#else
76	tty = open("/dev/tty", OPEN_READ);
77#endif
78	if (tty < 0)
79		tty = 2;
80#endif
81#endif
82}
83
84/*
85 * Close the keyboard.
86 */
87	public void
88close_getchr()
89{
90#if MSDOS_COMPILER==WIN32C
91	SetConsoleMode((HANDLE)tty, console_mode);
92	CloseHandle((HANDLE)tty);
93#endif
94}
95
96/*
97 * Get a character from the keyboard.
98 */
99	public int
100getchr()
101{
102	char c;
103	int result;
104
105	do
106	{
107#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
108		/*
109		 * In raw read, we don't see ^C so look here for it.
110		 */
111		flush();
112#if MSDOS_COMPILER==WIN32C
113		if (ABORT_SIGS())
114			return (READ_INTR);
115		c = WIN32getch(tty);
116#else
117		c = getch();
118#endif
119		result = 1;
120		if (c == '\003')
121			return (READ_INTR);
122#else
123		{
124			unsigned char uc;
125			result = iread(tty, &uc, sizeof(char));
126			c = (char) uc;
127		}
128		if (result == READ_INTR)
129			return (READ_INTR);
130		if (result < 0)
131		{
132			/*
133			 * Don't call error() here,
134			 * because error calls getchr!
135			 */
136			quit(QUIT_ERROR);
137		}
138#endif
139#if 0 /* allow entering arbitrary hex chars for testing */
140		/* ctrl-A followed by two hex chars makes a byte */
141	{
142		static int hex_in = 0;
143		static int hex_value = 0;
144		if (c == CONTROL('A'))
145		{
146			hex_in = 2;
147			result = 0;
148			continue;
149		}
150		if (hex_in > 0)
151		{
152			int v;
153			if (c >= '0' && c <= '9')
154				v = c - '0';
155			else if (c >= 'a' && c <= 'f')
156				v = c - 'a' + 10;
157			else if (c >= 'A' && c <= 'F')
158				v = c - 'A' + 10;
159			else
160				hex_in = 0;
161			hex_value = (hex_value << 4) | v;
162			if (--hex_in > 0)
163			{
164				result = 0;
165				continue;
166			}
167			c = hex_value;
168		}
169	}
170#endif
171		/*
172		 * Various parts of the program cannot handle
173		 * an input character of '\0'.
174		 * If a '\0' was actually typed, convert it to '\340' here.
175		 */
176		if (c == '\0')
177			c = '\340';
178	} while (result != 1);
179
180	return (c & 0xFF);
181}
182