summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2023-03-02 16:21:52 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2023-03-02 16:21:52 +0000
commit8c77d188a64ef31059fc9d87859aba8a228cc9c5 (patch)
treef0b2d273c69e56f2ca0b8d1ff43600cfd9cce2ff
parente49912572cfc5c5c5177956a7207931db8cf0a67 (diff)
When parsing %s, the result should be in the local time zone.
Based on a patch from enh@google. OK tb@
-rw-r--r--lib/libc/time/strptime.c60
1 files changed, 29 insertions, 31 deletions
diff --git a/lib/libc/time/strptime.c b/lib/libc/time/strptime.c
index 02017880457..2fae1f49220 100644
--- a/lib/libc/time/strptime.c
+++ b/lib/libc/time/strptime.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: strptime.c,v 1.30 2019/05/12 12:49:52 schwarze Exp $ */
+/* $OpenBSD: strptime.c,v 1.31 2023/03/02 16:21:51 millert Exp $ */
/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
/*-
* Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
@@ -29,8 +29,10 @@
*/
#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
#include <locale.h>
-#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -72,8 +74,8 @@ static const int mon_lengths[2][MONSPERYEAR] = {
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
-static int _conv_num64(const unsigned char **, int64_t *, int64_t, int64_t);
static int _conv_num(const unsigned char **, int *, int, int);
+static int epoch_to_tm(const unsigned char **, struct tm *);
static int leaps_thru_end_of(const int y);
static char *_strptime(const char *, const char *, struct tm *, int);
static const u_char *_find_string(const u_char *, int *, const char * const *,
@@ -338,15 +340,10 @@ literal:
if (!(_conv_num(&bp, &tm->tm_sec, 0, 60)))
return (NULL);
break;
- case 's': /* Seconds since epoch */
- {
- int64_t i64;
- if (!(_conv_num64(&bp, &i64, 0, INT64_MAX)))
- return (NULL);
- if (!gmtime_r(&i64, tm))
- return (NULL);
- fields = 0xffff; /* everything */
- }
+ case 's': /* Seconds since epoch. */
+ if (!(epoch_to_tm(&bp, tm)))
+ return (NULL);
+ fields = 0xffff; /* everything */
break;
case 'U': /* The week of year, beginning on sunday. */
case 'W': /* The week of year, beginning on monday. */
@@ -610,26 +607,27 @@ _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
}
static int
-_conv_num64(const unsigned char **buf, int64_t *dest, int64_t llim, int64_t ulim)
+epoch_to_tm(const unsigned char **buf, struct tm *tm)
{
- int result = 0;
- int64_t rulim = ulim;
-
- if (**buf < '0' || **buf > '9')
- return (0);
-
- /* we use rulim to break out of the loop when we run out of digits */
- do {
- result *= 10;
- result += *(*buf)++ - '0';
- rulim /= 10;
- } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
-
- if (result < llim || result > ulim)
- return (0);
-
- *dest = result;
- return (1);
+ int saved_errno = errno;
+ int ret = 0;
+ time_t secs;
+ char *ep;
+
+ errno = 0;
+ secs = strtoll(*buf, &ep, 10);
+ if (*buf == (unsigned char *)ep)
+ goto done;
+ if (secs < 0 ||
+ secs == LLONG_MAX && errno == ERANGE)
+ goto done;
+ if (localtime_r(&secs, tm) == NULL)
+ goto done;
+ ret = 1;
+done:
+ *buf = ep;
+ errno = saved_errno;
+ return (ret);
}
static const u_char *