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