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