Home | History | Annotate | Line # | Download | only in sleep
sleep.c revision 1.29
      1 /* $NetBSD: sleep.c,v 1.29 2019/01/27 02:00:45 christos Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1988, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\
     35  The Regents of the University of California.  All rights reserved.");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)sleep.c	8.3 (Berkeley) 4/2/94";
     41 #else
     42 __RCSID("$NetBSD: sleep.c,v 1.29 2019/01/27 02:00:45 christos Exp $");
     43 #endif
     44 #endif /* not lint */
     45 
     46 #include <ctype.h>
     47 #include <err.h>
     48 #include <locale.h>
     49 #include <math.h>
     50 #include <signal.h>
     51 #include <stdio.h>
     52 #include <stdlib.h>
     53 #include <time.h>
     54 #include <unistd.h>
     55 
     56 __dead static void alarmhandle(int);
     57 __dead static void usage(void);
     58 
     59 static void report(const time_t, const time_t, const char *const);
     60 
     61 static volatile sig_atomic_t report_requested;
     62 static void
     63 report_request(int signo __unused)
     64 {
     65 
     66 	report_requested = 1;
     67 }
     68 
     69 int
     70 main(int argc, char *argv[])
     71 {
     72 	char *arg, *temp;
     73 	const char *msg;
     74 	double fval, ival, val;
     75 	struct timespec ntime;
     76 	time_t original;
     77 	int ch, fracflag;
     78 	unsigned delay;
     79 
     80 	setprogname(argv[0]);
     81 	(void)setlocale(LC_ALL, "");
     82 
     83 	(void)signal(SIGALRM, alarmhandle);
     84 
     85 	while ((ch = getopt(argc, argv, "")) != -1)
     86 		switch(ch) {
     87 		default:
     88 			usage();
     89 		}
     90 	argc -= optind;
     91 	argv += optind;
     92 
     93 	if (argc != 1)
     94 		usage();
     95 
     96 	/*
     97 	 * Okay, why not just use atof for everything? Why bother
     98 	 * checking if there is a fraction in use? Because the old
     99 	 * sleep handled the full range of integers, that's why, and a
    100 	 * double can't handle a large long. This is fairly useless
    101 	 * given how large a number a double can hold on most
    102 	 * machines, but now we won't ever have trouble. If you want
    103 	 * 1000000000.9 seconds of sleep, well, that's your
    104 	 * problem. Why use an isdigit() check instead of checking for
    105 	 * a period? Because doing it this way means locales will be
    106 	 * handled transparently by the atof code.
    107 	 *
    108 	 * Since fracflag is set for any non-digit, we also fall
    109 	 * into the floating point conversion path if the input
    110 	 * is hex (the 'x' in 0xA is not a digit).  Then if
    111 	 * strtod() handles hex (on NetBSD it does) so will we.
    112 	 * That path is also taken for scientific notation (1.2e+3)
    113 	 * and when the input is simply nonsense.
    114 	 */
    115 	fracflag = 0;
    116 	arg = *argv;
    117 	for (temp = arg; *temp != '\0'; temp++)
    118 		if (!isdigit((unsigned char)*temp)) {
    119 			ch = *temp;
    120 			fracflag++;
    121 		}
    122 
    123 	if (fracflag) {
    124 		/*
    125 		 * If we cannot convert the value using the user's locale
    126 		 * then try again using the C locale, so strtod() can always
    127 		 * parse values like 2.5, even if the user's locale uses
    128 		 * a different decimal radix character (like ',')
    129 		 *
    130 		 * (but only if that is the potential problem)
    131 		 */
    132 		val = strtod(arg, &temp);
    133 		if (*temp != '\0')
    134 			val = strtod_l(arg, &temp, LC_C_LOCALE);
    135 		if (val < 0 || temp == arg || *temp != '\0')
    136 			usage();
    137 
    138 		ival = floor(val);
    139 		fval = (1000000000 * (val-ival));
    140 		ntime.tv_sec = ival;
    141 		if ((double)ntime.tv_sec != ival)
    142 			errx(1, "requested delay (%s) out of range", arg);
    143 		ntime.tv_nsec = fval;
    144 
    145 		if (ntime.tv_sec == 0 && ntime.tv_nsec == 0)
    146 			return EXIT_SUCCESS;	/* was 0.0 or underflowed */
    147 	} else {
    148 		ntime.tv_sec = strtol(arg, &temp, 10);
    149 		if (ntime.tv_sec < 0 || temp == arg || *temp != '\0')
    150 			usage();
    151 
    152 		if (ntime.tv_sec == 0)
    153 			return EXIT_SUCCESS;
    154 		ntime.tv_nsec = 0;
    155 	}
    156 
    157 	original = ntime.tv_sec;
    158 	if (ntime.tv_nsec != 0)
    159 		msg = " and a bit";
    160 	else
    161 		msg = "";
    162 
    163 	signal(SIGINFO, report_request);
    164 
    165 	if (ntime.tv_sec <= 10000) {			/* arbitrary */
    166 		while (nanosleep(&ntime, &ntime) != 0) {
    167 			if (report_requested) {
    168 				report(ntime.tv_sec, original, msg);
    169 				report_requested = 0;
    170 			} else
    171 				err(EXIT_FAILURE, "nanosleep failed");
    172 		}
    173 	} else while (ntime.tv_sec > 0) {
    174 		delay = (unsigned int)ntime.tv_sec;
    175 
    176 		if ((time_t)delay != ntime.tv_sec || delay > 30 * 86400)
    177 			delay = 30 * 86400;
    178 
    179 		ntime.tv_sec -= delay;
    180 		delay = sleep(delay);
    181 		ntime.tv_sec += delay;
    182 
    183 		if (delay != 0 && report_requested) {
    184 			report(ntime.tv_sec, original, "");
    185 			report_requested = 0;
    186 		} else
    187 			break;
    188 	}
    189 
    190 	return EXIT_SUCCESS;
    191 	/* NOTREACHED */
    192 }
    193 
    194 	/* Reporting does not bother with nanoseconds. */
    195 static void
    196 report(const time_t remain, const time_t original, const char * const msg)
    197 {
    198 	if (remain == 0)
    199 		warnx("In the final moments of the original"
    200 		    " %jd%s second%s", (intmax_t)original, msg,
    201 		    original == 1 && *msg == '\0' ? "" : "s");
    202 	else if (remain < 2000)
    203 		warnx("Between %jd and %jd seconds left"
    204 		    " out of the original %g%s",
    205 		    (intmax_t)remain, (intmax_t)remain + 1, (double)original,
    206 		    msg);
    207 	else if ((original - remain) < 100000 && (original-remain) < original/8)
    208 		warnx("Have waited only %jd seconds of the original %g",
    209 			(intmax_t)(original - remain), (double)original);
    210 	else
    211 		warnx("Approximately %g seconds left out of the original %g",
    212 			(double)remain, (double)original);
    213 }
    214 
    215 static void
    216 usage(void)
    217 {
    218 	(void)fprintf(stderr, "usage: %s seconds\n", getprogname());
    219 	exit(EXIT_FAILURE);
    220 	/* NOTREACHED */
    221 }
    222 
    223 /* ARGSUSED */
    224 static void
    225 alarmhandle(int i)
    226 {
    227 	_exit(EXIT_SUCCESS);
    228 	/* NOTREACHED */
    229 }
    230