[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