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