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 8long __timezone = 0; 9int __daylight = 0; 10char *__tzname[2] = { 0, 0 }; 11 12weak_alias(__timezone, timezone); 13weak_alias(__daylight, daylight); 14weak_alias(__tzname, tzname); 15 16static char std_name[TZNAME_MAX+1]; 17static char dst_name[TZNAME_MAX+1]; 18const char __gmt[] = "GMT"; 19 20static int dst_off; 21static int r0[5], r1[5]; 22 23static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end; 24static size_t map_size; 25 26static char old_tz_buf[32]; 27static char *old_tz = old_tz_buf; 28static size_t old_tz_size = sizeof old_tz_buf; 29 30static volatile int lock[2]; 31 32static 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 39static 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 60static 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 82static void getname(char *d, const char **p) 83{ 84 int i; 85 if (**p == '<') { 86 ++*p; 87 for (i=0; (*p)[i]!='>' && 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 100static 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 105static 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 116int __munmap(void *, size_t); 117 118static 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 243static 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 308static 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 316static 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 347void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppoff, 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 } 391std: 392 *isdst = 0; 393 *offset = -__timezone; 394 if (oppoff) *oppoff = -dst_off; 395 *zonename = __tzname[0]; 396 UNLOCK(lock); 397 return; 398dst: 399 *isdst = 1; 400 *offset = -dst_off; 401 if (oppoff) *oppoff = -__timezone; 402 *zonename = __tzname[1]; 403 UNLOCK(lock); 404} 405 406void __tzset() 407{ 408 LOCK(lock); 409 do_tzset(); 410 UNLOCK(lock); 411} 412 413weak_alias(__tzset, tzset); 414 415const 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} 426