]>
Commit | Line | Data |
---|---|---|
224c7076 A |
1 | /* |
2 | ** This file is in the public domain, so clarified as of | |
3 | ** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). | |
4 | */ | |
5 | ||
6 | #include <sys/cdefs.h> | |
7 | #ifndef lint | |
8 | #ifndef NOID | |
9 | static char elsieid[] __unused = "@(#)localtime.c 7.78"; | |
10 | #endif /* !defined NOID */ | |
11 | #endif /* !defined lint */ | |
1f2f436a | 12 | __FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11 davidxu Exp $"); |
224c7076 A |
13 | |
14 | /* | |
15 | ** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). | |
16 | ** POSIX-style TZ environment variable handling from Guy Harris | |
17 | ** (guy@auspex.com). | |
18 | */ | |
19 | ||
20 | /*LINTLIBRARY*/ | |
21 | ||
22 | #include "namespace.h" | |
23 | #include <sys/types.h> | |
24 | #include <sys/stat.h> | |
25 | #include <time.h> | |
26 | #include <fcntl.h> | |
27 | #include <pthread.h> | |
28 | #include <errno.h> | |
29 | #ifdef NOTIFY_TZ | |
30 | //#define NOTIFY_TZ_DEBUG | |
31 | //#define NOTIFY_TZ_DEBUG_FILE "/var/log/localtime.debug" | |
32 | //#define NOTIFY_TZ_LOG "/var/log/localtime.log" | |
33 | /* force ALL_STATE if NOTIFY_TZ is set */ | |
34 | #ifndef ALL_STATE | |
35 | #define ALL_STATE | |
36 | #endif /* ALL_STATE */ | |
37 | #include <mach/mach_init.h> | |
38 | #include <notify.h> | |
39 | #include <alloca.h> | |
40 | #endif /* NOTIFY_TZ */ | |
41 | #include "private.h" | |
42 | #include "un-namespace.h" | |
43 | ||
44 | #include "tzfile.h" | |
45 | ||
46 | #include "libc_private.h" | |
47 | ||
48 | #define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x) | |
49 | #define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) | |
224c7076 | 50 | |
1f2f436a A |
51 | #define _RWLOCK_RDLOCK(x) \ |
52 | do { \ | |
53 | if (__isthreaded) _pthread_rwlock_rdlock(x); \ | |
54 | } while (0) | |
55 | ||
56 | #define _RWLOCK_WRLOCK(x) \ | |
57 | do { \ | |
58 | if (__isthreaded) _pthread_rwlock_wrlock(x); \ | |
59 | } while (0) | |
60 | ||
61 | #define _RWLOCK_UNLOCK(x) \ | |
62 | do { \ | |
63 | if (__isthreaded) _pthread_rwlock_unlock(x); \ | |
64 | } while (0) | |
65 | ||
224c7076 A |
66 | /* |
67 | ** SunOS 4.1.1 headers lack O_BINARY. | |
68 | */ | |
69 | ||
70 | #ifdef O_BINARY | |
71 | #define OPEN_MODE (O_RDONLY | O_BINARY) | |
72 | #endif /* defined O_BINARY */ | |
73 | #ifndef O_BINARY | |
74 | #define OPEN_MODE O_RDONLY | |
75 | #endif /* !defined O_BINARY */ | |
76 | ||
77 | #ifndef WILDABBR | |
78 | /* | |
79 | ** Someone might make incorrect use of a time zone abbreviation: | |
80 | ** 1. They might reference tzname[0] before calling tzset (explicitly | |
81 | ** or implicitly). | |
82 | ** 2. They might reference tzname[1] before calling tzset (explicitly | |
83 | ** or implicitly). | |
84 | ** 3. They might reference tzname[1] after setting to a time zone | |
85 | ** in which Daylight Saving Time is never observed. | |
86 | ** 4. They might reference tzname[0] after setting to a time zone | |
87 | ** in which Standard Time is never observed. | |
88 | ** 5. They might reference tm.TM_ZONE after calling offtime. | |
89 | ** What's best to do in the above cases is open to debate; | |
90 | ** for now, we just set things up so that in any of the five cases | |
91 | ** WILDABBR is used. Another possibility: initialize tzname[0] to the | |
92 | ** string "tzname[0] used before set", and similarly for the other cases. | |
93 | ** And another: initialize tzname[0] to "ERA", with an explanation in the | |
94 | ** manual page of what this "time zone abbreviation" means (doing this so | |
95 | ** that tzname[0] has the "normal" length of three characters). | |
96 | */ | |
97 | #define WILDABBR " " | |
98 | #endif /* !defined WILDABBR */ | |
99 | ||
100 | static char wildabbr[] = "WILDABBR"; | |
101 | ||
102 | /* | |
103 | * In June 2004 it was decided UTC was a more appropriate default time | |
104 | * zone than GMT. | |
105 | */ | |
106 | ||
107 | static const char gmt[] = "UTC"; | |
108 | ||
109 | /* | |
110 | ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. | |
111 | ** We default to US rules as of 1999-08-17. | |
112 | ** POSIX 1003.1 section 8.1.1 says that the default DST rules are | |
113 | ** implementation dependent; for historical reasons, US rules are a | |
114 | ** common default. | |
115 | */ | |
116 | #ifndef TZDEFRULESTRING | |
117 | #define TZDEFRULESTRING ",M4.1.0,M10.5.0" | |
118 | #endif /* !defined TZDEFDST */ | |
119 | ||
120 | struct ttinfo { /* time type information */ | |
121 | long tt_gmtoff; /* UTC offset in seconds */ | |
122 | int tt_isdst; /* used to set tm_isdst */ | |
123 | int tt_abbrind; /* abbreviation list index */ | |
124 | int tt_ttisstd; /* TRUE if transition is std time */ | |
125 | int tt_ttisgmt; /* TRUE if transition is UTC */ | |
126 | }; | |
127 | ||
128 | struct lsinfo { /* leap second information */ | |
129 | time_t ls_trans; /* transition time */ | |
130 | long ls_corr; /* correction to apply */ | |
131 | }; | |
132 | ||
133 | #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) | |
134 | ||
135 | #ifdef TZNAME_MAX | |
136 | #define MY_TZNAME_MAX TZNAME_MAX | |
137 | #endif /* defined TZNAME_MAX */ | |
138 | #ifndef TZNAME_MAX | |
139 | #define MY_TZNAME_MAX 255 | |
140 | #endif /* !defined TZNAME_MAX */ | |
141 | ||
142 | struct state { | |
143 | int leapcnt; | |
144 | int timecnt; | |
145 | int typecnt; | |
146 | int charcnt; | |
147 | time_t ats[TZ_MAX_TIMES]; | |
148 | unsigned char types[TZ_MAX_TIMES]; | |
149 | struct ttinfo ttis[TZ_MAX_TYPES]; | |
150 | char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), | |
151 | (2 * (MY_TZNAME_MAX + 1)))]; | |
152 | struct lsinfo lsis[TZ_MAX_LEAPS]; | |
153 | }; | |
154 | ||
155 | struct rule { | |
156 | int r_type; /* type of rule--see below */ | |
157 | int r_day; /* day number of rule */ | |
158 | int r_week; /* week number of rule */ | |
159 | int r_mon; /* month number of rule */ | |
160 | long r_time; /* transition time of rule */ | |
161 | }; | |
162 | ||
163 | #define JULIAN_DAY 0 /* Jn - Julian day */ | |
164 | #define DAY_OF_YEAR 1 /* n - day of year */ | |
165 | #define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ | |
166 | ||
167 | #ifdef NOTIFY_TZ | |
168 | typedef struct { | |
169 | int token; | |
170 | int notify_was_off; | |
171 | int is_set; | |
172 | } notify_tz_t; | |
173 | ||
174 | #define NOTIFY_TZ_NAME "com.apple.system.timezone" | |
175 | #endif /* NOTIFY_TZ */ | |
176 | ||
177 | /* | |
178 | ** Prototypes for static functions. | |
179 | */ | |
180 | #define localsub _st_localsub | |
181 | #define time1 _st_time1 | |
182 | #define tzset_basic _st_tzset_basic | |
183 | __private_extern__ | |
184 | #ifdef __LP64__ | |
185 | struct tm * localsub(const time_t * timep, long offset, | |
186 | struct tm * tmp); | |
187 | #else /* !__LP64__ */ | |
188 | void localsub(const time_t * timep, long offset, | |
189 | struct tm * tmp); | |
190 | #endif /* __LP64__ */ | |
191 | __private_extern__ | |
192 | time_t time1(struct tm * tmp, | |
193 | #ifdef __LP64__ | |
194 | struct tm *(*funcp) (const time_t *, | |
195 | long, struct tm *), | |
196 | #else /* !__LP64__ */ | |
197 | void(*funcp) (const time_t *, | |
198 | long, struct tm *), | |
199 | #endif /* __LP64__ */ | |
200 | long offset, | |
201 | int unix03); | |
202 | __private_extern__ | |
1f2f436a | 203 | void tzset_basic(int); |
224c7076 A |
204 | |
205 | #if !BUILDING_VARIANT | |
206 | static long detzcode(const char * codep); | |
207 | static const char * getzname(const char * strp, char **name, size_t *len); | |
208 | static const char * getnum(const char * strp, int * nump, int min, | |
209 | int max); | |
210 | static const char * getsecs(const char * strp, long * secsp); | |
211 | static const char * getoffset(const char * strp, long * offsetp); | |
212 | static const char * getrule(const char * strp, struct rule * rulep); | |
213 | static void gmtload(struct state * sp); | |
214 | #ifdef __LP64__ | |
215 | static struct tm * gmtsub(const time_t * timep, long offset, | |
216 | struct tm * tmp); | |
217 | #else /* !__LP64__ */ | |
218 | static void gmtsub(const time_t * timep, long offset, | |
219 | struct tm * tmp); | |
220 | #endif /* __LP64__ */ | |
221 | static int increment_overflow(int * number, int delta); | |
222 | static int normalize_overflow(int * tensptr, int * unitsptr, | |
223 | int base); | |
224 | #ifdef NOTIFY_TZ | |
225 | static void notify_check_tz(notify_tz_t *p); | |
226 | static void notify_register_tz(char *file, notify_tz_t *p); | |
227 | #endif /* NOTIFY_TZ */ | |
228 | static void settzname(void); | |
229 | static time_t time2(struct tm *tmp, | |
230 | #ifdef __LP64__ | |
231 | struct tm *(*funcp) (const time_t *, | |
232 | long, struct tm*), | |
233 | #else /* !__LP64__ */ | |
234 | void(*funcp) (const time_t *, | |
235 | long, struct tm*), | |
236 | #endif /* __LP64__ */ | |
237 | long offset, int * okayp, int unix03); | |
238 | static time_t time2sub(struct tm *tmp, | |
239 | #ifdef __LP64__ | |
240 | struct tm *(*funcp) (const time_t *, | |
241 | long, struct tm*), | |
242 | #else /* !__LP64__ */ | |
243 | void(*funcp) (const time_t *, | |
244 | long, struct tm*), | |
245 | #endif /* __LP64__ */ | |
246 | long offset, int * okayp, int do_norm_secs, | |
247 | int unix03); | |
248 | #ifdef __LP64__ | |
249 | static struct tm * timesub(const time_t * timep, long offset, | |
250 | const struct state * sp, struct tm * tmp); | |
251 | #else /* !__LP64__ */ | |
252 | static void timesub(const time_t * timep, long offset, | |
253 | const struct state * sp, struct tm * tmp); | |
254 | #endif /* __LP64__ */ | |
255 | static int tmcomp(const struct tm * atmp, | |
256 | const struct tm * btmp); | |
257 | static time_t transtime(time_t janfirst, int year, | |
258 | const struct rule * rulep, long offset); | |
259 | static int tzload(const char * name, struct state * sp); | |
260 | static int tzparse(const char * name, struct state * sp, | |
261 | int lastditch); | |
262 | ||
263 | #ifdef ALL_STATE | |
264 | static struct state * lclptr; | |
265 | static struct state * gmtptr; | |
266 | #endif /* defined ALL_STATE */ | |
267 | ||
268 | #ifndef ALL_STATE | |
269 | static struct state lclmem; | |
270 | static struct state gmtmem; | |
271 | #define lclptr (&lclmem) | |
272 | #define gmtptr (&gmtmem) | |
273 | #endif /* State Farm */ | |
274 | ||
275 | #ifndef TZ_STRLEN_MAX | |
276 | #define TZ_STRLEN_MAX 255 | |
277 | #endif /* !defined TZ_STRLEN_MAX */ | |
278 | ||
279 | static char lcl_TZname[TZ_STRLEN_MAX + 1]; | |
280 | #ifdef NOTIFY_TZ | |
1f2f436a A |
281 | #define lcl_is_set (lcl_notify.is_set) |
282 | #define gmt_is_set (gmt_notify.is_set) | |
224c7076 A |
283 | #else /* ! NOTIFY_TZ */ |
284 | static int lcl_is_set; | |
285 | static int gmt_is_set; | |
286 | #endif /* NOTIFY_TZ */ | |
1f2f436a A |
287 | __private_extern__ pthread_rwlock_t lcl_rwlock = PTHREAD_RWLOCK_INITIALIZER; |
288 | static pthread_mutex_t gmt_mutex = PTHREAD_MUTEX_INITIALIZER; | |
224c7076 A |
289 | |
290 | char * tzname[2] = { | |
291 | wildabbr, | |
292 | wildabbr | |
293 | }; | |
294 | ||
295 | /* | |
296 | ** Section 4.12.3 of X3.159-1989 requires that | |
297 | ** Except for the strftime function, these functions [asctime, | |
298 | ** ctime, gmtime, localtime] return values in one of two static | |
299 | ** objects: a broken-down time structure and an array of char. | |
300 | ** Thanks to Paul Eggert (eggert@twinsun.com) for noting this. | |
301 | */ | |
302 | ||
303 | static struct tm tm; | |
304 | ||
305 | #define USG_COMPAT | |
306 | #define ALTZONE | |
307 | #ifdef USG_COMPAT | |
308 | int daylight = 0; | |
309 | __private_extern__ void _st_set_timezone(long); | |
310 | #endif /* defined USG_COMPAT */ | |
311 | ||
312 | #ifdef ALTZONE | |
313 | __private_extern__ long __darwin_altzone = 0; | |
314 | #define altzone __darwin_altzone | |
315 | #endif /* defined ALTZONE */ | |
316 | ||
317 | #ifdef NOTIFY_TZ | |
318 | #ifdef NOTIFY_TZ_DEBUG | |
319 | #ifdef NOTIFY_TZ_DEBUG_FILE | |
320 | #define NOTIFY_TZ_PRINTF(fmt, args...) \ | |
321 | { \ | |
322 | FILE *_notify_tz_fp_; \ | |
323 | if((_notify_tz_fp_ = fopen(NOTIFY_TZ_DEBUG_FILE, "a")) != NULL) { \ | |
324 | fprintf(_notify_tz_fp_, "%d: " fmt, getpid(), ## args); \ | |
325 | fclose(_notify_tz_fp_); \ | |
326 | } \ | |
327 | } | |
328 | #else /* ! NOTIFY_TZ_DEBUG_FILE */ | |
329 | #define NOTIFY_TZ_PRINTF(args...) fprintf(stdout, ## args) | |
330 | #endif /* NOTIFY_TZ_DEBUG_FILE */ | |
331 | #endif /* NOTIFY_TZ_DEBUG */ | |
332 | #ifdef NOTIFY_TZ_LOG | |
333 | #define NOTIFY_LOG(fmt, args...) \ | |
334 | { \ | |
335 | FILE *_notify_log_fp_; \ | |
336 | if((_notify_log_fp_ = fopen(NOTIFY_TZ_LOG, "a")) != NULL) { \ | |
337 | fprintf(_notify_log_fp_, "%d: " fmt, getpid(), ## args); \ | |
338 | fclose(_notify_log_fp_); \ | |
339 | } \ | |
340 | } | |
341 | #endif /* NOTIFY_TZ_LOG */ | |
342 | /*-------------------------------------------------------------------- | |
343 | * __notify_78945668_info__ is a global variable (defined in Libnotify) | |
344 | * that can be used to disable the notify mechanism. Set to a negative | |
345 | * value to disable. It can then be set back to zero to re-enable. | |
346 | *-------------------------------------------------------------------- */ | |
347 | extern int __notify_78945668_info__; | |
348 | ||
349 | /*-------------------------------------------------------------------- | |
350 | * fullname is used to pass the actual path of the timezone file to the | |
351 | * notify routines. If it is a nil string, that means no timezone file | |
352 | * is being used. | |
353 | *-------------------------------------------------------------------- */ | |
354 | static char * fullname = NULL; | |
355 | ||
356 | static notify_tz_t gmt_notify = {-1, 0, 0}; | |
357 | static notify_tz_t lcl_notify = {-1, 0, 0}; | |
358 | static char notify_tz_name[] = NOTIFY_TZ_NAME; | |
359 | #endif /* NOTIFY_TZ */ | |
360 | ||
361 | static long | |
362 | detzcode(codep) | |
363 | const char * const codep; | |
364 | { | |
365 | long result; | |
366 | int i; | |
367 | ||
368 | result = (codep[0] & 0x80) ? ~0L : 0L; | |
369 | for (i = 0; i < 4; ++i) | |
370 | result = (result << 8) | (codep[i] & 0xff); | |
371 | return result; | |
372 | } | |
373 | ||
374 | static void | |
375 | settzname(void) | |
376 | { | |
377 | struct state * sp = lclptr; | |
1f2f436a A |
378 | int i, need; |
379 | unsigned char * types; | |
380 | #define NEED_STD 1 | |
381 | #define NEED_DST 2 | |
382 | #define NEED_DAYLIGHT 4 | |
383 | #define NEED_ALL (NEED_STD | NEED_DST | NEED_DAYLIGHT) | |
224c7076 A |
384 | |
385 | tzname[0] = wildabbr; | |
386 | tzname[1] = wildabbr; | |
387 | #ifdef USG_COMPAT | |
388 | daylight = 0; | |
389 | _st_set_timezone(0); | |
390 | #endif /* defined USG_COMPAT */ | |
391 | #ifdef ALTZONE | |
392 | altzone = 0; | |
393 | #endif /* defined ALTZONE */ | |
394 | #ifdef ALL_STATE | |
395 | if (sp == NULL) { | |
396 | tzname[0] = tzname[1] = (char *)gmt; | |
397 | return; | |
398 | } | |
399 | #endif /* defined ALL_STATE */ | |
1f2f436a A |
400 | /* |
401 | * PR-3765457: The original settzname went sequentially through the ttis | |
402 | * array, rather than correctly indexing via the types array, to get | |
403 | * the real order of the timezone changes. In addition, as a speed up, | |
404 | * we start at the end of the changes, and work back, so that most of | |
405 | * the time, we don't have to look through the entire array. | |
406 | */ | |
407 | if (sp->timecnt == 0 && sp->typecnt == 1) { | |
408 | /* | |
409 | * Unfortunately, there is an edge case when typecnt == 1 and | |
410 | * timecnt == 0, which would cause the loop to never run. So | |
411 | * in that case, we fudge things up so that it is as if | |
412 | * timecnt == 1. | |
413 | */ | |
414 | i = 0; | |
415 | types = (unsigned char *)""; /* we use the null as index */ | |
416 | } else { | |
417 | /* the usual case */ | |
418 | i = sp->timecnt - 1; | |
419 | types = sp->types; | |
420 | } | |
421 | need = NEED_ALL; | |
422 | for (; i >= 0 && need; --i) { | |
423 | const struct ttinfo * const ttisp = &sp->ttis[types[i]]; | |
224c7076 | 424 | |
224c7076 | 425 | #ifdef USG_COMPAT |
1f2f436a A |
426 | if ((need & NEED_DAYLIGHT) && ttisp->tt_isdst) { |
427 | need &= ~NEED_DAYLIGHT; | |
224c7076 | 428 | daylight = 1; |
1f2f436a A |
429 | } |
430 | #endif /* defined USG_COMPAT */ | |
431 | if (ttisp->tt_isdst) { | |
432 | if (need & NEED_DST) { | |
433 | need &= ~NEED_DST; | |
434 | tzname[1] = &sp->chars[ttisp->tt_abbrind]; | |
435 | #ifdef ALTZONE | |
436 | altzone = -(ttisp->tt_gmtoff); | |
437 | #endif /* defined ALTZONE */ | |
438 | } | |
439 | } else if (need & NEED_STD) { | |
440 | need &= ~NEED_STD; | |
441 | tzname[0] = &sp->chars[ttisp->tt_abbrind]; | |
442 | #ifdef USG_COMPAT | |
224c7076 A |
443 | _st_set_timezone(-(ttisp->tt_gmtoff)); |
444 | #endif /* defined USG_COMPAT */ | |
1f2f436a A |
445 | } |
446 | #if defined(ALTZONE) || defined(USG_COMPAT) | |
447 | if (i == 0) { | |
448 | #endif /* defined(ALTZONE) || defined(USG_COMPAT) */ | |
224c7076 | 449 | #ifdef ALTZONE |
1f2f436a A |
450 | if (need & NEED_DST) |
451 | altzone = -(ttisp->tt_gmtoff); | |
224c7076 | 452 | #endif /* defined ALTZONE */ |
1f2f436a A |
453 | #ifdef USG_COMPAT |
454 | if (need & NEED_STD) | |
455 | _st_set_timezone(-(ttisp->tt_gmtoff)); | |
456 | #endif /* defined USG_COMPAT */ | |
457 | #if defined(ALTZONE) || defined(USG_COMPAT) | |
458 | } | |
459 | #endif /* defined(ALTZONE) || defined(USG_COMPAT) */ | |
224c7076 A |
460 | } |
461 | } | |
462 | ||
463 | #ifdef NOTIFY_TZ | |
464 | static void | |
465 | notify_check_tz(notify_tz_t *p) | |
466 | { | |
467 | unsigned int nstat; | |
468 | int ncheck; | |
469 | ||
470 | if (__notify_78945668_info__ < 0) { | |
471 | #ifdef NOTIFY_TZ_DEBUG | |
472 | if(!p->notify_was_off) NOTIFY_TZ_PRINTF("notify_check_tz: setting %s_notify->notify_was_off\n", (p == &lcl_notify ? "lcl" : "gmt")); | |
473 | #endif /* NOTIFY_TZ_DEBUG */ | |
474 | p->notify_was_off = 1; | |
475 | return; | |
476 | } | |
477 | /* force rereading the timezone file if notify was off */ | |
478 | if (p->notify_was_off) { | |
479 | #ifdef NOTIFY_TZ_DEBUG | |
480 | NOTIFY_TZ_PRINTF("notify_check_tz: saw %s_notify->notify_was_off\n", (p == &lcl_notify ? "lcl" : "gmt")); | |
481 | #endif /* NOTIFY_TZ_DEBUG */ | |
482 | p->is_set = 0; | |
483 | p->notify_was_off = 0; | |
484 | return; | |
485 | } | |
486 | if (p->token < 0) | |
487 | return; | |
488 | nstat = notify_check(p->token, &ncheck); | |
489 | if (nstat || ncheck) { | |
490 | p->is_set = 0; | |
491 | #ifdef NOTIFY_TZ_DEBUG | |
492 | NOTIFY_TZ_PRINTF("notify_check_tz: %s changed\n", (p == &lcl_notify) ? "lcl" : "gmt"); | |
493 | #endif /* NOTIFY_TZ_DEBUG */ | |
494 | } | |
495 | #ifdef NOTIFY_TZ_DEBUG | |
496 | NOTIFY_TZ_PRINTF("notify_check_tz: %s unchanged\n", (p == &lcl_notify) ? "lcl" : "gmt"); | |
497 | #endif /* NOTIFY_TZ_DEBUG */ | |
498 | } | |
499 | ||
500 | extern uint32_t notify_monitor_file(int token, char *path, int flags); | |
501 | ||
502 | static void | |
503 | notify_register_tz(char *file, notify_tz_t *p) | |
504 | { | |
505 | char *name; | |
506 | unsigned int nstat; | |
507 | int ncheck; | |
508 | ||
509 | if (__notify_78945668_info__ < 0) | |
510 | return; | |
511 | /*---------------------------------------------------------------- | |
512 | * Since we don't record the last time zone filename, just cancel | |
513 | * (which should remove the file monitor) and setup from scratch | |
514 | *----------------------------------------------------------------*/ | |
515 | if (p->token >= 0) | |
516 | notify_cancel(p->token); | |
517 | if (!file || *file == 0) { | |
518 | /* no time zone file to monitor */ | |
519 | p->token = -1; | |
520 | return; | |
521 | } | |
522 | /*---------------------------------------------------------------- | |
523 | * Just use com.apple.system.timezone if the path is /etc/localtime. | |
524 | * Otherwise use com.apple.system.timezone.<fullpath> | |
525 | *----------------------------------------------------------------*/ | |
526 | if (TZDEFAULT && strcmp(file, TZDEFAULT) == 0) | |
527 | name = notify_tz_name; | |
528 | else { | |
529 | name = alloca(sizeof(notify_tz_name) + strlen(file) + 1); | |
530 | if (name == NULL) { | |
531 | p->token = -1; | |
532 | return; | |
533 | } | |
534 | strcpy(name, notify_tz_name); | |
535 | strcat(name, "."); | |
536 | strcat(name, file); | |
537 | } | |
538 | #ifdef NOTIFY_TZ_DEBUG | |
539 | NOTIFY_TZ_PRINTF("notify_register_tz: file=%s name=%s\n", file, name); | |
540 | #endif /* NOTIFY_TZ_DEBUG */ | |
541 | nstat = notify_register_check(name, &p->token); | |
542 | if (nstat != 0) { | |
543 | p->token = -1; | |
544 | p->is_set = 0; | |
545 | #ifdef NOTIFY_TZ_DEBUG | |
546 | NOTIFY_TZ_PRINTF("***notify_register_tz: notify_register_check failed: %u\n", nstat); | |
547 | #endif /* NOTIFY_TZ_DEBUG */ | |
548 | #ifdef NOTIFY_TZ_LOG | |
549 | NOTIFY_LOG("notify_register_check(%s) failed: %u\n", name, nstat); | |
550 | #endif /* NOTIFY_TZ_LOG */ | |
551 | return; | |
552 | } | |
553 | /* don't need to request monitoring /etc/localtime */ | |
554 | if (name != notify_tz_name) { | |
555 | #ifdef NOTIFY_TZ_DEBUG | |
556 | NOTIFY_TZ_PRINTF("notify_register_tz: monitor %s\n", file); | |
557 | #endif /* NOTIFY_TZ_DEBUG */ | |
558 | nstat = notify_monitor_file(p->token, file, 0); | |
559 | if (nstat != 0) { | |
560 | notify_cancel(p->token); | |
561 | p->token = -1; | |
562 | p->is_set = 0; | |
563 | #ifdef NOTIFY_TZ_DEBUG | |
564 | NOTIFY_TZ_PRINTF("***notify_register_tz: notify_monitor_file failed: %u\n", nstat); | |
565 | #endif /* NOTIFY_TZ_DEBUG */ | |
566 | #ifdef NOTIFY_TZ_LOG | |
567 | NOTIFY_LOG("notify_monitor_file(%s) failed: %u\n", file, nstat); | |
568 | #endif /* NOTIFY_TZ_LOG */ | |
569 | return; | |
570 | } | |
571 | } | |
572 | notify_check(p->token, &ncheck); /* this always returns true */ | |
573 | } | |
574 | #endif /* NOTIFY_TZ */ | |
575 | ||
576 | static int | |
577 | tzload(name, sp) | |
578 | const char * name; | |
579 | struct state * const sp; | |
580 | { | |
581 | const char * p; | |
582 | int i; | |
583 | int fid; | |
584 | ||
585 | #ifdef NOTIFY_TZ_DEBUG | |
586 | NOTIFY_TZ_PRINTF("tzload: name=%s\n", name); | |
587 | #endif /* NOTIFY_TZ_DEBUG */ | |
588 | /* XXX The following is from OpenBSD, and I'm not sure it is correct */ | |
589 | if (name != NULL && issetugid() != 0) | |
590 | if ((name[0] == ':' && name[1] == '/') || | |
591 | name[0] == '/' || strchr(name, '.')) | |
592 | name = NULL; | |
593 | if (name == NULL && (name = TZDEFAULT) == NULL) | |
594 | return -1; | |
595 | { | |
596 | int doaccess; | |
597 | struct stat stab; | |
598 | /* | |
599 | ** Section 4.9.1 of the C standard says that | |
600 | ** "FILENAME_MAX expands to an integral constant expression | |
601 | ** that is the size needed for an array of char large enough | |
602 | ** to hold the longest file name string that the implementation | |
603 | ** guarantees can be opened." | |
604 | */ | |
605 | #ifdef NOTIFY_TZ | |
606 | if (!fullname) { | |
607 | fullname = malloc(FILENAME_MAX + 1); | |
608 | if (!fullname) | |
609 | return -1; | |
610 | } | |
611 | #else /* ! NOTIFY_TZ */ | |
612 | char fullname[FILENAME_MAX + 1]; | |
613 | #endif /* NOTIFY_TZ */ | |
614 | ||
615 | if (name[0] == ':') | |
616 | ++name; | |
617 | doaccess = name[0] == '/'; | |
618 | if (!doaccess) { | |
619 | if ((p = TZDIR) == NULL) | |
620 | return -1; | |
621 | #ifdef NOTIFY_TZ | |
622 | if ((strlen(p) + 1 + strlen(name) + 1) >= (FILENAME_MAX + 1)) | |
623 | #else /* ! NOTIFY_TZ */ | |
624 | if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname) | |
625 | #endif /* NOTIFY_TZ */ | |
626 | return -1; | |
627 | (void) strcpy(fullname, p); | |
628 | (void) strcat(fullname, "/"); | |
629 | (void) strcat(fullname, name); | |
630 | /* | |
631 | ** Set doaccess if '.' (as in "../") shows up in name. | |
632 | */ | |
633 | if (strchr(name, '.') != NULL) | |
634 | doaccess = TRUE; | |
635 | name = fullname; | |
636 | } | |
637 | #ifdef NOTIFY_TZ | |
638 | else | |
639 | strcpy(fullname, name); | |
640 | #endif /* NOTIFY_TZ */ | |
641 | if (doaccess && access(name, R_OK) != 0) | |
642 | return -1; | |
643 | if ((fid = _open(name, OPEN_MODE)) == -1) | |
644 | return -1; | |
645 | if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) { | |
646 | _close(fid); | |
647 | return -1; | |
648 | } | |
649 | } | |
650 | { | |
651 | struct tzhead * tzhp; | |
652 | union { | |
653 | struct tzhead tzhead; | |
654 | char buf[sizeof *sp + sizeof *tzhp]; | |
655 | } u; | |
656 | int ttisstdcnt; | |
657 | int ttisgmtcnt; | |
658 | ||
659 | #ifdef NOTIFY_TZ_DEBUG | |
660 | NOTIFY_TZ_PRINTF("tzload: reading %s\n", name); | |
661 | #endif /* NOTIFY_TZ_DEBUG */ | |
662 | i = _read(fid, u.buf, sizeof u.buf); | |
663 | if (_close(fid) != 0) | |
664 | return -1; | |
665 | ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); | |
666 | ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); | |
667 | sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); | |
668 | sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); | |
669 | sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); | |
670 | sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); | |
671 | p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; | |
672 | if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || | |
673 | sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || | |
674 | sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || | |
675 | sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || | |
676 | (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || | |
677 | (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) | |
678 | return -1; | |
679 | if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ | |
680 | sp->timecnt + /* types */ | |
681 | sp->typecnt * (4 + 2) + /* ttinfos */ | |
682 | sp->charcnt + /* chars */ | |
683 | sp->leapcnt * (4 + 4) + /* lsinfos */ | |
684 | ttisstdcnt + /* ttisstds */ | |
685 | ttisgmtcnt) /* ttisgmts */ | |
686 | return -1; | |
687 | for (i = 0; i < sp->timecnt; ++i) { | |
688 | sp->ats[i] = detzcode(p); | |
689 | p += 4; | |
690 | } | |
691 | for (i = 0; i < sp->timecnt; ++i) { | |
692 | sp->types[i] = (unsigned char) *p++; | |
693 | if (sp->types[i] >= sp->typecnt) | |
694 | return -1; | |
695 | } | |
696 | for (i = 0; i < sp->typecnt; ++i) { | |
697 | struct ttinfo * ttisp; | |
698 | ||
699 | ttisp = &sp->ttis[i]; | |
700 | ttisp->tt_gmtoff = detzcode(p); | |
701 | p += 4; | |
702 | ttisp->tt_isdst = (unsigned char) *p++; | |
703 | if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) | |
704 | return -1; | |
705 | ttisp->tt_abbrind = (unsigned char) *p++; | |
706 | if (ttisp->tt_abbrind < 0 || | |
707 | ttisp->tt_abbrind > sp->charcnt) | |
708 | return -1; | |
709 | } | |
710 | for (i = 0; i < sp->charcnt; ++i) | |
711 | sp->chars[i] = *p++; | |
712 | sp->chars[i] = '\0'; /* ensure '\0' at end */ | |
713 | for (i = 0; i < sp->leapcnt; ++i) { | |
714 | struct lsinfo * lsisp; | |
715 | ||
716 | lsisp = &sp->lsis[i]; | |
717 | lsisp->ls_trans = detzcode(p); | |
718 | p += 4; | |
719 | lsisp->ls_corr = detzcode(p); | |
720 | p += 4; | |
721 | } | |
722 | for (i = 0; i < sp->typecnt; ++i) { | |
723 | struct ttinfo * ttisp; | |
724 | ||
725 | ttisp = &sp->ttis[i]; | |
726 | if (ttisstdcnt == 0) | |
727 | ttisp->tt_ttisstd = FALSE; | |
728 | else { | |
729 | ttisp->tt_ttisstd = *p++; | |
730 | if (ttisp->tt_ttisstd != TRUE && | |
731 | ttisp->tt_ttisstd != FALSE) | |
732 | return -1; | |
733 | } | |
734 | } | |
735 | for (i = 0; i < sp->typecnt; ++i) { | |
736 | struct ttinfo * ttisp; | |
737 | ||
738 | ttisp = &sp->ttis[i]; | |
739 | if (ttisgmtcnt == 0) | |
740 | ttisp->tt_ttisgmt = FALSE; | |
741 | else { | |
742 | ttisp->tt_ttisgmt = *p++; | |
743 | if (ttisp->tt_ttisgmt != TRUE && | |
744 | ttisp->tt_ttisgmt != FALSE) | |
745 | return -1; | |
746 | } | |
747 | } | |
748 | } | |
749 | return 0; | |
750 | } | |
751 | ||
752 | static const int mon_lengths[2][MONSPERYEAR] = { | |
753 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, | |
754 | { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } | |
755 | }; | |
756 | ||
757 | static const int year_lengths[2] = { | |
758 | DAYSPERNYEAR, DAYSPERLYEAR | |
759 | }; | |
760 | ||
761 | /* | |
762 | ** Given a pointer into a time zone string, scan until a character that is not | |
763 | ** a valid character in a zone name is found. Return a pointer to that | |
764 | ** character. | |
765 | */ | |
766 | ||
767 | static const char * | |
768 | getzname(strp, name, len) | |
769 | const char * strp; | |
770 | char ** name; | |
771 | size_t * len; | |
772 | { | |
773 | char c; | |
774 | char * ket; | |
775 | ||
776 | if (*strp == '<' && (ket = strchr(strp, '>')) != NULL) { | |
777 | *name = (char *)(strp + 1); | |
778 | *len = ket - strp - 1; | |
779 | return ket + 1; | |
780 | } | |
781 | *name = (char *)strp; | |
782 | while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && | |
783 | c != '+') | |
784 | ++strp; | |
785 | *len = strp - *name; | |
786 | return strp; | |
787 | } | |
788 | ||
789 | /* | |
790 | ** Given a pointer into a time zone string, extract a number from that string. | |
791 | ** Check that the number is within a specified range; if it is not, return | |
792 | ** NULL. | |
793 | ** Otherwise, return a pointer to the first character not part of the number. | |
794 | */ | |
795 | ||
796 | static const char * | |
797 | getnum(strp, nump, min, max) | |
798 | const char * strp; | |
799 | int * const nump; | |
800 | const int min; | |
801 | const int max; | |
802 | { | |
803 | char c; | |
804 | int num; | |
805 | ||
806 | if (strp == NULL || !is_digit(c = *strp)) | |
807 | return NULL; | |
808 | num = 0; | |
809 | do { | |
810 | num = num * 10 + (c - '0'); | |
811 | if (num > max) | |
812 | return NULL; /* illegal value */ | |
813 | c = *++strp; | |
814 | } while (is_digit(c)); | |
815 | if (num < min) | |
816 | return NULL; /* illegal value */ | |
817 | *nump = num; | |
818 | return strp; | |
819 | } | |
820 | ||
821 | /* | |
822 | ** Given a pointer into a time zone string, extract a number of seconds, | |
823 | ** in hh[:mm[:ss]] form, from the string. | |
824 | ** If any error occurs, return NULL. | |
825 | ** Otherwise, return a pointer to the first character not part of the number | |
826 | ** of seconds. | |
827 | */ | |
828 | ||
829 | static const char * | |
830 | getsecs(strp, secsp) | |
831 | const char * strp; | |
832 | long * const secsp; | |
833 | { | |
834 | int num; | |
835 | ||
836 | /* | |
837 | ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like | |
838 | ** "M10.4.6/26", which does not conform to Posix, | |
839 | ** but which specifies the equivalent of | |
840 | ** ``02:00 on the first Sunday on or after 23 Oct''. | |
841 | */ | |
842 | strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); | |
843 | if (strp == NULL) | |
844 | return NULL; | |
845 | *secsp = num * (long) SECSPERHOUR; | |
846 | if (*strp == ':') { | |
847 | ++strp; | |
848 | strp = getnum(strp, &num, 0, MINSPERHOUR - 1); | |
849 | if (strp == NULL) | |
850 | return NULL; | |
851 | *secsp += num * SECSPERMIN; | |
852 | if (*strp == ':') { | |
853 | ++strp; | |
854 | /* `SECSPERMIN' allows for leap seconds. */ | |
855 | strp = getnum(strp, &num, 0, SECSPERMIN); | |
856 | if (strp == NULL) | |
857 | return NULL; | |
858 | *secsp += num; | |
859 | } | |
860 | } | |
861 | return strp; | |
862 | } | |
863 | ||
864 | /* | |
865 | ** Given a pointer into a time zone string, extract an offset, in | |
866 | ** [+-]hh[:mm[:ss]] form, from the string. | |
867 | ** If any error occurs, return NULL. | |
868 | ** Otherwise, return a pointer to the first character not part of the time. | |
869 | */ | |
870 | ||
871 | static const char * | |
872 | getoffset(strp, offsetp) | |
873 | const char * strp; | |
874 | long * const offsetp; | |
875 | { | |
876 | int neg = 0; | |
877 | ||
878 | if (*strp == '-') { | |
879 | neg = 1; | |
880 | ++strp; | |
881 | } else if (*strp == '+') | |
882 | ++strp; | |
883 | strp = getsecs(strp, offsetp); | |
884 | if (strp == NULL) | |
885 | return NULL; /* illegal time */ | |
886 | if (neg) | |
887 | *offsetp = -*offsetp; | |
888 | return strp; | |
889 | } | |
890 | ||
891 | /* | |
892 | ** Given a pointer into a time zone string, extract a rule in the form | |
893 | ** date[/time]. See POSIX section 8 for the format of "date" and "time". | |
894 | ** If a valid rule is not found, return NULL. | |
895 | ** Otherwise, return a pointer to the first character not part of the rule. | |
896 | */ | |
897 | ||
898 | static const char * | |
899 | getrule(strp, rulep) | |
900 | const char * strp; | |
901 | struct rule * const rulep; | |
902 | { | |
903 | if (*strp == 'J') { | |
904 | /* | |
905 | ** Julian day. | |
906 | */ | |
907 | rulep->r_type = JULIAN_DAY; | |
908 | ++strp; | |
909 | strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); | |
910 | } else if (*strp == 'M') { | |
911 | /* | |
912 | ** Month, week, day. | |
913 | */ | |
914 | rulep->r_type = MONTH_NTH_DAY_OF_WEEK; | |
915 | ++strp; | |
916 | strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); | |
917 | if (strp == NULL) | |
918 | return NULL; | |
919 | if (*strp++ != '.') | |
920 | return NULL; | |
921 | strp = getnum(strp, &rulep->r_week, 1, 5); | |
922 | if (strp == NULL) | |
923 | return NULL; | |
924 | if (*strp++ != '.') | |
925 | return NULL; | |
926 | strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); | |
927 | } else if (is_digit(*strp)) { | |
928 | /* | |
929 | ** Day of year. | |
930 | */ | |
931 | rulep->r_type = DAY_OF_YEAR; | |
932 | strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); | |
933 | } else return NULL; /* invalid format */ | |
934 | if (strp == NULL) | |
935 | return NULL; | |
936 | if (*strp == '/') { | |
937 | /* | |
938 | ** Time specified. | |
939 | */ | |
940 | ++strp; | |
941 | strp = getsecs(strp, &rulep->r_time); | |
942 | } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ | |
943 | return strp; | |
944 | } | |
945 | ||
946 | /* | |
947 | ** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the | |
948 | ** year, a rule, and the offset from UTC at the time that rule takes effect, | |
949 | ** calculate the Epoch-relative time that rule takes effect. | |
950 | */ | |
951 | ||
952 | static time_t | |
953 | transtime(janfirst, year, rulep, offset) | |
954 | const time_t janfirst; | |
955 | const int year; | |
956 | const struct rule * const rulep; | |
957 | const long offset; | |
958 | { | |
959 | int leapyear; | |
960 | time_t value; | |
961 | int i; | |
962 | int d, m1, yy0, yy1, yy2, dow; | |
963 | ||
964 | INITIALIZE(value); | |
965 | leapyear = isleap(year); | |
966 | switch (rulep->r_type) { | |
967 | ||
968 | case JULIAN_DAY: | |
969 | /* | |
970 | ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap | |
971 | ** years. | |
972 | ** In non-leap years, or if the day number is 59 or less, just | |
973 | ** add SECSPERDAY times the day number-1 to the time of | |
974 | ** January 1, midnight, to get the day. | |
975 | */ | |
976 | value = janfirst + (rulep->r_day - 1) * SECSPERDAY; | |
977 | if (leapyear && rulep->r_day >= 60) | |
978 | value += SECSPERDAY; | |
979 | break; | |
980 | ||
981 | case DAY_OF_YEAR: | |
982 | /* | |
983 | ** n - day of year. | |
984 | ** Just add SECSPERDAY times the day number to the time of | |
985 | ** January 1, midnight, to get the day. | |
986 | */ | |
987 | value = janfirst + rulep->r_day * SECSPERDAY; | |
988 | break; | |
989 | ||
990 | case MONTH_NTH_DAY_OF_WEEK: | |
991 | /* | |
992 | ** Mm.n.d - nth "dth day" of month m. | |
993 | */ | |
994 | value = janfirst; | |
995 | for (i = 0; i < rulep->r_mon - 1; ++i) | |
996 | value += mon_lengths[leapyear][i] * SECSPERDAY; | |
997 | ||
998 | /* | |
999 | ** Use Zeller's Congruence to get day-of-week of first day of | |
1000 | ** month. | |
1001 | */ | |
1002 | m1 = (rulep->r_mon + 9) % 12 + 1; | |
1003 | yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; | |
1004 | yy1 = yy0 / 100; | |
1005 | yy2 = yy0 % 100; | |
1006 | dow = ((26 * m1 - 2) / 10 + | |
1007 | 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; | |
1008 | if (dow < 0) | |
1009 | dow += DAYSPERWEEK; | |
1010 | ||
1011 | /* | |
1012 | ** "dow" is the day-of-week of the first day of the month. Get | |
1013 | ** the day-of-month (zero-origin) of the first "dow" day of the | |
1014 | ** month. | |
1015 | */ | |
1016 | d = rulep->r_day - dow; | |
1017 | if (d < 0) | |
1018 | d += DAYSPERWEEK; | |
1019 | for (i = 1; i < rulep->r_week; ++i) { | |
1020 | if (d + DAYSPERWEEK >= | |
1021 | mon_lengths[leapyear][rulep->r_mon - 1]) | |
1022 | break; | |
1023 | d += DAYSPERWEEK; | |
1024 | } | |
1025 | ||
1026 | /* | |
1027 | ** "d" is the day-of-month (zero-origin) of the day we want. | |
1028 | */ | |
1029 | value += d * SECSPERDAY; | |
1030 | break; | |
1031 | } | |
1032 | ||
1033 | /* | |
1034 | ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in | |
1035 | ** question. To get the Epoch-relative time of the specified local | |
1036 | ** time on that day, add the transition time and the current offset | |
1037 | ** from UTC. | |
1038 | */ | |
1039 | return value + rulep->r_time + offset; | |
1040 | } | |
1041 | ||
1042 | /* | |
1043 | ** Given a POSIX section 8-style TZ string, fill in the rule tables as | |
1044 | ** appropriate. | |
1045 | */ | |
1046 | ||
1047 | static int | |
1048 | tzparse(name, sp, lastditch) | |
1049 | const char * name; | |
1050 | struct state * const sp; | |
1051 | const int lastditch; | |
1052 | { | |
1053 | const char * stdname; | |
1054 | const char * dstname; | |
1055 | size_t stdlen; | |
1056 | size_t dstlen; | |
1057 | long stdoffset; | |
1058 | long dstoffset; | |
1059 | time_t * atp; | |
1060 | unsigned char * typep; | |
1061 | char * cp; | |
1062 | int load_result; | |
1063 | ||
1064 | INITIALIZE(dstname); | |
1065 | if (lastditch) { | |
1066 | stdname = name; | |
1067 | stdlen = strlen(name); /* length of standard zone name */ | |
1068 | name += stdlen; | |
1069 | if (stdlen >= sizeof sp->chars) | |
1070 | stdlen = (sizeof sp->chars) - 1; | |
1071 | stdoffset = 0; | |
1072 | } else { | |
1073 | name = getzname(name, (char **)&stdname, &stdlen); | |
1074 | if (stdlen < 3) | |
1075 | return -1; | |
1076 | if (*name == '\0') | |
1077 | return -1; /* was "stdoffset = 0;" */ | |
1078 | else { | |
1079 | name = getoffset(name, &stdoffset); | |
1080 | if (name == NULL) | |
1081 | return -1; | |
1082 | } | |
1083 | } | |
1084 | load_result = tzload(TZDEFRULES, sp); | |
1085 | #ifdef NOTIFY_TZ | |
1086 | *fullname = 0; /* mark fullname as invalid */ | |
1087 | #endif /* NOTIFY_TZ */ | |
1088 | if (load_result != 0) | |
1089 | sp->leapcnt = 0; /* so, we're off a little */ | |
1090 | if (*name != '\0') { | |
1091 | dstname = name; | |
1092 | name = getzname(name, (char **)&dstname, &dstlen); | |
1093 | if (dstlen < 3) | |
1094 | return -1; | |
1095 | if (*name != '\0' && *name != ',' && *name != ';') { | |
1096 | name = getoffset(name, &dstoffset); | |
1097 | if (name == NULL) | |
1098 | return -1; | |
1099 | } else dstoffset = stdoffset - SECSPERHOUR; | |
1100 | if (*name == '\0' && load_result != 0) | |
1101 | name = TZDEFRULESTRING; | |
1102 | if (*name == ',' || *name == ';') { | |
1103 | struct rule start; | |
1104 | struct rule end; | |
1105 | int year; | |
1106 | time_t janfirst; | |
1107 | time_t starttime; | |
1108 | time_t endtime; | |
1109 | ||
1110 | ++name; | |
1111 | if ((name = getrule(name, &start)) == NULL) | |
1112 | return -1; | |
1113 | if (*name++ != ',') | |
1114 | return -1; | |
1115 | if ((name = getrule(name, &end)) == NULL) | |
1116 | return -1; | |
1117 | if (*name != '\0') | |
1118 | return -1; | |
1119 | sp->typecnt = 2; /* standard time and DST */ | |
1120 | /* | |
1121 | ** Two transitions per year, from EPOCH_YEAR to 2037. | |
1122 | */ | |
1123 | sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); | |
1124 | if (sp->timecnt > TZ_MAX_TIMES) | |
1125 | return -1; | |
1126 | sp->ttis[0].tt_gmtoff = -dstoffset; | |
1127 | sp->ttis[0].tt_isdst = 1; | |
1128 | sp->ttis[0].tt_abbrind = stdlen + 1; | |
1129 | sp->ttis[1].tt_gmtoff = -stdoffset; | |
1130 | sp->ttis[1].tt_isdst = 0; | |
1131 | sp->ttis[1].tt_abbrind = 0; | |
1132 | atp = sp->ats; | |
1133 | typep = sp->types; | |
1134 | janfirst = 0; | |
1135 | for (year = EPOCH_YEAR; year <= 2037; ++year) { | |
1136 | starttime = transtime(janfirst, year, &start, | |
1137 | stdoffset); | |
1138 | endtime = transtime(janfirst, year, &end, | |
1139 | dstoffset); | |
1140 | if (starttime > endtime) { | |
1141 | *atp++ = endtime; | |
1142 | *typep++ = 1; /* DST ends */ | |
1143 | *atp++ = starttime; | |
1144 | *typep++ = 0; /* DST begins */ | |
1145 | } else { | |
1146 | *atp++ = starttime; | |
1147 | *typep++ = 0; /* DST begins */ | |
1148 | *atp++ = endtime; | |
1149 | *typep++ = 1; /* DST ends */ | |
1150 | } | |
1151 | janfirst += year_lengths[isleap(year)] * | |
1152 | SECSPERDAY; | |
1153 | } | |
1154 | } else { | |
1155 | long theirstdoffset; | |
1156 | long theirdstoffset; | |
1157 | long theiroffset; | |
1158 | int isdst; | |
1159 | int i; | |
1160 | int j; | |
1161 | ||
1162 | if (*name != '\0') | |
1163 | return -1; | |
1164 | /* | |
1165 | ** Initial values of theirstdoffset and theirdstoffset. | |
1166 | */ | |
1167 | theirstdoffset = 0; | |
1168 | for (i = 0; i < sp->timecnt; ++i) { | |
1169 | j = sp->types[i]; | |
1170 | if (!sp->ttis[j].tt_isdst) { | |
1171 | theirstdoffset = | |
1172 | -sp->ttis[j].tt_gmtoff; | |
1173 | break; | |
1174 | } | |
1175 | } | |
1176 | theirdstoffset = 0; | |
1177 | for (i = 0; i < sp->timecnt; ++i) { | |
1178 | j = sp->types[i]; | |
1179 | if (sp->ttis[j].tt_isdst) { | |
1180 | theirdstoffset = | |
1181 | -sp->ttis[j].tt_gmtoff; | |
1182 | break; | |
1183 | } | |
1184 | } | |
1185 | /* | |
1186 | ** Initially we're assumed to be in standard time. | |
1187 | */ | |
1188 | isdst = FALSE; | |
1189 | theiroffset = theirstdoffset; | |
1190 | /* | |
1191 | ** Now juggle transition times and types | |
1192 | ** tracking offsets as you do. | |
1193 | */ | |
1194 | for (i = 0; i < sp->timecnt; ++i) { | |
1195 | j = sp->types[i]; | |
1196 | sp->types[i] = sp->ttis[j].tt_isdst; | |
1197 | if (sp->ttis[j].tt_ttisgmt) { | |
1198 | /* No adjustment to transition time */ | |
1199 | } else { | |
1200 | /* | |
1201 | ** If summer time is in effect, and the | |
1202 | ** transition time was not specified as | |
1203 | ** standard time, add the summer time | |
1204 | ** offset to the transition time; | |
1205 | ** otherwise, add the standard time | |
1206 | ** offset to the transition time. | |
1207 | */ | |
1208 | /* | |
1209 | ** Transitions from DST to DDST | |
1210 | ** will effectively disappear since | |
1211 | ** POSIX provides for only one DST | |
1212 | ** offset. | |
1213 | */ | |
1214 | if (isdst && !sp->ttis[j].tt_ttisstd) { | |
1215 | sp->ats[i] += dstoffset - | |
1216 | theirdstoffset; | |
1217 | } else { | |
1218 | sp->ats[i] += stdoffset - | |
1219 | theirstdoffset; | |
1220 | } | |
1221 | } | |
1222 | theiroffset = -sp->ttis[j].tt_gmtoff; | |
1223 | if (sp->ttis[j].tt_isdst) | |
1224 | theirdstoffset = theiroffset; | |
1225 | else theirstdoffset = theiroffset; | |
1226 | } | |
1227 | /* | |
1228 | ** Finally, fill in ttis. | |
1229 | ** ttisstd and ttisgmt need not be handled. | |
1230 | */ | |
1231 | sp->ttis[0].tt_gmtoff = -stdoffset; | |
1232 | sp->ttis[0].tt_isdst = FALSE; | |
1233 | sp->ttis[0].tt_abbrind = 0; | |
1234 | sp->ttis[1].tt_gmtoff = -dstoffset; | |
1235 | sp->ttis[1].tt_isdst = TRUE; | |
1236 | sp->ttis[1].tt_abbrind = stdlen + 1; | |
1237 | sp->typecnt = 2; | |
1238 | } | |
1239 | } else { | |
1240 | dstlen = 0; | |
1241 | sp->typecnt = 1; /* only standard time */ | |
1242 | sp->timecnt = 0; | |
1243 | sp->ttis[0].tt_gmtoff = -stdoffset; | |
1244 | sp->ttis[0].tt_isdst = 0; | |
1245 | sp->ttis[0].tt_abbrind = 0; | |
1246 | } | |
1247 | sp->charcnt = stdlen + 1; | |
1248 | if (dstlen != 0) | |
1249 | sp->charcnt += dstlen + 1; | |
1250 | if ((size_t) sp->charcnt > sizeof sp->chars) | |
1251 | return -1; | |
1252 | cp = sp->chars; | |
1253 | (void) strncpy(cp, stdname, stdlen); | |
1254 | cp += stdlen; | |
1255 | *cp++ = '\0'; | |
1256 | if (dstlen != 0) { | |
1257 | (void) strncpy(cp, dstname, dstlen); | |
1258 | *(cp + dstlen) = '\0'; | |
1259 | } | |
1260 | return 0; | |
1261 | } | |
1262 | ||
1263 | static void | |
1264 | gmtload(sp) | |
1265 | struct state * const sp; | |
1266 | { | |
1267 | if (tzload(gmt, sp) != 0) | |
1268 | (void) tzparse(gmt, sp, TRUE); | |
1269 | } | |
1270 | ||
1271 | static void | |
1f2f436a | 1272 | tzsetwall_basic(int rdlocked) |
224c7076 A |
1273 | { |
1274 | #ifdef NOTIFY_TZ | |
1275 | notify_check_tz(&lcl_notify); | |
1f2f436a A |
1276 | #else |
1277 | if (TZDEFAULT) { | |
1278 | static struct timespec last_mtimespec = {0, 0}; | |
1279 | struct stat statbuf; | |
1280 | ||
1281 | if (lstat(TZDEFAULT, &statbuf) == 0) { | |
1282 | if (statbuf.st_mtimespec.tv_sec > last_mtimespec.tv_sec || | |
1283 | (statbuf.st_mtimespec.tv_sec == last_mtimespec.tv_sec && | |
1284 | statbuf.st_mtimespec.tv_nsec > last_mtimespec.tv_nsec)) { | |
1285 | /* Trigger resetting the local TZ */ | |
1286 | lcl_is_set = 0; | |
1287 | } | |
1288 | last_mtimespec = statbuf.st_mtimespec; | |
1289 | } | |
1290 | } | |
224c7076 | 1291 | #endif /* NOTIFY_TZ */ |
1f2f436a A |
1292 | if (!rdlocked) |
1293 | _RWLOCK_RDLOCK(&lcl_rwlock); | |
224c7076 | 1294 | if (lcl_is_set < 0) { |
1f2f436a | 1295 | #ifdef NOTIFY_TZ_DEBUG |
224c7076 | 1296 | NOTIFY_TZ_PRINTF("tzsetwall_basic lcl_is_set < 0\n"); |
1f2f436a A |
1297 | #endif |
1298 | if (!rdlocked) | |
1299 | _RWLOCK_UNLOCK(&lcl_rwlock); | |
224c7076 A |
1300 | return; |
1301 | } | |
1f2f436a | 1302 | #ifdef NOTIFY_TZ_DEBUG |
224c7076 | 1303 | NOTIFY_TZ_PRINTF("tzsetwall_basic not set\n"); |
1f2f436a A |
1304 | #endif |
1305 | _RWLOCK_UNLOCK(&lcl_rwlock); | |
1306 | ||
1307 | _RWLOCK_WRLOCK(&lcl_rwlock); | |
224c7076 A |
1308 | lcl_is_set = -1; |
1309 | ||
1310 | #ifdef ALL_STATE | |
1311 | if (lclptr == NULL) { | |
1312 | lclptr = (struct state *) malloc(sizeof *lclptr); | |
1313 | if (lclptr == NULL) { | |
1314 | settzname(); /* all we can do */ | |
1f2f436a A |
1315 | _RWLOCK_UNLOCK(&lcl_rwlock); |
1316 | if (rdlocked) | |
1317 | _RWLOCK_RDLOCK(&lcl_rwlock); | |
224c7076 A |
1318 | return; |
1319 | } | |
1320 | } | |
1321 | #endif /* defined ALL_STATE */ | |
1322 | if (tzload((char *) NULL, lclptr) != 0) | |
1323 | gmtload(lclptr); | |
1324 | #ifdef NOTIFY_TZ | |
1325 | notify_register_tz(fullname, &lcl_notify); | |
1326 | #endif /* NOTIFY_TZ */ | |
1327 | settzname(); | |
1f2f436a A |
1328 | _RWLOCK_UNLOCK(&lcl_rwlock); |
1329 | ||
1330 | if (rdlocked) | |
1331 | _RWLOCK_RDLOCK(&lcl_rwlock); | |
224c7076 A |
1332 | } |
1333 | ||
1334 | void | |
1335 | tzsetwall(void) | |
1336 | { | |
1337 | #ifdef NOTIFY_TZ_DEBUG | |
1338 | NOTIFY_TZ_PRINTF("tzsetwall called\n"); | |
1339 | #endif /* NOTIFY_TZ_DEBUG */ | |
1f2f436a | 1340 | tzsetwall_basic(0); |
224c7076 A |
1341 | } |
1342 | ||
1343 | __private_extern__ void | |
1f2f436a | 1344 | tzset_basic(int rdlocked) |
224c7076 A |
1345 | { |
1346 | const char * name; | |
1347 | ||
1348 | name = getenv("TZ"); | |
1349 | if (name == NULL) { | |
1f2f436a | 1350 | tzsetwall_basic(rdlocked); |
224c7076 A |
1351 | return; |
1352 | } | |
1353 | ||
1354 | #ifdef NOTIFY_TZ | |
1355 | notify_check_tz(&lcl_notify); | |
1356 | #endif /* NOTIFY_TZ */ | |
1f2f436a A |
1357 | if (!rdlocked) |
1358 | _RWLOCK_RDLOCK(&lcl_rwlock); | |
224c7076 | 1359 | if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) { |
1f2f436a A |
1360 | if (!rdlocked) |
1361 | _RWLOCK_UNLOCK(&lcl_rwlock); | |
1362 | #ifdef NOTIFY_TZ_DEBUG | |
224c7076 | 1363 | NOTIFY_TZ_PRINTF("tzset_basic matched %s\n", lcl_TZname); |
1f2f436a | 1364 | #endif |
224c7076 A |
1365 | return; |
1366 | } | |
1f2f436a A |
1367 | _RWLOCK_UNLOCK(&lcl_rwlock); |
1368 | ||
1369 | _RWLOCK_WRLOCK(&lcl_rwlock); | |
224c7076 A |
1370 | lcl_is_set = strlen(name) < sizeof lcl_TZname; |
1371 | if (lcl_is_set) | |
1372 | (void) strcpy(lcl_TZname, name); | |
1373 | ||
1374 | #ifdef ALL_STATE | |
1375 | if (lclptr == NULL) { | |
1376 | lclptr = (struct state *) malloc(sizeof *lclptr); | |
1377 | if (lclptr == NULL) { | |
1378 | settzname(); /* all we can do */ | |
1f2f436a A |
1379 | _RWLOCK_UNLOCK(&lcl_rwlock); |
1380 | if (rdlocked) | |
1381 | _RWLOCK_RDLOCK(&lcl_rwlock); | |
224c7076 A |
1382 | return; |
1383 | } | |
1384 | } | |
1385 | #endif /* defined ALL_STATE */ | |
1386 | if (*name == '\0') { | |
1387 | /* | |
1388 | ** User wants it fast rather than right. | |
1389 | */ | |
1390 | lclptr->leapcnt = 0; /* so, we're off a little */ | |
1391 | lclptr->timecnt = 0; | |
1392 | lclptr->typecnt = 0; | |
1393 | lclptr->ttis[0].tt_isdst = 0; | |
1394 | lclptr->ttis[0].tt_gmtoff = 0; | |
1395 | lclptr->ttis[0].tt_abbrind = 0; | |
1396 | (void) strcpy(lclptr->chars, gmt); | |
1397 | #ifdef NOTIFY_TZ | |
1398 | if (fullname) | |
1399 | *fullname = 0; | |
1400 | #endif /* NOTIFY_TZ */ | |
1401 | } else if (tzload(name, lclptr) != 0) | |
1402 | if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) | |
1403 | (void) gmtload(lclptr); | |
1404 | #ifdef NOTIFY_TZ | |
1405 | notify_register_tz(fullname, &lcl_notify); | |
1406 | #endif /* NOTIFY_TZ */ | |
1407 | settzname(); | |
1f2f436a A |
1408 | _RWLOCK_UNLOCK(&lcl_rwlock); |
1409 | ||
1410 | if (rdlocked) | |
1411 | _RWLOCK_RDLOCK(&lcl_rwlock); | |
224c7076 A |
1412 | } |
1413 | ||
1414 | void | |
1415 | tzset(void) | |
1416 | { | |
1417 | #ifdef NOTIFY_TZ_DEBUG | |
1418 | NOTIFY_TZ_PRINTF("tzset called TZ=%s\n", getenv("TZ")); | |
1419 | #endif /* NOTIFY_TZ_DEBUG */ | |
1f2f436a | 1420 | tzset_basic(0); |
224c7076 A |
1421 | } |
1422 | ||
1423 | /* | |
1424 | ** The easy way to behave "as if no library function calls" localtime | |
1425 | ** is to not call it--so we drop its guts into "localsub", which can be | |
1426 | ** freely called. (And no, the PANS doesn't require the above behavior-- | |
1427 | ** but it *is* desirable.) | |
1428 | ** | |
1429 | ** The unused offset argument is for the benefit of mktime variants. | |
1430 | */ | |
1431 | ||
1432 | /*ARGSUSED*/ | |
1433 | #ifdef __LP64__ | |
1434 | __private_extern__ struct tm * | |
1435 | #else /* !__LP64__ */ | |
1436 | __private_extern__ void | |
1437 | #endif /* __LP64__ */ | |
1438 | localsub(timep, offset, tmp) | |
1439 | const time_t * const timep; | |
1440 | const long offset; | |
1441 | struct tm * const tmp; | |
1442 | { | |
1443 | struct state * sp; | |
1444 | const struct ttinfo * ttisp; | |
1445 | int i; | |
1446 | const time_t t = *timep; | |
1447 | ||
1448 | #ifdef NOTIFY_TZ_DEBUG | |
1449 | NOTIFY_TZ_PRINTF("localsub called\n"); | |
1450 | #endif /* NOTIFY_TZ_DEBUG */ | |
1451 | sp = lclptr; | |
1452 | #ifdef ALL_STATE | |
1453 | if (sp == NULL) { | |
1454 | #ifdef __LP64__ | |
1455 | return gmtsub(timep, offset, tmp); | |
1456 | #else /* !__LP64__ */ | |
1457 | gmtsub(timep, offset, tmp); | |
1458 | return; | |
1459 | #endif /* __LP64__ */ | |
1460 | } | |
1461 | #endif /* defined ALL_STATE */ | |
1462 | if (sp->timecnt == 0 || t < sp->ats[0]) { | |
1463 | i = 0; | |
1464 | while (sp->ttis[i].tt_isdst) | |
1465 | if (++i >= sp->typecnt) { | |
1466 | i = 0; | |
1467 | break; | |
1468 | } | |
1469 | } else { | |
1470 | for (i = 1; i < sp->timecnt; ++i) | |
1471 | if (t < sp->ats[i]) | |
1472 | break; | |
1473 | i = sp->types[i - 1]; | |
1474 | } | |
1475 | ttisp = &sp->ttis[i]; | |
1476 | /* | |
1477 | ** To get (wrong) behavior that's compatible with System V Release 2.0 | |
1478 | ** you'd replace the statement below with | |
1479 | ** t += ttisp->tt_gmtoff; | |
1480 | ** timesub(&t, 0L, sp, tmp); | |
1481 | */ | |
1482 | #ifdef __LP64__ | |
1483 | if (timesub(&t, ttisp->tt_gmtoff, sp, tmp) == NULL) | |
1484 | return NULL; | |
1485 | #else /* !__LP64__ */ | |
1486 | timesub(&t, ttisp->tt_gmtoff, sp, tmp); | |
1487 | #endif /* __LP64__ */ | |
1488 | tmp->tm_isdst = ttisp->tt_isdst; | |
1489 | tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; | |
1490 | #ifdef TM_ZONE | |
1491 | tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; | |
1492 | #endif /* defined TM_ZONE */ | |
1493 | #ifdef __LP64__ | |
1494 | return tmp; | |
1495 | #endif /* __LP64__ */ | |
1496 | } | |
1497 | ||
1498 | struct tm * | |
1499 | localtime(timep) | |
1500 | const time_t * const timep; | |
1501 | { | |
1502 | static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER; | |
1503 | static pthread_key_t localtime_key = -1; | |
1504 | struct tm *p_tm; | |
1505 | ||
1506 | if (__isthreaded != 0) { | |
224c7076 | 1507 | if (localtime_key == (pthread_key_t)-1) { |
1f2f436a A |
1508 | _pthread_mutex_lock(&localtime_mutex); |
1509 | if (localtime_key == (pthread_key_t)-1) { | |
1510 | localtime_key = __LIBC_PTHREAD_KEY_LOCALTIME; | |
1511 | if (pthread_key_init_np(localtime_key, free) < 0) { | |
1512 | _pthread_mutex_unlock(&localtime_mutex); | |
1513 | return(NULL); | |
1514 | } | |
224c7076 | 1515 | } |
1f2f436a | 1516 | _pthread_mutex_unlock(&localtime_mutex); |
224c7076 | 1517 | } |
224c7076 A |
1518 | p_tm = _pthread_getspecific(localtime_key); |
1519 | if (p_tm == NULL) { | |
1520 | if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) | |
1521 | == NULL) | |
1522 | return(NULL); | |
1523 | _pthread_setspecific(localtime_key, p_tm); | |
1524 | } | |
1f2f436a A |
1525 | _RWLOCK_RDLOCK(&lcl_rwlock); |
1526 | tzset_basic(1); | |
224c7076 A |
1527 | #ifdef __LP64__ |
1528 | p_tm = localsub(timep, 0L, p_tm); | |
1529 | #else /* !__LP64__ */ | |
1530 | localsub(timep, 0L, p_tm); | |
1531 | #endif /* __LP64__ */ | |
1f2f436a | 1532 | _RWLOCK_UNLOCK(&lcl_rwlock); |
224c7076 A |
1533 | return(p_tm); |
1534 | } else { | |
1f2f436a | 1535 | tzset_basic(0); |
224c7076 A |
1536 | #ifdef __LP64__ |
1537 | return localsub(timep, 0L, &tm); | |
1538 | #else /* !__LP64__ */ | |
1539 | localsub(timep, 0L, &tm); | |
1540 | return(&tm); | |
1541 | #endif /* __LP64__ */ | |
1542 | } | |
1543 | } | |
1544 | ||
1545 | /* | |
1546 | ** Re-entrant version of localtime. | |
1547 | */ | |
1548 | ||
1549 | struct tm * | |
1550 | localtime_r(const time_t * const __restrict timep, struct tm * __restrict tm) | |
1551 | { | |
1f2f436a A |
1552 | _RWLOCK_RDLOCK(&lcl_rwlock); |
1553 | tzset_basic(1); | |
224c7076 A |
1554 | #ifdef __LP64__ |
1555 | tm = localsub(timep, 0L, tm); | |
1556 | #else /* !__LP64__ */ | |
1557 | localsub(timep, 0L, tm); | |
1558 | #endif /* __LP64__ */ | |
1f2f436a | 1559 | _RWLOCK_UNLOCK(&lcl_rwlock); |
224c7076 A |
1560 | return tm; |
1561 | } | |
1562 | ||
1563 | /* | |
1564 | ** gmtsub is to gmtime as localsub is to localtime. | |
1565 | */ | |
1566 | ||
1567 | #ifdef __LP64__ | |
1568 | static struct tm * | |
1569 | #else /* !__LP64__ */ | |
1570 | static void | |
1571 | #endif /* __LP64__ */ | |
1572 | gmtsub(timep, offset, tmp) | |
1573 | const time_t * const timep; | |
1574 | const long offset; | |
1575 | struct tm * const tmp; | |
1576 | { | |
1577 | #ifdef NOTIFY_TZ_DEBUG | |
1578 | NOTIFY_TZ_PRINTF("gmtsub called\n"); | |
1579 | #endif /* NOTIFY_TZ_DEBUG */ | |
224c7076 A |
1580 | #ifdef NOTIFY_TZ |
1581 | notify_check_tz(&gmt_notify); | |
1582 | #endif /* NOTIFY_TZ */ | |
1583 | if (!gmt_is_set) { | |
1f2f436a A |
1584 | _MUTEX_LOCK(&gmt_mutex); |
1585 | if (!gmt_is_set) { | |
224c7076 A |
1586 | #ifdef ALL_STATE |
1587 | #ifdef NOTIFY_TZ | |
1f2f436a | 1588 | if (gmtptr == NULL) |
224c7076 | 1589 | #endif /* NOTIFY_TZ */ |
1f2f436a A |
1590 | gmtptr = (struct state *) malloc(sizeof *gmtptr); |
1591 | if (gmtptr != NULL) | |
224c7076 | 1592 | #ifdef NOTIFY_TZ |
1f2f436a | 1593 | { |
224c7076 A |
1594 | #endif /* NOTIFY_TZ */ |
1595 | #endif /* defined ALL_STATE */ | |
1f2f436a | 1596 | gmtload(gmtptr); |
224c7076 | 1597 | #ifdef NOTIFY_TZ |
1f2f436a A |
1598 | notify_register_tz(fullname, &gmt_notify); |
1599 | #ifdef ALL_STATE | |
1600 | } | |
1601 | #endif | |
224c7076 | 1602 | #endif /* NOTIFY_TZ */ |
1f2f436a A |
1603 | gmt_is_set = TRUE; |
1604 | } | |
1605 | _MUTEX_UNLOCK(&gmt_mutex); | |
224c7076 | 1606 | } |
224c7076 A |
1607 | #ifdef __LP64__ |
1608 | if(timesub(timep, offset, gmtptr, tmp) == NULL) | |
1609 | return NULL; | |
1610 | #else /* !__LP64__ */ | |
1611 | timesub(timep, offset, gmtptr, tmp); | |
1612 | #endif /* __LP64__ */ | |
1613 | #ifdef TM_ZONE | |
1614 | /* | |
1615 | ** Could get fancy here and deliver something such as | |
1616 | ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, | |
1617 | ** but this is no time for a treasure hunt. | |
1618 | */ | |
1619 | if (offset != 0) | |
1620 | tmp->TM_ZONE = wildabbr; | |
1621 | else { | |
1622 | #ifdef ALL_STATE | |
1623 | if (gmtptr == NULL) | |
1624 | tmp->TM_ZONE = (char *)gmt; | |
1625 | else tmp->TM_ZONE = gmtptr->chars; | |
1626 | #endif /* defined ALL_STATE */ | |
1627 | #ifndef ALL_STATE | |
1628 | tmp->TM_ZONE = gmtptr->chars; | |
1629 | #endif /* State Farm */ | |
1630 | } | |
1631 | #endif /* defined TM_ZONE */ | |
1632 | #ifdef __LP64__ | |
1633 | return tmp; | |
1634 | #endif /* __LP64__ */ | |
1635 | } | |
1636 | ||
1637 | struct tm * | |
1638 | gmtime(timep) | |
1639 | const time_t * const timep; | |
1640 | { | |
1641 | static pthread_mutex_t gmtime_mutex = PTHREAD_MUTEX_INITIALIZER; | |
1642 | static pthread_key_t gmtime_key = -1; | |
1643 | struct tm *p_tm; | |
1644 | ||
224c7076 | 1645 | if (__isthreaded != 0) { |
224c7076 | 1646 | if (gmtime_key == (pthread_key_t)-1) { |
1f2f436a A |
1647 | _pthread_mutex_lock(&gmtime_mutex); |
1648 | if (gmtime_key == (pthread_key_t)-1) { | |
1649 | gmtime_key = __LIBC_PTHREAD_KEY_GMTIME; | |
1650 | if (pthread_key_init_np(gmtime_key, free) < 0) { | |
1651 | _pthread_mutex_unlock(&gmtime_mutex); | |
1652 | return(NULL); | |
1653 | } | |
224c7076 | 1654 | } |
1f2f436a | 1655 | _pthread_mutex_unlock(&gmtime_mutex); |
224c7076 | 1656 | } |
224c7076 A |
1657 | /* |
1658 | * Changed to follow POSIX.1 threads standard, which | |
1659 | * is what BSD currently has. | |
1660 | */ | |
1661 | if ((p_tm = _pthread_getspecific(gmtime_key)) == NULL) { | |
1662 | if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) | |
1663 | == NULL) { | |
1664 | return(NULL); | |
1665 | } | |
1666 | _pthread_setspecific(gmtime_key, p_tm); | |
1667 | } | |
1668 | #ifdef __LP64__ | |
1669 | return gmtsub(timep, 0L, p_tm); | |
1670 | #else /* !__LP64__ */ | |
1671 | gmtsub(timep, 0L, p_tm); | |
1672 | return(p_tm); | |
1673 | #endif /* __LP64__ */ | |
1674 | } | |
1675 | else { | |
1676 | #ifdef __LP64__ | |
1677 | return gmtsub(timep, 0L, &tm); | |
1678 | #else /* !__LP64__ */ | |
1679 | gmtsub(timep, 0L, &tm); | |
1680 | return(&tm); | |
1681 | #endif /* __LP64__ */ | |
1682 | } | |
1683 | } | |
1684 | ||
1685 | /* | |
1686 | * Re-entrant version of gmtime. | |
1687 | */ | |
1688 | ||
1689 | struct tm * | |
1690 | gmtime_r(timep, tm) | |
1691 | const time_t * const timep; | |
1692 | struct tm * tm; | |
1693 | { | |
1694 | ||
1695 | #ifdef __LP64__ | |
1696 | return gmtsub(timep, 0L, tm); | |
1697 | #else /* !__LP64__ */ | |
1698 | gmtsub(timep, 0L, tm); | |
1699 | return tm; | |
1700 | #endif /* __LP64__ */ | |
1701 | } | |
1702 | ||
1703 | #ifdef STD_INSPIRED | |
1704 | ||
1705 | struct tm * | |
1706 | offtime(timep, offset) | |
1707 | const time_t * const timep; | |
1708 | const long offset; | |
1709 | { | |
1710 | #ifdef __LP64__ | |
1711 | return gmtsub(timep, offset, &tm); | |
1712 | #else /* !__LP64__ */ | |
1713 | gmtsub(timep, offset, &tm); | |
1714 | return &tm; | |
1715 | #endif /* __LP64__ */ | |
1716 | } | |
1717 | ||
1718 | #endif /* defined STD_INSPIRED */ | |
1719 | ||
1720 | #ifdef __LP64__ | |
1721 | static struct tm * | |
1722 | #else /* !__LP64__ */ | |
1723 | static void | |
1724 | #endif /* __LP64__ */ | |
1725 | timesub(timep, offset, sp, tmp) | |
1726 | const time_t * const timep; | |
1727 | const long offset; | |
1728 | const struct state * const sp; | |
1729 | struct tm * const tmp; | |
1730 | { | |
1731 | const struct lsinfo * lp; | |
1732 | long days; | |
1733 | long rem; | |
1734 | long y; | |
1735 | int yleap; | |
1736 | const int * ip; | |
1737 | long corr; | |
1738 | int hit; | |
1739 | int i; | |
1740 | ||
1741 | corr = 0; | |
1742 | hit = 0; | |
1743 | #ifdef ALL_STATE | |
1744 | i = (sp == NULL) ? 0 : sp->leapcnt; | |
1745 | #endif /* defined ALL_STATE */ | |
1746 | #ifndef ALL_STATE | |
1747 | i = sp->leapcnt; | |
1748 | #endif /* State Farm */ | |
1749 | while (--i >= 0) { | |
1750 | lp = &sp->lsis[i]; | |
1751 | if (*timep >= lp->ls_trans) { | |
1752 | if (*timep == lp->ls_trans) { | |
1753 | hit = ((i == 0 && lp->ls_corr > 0) || | |
1754 | lp->ls_corr > sp->lsis[i - 1].ls_corr); | |
1755 | if (hit) | |
1756 | while (i > 0 && | |
1757 | sp->lsis[i].ls_trans == | |
1758 | sp->lsis[i - 1].ls_trans + 1 && | |
1759 | sp->lsis[i].ls_corr == | |
1760 | sp->lsis[i - 1].ls_corr + 1) { | |
1761 | ++hit; | |
1762 | --i; | |
1763 | } | |
1764 | } | |
1765 | corr = lp->ls_corr; | |
1766 | break; | |
1767 | } | |
1768 | } | |
1769 | days = *timep / SECSPERDAY; | |
1770 | rem = *timep % SECSPERDAY; | |
1771 | #ifdef mc68k | |
1772 | if (*timep == 0x80000000) { | |
1773 | /* | |
1774 | ** A 3B1 muffs the division on the most negative number. | |
1775 | */ | |
1776 | days = -24855; | |
1777 | rem = -11648; | |
1778 | } | |
1779 | #endif /* defined mc68k */ | |
1780 | rem += (offset - corr); | |
1781 | while (rem < 0) { | |
1782 | rem += SECSPERDAY; | |
1783 | --days; | |
1784 | } | |
1785 | while (rem >= SECSPERDAY) { | |
1786 | rem -= SECSPERDAY; | |
1787 | ++days; | |
1788 | } | |
1789 | tmp->tm_hour = (int) (rem / SECSPERHOUR); | |
1790 | rem = rem % SECSPERHOUR; | |
1791 | tmp->tm_min = (int) (rem / SECSPERMIN); | |
1792 | /* | |
1793 | ** A positive leap second requires a special | |
1794 | ** representation. This uses "... ??:59:60" et seq. | |
1795 | */ | |
1796 | tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; | |
1797 | tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); | |
1798 | if (tmp->tm_wday < 0) | |
1799 | tmp->tm_wday += DAYSPERWEEK; | |
1800 | y = EPOCH_YEAR; | |
1f2f436a A |
1801 | #define _LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) |
1802 | #ifdef __LP64__ | |
1803 | #define LEAPS_THRU_END_OF(y) ((y) >= 0 ? _LEAPS_THRU_END_OF(y) : _LEAPS_THRU_END_OF((y) + 1) - 1) | |
1804 | #else /* !__LP64__ */ | |
1805 | #define LEAPS_THRU_END_OF(y) _LEAPS_THRU_END_OF(y) | |
1806 | #endif /* __LP64__ */ | |
224c7076 A |
1807 | while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { |
1808 | long newy; | |
1809 | ||
1810 | newy = y + days / DAYSPERNYEAR; | |
1811 | if (days < 0) | |
1812 | --newy; | |
1813 | days -= (newy - y) * DAYSPERNYEAR + | |
1814 | LEAPS_THRU_END_OF(newy - 1) - | |
1815 | LEAPS_THRU_END_OF(y - 1); | |
1816 | y = newy; | |
1817 | } | |
1818 | #ifdef __LP64__ | |
1819 | y -= TM_YEAR_BASE; | |
1820 | if (y < INT_MIN || y > INT_MAX) { | |
1821 | errno = EOVERFLOW; | |
1822 | return NULL; | |
1823 | } | |
1824 | tmp->tm_year = y; | |
1825 | #else /* !__LP64__ */ | |
1826 | tmp->tm_year = y - TM_YEAR_BASE; | |
1827 | #endif /* __LP64__ */ | |
1828 | tmp->tm_yday = (int) days; | |
1829 | ip = mon_lengths[yleap]; | |
1830 | for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) | |
1831 | days = days - (long) ip[tmp->tm_mon]; | |
1832 | tmp->tm_mday = (int) (days + 1); | |
1833 | tmp->tm_isdst = 0; | |
1834 | #ifdef TM_GMTOFF | |
1835 | tmp->TM_GMTOFF = offset; | |
1836 | #endif /* defined TM_GMTOFF */ | |
1837 | #ifdef __LP64__ | |
1838 | return tmp; | |
1839 | #endif /* __LP64__ */ | |
1840 | } | |
1841 | ||
1842 | char * | |
1843 | ctime(timep) | |
1844 | const time_t * const timep; | |
1845 | { | |
1846 | /* | |
1847 | ** Section 4.12.3.2 of X3.159-1989 requires that | |
1848 | ** The ctime function converts the calendar time pointed to by timer | |
1849 | ** to local time in the form of a string. It is equivalent to | |
1850 | ** asctime(localtime(timer)) | |
1851 | */ | |
34e8f829 A |
1852 | #ifdef __LP64__ |
1853 | /* | |
1854 | * In 64-bit, the timep value may produce a time value with a year | |
1855 | * that exceeds 32-bits in size (won't fit in struct tm), so localtime | |
1856 | * will return NULL. | |
1857 | */ | |
1858 | struct tm *tm = localtime(timep); | |
1859 | ||
1860 | if (tm == NULL) | |
1861 | return NULL; | |
1862 | return asctime(tm); | |
1863 | #else /* !__LP64__ */ | |
224c7076 | 1864 | return asctime(localtime(timep)); |
34e8f829 | 1865 | #endif /* __LP64__ */ |
224c7076 A |
1866 | } |
1867 | ||
1868 | char * | |
1869 | ctime_r(timep, buf) | |
1870 | const time_t * const timep; | |
1871 | char * buf; | |
1872 | { | |
1873 | struct tm tm; | |
1874 | ||
34e8f829 A |
1875 | #ifdef __LP64__ |
1876 | /* | |
1877 | * In 64-bit, the timep value may produce a time value with a year | |
1878 | * that exceeds 32-bits in size (won't fit in struct tm), so localtime_r | |
1879 | * will return NULL. | |
1880 | */ | |
1881 | if (localtime_r(timep, &tm) == NULL) | |
1882 | return NULL; | |
1883 | return asctime_r(&tm, buf); | |
1884 | #else /* !__LP64__ */ | |
224c7076 | 1885 | return asctime_r(localtime_r(timep, &tm), buf); |
34e8f829 | 1886 | #endif /* __LP64__ */ |
224c7076 A |
1887 | } |
1888 | ||
1889 | /* | |
1890 | ** Adapted from code provided by Robert Elz, who writes: | |
1891 | ** The "best" way to do mktime I think is based on an idea of Bob | |
1892 | ** Kridle's (so its said...) from a long time ago. | |
1893 | ** [kridle@xinet.com as of 1996-01-16.] | |
1894 | ** It does a binary search of the time_t space. Since time_t's are | |
1895 | ** just 32 bits, its a max of 32 iterations (even at 64 bits it | |
1896 | ** would still be very reasonable). | |
1897 | */ | |
1898 | ||
1899 | #ifndef WRONG | |
1900 | #define WRONG (-1) | |
1901 | #endif /* !defined WRONG */ | |
1902 | ||
1903 | /* | |
1904 | ** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). | |
1905 | */ | |
1906 | ||
1907 | static int | |
1908 | increment_overflow(number, delta) | |
1909 | int * number; | |
1910 | int delta; | |
1911 | { | |
1912 | int number0; | |
1913 | ||
1914 | number0 = *number; | |
1915 | *number += delta; | |
1916 | return (*number < number0) != (delta < 0); | |
1917 | } | |
1918 | ||
1919 | static int | |
1920 | normalize_overflow(tensptr, unitsptr, base) | |
1921 | int * const tensptr; | |
1922 | int * const unitsptr; | |
1923 | const int base; | |
1924 | { | |
1925 | int tensdelta; | |
1926 | ||
1927 | tensdelta = (*unitsptr >= 0) ? | |
1928 | (*unitsptr / base) : | |
1929 | (-1 - (-1 - *unitsptr) / base); | |
1930 | *unitsptr -= tensdelta * base; | |
1931 | return increment_overflow(tensptr, tensdelta); | |
1932 | } | |
1933 | ||
1934 | static int | |
1935 | tmcomp(atmp, btmp) | |
1936 | const struct tm * const atmp; | |
1937 | const struct tm * const btmp; | |
1938 | { | |
1939 | int result; | |
1940 | ||
1f2f436a A |
1941 | /* |
1942 | * Assume that atmp and btmp point to normalized tm strutures. | |
1943 | * So only arithmetic with tm_year could overflow in 64-bit. | |
1944 | */ | |
1945 | if (atmp->tm_year != btmp->tm_year) { | |
1946 | return (atmp->tm_year > btmp->tm_year ? 1 : -1); | |
1947 | } | |
1948 | if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && | |
224c7076 A |
1949 | (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && |
1950 | (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && | |
1951 | (result = (atmp->tm_min - btmp->tm_min)) == 0) | |
1952 | result = atmp->tm_sec - btmp->tm_sec; | |
1953 | return result; | |
1954 | } | |
1955 | ||
1956 | static time_t | |
1957 | time2sub(tmp, funcp, offset, okayp, do_norm_secs, unix03) | |
1958 | struct tm * const tmp; | |
1959 | #ifdef __LP64__ | |
1960 | struct tm *(* const funcp)(const time_t*, long, struct tm*); | |
1961 | #else /* !__LP64__ */ | |
1962 | void (* const funcp)(const time_t*, long, struct tm*); | |
1963 | #endif /* __LP64__ */ | |
1964 | const long offset; | |
1965 | int * const okayp; | |
1966 | const int do_norm_secs; | |
1967 | int unix03; | |
1968 | { | |
1969 | const struct state * sp; | |
1970 | int dir; | |
1971 | int bits; | |
1972 | int i, j ; | |
1973 | int saved_seconds; | |
1974 | time_t newt; | |
1975 | time_t t; | |
1976 | struct tm yourtm, mytm; | |
1977 | #ifdef __LP64__ | |
1978 | long year, il; | |
1979 | #endif /* __LP64__ */ | |
1980 | ||
1981 | *okayp = FALSE; | |
1982 | yourtm = *tmp; | |
1983 | if (do_norm_secs) { | |
1984 | if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, | |
1985 | SECSPERMIN)) | |
1986 | return WRONG; | |
1987 | } | |
1988 | if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) | |
1989 | return WRONG; | |
1990 | if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) | |
1991 | return WRONG; | |
1992 | if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) | |
1993 | return WRONG; | |
1994 | /* | |
1995 | ** Turn yourtm.tm_year into an actual year number for now. | |
1996 | ** It is converted back to an offset from TM_YEAR_BASE later. | |
1997 | */ | |
1998 | #ifdef __LP64__ | |
1999 | year = (long)yourtm.tm_year + TM_YEAR_BASE; | |
2000 | #else /* !__LP64__ */ | |
2001 | if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) | |
2002 | return WRONG; | |
2003 | #endif /* __LP64__ */ | |
2004 | while (yourtm.tm_mday <= 0) { | |
2005 | #ifdef __LP64__ | |
2006 | year--; | |
2007 | il = year + (1 < yourtm.tm_mon); | |
2008 | yourtm.tm_mday += year_lengths[isleap(il)]; | |
2009 | #else /* !__LP64__ */ | |
2010 | if (increment_overflow(&yourtm.tm_year, -1)) | |
2011 | return WRONG; | |
2012 | i = yourtm.tm_year + (1 < yourtm.tm_mon); | |
2013 | yourtm.tm_mday += year_lengths[isleap(i)]; | |
2014 | #endif /* __LP64__ */ | |
2015 | } | |
2016 | while (yourtm.tm_mday > DAYSPERLYEAR) { | |
2017 | #ifdef __LP64__ | |
2018 | il = year + (1 < yourtm.tm_mon); | |
2019 | yourtm.tm_mday -= year_lengths[isleap(il)]; | |
2020 | year++; | |
2021 | #else /* !__LP64__ */ | |
2022 | i = yourtm.tm_year + (1 < yourtm.tm_mon); | |
2023 | yourtm.tm_mday -= year_lengths[isleap(i)]; | |
2024 | if (increment_overflow(&yourtm.tm_year, 1)) | |
2025 | return WRONG; | |
2026 | #endif /* __LP64__ */ | |
2027 | } | |
2028 | for ( ; ; ) { | |
2029 | #ifdef __LP64__ | |
2030 | i = mon_lengths[isleap(year)][yourtm.tm_mon]; | |
2031 | #else /* !__LP64__ */ | |
2032 | i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; | |
2033 | #endif /* __LP64__ */ | |
2034 | if (yourtm.tm_mday <= i) | |
2035 | break; | |
2036 | yourtm.tm_mday -= i; | |
2037 | if (++yourtm.tm_mon >= MONSPERYEAR) { | |
2038 | yourtm.tm_mon = 0; | |
2039 | #ifdef __LP64__ | |
2040 | year++; | |
2041 | #else /* !__LP64__ */ | |
2042 | if (increment_overflow(&yourtm.tm_year, 1)) | |
2043 | return WRONG; | |
2044 | #endif /* __LP64__ */ | |
2045 | } | |
2046 | } | |
2047 | #ifdef __LP64__ | |
2048 | year -= TM_YEAR_BASE; | |
2049 | if (year > INT_MAX || year < INT_MIN) | |
2050 | return WRONG; | |
2051 | yourtm.tm_year = year; | |
2052 | #else /* !__LP64__ */ | |
2053 | if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) | |
2054 | return WRONG; | |
2055 | #endif /* __LP64__ */ | |
2056 | /* Don't go below 1900 for POLA */ | |
2057 | if (yourtm.tm_year < 0) | |
2058 | return WRONG; | |
2059 | if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) | |
2060 | saved_seconds = 0; | |
2061 | else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { | |
2062 | /* | |
2063 | ** We can't set tm_sec to 0, because that might push the | |
2064 | ** time below the minimum representable time. | |
2065 | ** Set tm_sec to 59 instead. | |
2066 | ** This assumes that the minimum representable time is | |
2067 | ** not in the same minute that a leap second was deleted from, | |
2068 | ** which is a safer assumption than using 58 would be. | |
2069 | */ | |
2070 | if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) | |
2071 | return WRONG; | |
2072 | saved_seconds = yourtm.tm_sec; | |
2073 | yourtm.tm_sec = SECSPERMIN - 1; | |
2074 | } else { | |
2075 | saved_seconds = yourtm.tm_sec; | |
2076 | yourtm.tm_sec = 0; | |
2077 | } | |
2078 | /* | |
2079 | ** Divide the search space in half | |
2080 | ** (this works whether time_t is signed or unsigned). | |
2081 | */ | |
34e8f829 A |
2082 | #ifdef __LP64__ |
2083 | /* optimization: see if the value is 31-bit (signed) */ | |
2084 | t = (((time_t) 1) << (TYPE_BIT(int) - 1)) - 1; | |
2085 | bits = ((*funcp)(&t, offset, &mytm) == NULL || tmcomp(&mytm, &yourtm) < 0) ? TYPE_BIT(time_t) - 1 : TYPE_BIT(int) - 1; | |
2086 | #else /* !__LP64__ */ | |
224c7076 | 2087 | bits = TYPE_BIT(time_t) - 1; |
34e8f829 | 2088 | #endif /* __LP64__ */ |
224c7076 | 2089 | /* |
1f2f436a A |
2090 | ** In 64-bit, we now return an error if we cannot represent the |
2091 | ** struct tm value in a time_t. And tmcomp() is fixed to avoid | |
2092 | ** overflow in tm_year. So we only put a cap on bits because time_t | |
2093 | ** can't be larger that 56 bit (when tm_year == INT_MAX). | |
224c7076 A |
2094 | */ |
2095 | if (bits > 56) | |
2096 | bits = 56; | |
2097 | /* | |
2098 | ** If time_t is signed, then 0 is just above the median, | |
2099 | ** assuming two's complement arithmetic. | |
2100 | ** If time_t is unsigned, then (1 << bits) is just above the median. | |
2101 | */ | |
2102 | t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); | |
2103 | for ( ; ; ) { | |
2104 | #ifdef __LP64__ | |
2105 | if ((*funcp)(&t, offset, &mytm) == NULL) { | |
2106 | /* we overflowed, so t is too big */ | |
2107 | dir = 1; | |
2108 | goto skip_tmcomp; | |
2109 | } | |
2110 | #else /* !__LP64__ */ | |
2111 | (*funcp)(&t, offset, &mytm); | |
2112 | #endif /* __LP64__ */ | |
2113 | dir = tmcomp(&mytm, &yourtm); | |
2114 | #ifdef __LP64__ | |
2115 | skip_tmcomp: | |
2116 | #endif /* __LP64__ */ | |
2117 | if (dir != 0) { | |
2118 | if (bits-- < 0) | |
2119 | return WRONG; | |
2120 | if (bits < 0) | |
2121 | --t; /* may be needed if new t is minimal */ | |
2122 | else if (dir > 0) | |
2123 | t -= ((time_t) 1) << bits; | |
2124 | else t += ((time_t) 1) << bits; | |
2125 | continue; | |
2126 | } | |
2127 | sp = (funcp == localsub) ? lclptr : gmtptr; | |
2128 | if (unix03 && sp->typecnt == 1 && yourtm.tm_isdst > 0) | |
2129 | yourtm.tm_isdst = 0; /* alternative time does not apply */ | |
2130 | if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) | |
2131 | break; | |
2132 | /* | |
2133 | ** Right time, wrong type. | |
2134 | ** Hunt for right time, right type. | |
2135 | ** It's okay to guess wrong since the guess | |
2136 | ** gets checked. | |
2137 | */ | |
2138 | #ifdef ALL_STATE | |
2139 | if (sp == NULL) | |
2140 | return WRONG; | |
2141 | #endif /* defined ALL_STATE */ | |
2142 | for (i = sp->typecnt - 1; i >= 0; --i) { | |
2143 | if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) | |
2144 | continue; | |
2145 | for (j = sp->typecnt - 1; j >= 0; --j) { | |
2146 | if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) | |
2147 | continue; | |
2148 | newt = t + sp->ttis[j].tt_gmtoff - | |
2149 | sp->ttis[i].tt_gmtoff; | |
2150 | #ifdef __LP64__ | |
2151 | if ((*funcp)(&newt, offset, &mytm) == NULL) | |
2152 | return WRONG; | |
2153 | #else /* !__LP64__ */ | |
2154 | (*funcp)(&newt, offset, &mytm); | |
2155 | #endif /* __LP64__ */ | |
2156 | if (tmcomp(&mytm, &yourtm) != 0) | |
2157 | continue; | |
2158 | if (mytm.tm_isdst != yourtm.tm_isdst) | |
2159 | continue; | |
2160 | /* | |
2161 | ** We have a match. | |
2162 | */ | |
2163 | t = newt; | |
2164 | goto label; | |
2165 | } | |
2166 | } | |
2167 | return WRONG; | |
2168 | } | |
2169 | label: | |
2170 | newt = t + saved_seconds; | |
2171 | if ((newt < t) != (saved_seconds < 0)) | |
2172 | return WRONG; | |
2173 | t = newt; | |
2174 | #ifdef __LP64__ | |
2175 | if ((*funcp)(&t, offset, tmp) == NULL) | |
2176 | return WRONG; | |
2177 | #else /* !__LP64__ */ | |
2178 | (*funcp)(&t, offset, tmp); | |
2179 | #endif /* __LP64__ */ | |
2180 | *okayp = TRUE; | |
2181 | return t; | |
2182 | } | |
2183 | ||
2184 | static time_t | |
2185 | time2(tmp, funcp, offset, okayp, unix03) | |
2186 | struct tm * const tmp; | |
2187 | #ifdef __LP64__ | |
2188 | struct tm *(* const funcp)(const time_t*, long, struct tm*); | |
2189 | #else /* !__LP64__ */ | |
2190 | void (* const funcp)(const time_t*, long, struct tm*); | |
2191 | #endif /* __LP64__ */ | |
2192 | const long offset; | |
2193 | int * const okayp; | |
2194 | int unix03; | |
2195 | { | |
2196 | time_t t; | |
2197 | ||
2198 | /* | |
2199 | ** First try without normalization of seconds | |
2200 | ** (in case tm_sec contains a value associated with a leap second). | |
2201 | ** If that fails, try with normalization of seconds. | |
2202 | */ | |
2203 | t = time2sub(tmp, funcp, offset, okayp, FALSE, unix03); | |
2204 | return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, unix03); | |
2205 | } | |
2206 | ||
2207 | __private_extern__ time_t | |
2208 | time1(tmp, funcp, offset, unix03) | |
2209 | struct tm * const tmp; | |
2210 | #ifdef __LP64__ | |
2211 | struct tm *(* const funcp)(const time_t *, long, struct tm *); | |
2212 | #else /* !__LP64__ */ | |
2213 | void (* const funcp)(const time_t *, long, struct tm *); | |
2214 | #endif /* __LP64__ */ | |
2215 | const long offset; | |
2216 | int unix03; | |
2217 | { | |
2218 | time_t t; | |
2219 | const struct state * sp; | |
2220 | int samei, otheri; | |
2221 | int sameind, otherind; | |
2222 | int i; | |
2223 | int nseen; | |
2224 | int seen[TZ_MAX_TYPES]; | |
2225 | int types[TZ_MAX_TYPES]; | |
2226 | int okay; | |
2227 | ||
2228 | if (tmp->tm_isdst > 1) | |
2229 | tmp->tm_isdst = 1; | |
2230 | t = time2(tmp, funcp, offset, &okay, unix03); | |
2231 | #ifdef PCTS | |
2232 | /* | |
2233 | ** PCTS code courtesy Grant Sullivan (grant@osf.org). | |
2234 | */ | |
2235 | if (okay) | |
2236 | return t; | |
2237 | if (tmp->tm_isdst < 0) | |
2238 | tmp->tm_isdst = 0; /* reset to std and try again */ | |
2239 | #endif /* defined PCTS */ | |
2240 | #ifndef PCTS | |
2241 | if (okay || tmp->tm_isdst < 0) | |
2242 | return t; | |
2243 | #endif /* !defined PCTS */ | |
2244 | /* | |
2245 | ** We're supposed to assume that somebody took a time of one type | |
2246 | ** and did some math on it that yielded a "struct tm" that's bad. | |
2247 | ** We try to divine the type they started from and adjust to the | |
2248 | ** type they need. | |
2249 | */ | |
2250 | sp = (funcp == localsub) ? lclptr : gmtptr; | |
2251 | #ifdef ALL_STATE | |
2252 | if (sp == NULL) | |
2253 | return WRONG; | |
2254 | #endif /* defined ALL_STATE */ | |
2255 | for (i = 0; i < sp->typecnt; ++i) | |
2256 | seen[i] = FALSE; | |
2257 | nseen = 0; | |
2258 | for (i = sp->timecnt - 1; i >= 0; --i) | |
2259 | if (!seen[sp->types[i]]) { | |
2260 | seen[sp->types[i]] = TRUE; | |
2261 | types[nseen++] = sp->types[i]; | |
2262 | } | |
2263 | for (sameind = 0; sameind < nseen; ++sameind) { | |
2264 | samei = types[sameind]; | |
2265 | if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) | |
2266 | continue; | |
2267 | for (otherind = 0; otherind < nseen; ++otherind) { | |
2268 | otheri = types[otherind]; | |
2269 | if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) | |
2270 | continue; | |
2271 | tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - | |
2272 | sp->ttis[samei].tt_gmtoff; | |
2273 | tmp->tm_isdst = !tmp->tm_isdst; | |
2274 | t = time2(tmp, funcp, offset, &okay, unix03); | |
2275 | if (okay) | |
2276 | return t; | |
2277 | tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - | |
2278 | sp->ttis[samei].tt_gmtoff; | |
2279 | tmp->tm_isdst = !tmp->tm_isdst; | |
2280 | } | |
2281 | } | |
2282 | return WRONG; | |
2283 | } | |
2284 | #else /* BUILDING_VARIANT */ | |
1f2f436a | 2285 | __private_extern__ pthread_rwlock_t lcl_rwlock; |
224c7076 A |
2286 | #endif /* BUILDING_VARIANT */ |
2287 | ||
2288 | time_t | |
2289 | mktime(tmp) | |
2290 | struct tm * const tmp; | |
2291 | { | |
2292 | time_t mktime_return_value; | |
2293 | int serrno = errno; | |
1f2f436a A |
2294 | _RWLOCK_RDLOCK(&lcl_rwlock); |
2295 | tzset_basic(1); | |
224c7076 | 2296 | mktime_return_value = time1(tmp, localsub, 0L, __DARWIN_UNIX03); |
1f2f436a | 2297 | _RWLOCK_UNLOCK(&lcl_rwlock); |
224c7076 A |
2298 | errno = serrno; |
2299 | return(mktime_return_value); | |
2300 | } | |
2301 | ||
2302 | #if !BUILDING_VARIANT | |
2303 | #ifdef STD_INSPIRED | |
2304 | ||
2305 | time_t | |
2306 | timelocal(tmp) | |
2307 | struct tm * const tmp; | |
2308 | { | |
2309 | tmp->tm_isdst = -1; /* in case it wasn't initialized */ | |
2310 | return mktime(tmp); | |
2311 | } | |
2312 | ||
2313 | time_t | |
2314 | timegm(tmp) | |
2315 | struct tm * const tmp; | |
2316 | { | |
2317 | tmp->tm_isdst = 0; | |
2318 | return time1(tmp, gmtsub, 0L, __DARWIN_UNIX03); | |
2319 | } | |
2320 | ||
2321 | time_t | |
2322 | timeoff(tmp, offset) | |
2323 | struct tm * const tmp; | |
2324 | const long offset; | |
2325 | { | |
2326 | tmp->tm_isdst = 0; | |
2327 | return time1(tmp, gmtsub, offset, __DARWIN_UNIX03); | |
2328 | } | |
2329 | ||
2330 | #endif /* defined STD_INSPIRED */ | |
2331 | ||
2332 | #ifdef CMUCS | |
2333 | ||
2334 | /* | |
2335 | ** The following is supplied for compatibility with | |
2336 | ** previous versions of the CMUCS runtime library. | |
2337 | */ | |
2338 | ||
2339 | long | |
2340 | gtime(tmp) | |
2341 | struct tm * const tmp; | |
2342 | { | |
2343 | const time_t t = mktime(tmp); | |
2344 | ||
2345 | if (t == WRONG) | |
2346 | return -1; | |
2347 | return t; | |
2348 | } | |
2349 | ||
2350 | #endif /* defined CMUCS */ | |
2351 | ||
2352 | /* | |
2353 | ** XXX--is the below the right way to conditionalize?? | |
2354 | */ | |
2355 | ||
2356 | #ifdef STD_INSPIRED | |
2357 | ||
2358 | /* | |
2359 | ** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 | |
2360 | ** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which | |
2361 | ** is not the case if we are accounting for leap seconds. | |
2362 | ** So, we provide the following conversion routines for use | |
2363 | ** when exchanging timestamps with POSIX conforming systems. | |
2364 | */ | |
2365 | ||
2366 | static long | |
2367 | leapcorr(timep) | |
2368 | time_t * timep; | |
2369 | { | |
2370 | struct state * sp; | |
2371 | struct lsinfo * lp; | |
2372 | int i; | |
2373 | ||
2374 | sp = lclptr; | |
2375 | i = sp->leapcnt; | |
2376 | while (--i >= 0) { | |
2377 | lp = &sp->lsis[i]; | |
2378 | if (*timep >= lp->ls_trans) | |
2379 | return lp->ls_corr; | |
2380 | } | |
2381 | return 0; | |
2382 | } | |
2383 | ||
2384 | time_t | |
2385 | time2posix(t) | |
2386 | time_t t; | |
2387 | { | |
2388 | tzset(); | |
2389 | return t - leapcorr(&t); | |
2390 | } | |
2391 | ||
2392 | time_t | |
2393 | posix2time(t) | |
2394 | time_t t; | |
2395 | { | |
2396 | time_t x; | |
2397 | time_t y; | |
2398 | ||
2399 | tzset(); | |
2400 | /* | |
2401 | ** For a positive leap second hit, the result | |
2402 | ** is not unique. For a negative leap second | |
2403 | ** hit, the corresponding time doesn't exist, | |
2404 | ** so we return an adjacent second. | |
2405 | */ | |
2406 | x = t + leapcorr(&t); | |
2407 | y = x - leapcorr(&x); | |
2408 | if (y < t) { | |
2409 | do { | |
2410 | x++; | |
2411 | y = x - leapcorr(&x); | |
2412 | } while (y < t); | |
2413 | if (t != y) | |
2414 | return x - 1; | |
2415 | } else if (y > t) { | |
2416 | do { | |
2417 | --x; | |
2418 | y = x - leapcorr(&x); | |
2419 | } while (y > t); | |
2420 | if (t != y) | |
2421 | return x + 1; | |
2422 | } | |
2423 | return x; | |
2424 | } | |
2425 | ||
2426 | #endif /* defined STD_INSPIRED */ | |
2427 | #endif /* !BUILDING_VARIANT */ |