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