xref: /freebsd/contrib/less/ttyin.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*
2  * Copyright (C) 1984-2023  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 #define WIN32_LEAN_AND_MEAN
22 #ifndef _WIN32_WINNT
23 #define _WIN32_WINNT 0x400
24 #endif
25 #include <windows.h>
26 public DWORD console_mode;
27 public HANDLE tty;
28 #else
29 public int tty;
30 #endif
31 #if LESSTEST
32 public char *ttyin_name = NULL;
33 #endif /*LESSTEST*/
34 extern int sigs;
35 extern int utf_mode;
36 extern int wheel_lines;
37 
38 #if !MSDOS_COMPILER
39 static int open_tty_device(constant char* dev)
40 {
41 #if OS2
42 	/* The __open() system call translates "/dev/tty" to "con". */
43 	return __open(dev, OPEN_READ);
44 #else
45 	return open(dev, OPEN_READ);
46 #endif
47 }
48 
49 /*
50  * Open the tty device.
51  * Try ttyname(), then try /dev/tty, then use file descriptor 2.
52  * In Unix, file descriptor 2 is usually attached to the screen,
53  * but also usually lets you read from the keyboard.
54  */
55 public int open_tty(void)
56 {
57 	int fd = -1;
58 #if LESSTEST
59 	if (ttyin_name != NULL)
60 		fd = open_tty_device(ttyin_name);
61 #endif /*LESSTEST*/
62 #if HAVE_TTYNAME
63 	if (fd < 0)
64 	{
65 		constant char *dev = ttyname(2);
66 		if (dev != NULL)
67 			fd = open_tty_device(dev);
68 	}
69 #endif
70 	if (fd < 0)
71 		fd = open_tty_device("/dev/tty");
72 	if (fd < 0)
73 		fd = 2;
74 	return fd;
75 }
76 #endif /* MSDOS_COMPILER */
77 
78 /*
79  * Open keyboard for input.
80  */
81 public void open_getchr(void)
82 {
83 #if MSDOS_COMPILER==WIN32C
84 	/* Need this to let child processes inherit our console handle */
85 	SECURITY_ATTRIBUTES sa;
86 	memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
87 	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
88 	sa.bInheritHandle = TRUE;
89 	tty = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
90 			FILE_SHARE_READ, &sa,
91 			OPEN_EXISTING, 0L, NULL);
92 	GetConsoleMode(tty, &console_mode);
93 	/* Make sure we get Ctrl+C events. */
94 	SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
95 #else
96 #if MSDOS_COMPILER
97 	extern int fd0;
98 	/*
99 	 * Open a new handle to CON: in binary mode
100 	 * for unbuffered keyboard read.
101 	 */
102 	 fd0 = dup(0);
103 	 close(0);
104 	 tty = open("CON", OPEN_READ);
105 #if MSDOS_COMPILER==DJGPPC
106 	/*
107 	 * Setting stdin to binary causes Ctrl-C to not
108 	 * raise SIGINT.  We must undo that side-effect.
109 	 */
110 	(void) __djgpp_set_ctrl_c(1);
111 #endif
112 #else
113 	tty = open_tty();
114 #endif
115 #endif
116 }
117 
118 /*
119  * Close the keyboard.
120  */
121 public void close_getchr(void)
122 {
123 #if MSDOS_COMPILER==WIN32C
124 	SetConsoleMode(tty, console_mode);
125 	CloseHandle(tty);
126 #endif
127 }
128 
129 #if MSDOS_COMPILER==WIN32C
130 /*
131  * Close the pipe, restoring the keyboard (CMD resets it, losing the mouse).
132  */
133 public int pclose(FILE *f)
134 {
135 	int result;
136 
137 	result = _pclose(f);
138 	SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
139 	return result;
140 }
141 #endif
142 
143 /*
144  * Get the number of lines to scroll when mouse wheel is moved.
145  */
146 public int default_wheel_lines(void)
147 {
148 	int lines = 1;
149 #if MSDOS_COMPILER==WIN32C
150 	if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines, 0))
151 	{
152 		if (lines == WHEEL_PAGESCROLL)
153 			lines = 3;
154 	}
155 #endif
156 	return lines;
157 }
158 
159 /*
160  * Get a character from the keyboard.
161  */
162 public int getchr(void)
163 {
164 	char c;
165 	int result;
166 
167 	do
168 	{
169 		flush();
170 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
171 		/*
172 		 * In raw read, we don't see ^C so look here for it.
173 		 */
174 #if MSDOS_COMPILER==WIN32C
175 		if (ABORT_SIGS())
176 			return (READ_INTR);
177 		c = WIN32getch();
178 #else
179 		c = getch();
180 #endif
181 		result = 1;
182 		if (c == '\003')
183 			return (READ_INTR);
184 #else
185 		{
186 			unsigned char uc;
187 			result = iread(tty, &uc, sizeof(char));
188 			c = (char) uc;
189 		}
190 		if (result == READ_INTR)
191 			return (READ_INTR);
192 		if (result < 0)
193 		{
194 			/*
195 			 * Don't call error() here,
196 			 * because error calls getchr!
197 			 */
198 			quit(QUIT_ERROR);
199 		}
200 #endif
201 #if LESSTEST
202 		if (c == LESS_DUMP_CHAR)
203 		{
204 			dump_screen();
205 			result = 0;
206 			continue;
207 		}
208 #endif
209 #if 0 /* allow entering arbitrary hex chars for testing */
210 		/* ctrl-A followed by two hex chars makes a byte */
211 	{
212 		static int hex_in = 0;
213 		static int hex_value = 0;
214 		if (c == CONTROL('A'))
215 		{
216 			hex_in = 2;
217 			result = 0;
218 			continue;
219 		}
220 		if (hex_in > 0)
221 		{
222 			int v;
223 			if (c >= '0' && c <= '9')
224 				v = c - '0';
225 			else if (c >= 'a' && c <= 'f')
226 				v = c - 'a' + 10;
227 			else if (c >= 'A' && c <= 'F')
228 				v = c - 'A' + 10;
229 			else
230 				v = 0;
231 			hex_value = (hex_value << 4) | v;
232 			if (--hex_in > 0)
233 			{
234 				result = 0;
235 				continue;
236 			}
237 			c = hex_value;
238 		}
239 	}
240 #endif
241 		/*
242 		 * Various parts of the program cannot handle
243 		 * an input character of '\0'.
244 		 * If a '\0' was actually typed, convert it to '\340' here.
245 		 */
246 		if (c == '\0')
247 			c = '\340';
248 	} while (result != 1);
249 
250 	return (c & 0xFF);
251 }
252