mtimeout.1: Use correct dash for number ranges.
[misc] / pause.c
CommitLineData
1dec4fa3 1/* -*-c-*-
2 *
1dec4fa3 3 * Pause for a while
4 *
5 * (c) 1999 Mark Wooding
6 */
7
841e5aca 8/*----- Licensing notice --------------------------------------------------*
1dec4fa3 9 *
10 * This file is part of Pause.
11 *
12 * Pause is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later ersion.
841e5aca 16 *
1dec4fa3 17 * Pause is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
841e5aca 21 *
1dec4fa3 22 * You should have received a copy of the GNU General Public License
23 * along with Pause; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27/*----- Header files ------------------------------------------------------*/
28
29#include <errno.h>
30#include <math.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include <sys/types.h>
37#include <sys/time.h>
38#include <unistd.h>
39#include <termios.h>
40
41#include <mLib/mdwopt.h>
42#include <mLib/quis.h>
43#include <mLib/report.h>
44#include <mLib/tv.h>
45
46/*----- Static variables --------------------------------------------------*/
47
48static struct termios ts_old, ts;
49
50/*----- Main code ---------------------------------------------------------*/
51
52/* --- @sig@ --- *
53 *
54 * Arguments: @int s@ = signal number
55 *
56 * Returns: Doesn't.
57 *
58 * Use: Handles various fatal signals by resetting the terminal state
59 * and re-emitting the signal.
60 */
61
62static void sig(int s)
63{
64 tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts_old);
65 signal(s, SIG_DFL);
66 raise(s);
67}
68
69/* --- @suspend@ --- *
70 *
71 * Arguments: @int s@ = signal number
72 *
73 * Returns: ---
74 *
75 * Use: Handles a job control signal, as recommended in APUE. It
76 * resets the terminal state, resets the signal disposition,
77 * unblocks the signal and re-emits it (so that the shell
78 * shows the right message). When that call returns, the
79 * signal handler is reattached, and the terminal state is set
80 * again.
81 */
82
83static void suspend(int s)
84{
85 sigset_t ss;
86
87 tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts_old);
88 signal(s, SIG_DFL);
89 sigemptyset(&ss);
90 sigaddset(&ss, s);
91 sigprocmask(SIG_UNBLOCK, &ss, 0);
92 raise(s);
93 signal(s, suspend);
94 tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts);
95}
96
97/* --- Traditional GNUey help options --- */
98
99static void usage(FILE *fp)
100{
101 pquis(fp, "Usage: $ [time]\n");
102}
103
104static void version(FILE *fp)
105{
106 pquis(fp, "$ version " VERSION "\n");
107}
108
109static void help(FILE *fp)
110{
111 version(fp);
112 fputc('\n', fp);
113 usage(fp);
114 pquis(fp, "\n\
115Pauses until a key is pressed or an optional time-limit expires. The\n\
116time is given as a floating point number with an option suffix of `s',\n\
117`m', `h' or `d' (for `seconds', `minutes', `hours' or `days'\n\
118respectively).\n\
119\n\
120Options available:\n\
121\n\
122-h, --help Show this help message.\n\
123-v, --version Show program's version number.\n\
124-u, --usage Show terse usage summary.\n\
125");
126}
127
128/* --- @main@ --- *
129 *
130 * Arguments: @int argc@ = number of command line arguments
131 * @char *argv[]@ = vector of command line arguments
132 *
133 * Returns: Nonzero if it failed, zero if everything went well.
134 *
135 * Use: Souped-up version of `sleep'.
136 */
137
138int main(int argc, char *argv[])
139{
140 struct termios ts;
141 unsigned f = 0;
142 struct timeval when;
143
144 enum {
145 f_bogus = 1u,
146 f_timer = 2u,
147 f_key = 4u
148 };
149
150 /* --- Library initialization --- */
151
152 ego(argv[0]);
153
154 /* --- Parse command line options --- */
155
156 for (;;) {
157 static struct option opt[] = {
158 { "help", 0, 0, 'h' },
159 { "version", 0, 0, 'v' },
160 { "usage", 0, 0, 'u' },
161 { 0, 0, 0, 0 }
162 };
163 int i = mdwopt(argc, argv, "hvu", opt, 0, 0, 0);
164 if (i < 0)
165 break;
166 switch (i) {
167 case 'h':
168 help(stderr);
169 exit(0);
170 case 'v':
171 version(stderr);
172 exit(0);
173 case 'u':
174 usage(stderr);
175 exit(0);
176 default:
177 f |= f_bogus;
178 break;
179 }
180 }
181
182 /* --- Parse a timer spec --- */
183
184 if (!(f & f_bogus) && optind < argc) {
185 const char *p =argv[optind++];
186 char *q;
187 double t = strtod(p, &q);
188 switch (*q) {
189 case 'd': t *= 24;
190 case 'h': t *= 60;
191 case 'm': t *= 60;
192 case 's': if (q[1] != 0)
841e5aca 193 default: t = 0;
1dec4fa3 194 case 0: break;
195 }
196 if (t <= 0)
197 die(1, "bad time specification `%s'", p);
198 gettimeofday(&when, 0);
199 TV_ADDL(&when, &when, t, (t - floor(t)) * MILLION);
200 f |= f_timer;
201 }
202
203 /* --- Report a syntax error --- */
204
205 if (optind < argc)
206 f |= f_bogus;
207 if (f & f_bogus) {
208 usage(stderr);
209 exit(1);
210 }
211
212 /* --- Set up terminal for single keypresses --- */
213
214 if (tcgetattr(STDIN_FILENO, &ts_old) == 0) {
215 static struct { int sig; void (*func)(int); } sigs[] = {
216 { SIGINT, sig },
217 { SIGQUIT, sig },
218 { SIGTERM, sig },
219 { SIGTSTP, suspend },
220 { 0, 0 }
221 };
222 int i;
223
224 /* --- Set up the new terminal attributes --- */
225
226 ts = ts_old;
227 ts.c_lflag &= ~(ECHO | ICANON | TOSTOP);
228
229 /* --- Set up signal handlers to reset attributes --- */
230
231 for (i = 0; sigs[i].sig; i++) {
232 struct sigaction sa;
233 sigaction(sigs[i].sig, 0, &sa);
234 if (sa.sa_handler != SIG_IGN)
235 signal(sigs[i].sig, sigs[i].func);
236 }
237 signal(SIGTTIN, suspend);
238
239 /* --- Commit the terminal attribute changes --- */
240
241 tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts);
242 f |= f_key;
243 }
244
245 /* --- Main loop --- */
246
247 for (;;) {
248 fd_set fd, *pfd;
249 int maxfd;
250 struct timeval tv, *ptv;
251
252 /* --- Wait for a keypress --- */
253
254 if (f & f_key) {
255 FD_ZERO(&fd);
256 FD_SET(STDIN_FILENO, &fd);
257 maxfd = STDIN_FILENO + 1;
258 pfd = &fd;
259 } else {
260 maxfd = 0;
261 pfd = 0;
262 }
263
264 /* --- Wait for the timer to expire --- */
265
266 if (f & f_timer) {
267 gettimeofday(&tv, 0);
268 TV_SUB(&tv, &when, &tv);
269 ptv = &tv;
270 } else
271 ptv = 0;
272
273 /* --- See what interesting things have happened --- */
274
275 if (select(maxfd, pfd, 0, 0, ptv) < 0) {
276 if (errno == EINTR || errno == EAGAIN)
277 continue;
278 die(1, "error in select: %s", strerror(errno));
279 }
280
281 /* --- Check the timer --- */
282
283 if (f & f_timer) {
284 gettimeofday(&tv, 0);
285 if (TV_CMP(&tv, >=, &when))
286 break;
287 }
288
289 /* --- Check the key state --- */
290
291 if (f & f_key && FD_ISSET(STDIN_FILENO, &fd))
292 break;
293 }
294
295 /* --- See what gives --- */
296
297 if (f & f_key)
298 tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts_old);
299 return (0);
300}
301
302/*----- That's all, folks -------------------------------------------------*/