xref: /openbsd-src/usr.bin/less/signal.c (revision 6f05df2d9be0954bec42d51d943d77bd250fb664)
1 /*
2  * Copyright (C) 1984-2012  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 
11 /*
12  * Routines dealing with signals.
13  *
14  * A signal usually merely causes a bit to be set in the "signals" word.
15  * At some convenient time, the mainline code checks to see if any
16  * signals need processing by calling psignal().
17  */
18 
19 #include "less.h"
20 #include <signal.h>
21 
22 /*
23  * "sigs" contains bits indicating signals which need to be processed.
24  */
25 public volatile sig_atomic_t sigs;
26 
27 extern int sc_width, sc_height;
28 extern int screen_trashed;
29 extern int lnloop;
30 extern int linenums;
31 extern int wscroll;
32 extern int quit_on_intr;
33 extern long jump_sline_fraction;
34 
35 /*
36  * Interrupt signal handler.
37  */
38 	/* ARGSUSED*/
39 	static RETSIGTYPE
40 u_interrupt(type)
41 	int type;
42 {
43 #if OS2
44 	LSIGNAL(SIGINT, SIG_ACK);
45 #endif
46 	sigs |= S_INTERRUPT;
47 #if MSDOS_COMPILER==DJGPPC
48 	/*
49 	 * If a keyboard has been hit, it must be Ctrl-C
50 	 * (as opposed to Ctrl-Break), so consume it.
51 	 * (Otherwise, Less will beep when it sees Ctrl-C from keyboard.)
52 	 */
53 	if (kbhit())
54 		getkey();
55 #endif
56 }
57 
58 #ifdef SIGTSTP
59 /*
60  * "Stop" (^Z) signal handler.
61  */
62 	/* ARGSUSED*/
63 	static RETSIGTYPE
64 stop(type)
65 	int type;
66 {
67 	sigs |= S_STOP;
68 }
69 #endif
70 
71 #ifdef SIGWINCH
72 /*
73  * "Window" change handler
74  */
75 	/* ARGSUSED*/
76 	public RETSIGTYPE
77 winch(type)
78 	int type;
79 {
80 	sigs |= S_WINCH;
81 }
82 #else
83 #ifdef SIGWIND
84 /*
85  * "Window" change handler
86  */
87 	/* ARGSUSED*/
88 	public RETSIGTYPE
89 winch(type)
90 	int type;
91 {
92 	sigs |= S_WINCH;
93 }
94 #endif
95 #endif
96 
97 #if MSDOS_COMPILER==WIN32C
98 /*
99  * Handle CTRL-C and CTRL-BREAK keys.
100  */
101 #include "windows.h"
102 
103 	static BOOL WINAPI
104 wbreak_handler(dwCtrlType)
105 	DWORD dwCtrlType;
106 {
107 	switch (dwCtrlType)
108 	{
109 	case CTRL_C_EVENT:
110 	case CTRL_BREAK_EVENT:
111 		sigs |= S_INTERRUPT;
112 		return (TRUE);
113 	default:
114 		break;
115 	}
116 	return (FALSE);
117 }
118 #endif
119 
120 /*
121  * Set up the signal handlers.
122  */
123 	public void
124 init_signals(on)
125 	int on;
126 {
127 	if (on)
128 	{
129 		/*
130 		 * Set signal handlers.
131 		 */
132 		(void) LSIGNAL(SIGINT, u_interrupt);
133 #if MSDOS_COMPILER==WIN32C
134 		SetConsoleCtrlHandler(wbreak_handler, TRUE);
135 #endif
136 #ifdef SIGTSTP
137 		(void) LSIGNAL(SIGTSTP, stop);
138 #endif
139 #ifdef SIGWINCH
140 		(void) LSIGNAL(SIGWINCH, winch);
141 #endif
142 #ifdef SIGWIND
143 		(void) LSIGNAL(SIGWIND, winch);
144 #endif
145 #ifdef SIGQUIT
146 		(void) LSIGNAL(SIGQUIT, SIG_IGN);
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 			calc_jump_sline();
230 			calc_shift_count();
231 			screen_trashed = 1;
232 		}
233 	}
234 #endif
235 	if (tsignals & S_INTERRUPT)
236 	{
237 		bell();
238 		if (quit_on_intr)
239 			quit(QUIT_INTERRUPT);
240 	}
241 }
242 
243 /*
244  * Custom version of signal() that causes syscalls to be interrupted.
245  */
246 	public void
247 (*lsignal(s, a))()
248 	int s;
249 	void (*a) ();
250 {
251 	struct sigaction sa, osa;
252 
253 	sa.sa_handler = a;
254 	sigemptyset(&sa.sa_mask);
255 	sa.sa_flags = 0;		/* don't restart system calls */
256 	if (sigaction(s, &sa, &osa) != 0)
257 		return (SIG_ERR);
258 	return (osa.sa_handler);
259 }
260