xref: /openbsd/usr.bin/less/signal.c (revision 7b36286a)
1 /*
2  * Copyright (C) 1984-2002  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 about less, or for information on how to
8  * contact the author, see the README file.
9  */
10 
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  */
19 
20 #include "less.h"
21 #include <signal.h>
22 
23 /*
24  * "sigs" contains bits indicating signals which need to be processed.
25  */
26 public int sigs;
27 
28 extern int sc_width, sc_height;
29 extern int screen_trashed;
30 extern int lnloop;
31 extern int linenums;
32 extern int wscroll;
33 
34 /*
35  * Interrupt signal handler.
36  */
37 	/* ARGSUSED*/
38 	static RETSIGTYPE
39 u_interrupt(type)
40 	int type;
41 {
42 #if OS2
43 	LSIGNAL(SIGINT, SIG_ACK);
44 #endif
45 	sigs |= S_INTERRUPT;
46 #if MSDOS_COMPILER==DJGPPC
47 	/*
48 	 * If a keyboard has been hit, it must be Ctrl-C
49 	 * (as opposed to Ctrl-Break), so consume it.
50 	 * (Otherwise, Less will beep when it sees Ctrl-C from keyboard.)
51 	 */
52 	if (kbhit())
53 		getkey();
54 #endif
55 }
56 
57 #ifdef SIGTSTP
58 /*
59  * "Stop" (^Z) signal handler.
60  */
61 	/* ARGSUSED*/
62 	static RETSIGTYPE
63 stop(type)
64 	int type;
65 {
66 	sigs |= S_STOP;
67 }
68 #endif
69 
70 #ifdef SIGWINCH
71 /*
72  * "Window" change handler
73  */
74 	/* ARGSUSED*/
75 	public RETSIGTYPE
76 winch(type)
77 	int type;
78 {
79 	sigs |= S_WINCH;
80 }
81 #else
82 #ifdef SIGWIND
83 /*
84  * "Window" change handler
85  */
86 	/* ARGSUSED*/
87 	public RETSIGTYPE
88 winch(type)
89 	int type;
90 {
91 	sigs |= S_WINCH;
92 }
93 #endif
94 #endif
95 
96 #if MSDOS_COMPILER==WIN32C
97 /*
98  * Handle CTRL-C and CTRL-BREAK keys.
99  */
100 #include "windows.h"
101 
102 	static BOOL WINAPI
103 wbreak_handler(dwCtrlType)
104 	DWORD dwCtrlType;
105 {
106 	switch (dwCtrlType)
107 	{
108 	case CTRL_C_EVENT:
109 	case CTRL_BREAK_EVENT:
110 		sigs |= S_INTERRUPT;
111 		return (TRUE);
112 	default:
113 		break;
114 	}
115 	return (FALSE);
116 }
117 #endif
118 
119 /*
120  * Set up the signal handlers.
121  */
122 	public void
123 init_signals(on)
124 	int on;
125 {
126 	if (on)
127 	{
128 		/*
129 		 * Set signal handlers.
130 		 */
131 		(void) LSIGNAL(SIGINT, u_interrupt);
132 #if MSDOS_COMPILER==WIN32C
133 		SetConsoleCtrlHandler(wbreak_handler, TRUE);
134 #endif
135 #ifdef SIGTSTP
136 		(void) LSIGNAL(SIGTSTP, stop);
137 #endif
138 #ifdef SIGWINCH
139 		(void) LSIGNAL(SIGWINCH, winch);
140 #else
141 #ifdef SIGWIND
142 		(void) LSIGNAL(SIGWIND, winch);
143 #endif
144 #ifdef SIGQUIT
145 		(void) LSIGNAL(SIGQUIT, SIG_IGN);
146 #endif
147 #endif
148 	} else
149 	{
150 		/*
151 		 * Restore signals to defaults.
152 		 */
153 		(void) LSIGNAL(SIGINT, SIG_DFL);
154 #if MSDOS_COMPILER==WIN32C
155 		SetConsoleCtrlHandler(wbreak_handler, FALSE);
156 #endif
157 #ifdef SIGTSTP
158 		(void) LSIGNAL(SIGTSTP, SIG_DFL);
159 #endif
160 #ifdef SIGWINCH
161 		(void) LSIGNAL(SIGWINCH, SIG_IGN);
162 #endif
163 #ifdef SIGWIND
164 		(void) LSIGNAL(SIGWIND, SIG_IGN);
165 #endif
166 #ifdef SIGQUIT
167 		(void) LSIGNAL(SIGQUIT, SIG_DFL);
168 #endif
169 	}
170 }
171 
172 /*
173  * Process any signals we have received.
174  * A received signal cause a bit to be set in "sigs".
175  */
176 	public void
177 psignals()
178 {
179 	register int tsignals;
180 
181 	if ((tsignals = sigs) == 0)
182 		return;
183 	sigs = 0;
184 
185 #ifdef SIGTSTP
186 	if (tsignals & S_STOP)
187 	{
188 		/*
189 		 * Clean up the terminal.
190 		 */
191 #ifdef SIGTTOU
192 		LSIGNAL(SIGTTOU, SIG_IGN);
193 #endif
194 		clear_bot();
195 		deinit();
196 		flush();
197 		raw_mode(0);
198 #ifdef SIGTTOU
199 		LSIGNAL(SIGTTOU, SIG_DFL);
200 #endif
201 		LSIGNAL(SIGTSTP, SIG_DFL);
202 		kill(getpid(), SIGTSTP);
203 		/*
204 		 * ... Bye bye. ...
205 		 * Hopefully we'll be back later and resume here...
206 		 * Reset the terminal and arrange to repaint the
207 		 * screen when we get back to the main command loop.
208 		 */
209 		LSIGNAL(SIGTSTP, stop);
210 		raw_mode(1);
211 		init();
212 		screen_trashed = 1;
213 		tsignals |= S_WINCH;
214 	}
215 #endif
216 #ifdef S_WINCH
217 	if (tsignals & S_WINCH)
218 	{
219 		int old_width, old_height;
220 		/*
221 		 * Re-execute scrsize() to read the new window size.
222 		 */
223 		old_width = sc_width;
224 		old_height = sc_height;
225 		get_term();
226 		if (sc_width != old_width || sc_height != old_height)
227 		{
228 			wscroll = (sc_height + 1) / 2;
229 			screen_trashed = 1;
230 		}
231 	}
232 #endif
233 	if (tsignals & S_INTERRUPT)
234 	{
235 		bell();
236 		/*
237 		 * {{ You may wish to replace the bell() with
238 		 *    error("Interrupt", NULL_PARG); }}
239 		 */
240 
241 		/*
242 		 * If we were interrupted while in the "calculating
243 		 * line numbers" loop, turn off line numbers.
244 		 */
245 		if (lnloop)
246 		{
247 			lnloop = 0;
248 			if (linenums == OPT_ONPLUS)
249 				screen_trashed = 1;
250 			linenums = 0;
251 			error("Line numbers turned off", NULL_PARG);
252 		}
253 
254 	}
255 }
256 
257 /*
258  * Custom version of signal() that causes syscalls to be interrupted.
259  */
260 	public void
261 (*lsignal(s, a))()
262 	int s;
263 	void (*a) ();
264 {
265 	struct sigaction sa, osa;
266 
267 	sa.sa_handler = a;
268 	sigemptyset(&sa.sa_mask);
269 	sa.sa_flags = 0;		/* don't restart system calls */
270 	if (sigaction(s, &sa, &osa) != 0)
271 		return (SIG_ERR);
272 	return (osa.sa_handler);
273 }
274