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