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