OLD | NEW |
(Empty) | |
| 1 #include "time_impl.h" |
| 2 #include <stdint.h> |
| 3 #include <limits.h> |
| 4 #include <stdlib.h> |
| 5 #include <string.h> |
| 6 #include "libc.h" |
| 7 |
| 8 long __timezone = 0; |
| 9 int __daylight = 0; |
| 10 char *__tzname[2] = { 0, 0 }; |
| 11 |
| 12 weak_alias(__timezone, timezone); |
| 13 weak_alias(__daylight, daylight); |
| 14 weak_alias(__tzname, tzname); |
| 15 |
| 16 static char std_name[TZNAME_MAX+1]; |
| 17 static char dst_name[TZNAME_MAX+1]; |
| 18 const char __gmt[] = "GMT"; |
| 19 |
| 20 static int dst_off; |
| 21 static int r0[5], r1[5]; |
| 22 |
| 23 static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end; |
| 24 static size_t map_size; |
| 25 |
| 26 static char old_tz_buf[32]; |
| 27 static char *old_tz = old_tz_buf; |
| 28 static size_t old_tz_size = sizeof old_tz_buf; |
| 29 |
| 30 static volatile int lock[2]; |
| 31 |
| 32 static int getint(const char **p) |
| 33 { |
| 34 unsigned x; |
| 35 for (x=0; **p-'0'<10U; (*p)++) x = **p-'0' + 10*x; |
| 36 return x; |
| 37 } |
| 38 |
| 39 static int getoff(const char **p) |
| 40 { |
| 41 int neg = 0; |
| 42 if (**p == '-') { |
| 43 ++*p; |
| 44 neg = 1; |
| 45 } else if (**p == '+') { |
| 46 ++*p; |
| 47 } |
| 48 int off = 3600*getint(p); |
| 49 if (**p == ':') { |
| 50 ++*p; |
| 51 off += 60*getint(p); |
| 52 if (**p == ':') { |
| 53 ++*p; |
| 54 off += getint(p); |
| 55 } |
| 56 } |
| 57 return neg ? -off : off; |
| 58 } |
| 59 |
| 60 static void getrule(const char **p, int rule[5]) |
| 61 { |
| 62 int r = rule[0] = **p; |
| 63 |
| 64 if (r!='M') { |
| 65 if (r=='J') ++*p; |
| 66 else rule[0] = 0; |
| 67 rule[1] = getint(p); |
| 68 } else { |
| 69 ++*p; rule[1] = getint(p); |
| 70 ++*p; rule[2] = getint(p); |
| 71 ++*p; rule[3] = getint(p); |
| 72 } |
| 73 |
| 74 if (**p=='/') { |
| 75 ++*p; |
| 76 rule[4] = getoff(p); |
| 77 } else { |
| 78 rule[4] = 7200; |
| 79 } |
| 80 } |
| 81 |
| 82 static void getname(char *d, const char **p) |
| 83 { |
| 84 int i; |
| 85 if (**p == '<') { |
| 86 ++*p; |
| 87 for (i=0; **p!='>' && i<TZNAME_MAX; i++) |
| 88 d[i] = (*p)[i]; |
| 89 ++*p; |
| 90 } else { |
| 91 for (i=0; ((*p)[i]|32)-'a'<26U && i<TZNAME_MAX; i++) |
| 92 d[i] = (*p)[i]; |
| 93 } |
| 94 *p += i; |
| 95 d[i] = 0; |
| 96 } |
| 97 |
| 98 #define VEC(...) ((const unsigned char[]){__VA_ARGS__}) |
| 99 |
| 100 static uint32_t zi_read32(const unsigned char *z) |
| 101 { |
| 102 return (unsigned)z[0]<<24 | z[1]<<16 | z[2]<<8 | z[3]; |
| 103 } |
| 104 |
| 105 static size_t zi_dotprod(const unsigned char *z, const unsigned char *v, size_t
n) |
| 106 { |
| 107 size_t y; |
| 108 uint32_t x; |
| 109 for (y=0; n; n--, z+=4, v++) { |
| 110 x = zi_read32(z); |
| 111 y += x * *v; |
| 112 } |
| 113 return y; |
| 114 } |
| 115 |
| 116 int __munmap(void *, size_t); |
| 117 |
| 118 static void do_tzset() |
| 119 { |
| 120 char buf[NAME_MAX+25], *pathname=buf+24; |
| 121 const char *try, *s, *p; |
| 122 const unsigned char *map = 0; |
| 123 size_t i; |
| 124 static const char search[] = |
| 125 "/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0"; |
| 126 |
| 127 s = getenv("TZ"); |
| 128 if (!s) s = "/etc/localtime"; |
| 129 if (!*s) s = __gmt; |
| 130 |
| 131 if (old_tz && !strcmp(s, old_tz)) return; |
| 132 |
| 133 if (zi) __munmap((void *)zi, map_size); |
| 134 |
| 135 /* Cache the old value of TZ to check if it has changed. Avoid |
| 136 * free so as not to pull it into static programs. Growth |
| 137 * strategy makes it so free would have minimal benefit anyway. */ |
| 138 i = strlen(s); |
| 139 if (i > PATH_MAX+1) s = __gmt, i = 3; |
| 140 if (i >= old_tz_size) { |
| 141 old_tz_size *= 2; |
| 142 if (i >= old_tz_size) old_tz_size = i+1; |
| 143 if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2; |
| 144 old_tz = malloc(old_tz_size); |
| 145 } |
| 146 if (old_tz) memcpy(old_tz, s, i+1); |
| 147 |
| 148 /* Non-suid can use an absolute tzfile pathname or a relative |
| 149 * pathame beginning with "."; in secure mode, only the |
| 150 * standard path will be searched. */ |
| 151 if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) { |
| 152 if (*s == ':') s++; |
| 153 if (*s == '/' || *s == '.') { |
| 154 if (!libc.secure || !strcmp(s, "/etc/localtime")) |
| 155 map = __map_file(s, &map_size); |
| 156 } else { |
| 157 size_t l = strlen(s); |
| 158 if (l <= NAME_MAX && !strchr(s, '.')) { |
| 159 memcpy(pathname, s, l+1); |
| 160 pathname[l] = 0; |
| 161 for (try=search; !map && *try; try+=l+1) { |
| 162 l = strlen(try); |
| 163 memcpy(pathname-l, try, l); |
| 164 map = __map_file(pathname-l, &map_size); |
| 165 } |
| 166 } |
| 167 } |
| 168 if (!map) s = __gmt; |
| 169 } |
| 170 if (map && (map_size < 44 || memcmp(map, "TZif", 4))) { |
| 171 __munmap((void *)map, map_size); |
| 172 map = 0; |
| 173 s = __gmt; |
| 174 } |
| 175 |
| 176 zi = map; |
| 177 if (map) { |
| 178 int scale = 2; |
| 179 if (sizeof(time_t) > 4 && map[4]=='2') { |
| 180 size_t skip = zi_dotprod(zi+20, VEC(1,1,8,5,6,1), 6); |
| 181 trans = zi+skip+44+44; |
| 182 scale++; |
| 183 } else { |
| 184 trans = zi+44; |
| 185 } |
| 186 index = trans + (zi_read32(trans-12) << scale); |
| 187 types = index + zi_read32(trans-12); |
| 188 abbrevs = types + 6*zi_read32(trans-8); |
| 189 abbrevs_end = abbrevs + zi_read32(trans-4); |
| 190 if (zi[map_size-1] == '\n') { |
| 191 for (s = (const char *)zi+map_size-2; *s!='\n'; s--); |
| 192 s++; |
| 193 } else { |
| 194 const unsigned char *p; |
| 195 __tzname[0] = __tzname[1] = 0; |
| 196 __daylight = __timezone = dst_off = 0; |
| 197 for (i=0; i<5; i++) r0[i] = r1[i] = 0; |
| 198 for (p=types; p<abbrevs; p+=6) { |
| 199 if (!p[4] && !__tzname[0]) { |
| 200 __tzname[0] = (char *)abbrevs + p[5]; |
| 201 __timezone = -zi_read32(p); |
| 202 } |
| 203 if (p[4] && !__tzname[1]) { |
| 204 __tzname[1] = (char *)abbrevs + p[5]; |
| 205 dst_off = -zi_read32(p); |
| 206 __daylight = 1; |
| 207 } |
| 208 } |
| 209 if (!__tzname[0]) __tzname[0] = __tzname[1]; |
| 210 if (!__tzname[0]) __tzname[0] = (char *)__gmt; |
| 211 if (!__daylight) { |
| 212 __tzname[1] = __tzname[0]; |
| 213 dst_off = __timezone; |
| 214 } |
| 215 return; |
| 216 } |
| 217 } |
| 218 |
| 219 if (!s) s = __gmt; |
| 220 getname(std_name, &s); |
| 221 __tzname[0] = std_name; |
| 222 __timezone = getoff(&s); |
| 223 getname(dst_name, &s); |
| 224 __tzname[1] = dst_name; |
| 225 if (dst_name[0]) { |
| 226 __daylight = 1; |
| 227 if (*s == '+' || *s=='-' || *s-'0'<10U) |
| 228 dst_off = getoff(&s); |
| 229 else |
| 230 dst_off = __timezone - 3600; |
| 231 } else { |
| 232 __daylight = 0; |
| 233 dst_off = 0; |
| 234 } |
| 235 |
| 236 if (*s == ',') s++, getrule(&s, r0); |
| 237 if (*s == ',') s++, getrule(&s, r1); |
| 238 } |
| 239 |
| 240 /* Search zoneinfo rules to find the one that applies to the given time, |
| 241 * and determine alternate opposite-DST-status rule that may be needed. */ |
| 242 |
| 243 static size_t scan_trans(long long t, int local, size_t *alt) |
| 244 { |
| 245 int scale = 3 - (trans == zi+44); |
| 246 uint64_t x; |
| 247 int off = 0; |
| 248 |
| 249 size_t a = 0, n = (index-trans)>>scale, m; |
| 250 |
| 251 if (!n) { |
| 252 if (alt) *alt = 0; |
| 253 return 0; |
| 254 } |
| 255 |
| 256 /* Binary search for 'most-recent rule before t'. */ |
| 257 while (n > 1) { |
| 258 m = a + n/2; |
| 259 x = zi_read32(trans + (m<<scale)); |
| 260 if (scale == 3) x = x<<32 | zi_read32(trans + (m<<scale) + 4); |
| 261 else x = (int32_t)x; |
| 262 if (local) off = (int32_t)zi_read32(types + 6 * index[m-1]); |
| 263 if (t - off < (int64_t)x) { |
| 264 n /= 2; |
| 265 } else { |
| 266 a = m; |
| 267 n -= n/2; |
| 268 } |
| 269 } |
| 270 |
| 271 /* First and last entry are special. First means to use lowest-index |
| 272 * non-DST type. Last means to apply POSIX-style rule if available. */ |
| 273 n = (index-trans)>>scale; |
| 274 if (a == n-1) return -1; |
| 275 if (a == 0) { |
| 276 x = zi_read32(trans + (a<<scale)); |
| 277 if (scale == 3) x = x<<32 | zi_read32(trans + (a<<scale) + 4); |
| 278 else x = (int32_t)x; |
| 279 if (local) off = (int32_t)zi_read32(types + 6 * index[a-1]); |
| 280 if (t - off < (int64_t)x) { |
| 281 for (a=0; a<(abbrevs-types)/6; a++) { |
| 282 if (types[6*a+4] != types[4]) break; |
| 283 } |
| 284 if (a == (abbrevs-types)/6) a = 0; |
| 285 if (types[6*a+4]) { |
| 286 *alt = a; |
| 287 return 0; |
| 288 } else { |
| 289 *alt = 0; |
| 290 return a; |
| 291 } |
| 292 } |
| 293 } |
| 294 |
| 295 /* Try to find a neighboring opposite-DST-status rule. */ |
| 296 if (alt) { |
| 297 if (a && types[6*index[a-1]+4] != types[6*index[a]+4]) |
| 298 *alt = index[a-1]; |
| 299 else if (a+1<n && types[6*index[a+1]+4] != types[6*index[a]+4]) |
| 300 *alt = index[a+1]; |
| 301 else |
| 302 *alt = index[a]; |
| 303 } |
| 304 |
| 305 return index[a]; |
| 306 } |
| 307 |
| 308 static int days_in_month(int m, int is_leap) |
| 309 { |
| 310 if (m==2) return 28+is_leap; |
| 311 else return 30+((0xad5>>(m-1))&1); |
| 312 } |
| 313 |
| 314 /* Convert a POSIX DST rule plus year to seconds since epoch. */ |
| 315 |
| 316 static long long rule_to_secs(const int *rule, int year) |
| 317 { |
| 318 int is_leap; |
| 319 long long t = __year_to_secs(year, &is_leap); |
| 320 int x, m, n, d; |
| 321 if (rule[0]!='M') { |
| 322 x = rule[1]; |
| 323 if (rule[0]=='J' && (x < 60 || !is_leap)) x--; |
| 324 t += 86400 * x; |
| 325 } else { |
| 326 m = rule[1]; |
| 327 n = rule[2]; |
| 328 d = rule[3]; |
| 329 t += __month_to_secs(m-1, is_leap); |
| 330 int wday = (int)((t + 4*86400) % (7*86400)) / 86400; |
| 331 int days = d - wday; |
| 332 if (days < 0) days += 7; |
| 333 if (n == 5 && days+28 >= days_in_month(m, is_leap)) n = 4; |
| 334 t += 86400 * (days + 7*(n-1)); |
| 335 } |
| 336 t += rule[4]; |
| 337 return t; |
| 338 } |
| 339 |
| 340 /* Determine the time zone in effect for a given time in seconds since the |
| 341 * epoch. It can be given in local or universal time. The results will |
| 342 * indicate whether DST is in effect at the queried time, and will give both |
| 343 * the GMT offset for the active zone/DST rule and the opposite DST. This |
| 344 * enables a caller to efficiently adjust for the case where an explicit |
| 345 * DST specification mismatches what would be in effect at the time. */ |
| 346 |
| 347 void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppo
ff, const char **zonename) |
| 348 { |
| 349 LOCK(lock); |
| 350 |
| 351 do_tzset(); |
| 352 |
| 353 if (zi) { |
| 354 size_t alt, i = scan_trans(t, local, &alt); |
| 355 if (i != -1) { |
| 356 *isdst = types[6*i+4]; |
| 357 *offset = (int32_t)zi_read32(types+6*i); |
| 358 *zonename = (const char *)abbrevs + types[6*i+5]; |
| 359 if (oppoff) *oppoff = (int32_t)zi_read32(types+6*alt); |
| 360 UNLOCK(lock); |
| 361 return; |
| 362 } |
| 363 } |
| 364 |
| 365 if (!__daylight) goto std; |
| 366 |
| 367 /* FIXME: may be broken if DST changes right at year boundary? |
| 368 * Also, this could be more efficient.*/ |
| 369 long long y = t / 31556952 + 70; |
| 370 while (__year_to_secs(y, 0) > t) y--; |
| 371 while (__year_to_secs(y+1, 0) < t) y++; |
| 372 |
| 373 long long t0 = rule_to_secs(r0, y); |
| 374 long long t1 = rule_to_secs(r1, y); |
| 375 |
| 376 if (t0 < t1) { |
| 377 if (!local) { |
| 378 t0 += __timezone; |
| 379 t1 += dst_off; |
| 380 } |
| 381 if (t >= t0 && t < t1) goto dst; |
| 382 goto std; |
| 383 } else { |
| 384 if (!local) { |
| 385 t1 += __timezone; |
| 386 t0 += dst_off; |
| 387 } |
| 388 if (t >= t1 && t < t0) goto std; |
| 389 goto dst; |
| 390 } |
| 391 std: |
| 392 *isdst = 0; |
| 393 *offset = -__timezone; |
| 394 if (oppoff) *oppoff = -dst_off; |
| 395 *zonename = __tzname[0]; |
| 396 UNLOCK(lock); |
| 397 return; |
| 398 dst: |
| 399 *isdst = 1; |
| 400 *offset = -dst_off; |
| 401 if (oppoff) *oppoff = -__timezone; |
| 402 *zonename = __tzname[1]; |
| 403 UNLOCK(lock); |
| 404 } |
| 405 |
| 406 void __tzset() |
| 407 { |
| 408 LOCK(lock); |
| 409 do_tzset(); |
| 410 UNLOCK(lock); |
| 411 } |
| 412 |
| 413 weak_alias(__tzset, tzset); |
| 414 |
| 415 const char *__tm_to_tzname(const struct tm *tm) |
| 416 { |
| 417 const void *p = tm->__tm_zone; |
| 418 LOCK(lock); |
| 419 do_tzset(); |
| 420 if (p != __gmt && p != __tzname[0] && p != __tzname[1] && |
| 421 (!zi || (uintptr_t)p-(uintptr_t)abbrevs >= abbrevs_end - abbrevs)) |
| 422 p = ""; |
| 423 UNLOCK(lock); |
| 424 return p; |
| 425 } |
OLD | NEW |