diff options
Diffstat (limited to 'src/sleep.c')
-rw-r--r-- | src/sleep.c | 173 |
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); } |