xref: /openbsd/usr.bin/less/signal.c (revision d25d28bf)
1 /*
2  * Copyright (C) 1984-2012  Mark Nudelman
3  * Modified for use with illumos by Garrett D'Amore.
4  * Copyright 2015 Garrett D'Amore <garrett@damore.org>
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information, see the README file.
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 <signal.h>
21 
22 #include "less.h"
23 
24 /*
25  * "sigs" contains bits indicating signals which need to be processed.
26  */
27 volatile sig_atomic_t sigs;
28 
29 extern int sc_width, sc_height;
30 extern int screen_trashed;
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 static void
40 u_interrupt(int type)
41 {
42 	sigs |= S_INTERRUPT;
43 }
44 
45 /*
46  * "Stop" (^Z) signal handler.
47  */
48 static void
49 stop(int type)
50 {
51 	sigs |= S_STOP;
52 }
53 
54 /*
55  * "Window" change handler
56  */
57 void
58 sigwinch(int type)
59 {
60 	sigs |= S_WINCH;
61 }
62 
63 /*
64  * Set up the signal handlers.
65  */
66 void
67 init_signals(int on)
68 {
69 	if (on) {
70 		/*
71 		 * Set signal handlers.
72 		 */
73 		(void) lsignal(SIGINT, u_interrupt);
74 		(void) lsignal(SIGTSTP, stop);
75 		(void) lsignal(SIGWINCH, sigwinch);
76 		(void) lsignal(SIGQUIT, SIG_IGN);
77 	} else {
78 		/*
79 		 * Restore signals to defaults.
80 		 */
81 		(void) lsignal(SIGINT, SIG_DFL);
82 		(void) lsignal(SIGTSTP, SIG_DFL);
83 		(void) lsignal(SIGWINCH, SIG_IGN);
84 		(void) lsignal(SIGQUIT, SIG_DFL);
85 	}
86 }
87 
88 /*
89  * Process any signals we have received.
90  * A received signal cause a bit to be set in "sigs".
91  */
92 void
93 psignals(void)
94 {
95 	int tsignals;
96 
97 	if ((tsignals = sigs) == 0)
98 		return;
99 	sigs = 0;
100 
101 	if (tsignals & S_STOP) {
102 		/*
103 		 * Clean up the terminal.
104 		 */
105 		lsignal(SIGTTOU, SIG_IGN);
106 		clear_bot();
107 		deinit();
108 		flush(0);
109 		raw_mode(0);
110 		lsignal(SIGTTOU, SIG_DFL);
111 		lsignal(SIGTSTP, SIG_DFL);
112 		kill(getpid(), SIGTSTP);
113 		/*
114 		 * ... Bye bye. ...
115 		 * Hopefully we'll be back later and resume here...
116 		 * Reset the terminal and arrange to repaint the
117 		 * screen when we get back to the main command loop.
118 		 */
119 		lsignal(SIGTSTP, stop);
120 		raw_mode(1);
121 		init();
122 		screen_trashed = 1;
123 		tsignals |= S_WINCH;
124 	}
125 	if (tsignals & S_WINCH) {
126 		int old_width, old_height;
127 		/*
128 		 * Re-execute scrsize() to read the new window size.
129 		 */
130 		old_width = sc_width;
131 		old_height = sc_height;
132 		get_term();
133 		if (sc_width != old_width || sc_height != old_height) {
134 			wscroll = (sc_height + 1) / 2;
135 			calc_jump_sline();
136 			calc_shift_count();
137 			screen_trashed = 1;
138 		}
139 	}
140 	if (tsignals & S_INTERRUPT) {
141 		ring_bell();
142 		if (quit_on_intr)
143 			quit(QUIT_INTERRUPT);
144 	}
145 }
146 
147 /*
148  * Custom version of signal() that causes syscalls to be interrupted.
149  */
150 void *
151 lsignal(int s, void (*a)(int))
152 {
153 	struct sigaction sa, osa;
154 
155 	sa.sa_handler = a;
156 	sigemptyset(&sa.sa_mask);
157 	sa.sa_flags = 0;		/* don't restart system calls */
158 	if (sigaction(s, &sa, &osa) != 0)
159 		return (SIG_ERR);
160 	return (osa.sa_handler);
161 }
162