[tz] [PATCH 3/4] Add FreeBSD-style -r option to 'date'.

Paul Eggert eggert at cs.ucla.edu
Fri Aug 9 09:11:28 UTC 2013


* date.1: Document -r.
* date.c (main, usage): Support -r.
(main, reset): Remove EBUG code.
(display): New argument NOW.  Do not exit; that's now the
caller's responsibility.  All callers changed.
(display, timeout, convert, checkfinal, iffy):
Don't assume that localtime and gmtime succeed.
This prevents a core dump for, e.g., 'date -r 0xffffffffffffffff'.
* private.h: Include float.h.
(strtoimax): New macro, for pre-C99 systems that lack strtoimax.
(time_t_min, time_t_max): New constants, from zdump, with
different names to avoid a clash when zdump.c includes private.h.
---
 date.1    |  11 +++++
 date.c    | 142 ++++++++++++++++++++++++++++++++++++++------------------------
 private.h |  27 ++++++++++++
 3 files changed, 125 insertions(+), 55 deletions(-)

diff --git a/date.1 b/date.1
index 408525c..7957f74 100644
--- a/date.1
+++ b/date.1
@@ -10,6 +10,9 @@ date \- show and set date and time
 ] [
 .B \-c
 ] [
+.B \-r
+seconds
+] [
 .B \-n
 ] [
 .B \-d
@@ -133,6 +136,14 @@ These options are available:
 .BR \-u " or " \-c
 Use UTC when setting and showing the date and time.
 .TP
+.BI "\-r " seconds
+Output the date that corresponds to
+.I seconds
+past the epoch of 1970-01-01 00:00:00 UTC, where
+.I seconds
+should be an integer, either decimal, octal (leading 0), or
+hexadecimal (leading 0x), preceded by an optional sign.
+.TP
 .B \-n
 Do not notify other networked systems of the time change.
 .TP
diff --git a/date.c b/date.c
index bae90f3..4355a7b 100644
--- a/date.c
+++ b/date.c
@@ -63,7 +63,7 @@ static int		retval = EXIT_SUCCESS;
 
 static void		checkfinal(const char *, int, time_t, time_t);
 static time_t		convert(const char *, int, time_t);
-static void		display(const char *);
+static void		display(const char *, time_t);
 static void		dogmt(void);
 static void		errensure(void);
 static void		iffy(time_t, time_t, const char *, const char *);
@@ -89,11 +89,14 @@ main(const int argc, char *argv[])
 	register int		dflag = 0;
 	register int		nflag = 0;
 	register int		tflag = 0;
+	register int		rflag = 0;
 	register int		minuteswest;
 	register int		dsttime;
 	register double		adjust;
 	time_t			now;
 	time_t			t;
+	intmax_t		secs;
+	char *			endarg;
 
 	INITIALIZE(dousg);
 	INITIALIZE(minuteswest);
@@ -109,9 +112,9 @@ main(const int argc, char *argv[])
 #endif /* defined(TEXTDOMAINDIR) */
 	(void) textdomain(TZ_DOMAIN);
 #endif /* HAVE_GETTEXT */
-	(void) time(&now);
+	t = now = time(NULL);
 	format = value = NULL;
-	while ((ch = getopt(argc, argv, "ucnd:t:a:")) != EOF && ch != -1) {
+	while ((ch = getopt(argc, argv, "ucr:nd:t:a:")) != EOF && ch != -1) {
 		switch (ch) {
 		default:
 			usage();
@@ -119,6 +122,26 @@ main(const int argc, char *argv[])
 		case 'c':
 			dogmt();
 			break;
+		case 'r':		/* seconds since 1970 */
+			if (rflag) {
+				(void) fprintf(stderr,
+					_("date: error: multiple -r's used"));
+				usage();
+			}
+			rflag = 1;
+			errno = 0;
+			secs = strtoimax (optarg, &endarg, 0);
+			if (*endarg || optarg == endarg)
+				errno = EINVAL;
+			else if (! (time_t_min <= secs && secs <= time_t_max))
+				errno = ERANGE;
+			if (errno) {
+				perror(optarg);
+				errensure();
+				exit(retval);
+			}
+			t = secs;
+			break;
 		case 'n':		/* don't set network */
 			nflag = 1;
 			break;
@@ -183,7 +206,7 @@ main(const int argc, char *argv[])
 _("date: error: multiple formats in command line\n"));
 				usage();
 			}
-		else	if (value == NULL)
+		else	if (value == NULL && !rflag)
 				value = cp;
 			else {
 				(void) fprintf(stderr,
@@ -272,28 +295,14 @@ _("date: warning: kernel doesn't keep -d/-t information, option ignored\n"));
 #endif /* HAVE_SETTIMEOFDAY != 2 */
 	}
 
-	if (value == NULL)
-		display(format);
-
-	reset(t, nflag);
-
-	checkfinal(value, dousg, t, now);
-
-#ifdef EBUG
-	{
-		struct tm	tm;
-
-		tm = *localtime(&t);
-		timeout(stdout, "%c\n", &tm);
-		exit(retval);
+	if (value) {
+		reset(t, nflag);
+		checkfinal(value, dousg, t, now);
+		t = time(NULL);
 	}
-#endif /* defined EBUG */
-
-	display(format);
 
-	/* gcc -Wall pacifier */
-	for ( ; ; )
-		continue;
+	display(format, t);
+	return retval;
 }
 
 static void
@@ -449,9 +458,6 @@ reset(const time_t newt, const int nflag)
 	register const char *	username;
 	static struct timeval	tv;	/* static so tv_usec is 0 */
 
-#ifdef EBUG
-	return;
-#endif /* defined EBUG */
 	username = getlogin();
 	if (username == NULL || *username == '\0') /* single-user or no tty */
 		username = "root";
@@ -502,8 +508,10 @@ nondigit(register const char *cp)
 static void
 usage(void)
 {
-	(void) fprintf(stderr, _("date: usage is date [-u] [-c] [-n] [-d dst] \
-[-t min-west] [-a sss.fff] [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n"));
+	(void) fprintf(stderr,
+		       _("date: usage: date [-u] [-c] [-r seconds] [-n]"
+			 " [-d dst] [-t min-west] [-a sss.fff]"
+			 " [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n"));
 	errensure();
 	exit(retval);
 }
@@ -517,18 +525,23 @@ oops(const char *const string)
 	errno = e;
 	(void) perror(string);
 	errensure();
-	display(NULL);
+	display(NULL, time(NULL));
+	exit(retval);
 }
 
 static void
-display(const char *const format)
+display(const char *const format, time_t const now)
 {
-	struct tm	tm;
-	time_t		now;
+	struct tm *tmp;
 
-	(void) time(&now);
-	tm = *localtime(&now);
-	timeout(stdout, format ? format : "%+", &tm);
+	tmp = localtime(&now);
+	if (!tmp) {
+		(void) fprintf(stderr,
+			_("date: error: time out of range\n"));
+		errensure();
+		return;
+	}
+	timeout(stdout, format ? format : "%+", tmp);
 	(void) putchar('\n');
 	(void) fflush(stdout);
 	(void) fflush(stderr);
@@ -537,20 +550,27 @@ display(const char *const format)
 			_("date: error: couldn't write results\n"));
 		errensure();
 	}
-	exit(retval);
 }
 
 #define INCR	1024
 
 static void
-timeout(FILE *const fp, const char *const format, const struct tm *const tmp)
+timeout(FILE *const fp, const char *const format, const struct tm *tmp)
 {
 	char *	cp;
 	size_t	result;
 	size_t	size;
+	struct tm tm;
 
 	if (*format == '\0')
 		return;
+	if (!tmp) {
+		(void) fprintf(stderr, _("date: error: time out of range\n"));
+		errensure();
+		return;
+	}
+	tm = *tmp;
+	tmp = &tm;
 	size = INCR;
 	cp = malloc(size);
 	for ( ; ; ) {
@@ -596,10 +616,13 @@ convert(register const char * const value, const int dousg, const time_t t)
 	register const char *	cp;
 	register const char *	dotp;
 	register int	cent, year_in_cent, month, hour, day, mins, secs;
-	struct tm	tm, outtm;
+	struct tm	tm, outtm, *tmp;
 	time_t		outt;
 
-	tm = *localtime(&t);
+	tmp = localtime(&t);
+	if (!tmp)
+		return -1;
+	tm = *tmp;
 #define DIVISOR	100
 	year_in_cent = tm.tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
 	cent = tm.tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
@@ -701,7 +724,7 @@ checkfinal(const char * const	value,
 	   const time_t		oldnow)
 {
 	time_t		othert;
-	struct tm	tm;
+	struct tm	tm, *tmp;
 	struct tm	othertm;
 	register int	pass, offset;
 
@@ -714,8 +737,10 @@ checkfinal(const char * const	value,
 	/*
 	** See if there's both a DST and a STD version.
 	*/
-	tm = *localtime(&t);
-	othertm = tm;
+	tmp = localtime(&t);
+	if (!tmp)
+		iffy(t, othert, value, _("time out of range"));
+	othertm = tm = *tmp;
 	othertm.tm_isdst = !tm.tm_isdst;
 	othert = mktime(&othertm);
 	if (othert != -1 && othertm.tm_isdst != tm.tm_isdst &&
@@ -748,7 +773,11 @@ checkfinal(const char * const	value,
 			else if (pass == 3)
 				othert = t + 60 * offset;
 			else	othert = t - 60 * offset;
-			othertm = *localtime(&othert);
+			tmp = localtime(&othert);
+			if (!tmp)
+				iffy(t, othert, value,
+					_("time out of range"));
+			othertm = *tmp;
 			if (sametm(&tm, &othertm))
 				iffy(t, othert, value,
 					_("multiple matching times exist"));
@@ -759,29 +788,32 @@ static void
 iffy(const time_t thist, const time_t thatt,
 	const char * const value, const char * const reason)
 {
-	struct tm	tm;
+	struct tm *tmp;
+	int dst;
 
 	(void) fprintf(stderr, _("date: warning: ambiguous time \"%s\", %s.\n"),
 		value, reason);
-	tm = *gmtime(&thist);
+	tmp = gmtime(&thist);
 	/*
 	** Avoid running afoul of SCCS!
 	*/
 	timeout(stderr, _("Time was set as if you used\n\tdate -u %m%d%H\
 %M\
-%Y.%S\n"), &tm);
-	tm = *localtime(&thist);
-	timeout(stderr, _("to get %c"), &tm);
+%Y.%S\n"), tmp);
+	tmp = localtime(&thist);
+	dst = tmp ? tmp->tm_isdst : 0;
+	timeout(stderr, _("to get %c"), tmp);
 	(void) fprintf(stderr, _(" (%s).  Use\n"),
-		tm.tm_isdst ? _("summer time") : _("standard time"));
-	tm = *gmtime(&thatt);
+		dst ? _("summer time") : _("standard time"));
+	tmp = gmtime(&thatt);
 	timeout(stderr, _("\tdate -u %m%d%H\
 %M\
-%Y.%S\n"), &tm);
-	tm = *localtime(&thatt);
-	timeout(stderr, _("to get %c"), &tm);
+%Y.%S\n"), tmp);
+	tmp = localtime(&thatt);
+	dst = tmp ? tmp->tm_isdst : 0;
+	timeout(stderr, _("to get %c"), tmp);
 	(void) fprintf(stderr, _(" (%s).\n"),
-		tm.tm_isdst ? _("summer time") : _("standard time"));
+		dst ? _("summer time") : _("standard time"));
 	errensure();
 	exit(retval);
 }
diff --git a/private.h b/private.h
index bf6bad2..5f4384e 100644
--- a/private.h
+++ b/private.h
@@ -74,6 +74,7 @@
 #include "sys/types.h"	/* for time_t */
 #include "stdio.h"
 #include "errno.h"
+#include "float.h"	/* for FLT_MAX and DBL_MAX */
 #include "string.h"
 #include "limits.h"	/* for CHAR_BIT et al. */
 #include "time.h"
@@ -166,6 +167,7 @@ typedef int int_fast32_t;
 #ifndef INTMAX_MAX
 # if defined LLONG_MAX || defined __LONG_LONG_MAX__
 typedef long long intmax_t;
+#  define strtoimax strtoll
 #  define PRIdMAX "lld"
 #  ifdef LLONG_MAX
 #   define INTMAX_MAX LLONG_MAX
@@ -176,6 +178,7 @@ typedef long long intmax_t;
 #  endif
 # else
 typedef long intmax_t;
+#  define strtoimax strtol
 #  define PRIdMAX "ld"
 #  define INTMAX_MAX LONG_MAX
 #  define INTMAX_MIN LONG_MIN
@@ -313,6 +316,30 @@ const char *	scheck(const char * string, const char * format);
 #define TYPE_SIGNED(type) (((type) -1) < 0)
 #endif /* !defined TYPE_SIGNED */
 
+/* The minimum and maximum finite time values.  */
+static time_t const time_t_min =
+  ((time_t) 0.5 == 0.5
+   ? (sizeof (time_t) == sizeof (float) ? (time_t) -FLT_MAX
+      : sizeof (time_t) == sizeof (double) ? (time_t) -DBL_MAX
+      : sizeof (time_t) == sizeof (long double) ? (time_t) -LDBL_MAX
+      : 0)
+#ifndef TIME_T_FLOATING
+   : (time_t) -1 < 0
+   ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
+#endif
+   : 0);
+static time_t const time_t_max =
+  ((time_t) 0.5 == 0.5
+   ? (sizeof (time_t) == sizeof (float) ? (time_t) FLT_MAX
+      : sizeof (time_t) == sizeof (double) ? (time_t) DBL_MAX
+      : sizeof (time_t) == sizeof (long double) ? (time_t) LDBL_MAX
+      : -1)
+#ifndef TIME_T_FLOATING
+   : (time_t) -1 < 0
+   ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
+#endif
+   : -1);
+
 /*
 ** Since the definition of TYPE_INTEGRAL contains floating point numbers,
 ** it cannot be used in preprocessor directives.
-- 
1.8.1.2




More information about the tz mailing list