Add #pragma STDC FENV_ACCESS ON if C99.
authorJim Meyering <jim@meyering.net>
Wed, 5 Jan 2000 12:17:19 +0000 (12:17 +0000)
committerJim Meyering <jim@meyering.net>
Wed, 5 Jan 2000 12:17:19 +0000 (12:17 +0000)
(<fenv.h>): Include if available.
(main): Always round floating-point interval calculations upwards,
as we must sleep for at least the specified time.
Do the right thing with NaNs.

src/sleep.c

index 3947a30..9d96e39 100644 (file)
 #include "long-options.h"
 #include "xstrtod.h"
 
+#if HAVE_FENV_H
+# include <fenv.h>
+#endif
+#if 199901 <= __STDC_VERSION__
+ #pragma STDC FENV_ACCESS ON
+#endif
+
 /* The official name of this program (e.g., no `g' prefix).  */
 #define PROGRAM_NAME "sleep"
 
@@ -81,8 +88,6 @@ apply_suffix (double *s, char suffix_char)
 {
   unsigned int multiplier;
 
-  assert (*s <= TIME_T_MAX);
-
   switch (suffix_char)
     {
     case 0:
@@ -148,7 +153,7 @@ clock_get_realtime (struct timespec *ts)
 {
   int fail;
 #if USE_CLOCK_GETTIME
-  fail = clock_gettime (CLOCK_REALTIME, &ts);
+  fail = clock_gettime (CLOCK_REALTIME, ts);
 #else
   struct timeval tv;
   fail = gettimeofday (&tv, NULL);
@@ -168,8 +173,10 @@ main (int argc, char **argv)
 {
   int i;
   double seconds = 0.0;
+  double ns;
   int c;
   int fail = 0;
+  int forever;
   struct timespec ts_start;
   struct timespec ts_stop;
   struct timespec ts_sleep;
@@ -203,38 +210,56 @@ main (int argc, char **argv)
       usage (1);
     }
 
+#ifdef FE_UPWARD
+  /* Always round up, since we must sleep for at least the specified
+     interval.  */
+  fesetround (FE_UPWARD);
+#endif
+
   for (i = optind; i < argc; i++)
     {
       double s;
       const char *p;
       if (xstrtod (argv[i], &p, &s)
-         /* No negative intervals.  */
-         || s < 0
-         /* S must fit in a time_t.  */
-         || TIME_T_MAX < s
+         /* Nonnegative interval.  */
+         || ! (0 <= s)
          /* No extra chars after the number and an optional s,m,h,d char.  */
          || (*p && *(p+1))
          /* Check any suffix char and update S based on the suffix.  */
-         || apply_suffix (&s, *p)
-         /* Make sure the sum fits in a time_t.  */
-         || TIME_T_MAX < (seconds += s)
-         )
+         || apply_suffix (&s, *p))
        {
          error (0, 0, _("invalid time interval `%s'"), argv[i]);
          fail = 1;
        }
+
+      seconds += s;
     }
 
   if (fail)
     usage (1);
 
-  /* Add this here so we end up rounding to the nearest nanosecond.
-     This ensures that that tv_nsec will be no larger than 999,999,999.  */
-  seconds += .0000000005;
-
-  /* Separate whole seconds from nanoseconds.  */
+  /* Separate whole seconds from nanoseconds.
+     Be careful to detect any overflow.  */
   ts_sleep.tv_sec = seconds;
-  ts_sleep.tv_nsec = (seconds - ts_sleep.tv_sec) * 1000000000;
+  ns = 1e9 * (seconds - ts_sleep.tv_sec);
+  forever = ! (ts_sleep.tv_sec <= seconds && 0 <= ns && ns <= 1e9);
+  ts_sleep.tv_nsec = ns;
+
+  /* Round up to the next whole number, if necessary, so that we
+     always sleep for at least the request amount of time.  */
+  ts_sleep.tv_nsec += (ts_sleep.tv_nsec < ns);
+
+  /* Normalize the interval length.  nanosleep requires this.  */
+  if (1000000000 <= ts_sleep.tv_nsec)
+    {
+      time_t t = ts_sleep.tv_sec + 1;
+
+      /* Detect floating point overflow (NaN) in interval length. */
+      forever |= (t < ts_sleep.tv_sec);
+
+      ts_sleep.tv_sec = t;
+      ts_sleep.tv_nsec -= 1000000000;
+    }
 
   ts_stop.tv_sec = ts_start.tv_sec + ts_sleep.tv_sec;
   ts_stop.tv_nsec = ts_start.tv_nsec + ts_sleep.tv_nsec;
@@ -244,6 +269,17 @@ main (int argc, char **argv)
       ts_stop.tv_nsec -= 1000000000;
     }
 
+  /* Detect integer overflow.  */
+  forever |= (ts_stop.tv_sec < ts_start.tv_sec
+             || (ts_stop.tv_sec == ts_start.tv_sec
+                 && ts_stop.tv_nsec < ts_start.tv_nsec));
+
+  if (forever)
+    {
+      ts_sleep.tv_sec = ts_stop.tv_sec = TIME_T_MAX;
+      ts_sleep.tv_nsec = ts_stop.tv_nsec = 999999999;
+    }
+
   while (1)
     {
       struct timespec remaining;