From 1dec4fa32a4a6c57ffb2c736c0c6b95b0519cb67 Mon Sep 17 00:00:00 2001 Message-Id: <1dec4fa32a4a6c57ffb2c736c0c6b95b0519cb67.1715763573.git.mdw@distorted.org.uk> From: Mark Wooding Date: Sat, 11 Sep 2004 17:20:44 +0000 Subject: [PATCH] Add `pause' program. Organization: Straylight/Edgeware From: mdw --- Makefile | 11 +- debian/changelog | 6 + debian/control | 7 ++ debian/inst | 4 +- gorp.1 | 2 +- pause.1 | 52 ++++++++ pause.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 380 insertions(+), 6 deletions(-) create mode 100644 pause.1 create mode 100644 pause.c diff --git a/Makefile b/Makefile index bfa978e..514bab2 100644 --- a/Makefile +++ b/Makefile @@ -3,26 +3,26 @@ ## No proper build system here. Just kludgy hacks. PACKAGE = nsict-utils -VERSION = 1.1.0 +VERSION = 1.1.1 BINSCRIPTS = cdb-assign cdb-list check-sender unfwd splitconf z SBINSCRIPTS = shadowfix SCRIPTS = $(BINSCRIPTS) $(SBINSCRIPTS) -BINPROGS = not cdb-probe cdb-check-domain gorp locking if-mtu +BINPROGS = not cdb-probe cdb-check-domain gorp locking if-mtu pause SBINPROGS = qmail-checkspam PROGS = $(BINPROGS) $(SBINPROGS) PERLLIBS = MdwOpt.pm LIBS = xtitle.so DISTMAN1 = \ not.1 z.1 cdb-assign.1 cdb-list.1 cdb-probe.1 cdb-check-domain.1 \ - gorp.1 unfwd.1 splitconf.1 locking.1 if-mtu.1 + gorp.1 unfwd.1 splitconf.1 locking.1 if-mtu.1 pause.1 MAN1 = $(DISTMAN1) DISTMAN8 = qmail-checkspam.8 MAN8 = $(DISTMAN8) shadowfix.8 BUILDFILES = shadowfix.8 SOURCES = \ not.c cdb-probe.c cdb-check-domain.c gorp.c locking.c if-mtu.c \ - qmail-checkspam.c xtitle.c + qmail-checkspam.c xtitle.c pause.c CC = gcc LD = gcc @@ -85,6 +85,9 @@ xtitle.so: xtitle.o gorp: gorp.o $(LINK) -lcatacomb -lmLib +pause: pause.o + $(LINK) -lmLib + locking: locking.o $(LINK) -lmLib diff --git a/debian/changelog b/debian/changelog index 6b41c19..8e19ae5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +nsict-utils (1.1.1) experimental; urgency=low + + * Add pause program. + + -- Mark Wooding Sat, 11 Sep 2004 18:13:25 +0100 + nsict-utils (1.1.0) experimental; urgency=low * Make gorp a much more useful program. diff --git a/debian/control b/debian/control index c1d2749..f928af9 100644 --- a/debian/control +++ b/debian/control @@ -79,3 +79,10 @@ Architecture: any Depends: ${shlibs:Depends} Section: utils Description: Print a random base64 string. + +Package: pause +Architecture: any +Depends: ${shlibs:Depends} +Section: utils +Description: Wait for a given time, or until a key is pressed. + diff --git a/debian/inst b/debian/inst index 9052cc6..9793ede 100644 --- a/debian/inst +++ b/debian/inst @@ -17,6 +17,8 @@ locking locking /usr/bin locking.1 locking /usr/share/man/man1 not nsict-mail /usr/bin not.1 nsict-mail /usr/share/man/man1 +pause pause /usr/bin +pause.1 pause /usr/share/man/man1 qmail-checkspam qmail-checkspam /usr/sbin qmail-checkspam.8 qmail-checkspam /usr/share/man/man8 shadowfix shadowfix /usr/sbin @@ -25,6 +27,6 @@ splitconf splitconf /usr/bin splitconf.1 splitconf /usr/share/man/man1 unfwd nsict-mail /usr/bin unfwd.1 nsict-mail /usr/share/man/man1 -xtitle.so xtitle /usr/lib/bash/ +xtitle.so xtitle /usr/lib/bash z zz /usr/bin z.1 zz /usr/share/man/man1 diff --git a/gorp.1 b/gorp.1 index 6f9000d..66de0a1 100644 --- a/gorp.1 +++ b/gorp.1 @@ -46,7 +46,7 @@ so the output is suitable for use as a filename), .B raw (raw binary output, not printable). .TP -.B "\-l, \-\-line=" length +.BI "\-l, \-\-line=" length Breaks textual output into lines of at most .I length characters, and does all encoding in a strictly conforming way. By diff --git a/pause.1 b/pause.1 new file mode 100644 index 0000000..6aeef11 --- /dev/null +++ b/pause.1 @@ -0,0 +1,52 @@ +.\" -*-nroff-*- +.de hP +.IP +.ft B +\h'-\w'\\$1\ 'u'\\$1\ \c +.ft P +.. +.ie t .ds o \(bu +.el .ds o o +.TH pause 1 "27 October 1999" +.SH "NAME" +pause \- wait for a period of time, or for a keypress +.SH "SYNOPSIS" +.B pause +.RI [ time ] +.SH "DESCRIPTION" +The +.B pause +program will wait until one of the following conditions is true: +.hP \*o +Standard input is a terminal, and a key has been pressed on the terminal +since the program started. +.hP \*o +A time limit was specified on the command line, and sufficient time has +elapsed since the program started. +.hP \*o +The program received a fatal signal. +.PP +The time limit is given as a positive real number, followed by a suffix +of +.RB ` d ', +.RB ` h ', +.RB ` m ' +or +.RB ` s ' +denoting days, hours, minutes or seconds (which are the default). +.PP +The standard GNU help options are understood: +.B \-\-help +(which may be abbreviated as +.BR \-h ) +prints a description of the program to standard output and exits; +.B \-\-version +.RB ( \-v ) +prints the version number and exits; and +.B \-\-usage +.RB ( \-u ) +prints a brief usage summary and exits. +.SH "SEE ALSO" +.BR sleep (1). +.SH "AUTHOR" +Mark Wooding, diff --git a/pause.c b/pause.c new file mode 100644 index 0000000..fd688e5 --- /dev/null +++ b/pause.c @@ -0,0 +1,304 @@ +/* -*-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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/*----- 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 -------------------------------------------------*/ -- [mdw]