xref: /openbsd/usr.bin/less/signal.c (revision d65139b4)
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  * signals which need to be processed.
26  */
27 volatile sig_atomic_t signal_intr;
28 volatile sig_atomic_t signal_stop;
29 volatile sig_atomic_t signal_winch;
30 
31 extern int sc_width, sc_height;
32 extern int screen_trashed;
33 extern int linenums;
34 extern int wscroll;
35 extern int quit_on_intr;
36 extern long jump_sline_fraction;
37 
38 /*
39  * Interrupt signal handler.
40  */
41 static void
u_interrupt(int type)42 u_interrupt(int type)
43 {
44 	signal_intr = 1;
45 }
46 
47 /*
48  * "Stop" (^Z) signal handler.
49  */
50 static void
stop(int type)51 stop(int type)
52 {
53 	signal_stop = 1;
54 }
55 
56 /*
57  * "Window" change handler
58  */
59 void
sigwinch(int type)60 sigwinch(int type)
61 {
62 	signal_winch = 1;
63 }
64 
65 /*
66  * Set up the signal handlers.
67  */
68 void
init_signals(int on)69 init_signals(int on)
70 {
71 	if (on) {
72 		/*
73 		 * Set signal handlers.
74 		 */
75 		(void) lsignal(SIGINT, u_interrupt);
76 		(void) lsignal(SIGTSTP, stop);
77 		(void) lsignal(SIGWINCH, sigwinch);
78 		(void) lsignal(SIGQUIT, SIG_IGN);
79 	} else {
80 		/*
81 		 * Restore signals to defaults.
82 		 */
83 		(void) lsignal(SIGINT, SIG_DFL);
84 		(void) lsignal(SIGTSTP, SIG_DFL);
85 		(void) lsignal(SIGWINCH, SIG_IGN);
86 		(void) lsignal(SIGQUIT, SIG_DFL);
87 	}
88 }
89 
90 /*
91  * Process any signals we have received.
92  */
93 void
psignals(void)94 psignals(void)
95 {
96 	if (signal_stop) {
97 		signal_stop = 0;
98 		/*
99 		 * Clean up the terminal.
100 		 */
101 		lsignal(SIGTTOU, SIG_IGN);
102 		clear_bot();
103 		deinit();
104 		flush(0);
105 		raw_mode(0);
106 		lsignal(SIGTTOU, SIG_DFL);
107 		lsignal(SIGTSTP, SIG_DFL);
108 		kill(getpid(), SIGTSTP);
109 		/*
110 		 * ... Bye bye. ...
111 		 * Hopefully we'll be back later and resume here...
112 		 * Reset the terminal and arrange to repaint the
113 		 * screen when we get back to the main command loop.
114 		 */
115 		lsignal(SIGTSTP, stop);
116 		raw_mode(1);
117 		init();
118 		screen_trashed = 1;
119 		signal_winch = 1;
120 	}
121 	if (signal_winch) {
122 		signal_winch = 0;
123 		int old_width, old_height;
124 		/*
125 		 * Re-execute scrsize() to read the new window size.
126 		 */
127 		old_width = sc_width;
128 		old_height = sc_height;
129 		get_term();
130 		if (sc_width != old_width || sc_height != old_height) {
131 			wscroll = (sc_height + 1) / 2;
132 			calc_jump_sline();
133 			calc_shift_count();
134 			screen_trashed = 1;
135 		}
136 	}
137 	if (signal_intr) {
138 		signal_intr = 0;
139 		ring_bell();
140 		if (quit_on_intr)
141 			quit(QUIT_INTERRUPT);
142 	}
143 }
144 
145 /*
146  * Custom version of signal() that causes syscalls to be interrupted.
147  */
148 void *
lsignal(int s,void (* a)(int))149 lsignal(int s, void (*a)(int))
150 {
151 	struct sigaction sa, osa;
152 
153 	sa.sa_handler = a;
154 	sigemptyset(&sa.sa_mask);
155 	sa.sa_flags = 0;		/* don't restart system calls */
156 	if (sigaction(s, &sa, &osa) != 0)
157 		return (SIG_ERR);
158 	return (osa.sa_handler);
159 }
160 
161 int
any_sigs(void)162 any_sigs(void)
163 {
164 	return (signal_intr || signal_stop || signal_winch);
165 }
166 
167 int
abort_sigs(void)168 abort_sigs(void)
169 {
170 	return (signal_intr || signal_stop);
171 }
172 
173