| --- 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; |
| |