]>
Commit | Line | Data |
---|---|---|
2aa62f2b | 1 | /** @file\r |
2 | Time Zone processing.\r | |
3 | \r | |
4 | Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r | |
5 | This program and the accompanying materials are licensed and made available under\r | |
6 | the terms and conditions of the BSD License that accompanies this distribution.\r | |
7 | The full text of the license may be found at\r | |
8 | http://opensource.org/licenses/bsd-license.php.\r | |
9 | \r | |
10 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
11 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
12 | \r | |
13 | Portions derived from the NIH time zone package file, localtime.c,\r | |
14 | which contains the following notice:\r | |
15 | \r | |
16 | This file is in the public domain, so clarified as of\r | |
17 | 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).\r | |
18 | \r | |
19 | NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp\r | |
20 | **/\r | |
21 | #include <LibConfig.h>\r | |
22 | #include <sys/EfiSysCall.h>\r | |
23 | \r | |
24 | #include <ctype.h>\r | |
25 | #include <fcntl.h>\r | |
26 | #include <stdio.h>\r | |
27 | #include <stdlib.h>\r | |
28 | #include <string.h>\r | |
29 | #include <time.h>\r | |
30 | #include "tzfile.h"\r | |
31 | #include "TimeVals.h"\r | |
32 | \r | |
33 | #ifndef WILDABBR\r | |
34 | /*\r | |
35 | ** Someone might make incorrect use of a time zone abbreviation:\r | |
36 | ** 1. They might reference tzname[0] before calling tzset (explicitly\r | |
37 | ** or implicitly).\r | |
38 | ** 2. They might reference tzname[1] before calling tzset (explicitly\r | |
39 | ** or implicitly).\r | |
40 | ** 3. They might reference tzname[1] after setting to a time zone\r | |
41 | ** in which Daylight Saving Time is never observed.\r | |
42 | ** 4. They might reference tzname[0] after setting to a time zone\r | |
43 | ** in which Standard Time is never observed.\r | |
44 | ** 5. They might reference tm.TM_ZONE after calling offtime.\r | |
45 | ** What's best to do in the above cases is open to debate;\r | |
46 | ** for now, we just set things up so that in any of the five cases\r | |
47 | ** WILDABBR is used. Another possibility: initialize tzname[0] to the\r | |
48 | ** string "tzname[0] used before set", and similarly for the other cases.\r | |
49 | ** And another: initialize tzname[0] to "ERA", with an explanation in the\r | |
50 | ** manual page of what this "time zone abbreviation" means (doing this so\r | |
51 | ** that tzname[0] has the "normal" length of three characters).\r | |
52 | */\r | |
53 | #define WILDABBR " "\r | |
54 | #endif /* !defined WILDABBR */\r | |
55 | \r | |
56 | const char wildabbr[9] = "WILDABBR";\r | |
57 | const char gmt[4] = "GMT";\r | |
58 | \r | |
59 | struct state * lclptr = NULL;\r | |
60 | struct state * gmtptr = NULL;\r | |
61 | \r | |
62 | #ifndef TZ_STRLEN_MAX\r | |
63 | #define TZ_STRLEN_MAX 255\r | |
64 | #endif /* !defined TZ_STRLEN_MAX */\r | |
65 | \r | |
66 | static char lcl_TZname[TZ_STRLEN_MAX + 1];\r | |
67 | static int lcl_is_set = 0;\r | |
68 | //static int gmt_is_set = 0;\r | |
69 | \r | |
70 | char * tzname[2] = {\r | |
71 | (char *)__UNCONST(wildabbr),\r | |
72 | (char *)__UNCONST(wildabbr)\r | |
73 | };\r | |
74 | \r | |
75 | long int timezone = 0;\r | |
76 | int daylight = 0;\r | |
77 | \r | |
78 | #ifndef NO_ZONEINFO_FILES\r | |
79 | /** Get first 4 characters of codep as a 32-bit integer.\r | |
80 | \r | |
81 | The first character of codep becomes the MSB of the resultant integer.\r | |
82 | **/\r | |
83 | static INT32\r | |
84 | detzcode(const char * const codep)\r | |
85 | {\r | |
86 | register INT32 result;\r | |
87 | \r | |
88 | /*\r | |
89 | ** The first character must be sign extended on systems with >32bit\r | |
90 | ** longs. This was solved differently in the master tzcode sources\r | |
91 | ** (the fix first appeared in tzcode95c.tar.gz). But I believe\r | |
92 | ** that this implementation is superior.\r | |
93 | */\r | |
94 | #define SIGN_EXTEND_CHAR(x) ((signed char) x)\r | |
95 | \r | |
96 | result = (SIGN_EXTEND_CHAR(codep[0]) << 24) \\r | |
97 | | (codep[1] & 0xff) << 16 \\r | |
98 | | (codep[2] & 0xff) << 8\r | |
99 | | (codep[3] & 0xff);\r | |
100 | return result;\r | |
101 | }\r | |
102 | #endif /* NO_ZONEINFO_FILES */\r | |
103 | \r | |
104 | static void\r | |
105 | settzname (void)\r | |
106 | {\r | |
107 | register struct state * const sp = lclptr;\r | |
108 | register int i;\r | |
109 | \r | |
110 | tzname[0] = (char *)__UNCONST(wildabbr);\r | |
111 | tzname[1] = (char *)__UNCONST(wildabbr);\r | |
112 | daylight = 0;\r | |
113 | timezone = 0;\r | |
114 | if (sp == NULL) {\r | |
115 | tzname[0] = tzname[1] = (char *)__UNCONST(gmt);\r | |
116 | return;\r | |
117 | }\r | |
118 | for (i = 0; i < sp->typecnt; ++i) {\r | |
119 | register const struct ttinfo * const ttisp = &sp->ttis[i];\r | |
120 | \r | |
121 | tzname[ttisp->tt_isdst] =\r | |
122 | &sp->chars[ttisp->tt_abbrind];\r | |
123 | if (ttisp->tt_isdst)\r | |
124 | daylight = 1;\r | |
125 | if (i == 0 || !ttisp->tt_isdst)\r | |
126 | timezone = -(ttisp->tt_gmtoff);\r | |
127 | }\r | |
128 | /*\r | |
129 | ** And to get the latest zone names into tzname. . .\r | |
130 | */\r | |
131 | for (i = 0; i < sp->timecnt; ++i) {\r | |
132 | register const struct ttinfo * const ttisp =\r | |
133 | &sp->ttis[ sp->types[i] ];\r | |
134 | \r | |
135 | tzname[ttisp->tt_isdst] =\r | |
136 | &sp->chars[ttisp->tt_abbrind];\r | |
137 | }\r | |
138 | }\r | |
139 | \r | |
140 | /*\r | |
141 | ** Given a pointer into a time zone string, scan until a character that is not\r | |
142 | ** a valid character in a zone name is found. Return a pointer to that\r | |
143 | ** character.\r | |
144 | */\r | |
145 | static const char *\r | |
146 | getzname(register const char *strp)\r | |
147 | {\r | |
148 | register char c;\r | |
149 | \r | |
150 | while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&\r | |
151 | c != '+')\r | |
152 | ++strp;\r | |
153 | return strp;\r | |
154 | }\r | |
155 | \r | |
156 | /*\r | |
157 | ** Given a pointer into a time zone string, extract a number from that string.\r | |
158 | ** Check that the number is within a specified range; if it is not, return\r | |
159 | ** NULL.\r | |
160 | ** Otherwise, return a pointer to the first character not part of the number.\r | |
161 | */\r | |
162 | static const char *\r | |
163 | getnum(\r | |
164 | register const char *strp,\r | |
165 | int * const nump,\r | |
166 | const int min,\r | |
167 | const int max\r | |
168 | )\r | |
169 | {\r | |
170 | register char c;\r | |
171 | register int num;\r | |
172 | \r | |
173 | if (strp == NULL || !is_digit(c = *strp))\r | |
174 | return NULL;\r | |
175 | num = 0;\r | |
176 | do {\r | |
177 | num = num * 10 + (c - '0');\r | |
178 | if (num > max)\r | |
179 | return NULL; /* illegal value */\r | |
180 | c = *++strp;\r | |
181 | } while (is_digit(c));\r | |
182 | if (num < min)\r | |
183 | return NULL; /* illegal value */\r | |
184 | *nump = num;\r | |
185 | return strp;\r | |
186 | }\r | |
187 | \r | |
188 | /*\r | |
189 | ** Given a pointer into a time zone string, extract a number of seconds,\r | |
190 | ** in hh[:mm[:ss]] form, from the string.\r | |
191 | ** If any error occurs, return NULL.\r | |
192 | ** Otherwise, return a pointer to the first character not part of the number\r | |
193 | ** of seconds.\r | |
194 | */\r | |
195 | static const char *\r | |
196 | getsecs(\r | |
197 | register const char *strp,\r | |
198 | LONG32 * const secsp\r | |
199 | )\r | |
200 | {\r | |
201 | int num;\r | |
202 | \r | |
203 | /*\r | |
204 | ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like\r | |
205 | ** "M10.4.6/26", which does not conform to Posix,\r | |
206 | ** but which specifies the equivalent of\r | |
207 | ** ``02:00 on the first Sunday on or after 23 Oct''.\r | |
208 | */\r | |
209 | strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);\r | |
210 | if (strp == NULL)\r | |
211 | return NULL;\r | |
212 | *secsp = (long)(num * SECSPERHOUR);\r | |
213 | if (*strp == ':') {\r | |
214 | ++strp;\r | |
215 | strp = getnum(strp, &num, 0, MINSPERHOUR - 1);\r | |
216 | if (strp == NULL)\r | |
217 | return NULL;\r | |
218 | *secsp += num * SECSPERMIN;\r | |
219 | if (*strp == ':') {\r | |
220 | ++strp;\r | |
221 | /* `SECSPERMIN' allows for leap seconds. */\r | |
222 | strp = getnum(strp, &num, 0, SECSPERMIN);\r | |
223 | if (strp == NULL)\r | |
224 | return NULL;\r | |
225 | *secsp += num;\r | |
226 | }\r | |
227 | }\r | |
228 | return strp;\r | |
229 | }\r | |
230 | \r | |
231 | /*\r | |
232 | ** Given a pointer into a time zone string, extract an offset, in\r | |
233 | ** [+-]hh[:mm[:ss]] form, from the string.\r | |
234 | ** If any error occurs, return NULL.\r | |
235 | ** Otherwise, return a pointer to the first character not part of the time.\r | |
236 | */\r | |
237 | static const char *\r | |
238 | getoffset(\r | |
239 | register const char *strp,\r | |
240 | LONG32 * const offsetp\r | |
241 | )\r | |
242 | {\r | |
243 | register int neg = 0;\r | |
244 | \r | |
245 | if (*strp == '-') {\r | |
246 | neg = 1;\r | |
247 | ++strp;\r | |
248 | } else if (*strp == '+')\r | |
249 | ++strp;\r | |
250 | strp = getsecs(strp, offsetp);\r | |
251 | if (strp == NULL)\r | |
252 | return NULL; /* illegal time */\r | |
253 | if (neg)\r | |
254 | *offsetp = -*offsetp;\r | |
255 | return strp;\r | |
256 | }\r | |
257 | \r | |
258 | /*\r | |
259 | ** Given a pointer into a time zone string, extract a rule in the form\r | |
260 | ** date[/time]. See POSIX section 8 for the format of "date" and "time".\r | |
261 | ** If a valid rule is not found, return NULL.\r | |
262 | ** Otherwise, return a pointer to the first character not part of the rule.\r | |
263 | */\r | |
264 | static const char *\r | |
265 | getrule(\r | |
266 | const char *strp,\r | |
267 | register struct rule * const rulep\r | |
268 | )\r | |
269 | {\r | |
270 | if (*strp == 'J') {\r | |
271 | /*\r | |
272 | ** Julian day.\r | |
273 | */\r | |
274 | rulep->r_type = JULIAN_DAY;\r | |
275 | ++strp;\r | |
276 | strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);\r | |
277 | } else if (*strp == 'M') {\r | |
278 | /*\r | |
279 | ** Month, week, day.\r | |
280 | */\r | |
281 | rulep->r_type = MONTH_NTH_DAY_OF_WEEK;\r | |
282 | ++strp;\r | |
283 | strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);\r | |
284 | if (strp == NULL)\r | |
285 | return NULL;\r | |
286 | if (*strp++ != '.')\r | |
287 | return NULL;\r | |
288 | strp = getnum(strp, &rulep->r_week, 1, 5);\r | |
289 | if (strp == NULL)\r | |
290 | return NULL;\r | |
291 | if (*strp++ != '.')\r | |
292 | return NULL;\r | |
293 | strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);\r | |
294 | } else if (is_digit(*strp)) {\r | |
295 | /*\r | |
296 | ** Day of year.\r | |
297 | */\r | |
298 | rulep->r_type = DAY_OF_YEAR;\r | |
299 | strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);\r | |
300 | } else return NULL; /* invalid format */\r | |
301 | if (strp == NULL)\r | |
302 | return NULL;\r | |
303 | if (*strp == '/') {\r | |
304 | /*\r | |
305 | ** Time specified.\r | |
306 | */\r | |
307 | ++strp;\r | |
308 | strp = getsecs(strp, &rulep->r_time);\r | |
309 | } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */\r | |
310 | return strp;\r | |
311 | }\r | |
312 | \r | |
313 | static int\r | |
314 | tzload(register const char *name, register struct state * const sp)\r | |
315 | {\r | |
316 | #ifndef NO_ZONEINFO_FILES\r | |
317 | register const char * p;\r | |
318 | register int i;\r | |
319 | register int fid;\r | |
320 | \r | |
321 | if (name == NULL && (name = TZDEFAULT) == NULL)\r | |
322 | return -1;\r | |
323 | \r | |
324 | {\r | |
325 | register int doaccess;\r | |
326 | /*\r | |
327 | ** Section 4.9.1 of the C standard says that\r | |
328 | ** "FILENAME_MAX expands to an integral constant expression\r | |
329 | ** that is the size needed for an array of char large enough\r | |
330 | ** to hold the longest file name string that the implementation\r | |
331 | ** guarantees can be opened."\r | |
332 | */\r | |
333 | char fullname[FILENAME_MAX + 1];\r | |
334 | \r | |
335 | if (name[0] == ':')\r | |
336 | ++name;\r | |
337 | doaccess = name[0] == '/';\r | |
338 | if (!doaccess) {\r | |
339 | if ((p = TZDIR) == NULL)\r | |
340 | return -1;\r | |
341 | if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)\r | |
342 | return -1;\r | |
343 | (void) strcpy(fullname, p); /* XXX strcpy is safe */\r | |
344 | (void) strcat(fullname, "/"); /* XXX strcat is safe */\r | |
345 | (void) strcat(fullname, name); /* XXX strcat is safe */\r | |
346 | /*\r | |
347 | ** Set doaccess if '.' (as in "../") shows up in name.\r | |
348 | */\r | |
349 | if (strchr(name, '.') != NULL)\r | |
350 | doaccess = TRUE;\r | |
351 | name = fullname;\r | |
352 | }\r | |
353 | if (doaccess && access(name, R_OK) != 0)\r | |
354 | return -1;\r | |
355 | /*\r | |
356 | * XXX potential security problem here if user of a set-id\r | |
357 | * program has set TZ (which is passed in as name) here,\r | |
358 | * and uses a race condition trick to defeat the access(2)\r | |
359 | * above.\r | |
360 | */\r | |
361 | if ((fid = open(name, OPEN_MODE)) == -1)\r | |
362 | return -1;\r | |
363 | }\r | |
364 | {\r | |
365 | struct tzhead * tzhp;\r | |
366 | union {\r | |
367 | struct tzhead tzhead;\r | |
368 | char buf[sizeof *sp + sizeof *tzhp];\r | |
369 | } u;\r | |
370 | int ttisstdcnt;\r | |
371 | int ttisgmtcnt;\r | |
372 | \r | |
373 | i = read(fid, u.buf, sizeof u.buf);\r | |
374 | if (close(fid) != 0)\r | |
375 | return -1;\r | |
376 | ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);\r | |
377 | ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);\r | |
378 | sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);\r | |
379 | sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);\r | |
380 | sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);\r | |
381 | sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);\r | |
382 | p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;\r | |
383 | if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||\r | |
384 | sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||\r | |
385 | sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||\r | |
386 | sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||\r | |
387 | (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||\r | |
388 | (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))\r | |
389 | return -1;\r | |
390 | if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */\r | |
391 | sp->timecnt + /* types */\r | |
392 | sp->typecnt * (4 + 2) + /* ttinfos */\r | |
393 | sp->charcnt + /* chars */\r | |
394 | sp->leapcnt * (4 + 4) + /* lsinfos */\r | |
395 | ttisstdcnt + /* ttisstds */\r | |
396 | ttisgmtcnt) /* ttisgmts */\r | |
397 | return -1;\r | |
398 | for (i = 0; i < sp->timecnt; ++i) {\r | |
399 | sp->ats[i] = detzcode(p);\r | |
400 | p += 4;\r | |
401 | }\r | |
402 | for (i = 0; i < sp->timecnt; ++i) {\r | |
403 | sp->types[i] = (unsigned char) *p++;\r | |
404 | if (sp->types[i] >= sp->typecnt)\r | |
405 | return -1;\r | |
406 | }\r | |
407 | for (i = 0; i < sp->typecnt; ++i) {\r | |
408 | register struct ttinfo * ttisp;\r | |
409 | \r | |
410 | ttisp = &sp->ttis[i];\r | |
411 | ttisp->tt_gmtoff = detzcode(p);\r | |
412 | p += 4;\r | |
413 | ttisp->tt_isdst = (unsigned char) *p++;\r | |
414 | if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)\r | |
415 | return -1;\r | |
416 | ttisp->tt_abbrind = (unsigned char) *p++;\r | |
417 | if (ttisp->tt_abbrind < 0 ||\r | |
418 | ttisp->tt_abbrind > sp->charcnt)\r | |
419 | return -1;\r | |
420 | }\r | |
421 | for (i = 0; i < sp->charcnt; ++i)\r | |
422 | sp->chars[i] = *p++;\r | |
423 | sp->chars[i] = '\0'; /* ensure '\0' at end */\r | |
424 | for (i = 0; i < sp->leapcnt; ++i) {\r | |
425 | register struct lsinfo * lsisp;\r | |
426 | \r | |
427 | lsisp = &sp->lsis[i];\r | |
428 | lsisp->ls_trans = detzcode(p);\r | |
429 | p += 4;\r | |
430 | lsisp->ls_corr = detzcode(p);\r | |
431 | p += 4;\r | |
432 | }\r | |
433 | for (i = 0; i < sp->typecnt; ++i) {\r | |
434 | register struct ttinfo * ttisp;\r | |
435 | \r | |
436 | ttisp = &sp->ttis[i];\r | |
437 | if (ttisstdcnt == 0)\r | |
438 | ttisp->tt_ttisstd = FALSE;\r | |
439 | else {\r | |
440 | ttisp->tt_ttisstd = *p++;\r | |
441 | if (ttisp->tt_ttisstd != TRUE &&\r | |
442 | ttisp->tt_ttisstd != FALSE)\r | |
443 | return -1;\r | |
444 | }\r | |
445 | }\r | |
446 | for (i = 0; i < sp->typecnt; ++i) {\r | |
447 | register struct ttinfo * ttisp;\r | |
448 | \r | |
449 | ttisp = &sp->ttis[i];\r | |
450 | if (ttisgmtcnt == 0)\r | |
451 | ttisp->tt_ttisgmt = FALSE;\r | |
452 | else {\r | |
453 | ttisp->tt_ttisgmt = *p++;\r | |
454 | if (ttisp->tt_ttisgmt != TRUE &&\r | |
455 | ttisp->tt_ttisgmt != FALSE)\r | |
456 | return -1;\r | |
457 | }\r | |
458 | }\r | |
459 | }\r | |
460 | return 0;\r | |
461 | #else /* ! NO_ZONEINFO_FILES */\r | |
462 | return -1;\r | |
463 | #endif\r | |
464 | }\r | |
465 | \r | |
466 | /*\r | |
467 | ** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the\r | |
468 | ** year, a rule, and the offset from UTC at the time that rule takes effect,\r | |
469 | ** calculate the Epoch-relative time that rule takes effect.\r | |
470 | */\r | |
471 | static\r | |
472 | time_t\r | |
473 | transtime(\r | |
474 | const time_t janfirst,\r | |
475 | const int year,\r | |
476 | const struct rule * const rulep,\r | |
477 | const LONG32 offset\r | |
478 | )\r | |
479 | {\r | |
480 | register int leapyear;\r | |
481 | register time_t value;\r | |
482 | register int i;\r | |
483 | int d, m1, yy0, yy1, yy2, dow;\r | |
484 | \r | |
485 | INITIALIZE(value);\r | |
486 | leapyear = isleap(year);\r | |
487 | switch (rulep->r_type) {\r | |
488 | \r | |
489 | case JULIAN_DAY:\r | |
490 | /*\r | |
491 | ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap\r | |
492 | ** years.\r | |
493 | ** In non-leap years, or if the day number is 59 or less, just\r | |
494 | ** add SECSPERDAY times the day number-1 to the time of\r | |
495 | ** January 1, midnight, to get the day.\r | |
496 | */\r | |
497 | value = janfirst + (rulep->r_day - 1) * SECSPERDAY;\r | |
498 | if (leapyear && rulep->r_day >= 60)\r | |
499 | value += SECSPERDAY;\r | |
500 | break;\r | |
501 | \r | |
502 | case DAY_OF_YEAR:\r | |
503 | /*\r | |
504 | ** n - day of year.\r | |
505 | ** Just add SECSPERDAY times the day number to the time of\r | |
506 | ** January 1, midnight, to get the day.\r | |
507 | */\r | |
508 | value = janfirst + rulep->r_day * SECSPERDAY;\r | |
509 | break;\r | |
510 | \r | |
511 | case MONTH_NTH_DAY_OF_WEEK:\r | |
512 | /*\r | |
513 | ** Mm.n.d - nth "dth day" of month m.\r | |
514 | */\r | |
515 | value = janfirst;\r | |
516 | for (i = 0; i < rulep->r_mon - 1; ++i)\r | |
517 | value += mon_lengths[leapyear][i] * SECSPERDAY;\r | |
518 | \r | |
519 | /*\r | |
520 | ** Use Zeller's Congruence to get day-of-week of first day of\r | |
521 | ** month.\r | |
522 | */\r | |
523 | m1 = (rulep->r_mon + 9) % 12 + 1;\r | |
524 | yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;\r | |
525 | yy1 = yy0 / 100;\r | |
526 | yy2 = yy0 % 100;\r | |
527 | dow = ((26 * m1 - 2) / 10 +\r | |
528 | 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;\r | |
529 | if (dow < 0)\r | |
530 | dow += DAYSPERWEEK;\r | |
531 | \r | |
532 | /*\r | |
533 | ** "dow" is the day-of-week of the first day of the month. Get\r | |
534 | ** the day-of-month (zero-origin) of the first "dow" day of the\r | |
535 | ** month.\r | |
536 | */\r | |
537 | d = rulep->r_day - dow;\r | |
538 | if (d < 0)\r | |
539 | d += DAYSPERWEEK;\r | |
540 | for (i = 1; i < rulep->r_week; ++i) {\r | |
541 | if (d + DAYSPERWEEK >=\r | |
542 | mon_lengths[leapyear][rulep->r_mon - 1])\r | |
543 | break;\r | |
544 | d += DAYSPERWEEK;\r | |
545 | }\r | |
546 | \r | |
547 | /*\r | |
548 | ** "d" is the day-of-month (zero-origin) of the day we want.\r | |
549 | */\r | |
550 | value += d * SECSPERDAY;\r | |
551 | break;\r | |
552 | }\r | |
553 | \r | |
554 | /*\r | |
555 | ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in\r | |
556 | ** question. To get the Epoch-relative time of the specified local\r | |
557 | ** time on that day, add the transition time and the current offset\r | |
558 | ** from UTC.\r | |
559 | */\r | |
560 | return value + rulep->r_time + offset;\r | |
561 | }\r | |
562 | \r | |
563 | /*\r | |
564 | ** Given a POSIX section 8-style TZ string, fill in the rule tables as\r | |
565 | ** appropriate.\r | |
566 | */\r | |
567 | static int\r | |
568 | tzparse(\r | |
569 | const char * name,\r | |
570 | struct state * const sp,\r | |
571 | const int lastditch\r | |
572 | )\r | |
573 | {\r | |
574 | const char *stdname;\r | |
575 | const char *dstname;\r | |
576 | size_t stdlen;\r | |
577 | size_t dstlen;\r | |
578 | LONG32 stdoffset;\r | |
579 | LONG32 dstoffset;\r | |
580 | time_t *atp;\r | |
581 | unsigned char *typep;\r | |
582 | char *cp;\r | |
583 | int load_result;\r | |
584 | \r | |
585 | dstname = NULL;\r | |
586 | stdname = name;\r | |
587 | if (lastditch) {\r | |
588 | stdlen = strlen(name); /* length of standard zone name */\r | |
589 | name += stdlen;\r | |
590 | if (stdlen >= sizeof sp->chars)\r | |
591 | stdlen = (sizeof sp->chars) - 1;\r | |
592 | stdoffset = 0;\r | |
593 | } else {\r | |
594 | name = getzname(name);\r | |
595 | stdlen = name - stdname;\r | |
596 | if (stdlen < 3)\r | |
597 | return -1;\r | |
598 | if (*name == '\0')\r | |
599 | return -1;\r | |
600 | name = getoffset(name, &stdoffset);\r | |
601 | if (name == NULL)\r | |
602 | return -1;\r | |
603 | }\r | |
604 | load_result = tzload(TZDEFRULES, sp);\r | |
605 | if (load_result != 0)\r | |
606 | sp->leapcnt = 0; /* so, we're off a little */\r | |
607 | if (*name != '\0') {\r | |
608 | dstname = name;\r | |
609 | name = getzname(name);\r | |
610 | dstlen = name - dstname; /* length of DST zone name */\r | |
611 | if (dstlen < 3)\r | |
612 | return -1;\r | |
613 | if (*name != '\0' && *name != ',' && *name != ';') {\r | |
614 | name = getoffset(name, &dstoffset);\r | |
615 | if (name == NULL)\r | |
616 | return -1;\r | |
617 | } else dstoffset = stdoffset - SECSPERHOUR;\r | |
618 | if (*name == '\0' && load_result != 0)\r | |
619 | name = TZDEFRULESTRING;\r | |
620 | if (*name == ',' || *name == ';') {\r | |
621 | struct rule start;\r | |
622 | struct rule end;\r | |
623 | register int year;\r | |
624 | register time_t janfirst;\r | |
625 | time_t starttime;\r | |
626 | time_t endtime;\r | |
627 | \r | |
628 | ++name;\r | |
629 | if ((name = getrule(name, &start)) == NULL)\r | |
630 | return -1;\r | |
631 | if (*name++ != ',')\r | |
632 | return -1;\r | |
633 | if ((name = getrule(name, &end)) == NULL)\r | |
634 | return -1;\r | |
635 | if (*name != '\0')\r | |
636 | return -1;\r | |
637 | sp->typecnt = 2; /* standard time and DST */\r | |
638 | /*\r | |
639 | ** Two transitions per year, from EPOCH_YEAR to 2037.\r | |
640 | */\r | |
641 | sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);\r | |
642 | if (sp->timecnt > TZ_MAX_TIMES)\r | |
643 | return -1;\r | |
644 | sp->ttis[0].tt_gmtoff = -dstoffset;\r | |
645 | sp->ttis[0].tt_isdst = 1;\r | |
646 | sp->ttis[0].tt_abbrind = (int)stdlen + 1;\r | |
647 | sp->ttis[1].tt_gmtoff = -stdoffset;\r | |
648 | sp->ttis[1].tt_isdst = 0;\r | |
649 | sp->ttis[1].tt_abbrind = 0;\r | |
650 | atp = sp->ats;\r | |
651 | typep = sp->types;\r | |
652 | janfirst = 0;\r | |
653 | for (year = EPOCH_YEAR; year <= 2037; ++year) {\r | |
654 | starttime = transtime(janfirst, year, &start,\r | |
655 | stdoffset);\r | |
656 | endtime = transtime(janfirst, year, &end,\r | |
657 | dstoffset);\r | |
658 | if (starttime > endtime) {\r | |
659 | *atp++ = endtime;\r | |
660 | *typep++ = 1; /* DST ends */\r | |
661 | *atp++ = starttime;\r | |
662 | *typep++ = 0; /* DST begins */\r | |
663 | } else {\r | |
664 | *atp++ = starttime;\r | |
665 | *typep++ = 0; /* DST begins */\r | |
666 | *atp++ = endtime;\r | |
667 | *typep++ = 1; /* DST ends */\r | |
668 | }\r | |
669 | janfirst += year_lengths[isleap(year)] *\r | |
670 | SECSPERDAY;\r | |
671 | }\r | |
672 | } else {\r | |
673 | register LONG32 theirstdoffset;\r | |
674 | register LONG32 theiroffset;\r | |
675 | register int i;\r | |
676 | register int j;\r | |
677 | \r | |
678 | if (*name != '\0')\r | |
679 | return -1;\r | |
680 | /*\r | |
681 | ** Initial values of theirstdoffset\r | |
682 | */\r | |
683 | theirstdoffset = 0;\r | |
684 | for (i = 0; i < sp->timecnt; ++i) {\r | |
685 | j = sp->types[i];\r | |
686 | if (!sp->ttis[j].tt_isdst) {\r | |
687 | theirstdoffset =\r | |
688 | -sp->ttis[j].tt_gmtoff;\r | |
689 | break;\r | |
690 | }\r | |
691 | }\r | |
692 | /*\r | |
693 | ** Initially we're assumed to be in standard time.\r | |
694 | */\r | |
695 | theiroffset = theirstdoffset;\r | |
696 | /*\r | |
697 | ** Now juggle transition times and types\r | |
698 | ** tracking offsets as you do.\r | |
699 | */\r | |
700 | for (i = 0; i < sp->timecnt; ++i) {\r | |
701 | j = sp->types[i];\r | |
702 | sp->types[i] = (unsigned char)sp->ttis[j].tt_isdst;\r | |
703 | if (sp->ttis[j].tt_ttisgmt) {\r | |
704 | /* No adjustment to transition time */\r | |
705 | } else {\r | |
706 | /*\r | |
707 | ** If summer time is in effect, and the\r | |
708 | ** transition time was not specified as\r | |
709 | ** standard time, add the summer time\r | |
710 | ** offset to the transition time;\r | |
711 | ** otherwise, add the standard time\r | |
712 | ** offset to the transition time.\r | |
713 | */\r | |
714 | /*\r | |
715 | ** Transitions from DST to DDST\r | |
716 | ** will effectively disappear since\r | |
717 | ** POSIX provides for only one DST\r | |
718 | ** offset.\r | |
719 | */\r | |
720 | sp->ats[i] += stdoffset -\r | |
721 | theirstdoffset;\r | |
722 | }\r | |
723 | theiroffset = -sp->ttis[j].tt_gmtoff;\r | |
724 | if (!sp->ttis[j].tt_isdst)\r | |
725 | theirstdoffset = theiroffset;\r | |
726 | }\r | |
727 | /*\r | |
728 | ** Finally, fill in ttis.\r | |
729 | ** ttisstd and ttisgmt need not be handled.\r | |
730 | */\r | |
731 | sp->ttis[0].tt_gmtoff = -stdoffset;\r | |
732 | sp->ttis[0].tt_isdst = FALSE;\r | |
733 | sp->ttis[0].tt_abbrind = 0;\r | |
734 | sp->ttis[1].tt_gmtoff = -dstoffset;\r | |
735 | sp->ttis[1].tt_isdst = TRUE;\r | |
736 | sp->ttis[1].tt_abbrind = (int)stdlen + 1;\r | |
737 | sp->typecnt = 2;\r | |
738 | }\r | |
739 | } else {\r | |
740 | dstlen = 0;\r | |
741 | sp->typecnt = 1; /* only standard time */\r | |
742 | sp->timecnt = 0;\r | |
743 | sp->ttis[0].tt_gmtoff = -stdoffset;\r | |
744 | sp->ttis[0].tt_isdst = 0;\r | |
745 | sp->ttis[0].tt_abbrind = 0;\r | |
746 | }\r | |
747 | sp->charcnt = (int)stdlen + 1;\r | |
748 | if (dstlen != 0)\r | |
749 | sp->charcnt += (int)dstlen + 1;\r | |
750 | if ((size_t) sp->charcnt > sizeof sp->chars)\r | |
751 | return -1;\r | |
752 | cp = sp->chars;\r | |
753 | (void) strncpy(cp, stdname, stdlen);\r | |
754 | cp += stdlen;\r | |
755 | *cp++ = '\0';\r | |
756 | if (dstlen != 0) {\r | |
757 | (void) strncpy(cp, dstname, dstlen);\r | |
758 | *(cp + dstlen) = '\0';\r | |
759 | }\r | |
760 | return 0;\r | |
761 | }\r | |
762 | \r | |
763 | void\r | |
764 | EFIAPI\r | |
765 | gmtload(struct state * const sp)\r | |
766 | {\r | |
767 | if (tzload(gmt, sp) != 0)\r | |
768 | (void) tzparse(gmt, sp, TRUE);\r | |
769 | }\r | |
770 | \r | |
771 | static void\r | |
772 | tzsetwall(void)\r | |
773 | {\r | |
774 | if (lcl_is_set < 0)\r | |
775 | return;\r | |
776 | lcl_is_set = -1;\r | |
777 | \r | |
778 | if (lclptr == NULL) {\r | |
779 | lclptr = (struct state *) malloc(sizeof *lclptr);\r | |
780 | if (lclptr == NULL) {\r | |
781 | settzname(); /* all we can do */\r | |
782 | return;\r | |
783 | }\r | |
784 | }\r | |
785 | if (tzload((char *) NULL, lclptr) != 0)\r | |
786 | gmtload(lclptr);\r | |
787 | settzname();\r | |
788 | }\r | |
789 | \r | |
790 | void\r | |
791 | EFIAPI\r | |
792 | tzset(void)\r | |
793 | {\r | |
794 | register const char * name;\r | |
795 | \r | |
796 | name = getenv("TZ");\r | |
797 | if (name == NULL) {\r | |
798 | tzsetwall();\r | |
799 | return;\r | |
800 | }\r | |
801 | \r | |
802 | if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)\r | |
803 | return;\r | |
804 | lcl_is_set = strlen(name) < sizeof lcl_TZname;\r | |
805 | if (lcl_is_set)\r | |
806 | (void)strncpyX(lcl_TZname, name, sizeof(lcl_TZname));\r | |
807 | \r | |
808 | if (lclptr == NULL) {\r | |
809 | lclptr = (struct state *) malloc(sizeof *lclptr);\r | |
810 | if (lclptr == NULL) {\r | |
811 | settzname(); /* all we can do */\r | |
812 | return;\r | |
813 | }\r | |
814 | }\r | |
815 | if (*name == '\0') {\r | |
816 | /*\r | |
817 | ** User wants it fast rather than right.\r | |
818 | */\r | |
819 | lclptr->leapcnt = 0; /* so, we're off a little */\r | |
820 | lclptr->timecnt = 0;\r | |
821 | lclptr->typecnt = 0;\r | |
822 | lclptr->ttis[0].tt_isdst = 0;\r | |
823 | lclptr->ttis[0].tt_gmtoff = 0;\r | |
824 | lclptr->ttis[0].tt_abbrind = 0;\r | |
825 | (void)strncpyX(lclptr->chars, gmt, sizeof(lclptr->chars));\r | |
826 | } else if (tzload(name, lclptr) != 0)\r | |
827 | if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)\r | |
828 | (void) gmtload(lclptr);\r | |
829 | settzname();\r | |
830 | }\r |