xref: /freebsd/contrib/less/signal.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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 /* $FreeBSD$ */
11 
12 /*
13  * Routines dealing with signals.
14  *
15  * A signal usually merely causes a bit to be set in the "signals" word.
16  * At some convenient time, the mainline code checks to see if any
17  * signals need processing by calling psignal().
18  * If we happen to be reading from a file [in iread()] at the time
19  * the signal is received, we call intread to interrupt the iread.
20  */
21 
22 #include "less.h"
23 #include <signal.h>
24 
25 /*
26  * "sigs" contains bits indicating signals which need to be processed.
27  */
28 public int sigs;
29 
30 extern int sc_width, sc_height;
31 extern int linenums;
32 extern int wscroll;
33 extern int reading;
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*/
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 	if (reading)
68 		intread(); /* May longjmp */
69 }
70 #endif
71 
72 #ifdef SIGTSTP
73 /*
74  * "Stop" (^Z) signal handler.
75  */
76 	/* ARGSUSED*/
77 static RETSIGTYPE stop(int type)
78 {
79 	(void) type;
80 	LSIGNAL(SIGTSTP, stop);
81 	sigs |= S_STOP;
82 	if (reading)
83 		intread();
84 }
85 #endif
86 
87 #undef SIG_LESSWINDOW
88 #ifdef SIGWINCH
89 #define SIG_LESSWINDOW SIGWINCH
90 #else
91 #ifdef SIGWIND
92 #define SIG_LESSWINDOW SIGWIND
93 #endif
94 #endif
95 
96 #ifdef SIG_LESSWINDOW
97 /*
98  * "Window" change handler
99  */
100 	/* ARGSUSED*/
101 public RETSIGTYPE winch(int type)
102 {
103 	(void) type;
104 	LSIGNAL(SIG_LESSWINDOW, winch);
105 	sigs |= S_WINCH;
106 	if (reading)
107 		intread();
108 }
109 #endif
110 
111 #if MSDOS_COMPILER==WIN32C
112 /*
113  * Handle CTRL-C and CTRL-BREAK keys.
114  */
115 #define WIN32_LEAN_AND_MEAN
116 #include <windows.h>
117 
118 static BOOL WINAPI wbreak_handler(DWORD dwCtrlType)
119 {
120 	switch (dwCtrlType)
121 	{
122 	case CTRL_C_EVENT:
123 	case CTRL_BREAK_EVENT:
124 		sigs |= S_INTERRUPT;
125 #if HILITE_SEARCH
126 		set_filter_pattern(NULL, 0);
127 #endif
128 		return (TRUE);
129 	default:
130 		break;
131 	}
132 	return (FALSE);
133 }
134 #endif
135 
136 static RETSIGTYPE terminate(int type)
137 {
138 	(void) type;
139 	quit(15);
140 }
141 
142 /*
143  * Set up the signal handlers.
144  */
145 public void init_signals(int on)
146 {
147 	if (on)
148 	{
149 		/*
150 		 * Set signal handlers.
151 		 */
152 #if MSDOS_COMPILER==WIN32C
153 		SetConsoleCtrlHandler(wbreak_handler, TRUE);
154 #else
155 		(void) LSIGNAL(SIGINT, u_interrupt);
156 #endif
157 #ifdef SIGTSTP
158 		(void) LSIGNAL(SIGTSTP, !secure_allow(SF_STOP) ? SIG_IGN : stop);
159 #endif
160 #ifdef SIGWINCH
161 		(void) LSIGNAL(SIGWINCH, winch);
162 #endif
163 #ifdef SIGWIND
164 		(void) LSIGNAL(SIGWIND, winch);
165 #endif
166 #ifdef SIGQUIT
167 		(void) LSIGNAL(SIGQUIT, SIG_IGN);
168 #endif
169 #ifdef SIGTERM
170 		(void) LSIGNAL(SIGTERM, terminate);
171 #endif
172 	} else
173 	{
174 		/*
175 		 * Restore signals to defaults.
176 		 */
177 #if MSDOS_COMPILER==WIN32C
178 		SetConsoleCtrlHandler(wbreak_handler, FALSE);
179 #else
180 		(void) LSIGNAL(SIGINT, SIG_DFL);
181 #endif
182 #ifdef SIGTSTP
183 		(void) LSIGNAL(SIGTSTP, SIG_DFL);
184 #endif
185 #ifdef SIGWINCH
186 		(void) LSIGNAL(SIGWINCH, SIG_IGN);
187 #endif
188 #ifdef SIGWIND
189 		(void) LSIGNAL(SIGWIND, SIG_IGN);
190 #endif
191 #ifdef SIGQUIT
192 		(void) LSIGNAL(SIGQUIT, SIG_DFL);
193 #endif
194 #ifdef SIGTERM
195 		(void) LSIGNAL(SIGTERM, SIG_DFL);
196 #endif
197 	}
198 }
199 
200 /*
201  * Process any signals we have received.
202  * A received signal cause a bit to be set in "sigs".
203  */
204 public void psignals(void)
205 {
206 	int tsignals;
207 
208 	if ((tsignals = sigs) == 0)
209 		return;
210 	sigs = 0;
211 
212 #ifdef SIGTSTP
213 	if (tsignals & S_STOP)
214 	{
215 		/*
216 		 * Clean up the terminal.
217 		 */
218 #ifdef SIGTTOU
219 		LSIGNAL(SIGTTOU, SIG_IGN);
220 #endif
221 		clear_bot();
222 		deinit();
223 		flush();
224 		raw_mode(0);
225 #ifdef SIGTTOU
226 		LSIGNAL(SIGTTOU, SIG_DFL);
227 #endif
228 		LSIGNAL(SIGTSTP, SIG_DFL);
229 		kill(getpid(), SIGTSTP);
230 		/*
231 		 * ... Bye bye. ...
232 		 * Hopefully we'll be back later and resume here...
233 		 * Reset the terminal and arrange to repaint the
234 		 * screen when we get back to the main command loop.
235 		 */
236 		LSIGNAL(SIGTSTP, stop);
237 		raw_mode(1);
238 		init();
239 		screen_trashed();
240 		tsignals |= S_WINCH;
241 	}
242 #endif
243 #ifdef S_WINCH
244 	if (tsignals & S_WINCH)
245 	{
246 		int old_width, old_height;
247 		/*
248 		 * Re-execute scrsize() to read the new window size.
249 		 */
250 		old_width = sc_width;
251 		old_height = sc_height;
252 		get_term();
253 		if (sc_width != old_width || sc_height != old_height)
254 		{
255 			wscroll = (sc_height + 1) / 2;
256 			screen_size_changed();
257 		}
258 		screen_trashed();
259 	}
260 #endif
261 	if (tsignals & S_INTERRUPT)
262 	{
263 		if (quit_on_intr)
264 			quit(QUIT_INTERRUPT);
265 		getcc_clear();
266 #if MSDOS_COMPILER==WIN32C
267 		win32_getch_clear();
268 #endif
269 	}
270 }
271