xref: /freebsd/contrib/less/signal.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*
2  * Copyright (C) 1984-2023  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 screen_trashed;
32 extern int lnloop;
33 extern int linenums;
34 extern int wscroll;
35 extern int reading;
36 extern int quit_on_intr;
37 extern int secure;
38 extern long jump_sline_fraction;
39 
40 extern int less_is_more;
41 
42 /*
43  * Interrupt signal handler.
44  */
45 #if MSDOS_COMPILER!=WIN32C
46 	/* ARGSUSED*/
47 static RETSIGTYPE u_interrupt(int type)
48 {
49 	bell();
50 #if OS2
51 	LSIGNAL(SIGINT, SIG_ACK);
52 #endif
53 	LSIGNAL(SIGINT, u_interrupt);
54 	sigs |= S_INTERRUPT;
55 #if MSDOS_COMPILER==DJGPPC
56 	/*
57 	 * If a keyboard has been hit, it must be Ctrl-C
58 	 * (as opposed to Ctrl-Break), so consume it.
59 	 * (Otherwise, Less will beep when it sees Ctrl-C from keyboard.)
60 	 */
61 	if (kbhit())
62 		getkey();
63 #endif
64 	if (less_is_more)
65 		quit(0);
66 #if HILITE_SEARCH
67 	set_filter_pattern(NULL, 0);
68 #endif
69 	if (reading)
70 		intread(); /* May longjmp */
71 }
72 #endif
73 
74 #ifdef SIGTSTP
75 /*
76  * "Stop" (^Z) signal handler.
77  */
78 	/* ARGSUSED*/
79 static RETSIGTYPE stop(int type)
80 {
81 	LSIGNAL(SIGTSTP, stop);
82 	sigs |= S_STOP;
83 	if (reading)
84 		intread();
85 }
86 #endif
87 
88 #undef SIG_LESSWINDOW
89 #ifdef SIGWINCH
90 #define SIG_LESSWINDOW SIGWINCH
91 #else
92 #ifdef SIGWIND
93 #define SIG_LESSWINDOW SIGWIND
94 #endif
95 #endif
96 
97 #ifdef SIG_LESSWINDOW
98 /*
99  * "Window" change handler
100  */
101 	/* ARGSUSED*/
102 public RETSIGTYPE winch(int type)
103 {
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 	quit(15);
139 }
140 
141 /*
142  * Set up the signal handlers.
143  */
144 public void init_signals(int on)
145 {
146 	if (on)
147 	{
148 		/*
149 		 * Set signal handlers.
150 		 */
151 #if MSDOS_COMPILER==WIN32C
152 		SetConsoleCtrlHandler(wbreak_handler, TRUE);
153 #else
154 		(void) LSIGNAL(SIGINT, u_interrupt);
155 #endif
156 #ifdef SIGTSTP
157 		(void) LSIGNAL(SIGTSTP, secure ? SIG_IGN : stop);
158 #endif
159 #ifdef SIGWINCH
160 		(void) LSIGNAL(SIGWINCH, winch);
161 #endif
162 #ifdef SIGWIND
163 		(void) LSIGNAL(SIGWIND, winch);
164 #endif
165 #ifdef SIGQUIT
166 		(void) LSIGNAL(SIGQUIT, SIG_IGN);
167 #endif
168 #ifdef SIGTERM
169 		(void) LSIGNAL(SIGTERM, terminate);
170 #endif
171 	} else
172 	{
173 		/*
174 		 * Restore signals to defaults.
175 		 */
176 #if MSDOS_COMPILER==WIN32C
177 		SetConsoleCtrlHandler(wbreak_handler, FALSE);
178 #else
179 		(void) LSIGNAL(SIGINT, SIG_DFL);
180 #endif
181 #ifdef SIGTSTP
182 		(void) LSIGNAL(SIGTSTP, SIG_DFL);
183 #endif
184 #ifdef SIGWINCH
185 		(void) LSIGNAL(SIGWINCH, SIG_IGN);
186 #endif
187 #ifdef SIGWIND
188 		(void) LSIGNAL(SIGWIND, SIG_IGN);
189 #endif
190 #ifdef SIGQUIT
191 		(void) LSIGNAL(SIGQUIT, SIG_DFL);
192 #endif
193 #ifdef SIGTERM
194 		(void) LSIGNAL(SIGTERM, SIG_DFL);
195 #endif
196 	}
197 }
198 
199 /*
200  * Process any signals we have received.
201  * A received signal cause a bit to be set in "sigs".
202  */
203 public void psignals(void)
204 {
205 	int tsignals;
206 
207 	if ((tsignals = sigs) == 0)
208 		return;
209 	sigs = 0;
210 
211 #ifdef SIGTSTP
212 	if (tsignals & S_STOP)
213 	{
214 		/*
215 		 * Clean up the terminal.
216 		 */
217 #ifdef SIGTTOU
218 		LSIGNAL(SIGTTOU, SIG_IGN);
219 #endif
220 		clear_bot();
221 		deinit();
222 		flush();
223 		raw_mode(0);
224 #ifdef SIGTTOU
225 		LSIGNAL(SIGTTOU, SIG_DFL);
226 #endif
227 		LSIGNAL(SIGTSTP, SIG_DFL);
228 		kill(getpid(), SIGTSTP);
229 		/*
230 		 * ... Bye bye. ...
231 		 * Hopefully we'll be back later and resume here...
232 		 * Reset the terminal and arrange to repaint the
233 		 * screen when we get back to the main command loop.
234 		 */
235 		LSIGNAL(SIGTSTP, stop);
236 		raw_mode(1);
237 		init();
238 		screen_trashed = 1;
239 		tsignals |= S_WINCH;
240 	}
241 #endif
242 #ifdef S_WINCH
243 	if (tsignals & S_WINCH)
244 	{
245 		int old_width, old_height;
246 		/*
247 		 * Re-execute scrsize() to read the new window size.
248 		 */
249 		old_width = sc_width;
250 		old_height = sc_height;
251 		get_term();
252 		if (sc_width != old_width || sc_height != old_height)
253 		{
254 			wscroll = (sc_height + 1) / 2;
255 			calc_jump_sline();
256 			calc_shift_count();
257 		}
258 		screen_trashed = 1;
259 	}
260 #endif
261 	if (tsignals & S_INTERRUPT)
262 	{
263 		if (quit_on_intr)
264 			quit(QUIT_INTERRUPT);
265 	}
266 }
267