xref: /freebsd/contrib/less/signal.c (revision 252d6dde57d5dd0184929d1f8fb65e7713f51c6d)
1 #include <errno.h>
2 /*
3  * Copyright (C) 1984-2025  Mark Nudelman
4  *
5  * You may distribute under the terms of either the GNU General Public
6  * License or the Less License, as specified in the README file.
7  *
8  * For more information, see the README file.
9  */
10 
11 /* $FreeBSD$ */
12 
13 /*
14  * Routines dealing with signals.
15  *
16  * A signal usually merely causes a bit to be set in the "signals" word.
17  * At some convenient time, the mainline code checks to see if any
18  * signals need processing by calling psignal().
19  * If we happen to be reading from a file [in iread()] at the time
20  * the signal is received, we call intio to interrupt the iread.
21  */
22 
23 #include "less.h"
24 #include <signal.h>
25 
26 /*
27  * "sigs" contains bits indicating signals which need to be processed.
28  */
29 public int sigs;
30 
31 extern int sc_width, sc_height;
32 extern int linenums;
33 extern int wscroll;
34 extern int quit_on_intr;
35 extern long jump_sline_fraction;
36 
37 extern int less_is_more;
38 
39 /*
40  * Interrupt signal handler.
41  */
42 #if MSDOS_COMPILER!=WIN32C
43 	/* ARGSUSED*/
u_interrupt(int type)44 static RETSIGTYPE u_interrupt(int type)
45 {
46 	(void) type;
47 	bell();
48 #if OS2
49 	LSIGNAL(SIGINT, SIG_ACK);
50 #endif
51 	LSIGNAL(SIGINT, u_interrupt);
52 	sigs |= S_INTERRUPT;
53 #if MSDOS_COMPILER==DJGPPC
54 	/*
55 	 * If a keyboard has been hit, it must be Ctrl-C
56 	 * (as opposed to Ctrl-Break), so consume it.
57 	 * (Otherwise, Less will beep when it sees Ctrl-C from keyboard.)
58 	 */
59 	if (kbhit())
60 		getkey();
61 #endif
62 	if (less_is_more)
63 		quit(0);
64 #if HILITE_SEARCH
65 	set_filter_pattern(NULL, 0);
66 #endif
67 	intio();
68 }
69 #endif
70 
71 #ifdef SIGTSTP
72 /*
73  * "Stop" (^Z) signal handler.
74  */
75 	/* ARGSUSED*/
stop(int type)76 static RETSIGTYPE stop(int type)
77 {
78 	(void) type;
79 	LSIGNAL(SIGTSTP, stop);
80 	sigs |= S_STOP;
81 	intio();
82 }
83 #endif
84 
85 #undef SIG_LESSWINDOW
86 #ifdef SIGWINCH
87 #define SIG_LESSWINDOW SIGWINCH
88 #else
89 #ifdef SIGWIND
90 #define SIG_LESSWINDOW SIGWIND
91 #endif
92 #endif
93 
94 #ifdef SIG_LESSWINDOW
95 /*
96  * "Window" change handler
97  */
98 	/* ARGSUSED*/
winch(int type)99 public RETSIGTYPE winch(int type)
100 {
101 	(void) type;
102 	LSIGNAL(SIG_LESSWINDOW, winch);
103 #if LESSTEST
104 	/*
105 	 * Ignore window changes during lesstest.
106 	 * Changes in the real window are unrelated to the simulated
107 	 * screen used by lesstest.
108 	 */
109 	if (is_lesstest())
110 		return;
111 #endif
112 	sigs |= S_WINCH;
113 	intio();
114 }
115 #endif
116 
117 #if MSDOS_COMPILER==WIN32C
118 /*
119  * Handle CTRL-C and CTRL-BREAK keys.
120  */
121 #define WIN32_LEAN_AND_MEAN
122 #include <windows.h>
123 
wbreak_handler(DWORD dwCtrlType)124 static BOOL WINAPI wbreak_handler(DWORD dwCtrlType)
125 {
126 	switch (dwCtrlType)
127 	{
128 	case CTRL_C_EVENT:
129 	case CTRL_BREAK_EVENT:
130 		sigs |= S_INTERRUPT;
131 #if HILITE_SEARCH
132 		set_filter_pattern(NULL, 0);
133 #endif
134 		return (TRUE);
135 	default:
136 		break;
137 	}
138 	return (FALSE);
139 }
140 #endif
141 
terminate(int type)142 static RETSIGTYPE terminate(int type)
143 {
144 	(void) type;
145 	quit(15);
146 }
147 
148 /*
149  * Handle a SIGUSR signal.
150  */
151 #ifdef SIGUSR1
sigusr(constant char * var)152 static void sigusr(constant char *var)
153 {
154 	constant char *cmd = lgetenv(var);
155 	if (isnullenv(cmd))
156 		return;
157 	ungetsc(cmd);
158 	intio();
159 }
160 
sigusr1(int type)161 static RETSIGTYPE sigusr1(int type)
162 {
163 	(void) type;
164 	LSIGNAL(SIGUSR1, sigusr1);
165 	sigusr("LESS_SIGUSR1");
166 }
167 #endif
168 
169 /*
170  * Set up the signal handlers.
171  */
init_signals(int on)172 public void init_signals(int on)
173 {
174 	if (on)
175 	{
176 		/*
177 		 * Set signal handlers.
178 		 */
179 #if MSDOS_COMPILER==WIN32C
180 		SetConsoleCtrlHandler(wbreak_handler, TRUE);
181 #else
182 		(void) LSIGNAL(SIGINT, u_interrupt);
183 #endif
184 #ifdef SIGTSTP
185 		(void) LSIGNAL(SIGTSTP, !secure_allow(SF_STOP) ? SIG_IGN : stop);
186 #endif
187 #ifdef SIGWINCH
188 		(void) LSIGNAL(SIGWINCH, winch);
189 #endif
190 #ifdef SIGWIND
191 		(void) LSIGNAL(SIGWIND, winch);
192 #endif
193 #ifdef SIGQUIT
194 		(void) LSIGNAL(SIGQUIT, SIG_IGN);
195 #endif
196 #ifdef SIGTERM
197 		(void) LSIGNAL(SIGTERM, terminate);
198 #endif
199 #ifdef SIGUSR1
200 		(void) LSIGNAL(SIGUSR1, sigusr1);
201 #endif
202 	} else
203 	{
204 		/*
205 		 * Restore signals to defaults.
206 		 */
207 #if MSDOS_COMPILER==WIN32C
208 		SetConsoleCtrlHandler(wbreak_handler, FALSE);
209 #else
210 		(void) LSIGNAL(SIGINT, SIG_DFL);
211 #endif
212 #ifdef SIGTSTP
213 		(void) LSIGNAL(SIGTSTP, SIG_DFL);
214 #endif
215 #ifdef SIGWINCH
216 		(void) LSIGNAL(SIGWINCH, SIG_IGN);
217 #endif
218 #ifdef SIGWIND
219 		(void) LSIGNAL(SIGWIND, SIG_IGN);
220 #endif
221 #ifdef SIGQUIT
222 		(void) LSIGNAL(SIGQUIT, SIG_DFL);
223 #endif
224 #ifdef SIGTERM
225 		(void) LSIGNAL(SIGTERM, SIG_DFL);
226 #endif
227 #ifdef SIGUSR1
228 		(void) LSIGNAL(SIGUSR1, SIG_DFL);
229 #endif
230 	}
231 }
232 
233 /*
234  * Process any signals we have received.
235  * A received signal cause a bit to be set in "sigs".
236  */
psignals(void)237 public void psignals(void)
238 {
239 	int tsignals;
240 
241 	if ((tsignals = sigs) == 0)
242 		return;
243 	sigs = 0;
244 
245 #ifdef SIGTSTP
246 	if (tsignals & S_STOP)
247 	{
248 		/*
249 		 * Clean up the terminal.
250 		 */
251 #ifdef SIGTTOU
252 		LSIGNAL(SIGTTOU, SIG_IGN);
253 #endif
254 		clear_bot();
255 		deinit();
256 		flush();
257 		raw_mode(0);
258 #ifdef SIGTTOU
259 		LSIGNAL(SIGTTOU, SIG_DFL);
260 #endif
261 		LSIGNAL(SIGTSTP, SIG_DFL);
262 		kill(getpid(), SIGTSTP);
263 		/*
264 		 * ... Bye bye. ...
265 		 * Hopefully we'll be back later and resume here...
266 		 * Reset the terminal and arrange to repaint the
267 		 * screen when we get back to the main command loop.
268 		 */
269 		LSIGNAL(SIGTSTP, stop);
270 		raw_mode(1);
271 		init();
272 		screen_trashed();
273 		tsignals |= S_WINCH;
274 	}
275 #endif
276 #ifdef S_WINCH
277 	if (tsignals & S_WINCH)
278 	{
279 		int old_width, old_height;
280 		/*
281 		 * Re-execute scrsize() to read the new window size.
282 		 */
283 		old_width = sc_width;
284 		old_height = sc_height;
285 		get_term();
286 		if (sc_width != old_width || sc_height != old_height)
287 		{
288 			wscroll = (sc_height + 1) / 2;
289 			screen_size_changed();
290 		}
291 		screen_trashed();
292 	}
293 #endif
294 	if (tsignals & S_INTERRUPT)
295 	{
296 		if (quit_on_intr)
297 			quit(QUIT_INTERRUPT);
298 	}
299 }
300