xref: /freebsd/contrib/less/ttyin.c (revision 4523eebc6c1828d4fd41d7f1ab943cf079043bc8)
1 /*
2  * Copyright (C) 1984-2026  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 	{
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  */
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  */
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  */
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  */
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  */
187 public int getchr(void)
188 {
189 	char c;
190 	ssize_t result;
191 
192 	do
193 	{
194 		flush();
195 #if !LESS_IREAD_TTY
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 /* LESS_IREAD_TTY */
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 			 * EOF on the tty means there is no more keyboard input.
223 			 * Don't loop forever waiting for a byte which cannot arrive.
224 			 */
225 			quit(QUIT_ERROR);
226 		}
227 		if (result < 0)
228 		{
229 			/*
230 			 * Don't call error() here,
231 			 * because error calls getchr!
232 			 */
233 			quit(QUIT_ERROR);
234 		}
235 #endif
236 #if LESSTEST
237 		if (c == LESS_DUMP_CHAR)
238 		{
239 			dump_screen();
240 			result = 0;
241 			continue;
242 		}
243 #endif
244 #if 0 /* allow entering arbitrary hex chars for testing */
245 		/* ctrl-A followed by two hex chars makes a byte */
246 	{
247 		static int hex_in = 0;
248 		static int hex_value = 0;
249 		if (c == CONTROL('A'))
250 		{
251 			hex_in = 2;
252 			result = 0;
253 			continue;
254 		}
255 		if (hex_in > 0)
256 		{
257 			int v;
258 			if (c >= '0' && c <= '9')
259 				v = c - '0';
260 			else if (c >= 'a' && c <= 'f')
261 				v = c - 'a' + 10;
262 			else if (c >= 'A' && c <= 'F')
263 				v = c - 'A' + 10;
264 			else
265 				v = 0;
266 			hex_value = (hex_value << 4) | v;
267 			if (--hex_in > 0)
268 			{
269 				result = 0;
270 				continue;
271 			}
272 			c = hex_value;
273 		}
274 	}
275 #endif
276 		/*
277 		 * Various parts of the program cannot handle
278 		 * an input character of '\0'.
279 		 * If a '\0' was actually typed, convert it to '\340' here.
280 		 */
281 		if (c == '\0')
282 			c = '\340';
283 	} while (result != 1);
284 
285 	return (unsigned char) c;
286 }
287