blob: c6d5d9032cad2660cffab2093d4ae4e88aba1aff [file] [log] [blame]
--- a/src/time/__tz.c
+++ b/src/time/__tz.c
@@ -5,6 +5,10 @@
#include <string.h>
#include <sys/mman.h>
#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include "libc.h"
#include "lock.h"
#include "fork_impl.h"
@@ -123,6 +127,67 @@ static size_t zi_dotprod(const unsigned
return y;
}
+#define TZ_BUFLEN (2*TZNAME_MAX + 56)
+static char *read_TZ_file(char *buf)
+{
+ int r;
+ int fd;
+ char *p = NULL;
+
+ fd = open("/etc/TZ", O_RDONLY);
+ if (fd >= 0) {
+ /* Shorter, and does one fewer read syscall */
+ r = read(fd, buf, TZ_BUFLEN);
+ if (r < 0)
+ goto ERROR;
+ p = buf + r;
+
+ if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline */
+ p[-1] = 0;
+ p = buf;
+ } else {
+ERROR:
+ p = NULL;
+ }
+ close(fd);
+ } else {
+ fd = open("/etc/localtime", O_RDONLY);
+ if (fd >= 0) {
+ r = read(fd, buf, TZ_BUFLEN);
+ if (r != TZ_BUFLEN
+ || strncmp(buf, "TZif", 4) != 0
+ || (unsigned char)buf[4] < 2
+ || lseek(fd, -TZ_BUFLEN, SEEK_END) < 0
+ ) {
+ goto ERROR;
+ }
+ /* tzfile.h from tzcode database says about TZif2+ files:
+ **
+ ** If tzh_version is '2' or greater, the above is followed by a second instance
+ ** of tzhead and a second instance of the data in which each coded transition
+ ** time uses 8 rather than 4 chars,
+ ** then a POSIX-TZ-environment-variable-style string for use in handling
+ ** instants after the last transition time stored in the file
+ ** (with nothing between the newlines if there is no POSIX representation for
+ ** such instants).
+ */
+ r = read(fd, buf, TZ_BUFLEN);
+ if (r <= 0 || buf[--r] != '\n')
+ goto ERROR;
+ buf[r] = 0;
+ while (r != 0) {
+ if (buf[--r] == '\n') {
+ p = buf + r + 1;
+ break;
+ }
+ } /* else ('\n' not found): p remains NULL */
+ close(fd);
+ }
+ }
+
+ return p;
+}
+
static void do_tzset()
{
char buf[NAME_MAX+25], *pathname=buf+24;
@@ -131,8 +196,15 @@ static void do_tzset()
size_t i;
static const char search[] =
"/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0";
+ char tzbuf[TZ_BUFLEN];
s = getenv("TZ");
+
+ /* if TZ is empty try to read it from /etc/TZ */
+ if (!s || !*s) {
+ s = read_TZ_file(tzbuf);
+ }
+
if (!s) s = "/etc/localtime";
if (!*s) s = __utc;