--- /dev/null
+/* -*-c-*-
+ *
+ * $Id$
+ *
+ * Pause for a while
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Pause.
+ *
+ * Pause is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later ersion.
+ *
+ * Pause is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pause; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/tv.h>
+
+/*----- Static variables --------------------------------------------------*/
+
+static struct termios ts_old, ts;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sig@ --- *
+ *
+ * Arguments: @int s@ = signal number
+ *
+ * Returns: Doesn't.
+ *
+ * Use: Handles various fatal signals by resetting the terminal state
+ * and re-emitting the signal.
+ */
+
+static void sig(int s)
+{
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts_old);
+ signal(s, SIG_DFL);
+ raise(s);
+}
+
+/* --- @suspend@ --- *
+ *
+ * Arguments: @int s@ = signal number
+ *
+ * Returns: ---
+ *
+ * Use: Handles a job control signal, as recommended in APUE. It
+ * resets the terminal state, resets the signal disposition,
+ * unblocks the signal and re-emits it (so that the shell
+ * shows the right message). When that call returns, the
+ * signal handler is reattached, and the terminal state is set
+ * again.
+ */
+
+static void suspend(int s)
+{
+ sigset_t ss;
+
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts_old);
+ signal(s, SIG_DFL);
+ sigemptyset(&ss);
+ sigaddset(&ss, s);
+ sigprocmask(SIG_UNBLOCK, &ss, 0);
+ raise(s);
+ signal(s, suspend);
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts);
+}
+
+/* --- Traditional GNUey help options --- */
+
+static void usage(FILE *fp)
+{
+ pquis(fp, "Usage: $ [time]\n");
+}
+
+static void version(FILE *fp)
+{
+ pquis(fp, "$ version " VERSION "\n");
+}
+
+static void help(FILE *fp)
+{
+ version(fp);
+ fputc('\n', fp);
+ usage(fp);
+ pquis(fp, "\n\
+Pauses until a key is pressed or an optional time-limit expires. The\n\
+time is given as a floating point number with an option suffix of `s',\n\
+`m', `h' or `d' (for `seconds', `minutes', `hours' or `days'\n\
+respectively).\n\
+\n\
+Options available:\n\
+\n\
+-h, --help Show this help message.\n\
+-v, --version Show program's version number.\n\
+-u, --usage Show terse usage summary.\n\
+");
+}
+
+/* --- @main@ --- *
+ *
+ * Arguments: @int argc@ = number of command line arguments
+ * @char *argv[]@ = vector of command line arguments
+ *
+ * Returns: Nonzero if it failed, zero if everything went well.
+ *
+ * Use: Souped-up version of `sleep'.
+ */
+
+int main(int argc, char *argv[])
+{
+ struct termios ts;
+ unsigned f = 0;
+ struct timeval when;
+
+ enum {
+ f_bogus = 1u,
+ f_timer = 2u,
+ f_key = 4u
+ };
+
+ /* --- Library initialization --- */
+
+ ego(argv[0]);
+
+ /* --- Parse command line options --- */
+
+ for (;;) {
+ static struct option opt[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "usage", 0, 0, 'u' },
+ { 0, 0, 0, 0 }
+ };
+ int i = mdwopt(argc, argv, "hvu", opt, 0, 0, 0);
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'h':
+ help(stderr);
+ exit(0);
+ case 'v':
+ version(stderr);
+ exit(0);
+ case 'u':
+ usage(stderr);
+ exit(0);
+ default:
+ f |= f_bogus;
+ break;
+ }
+ }
+
+ /* --- Parse a timer spec --- */
+
+ if (!(f & f_bogus) && optind < argc) {
+ const char *p =argv[optind++];
+ char *q;
+ double t = strtod(p, &q);
+ switch (*q) {
+ case 'd': t *= 24;
+ case 'h': t *= 60;
+ case 'm': t *= 60;
+ case 's': if (q[1] != 0)
+ default: t = 0;
+ case 0: break;
+ }
+ if (t <= 0)
+ die(1, "bad time specification `%s'", p);
+ gettimeofday(&when, 0);
+ TV_ADDL(&when, &when, t, (t - floor(t)) * MILLION);
+ f |= f_timer;
+ }
+
+ /* --- Report a syntax error --- */
+
+ if (optind < argc)
+ f |= f_bogus;
+ if (f & f_bogus) {
+ usage(stderr);
+ exit(1);
+ }
+
+ /* --- Set up terminal for single keypresses --- */
+
+ if (tcgetattr(STDIN_FILENO, &ts_old) == 0) {
+ static struct { int sig; void (*func)(int); } sigs[] = {
+ { SIGINT, sig },
+ { SIGQUIT, sig },
+ { SIGTERM, sig },
+ { SIGTSTP, suspend },
+ { 0, 0 }
+ };
+ int i;
+
+ /* --- Set up the new terminal attributes --- */
+
+ ts = ts_old;
+ ts.c_lflag &= ~(ECHO | ICANON | TOSTOP);
+
+ /* --- Set up signal handlers to reset attributes --- */
+
+ for (i = 0; sigs[i].sig; i++) {
+ struct sigaction sa;
+ sigaction(sigs[i].sig, 0, &sa);
+ if (sa.sa_handler != SIG_IGN)
+ signal(sigs[i].sig, sigs[i].func);
+ }
+ signal(SIGTTIN, suspend);
+
+ /* --- Commit the terminal attribute changes --- */
+
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts);
+ f |= f_key;
+ }
+
+ /* --- Main loop --- */
+
+ for (;;) {
+ fd_set fd, *pfd;
+ int maxfd;
+ struct timeval tv, *ptv;
+
+ /* --- Wait for a keypress --- */
+
+ if (f & f_key) {
+ FD_ZERO(&fd);
+ FD_SET(STDIN_FILENO, &fd);
+ maxfd = STDIN_FILENO + 1;
+ pfd = &fd;
+ } else {
+ maxfd = 0;
+ pfd = 0;
+ }
+
+ /* --- Wait for the timer to expire --- */
+
+ if (f & f_timer) {
+ gettimeofday(&tv, 0);
+ TV_SUB(&tv, &when, &tv);
+ ptv = &tv;
+ } else
+ ptv = 0;
+
+ /* --- See what interesting things have happened --- */
+
+ if (select(maxfd, pfd, 0, 0, ptv) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ die(1, "error in select: %s", strerror(errno));
+ }
+
+ /* --- Check the timer --- */
+
+ if (f & f_timer) {
+ gettimeofday(&tv, 0);
+ if (TV_CMP(&tv, >=, &when))
+ break;
+ }
+
+ /* --- Check the key state --- */
+
+ if (f & f_key && FD_ISSET(STDIN_FILENO, &fd))
+ break;
+ }
+
+ /* --- See what gives --- */
+
+ if (f & f_key)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts_old);
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/