summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1999-11-27 20:00:54 +0000
committerJim Meyering <jim@meyering.net>1999-11-27 20:00:54 +0000
commit56172516598e1f48a629830aa5ce2c9ab9240bb7 (patch)
tree49dea6deef280bcc3f5eba5c82c84ebb44891eff /src
parent398516f242b1ba9d8cb91e57a1fea0eb858d377f (diff)
downloadcoreutils-56172516598e1f48a629830aa5ce2c9ab9240bb7.tar.xz
Rewrite to allow fractional seconds and to handle SIGCONT.
(main): Rewrite. (sighandler): New function. (apply_suffix): New function. (timeval_subtract): New function.
Diffstat (limited to 'src')
-rw-r--r--src/sleep.c173
1 files changed, 153 insertions, 20 deletions
diff --git a/src/sleep.c b/src/sleep.c
index d573eb693..5ff91cb31 100644
--- a/src/sleep.c
+++ b/src/sleep.c
@@ -18,11 +18,22 @@
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
+#include <signal.h>
+#include <sys/time.h>
#include <getopt.h>
+#include <math.h>
+#if HAVE_FLOAT_H
+# include <float.h>
+#else
+# define DBL_MAX 1.7976931348623159e+308
+# define DBL_MIN 2.2250738585072010e-308
+#endif
+
#include "system.h"
#include "error.h"
#include "long-options.h"
+#include "xstrtod.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "sleep"
@@ -32,6 +43,9 @@
/* The name by which this program was run. */
char *program_name;
+/* This is set once we've received the SIGCONT signal. */
+static int suspended;
+
static struct option const long_options[] =
{
{0, 0, 0, 0}
@@ -61,45 +75,111 @@ h for hours or d for days.\n\
exit (status);
}
-static long
-argdecode (const char *s)
+/* Handle SIGCONT. */
+
+static void
+sighandler (int sig)
{
- long value;
- register const char *p = s;
- register char c;
+#ifdef SA_INTERRUPT
+ struct sigaction sigact;
- value = 0;
- while ((c = *p++) >= '0' && c <= '9')
- value = value * 10 + c - '0';
+ sigact.sa_handler = SIG_DFL;
+ sigemptyset (&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction (sig, &sigact, NULL);
+#else
+ signal (sig, SIG_DFL);
+#endif
+ printf ("in handler\n");
+
+ suspended = 1;
+ kill (getpid (), sig);
+}
- switch (c)
+/* FIXME: describe */
+
+static int
+apply_suffix (double *s, char suffix_char)
+{
+ unsigned int multiplier;
+
+ switch (suffix_char)
{
+ case 0:
case 's':
+ multiplier = 1;
break;
case 'm':
- value *= 60;
+ multiplier = 60;
break;
case 'h':
- value *= 60 * 60;
+ multiplier = 60 * 60;
break;
case 'd':
- value *= 60 * 60 * 24;
+ multiplier = 60 * 60 * 24;
break;
default:
- p--;
+ multiplier = 0;
}
- if (*p)
- error (1, 0, _("invalid time interval `%s'"), s);
- return value;
+ if (multiplier == 0 || *s > DBL_MAX / multiplier)
+ return 1;
+
+ *s *= multiplier;
+
+ return 0;
+}
+
+/* Subtract the `struct timeval' values X and Y,
+ storing the result in RESULT.
+ Return 1 if the difference is negative, otherwise 0.
+ From the GNU libc manual. */
+
+static int
+timeval_subtract (struct timeval *result,
+ const struct timeval *x, struct timeval *y)
+{
+ /* Perform the carry for the later subtraction by updating Y. */
+ if (x->tv_usec < y->tv_usec)
+ {
+ int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+ y->tv_usec -= 1000000 * nsec;
+ y->tv_sec += nsec;
+ }
+
+ if (x->tv_usec - y->tv_usec > 1000000)
+ {
+ int nsec = (y->tv_usec - x->tv_usec) / 1000000;
+ y->tv_usec += 1000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ /* Compute the time remaining to wait.
+ `tv_usec' is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_usec = x->tv_usec - y->tv_usec;
+
+ /* Return 1 if result is negative. */
+ return x->tv_sec < y->tv_sec;
}
int
main (int argc, char **argv)
{
int i;
- unsigned seconds = 0;
+ double seconds = 0.0;
int c;
+ int fail = 0;
+ struct timeval tv_start;
+ struct timeval tv_done;
+ int i_sec;
+ int i_usec;
+#ifdef SA_INTERRUPT
+ struct sigaction oldact, newact;
+#endif
+
+ /* FIXME */
+ gettimeofday (&tv_start, NULL);
program_name = argv[0];
setlocale (LC_ALL, "");
@@ -127,10 +207,63 @@ main (int argc, char **argv)
usage (1);
}
- for (i = 1; i < argc; i++)
- seconds += argdecode (argv[i]);
+ for (i = optind; i < argc; i++)
+ {
+ double s;
+ const char *p;
+ if (xstrtod (argv[i], &p, &s) || s < 0 || apply_suffix (&s, *p))
+ {
+ error (0, 0, _("invalid time interval `%s'"), argv[i]);
+ fail = 1;
+ continue;
+ }
+ seconds += s;
+ }
+
- sleep (seconds);
+ if (fail)
+ usage (1);
+
+#ifdef SA_INTERRUPT
+ newact.sa_handler = sighandler;
+ sigemptyset (&newact.sa_mask);
+ newact.sa_flags = 0;
+
+ sigaction (SIGCONT, NULL, &oldact);
+ if (oldact.sa_handler != SIG_IGN)
+ sigaction (SIGCONT, &newact, NULL);
+#else /* !SA_INTERRUPT */
+ if (signal (SIGCONT, SIG_IGN) != SIG_IGN)
+ signal (SIGCONT, sighandler);
+#endif /* !SA_INTERRUPT */
+
+ i_sec = floor (seconds);
+ printf ("sleeping for: %d sec\n", i_sec);
+ if (i_sec > 0)
+ sleep (i_sec);
+ i_usec = (int) ((seconds - i_sec) * 1000000);
+ printf ("u-sleeping for: %d usec\n", i_usec);
+ usleep (i_usec);
+
+ if (!suspended)
+ exit (0);
+
+ tv_done.tv_sec = tv_start.tv_sec + i_sec;
+ tv_done.tv_usec = tv_start.tv_usec + i_usec;
+
+ while (1)
+ {
+ struct timeval diff;
+ struct timeval tv_now;
+ int negative;
+ gettimeofday (&tv_now, NULL);
+ negative = timeval_subtract (&diff, &tv_done, &tv_now);
+ if (negative)
+ break;
+ tv_sleep (&diff);
+ sleep (diff->tv_sec);
+ usleep (diff->tv_usec);
+ }
exit (0);
}