2003-12-30 Paul Eggert <eggert@twinsun.com>
[kopensolaris-gnu/glibc.git] / time / tzfile.c
1 /* Copyright (C) 1991-1993, 1995-2001, 2003 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <assert.h>
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdio_ext.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27
28 #define NOID
29 #include <timezone/tzfile.h>
30
31 int __use_tzfile;
32
33 struct ttinfo
34   {
35     long int offset;            /* Seconds east of GMT.  */
36     unsigned char isdst;        /* Used to set tm_isdst.  */
37     unsigned char idx;          /* Index into `zone_names'.  */
38     unsigned char isstd;        /* Transition times are in standard time.  */
39     unsigned char isgmt;        /* Transition times are in GMT.  */
40   };
41
42 struct leap
43   {
44     time_t transition;          /* Time the transition takes effect.  */
45     long int change;            /* Seconds of correction to apply.  */
46   };
47
48 static struct ttinfo *find_transition (time_t timer) internal_function;
49 static void compute_tzname_max (size_t) internal_function;
50
51 static size_t num_transitions;
52 libc_freeres_ptr (static time_t *transitions);
53 static unsigned char *type_idxs;
54 static size_t num_types;
55 static struct ttinfo *types;
56 static char *zone_names;
57 static long int rule_stdoff;
58 static long int rule_dstoff;
59 static size_t num_leaps;
60 static struct leap *leaps;
61
62 #include <endian.h>
63 #include <byteswap.h>
64
65 /* Decode the four bytes at PTR as a signed integer in network byte order.  */
66 static inline int
67 __attribute ((always_inline))
68 decode (const void *ptr)
69 {
70   if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
71     return *(const int *) ptr;
72   else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4)
73     return bswap_32 (*(const int *) ptr);
74   else
75     {
76       const unsigned char *p = ptr;
77       int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
78
79       result = (result << 8) | *p++;
80       result = (result << 8) | *p++;
81       result = (result << 8) | *p++;
82       result = (result << 8) | *p++;
83
84       return result;
85     }
86 }
87
88 void
89 __tzfile_read (const char *file, size_t extra, char **extrap)
90 {
91   static const char default_tzdir[] = TZDIR;
92   size_t num_isstd, num_isgmt;
93   register FILE *f;
94   struct tzhead tzhead;
95   size_t chars;
96   register size_t i;
97   size_t total_size;
98   size_t types_idx;
99   size_t leaps_idx;
100
101   __use_tzfile = 0;
102
103   if (transitions != NULL)
104     free ((void *) transitions);
105   transitions = NULL;
106
107   if (file == NULL)
108     /* No user specification; use the site-wide default.  */
109     file = TZDEFAULT;
110   else if (*file == '\0')
111     /* User specified the empty string; use UTC with no leap seconds.  */
112     return;
113   else
114     {
115       /* We must not allow to read an arbitrary file in a setuid
116          program.  So we fail for any file which is not in the
117          directory hierachy starting at TZDIR
118          and which is not the system wide default TZDEFAULT.  */
119       if (__libc_enable_secure
120           && ((*file == '/'
121                && memcmp (file, TZDEFAULT, sizeof TZDEFAULT)
122                && memcmp (file, default_tzdir, sizeof (default_tzdir) - 1))
123               || strstr (file, "../") != NULL))
124         /* This test is certainly a bit too restrictive but it should
125            catch all critical cases.  */
126         return;
127     }
128
129   if (*file != '/')
130     {
131       const char *tzdir;
132       unsigned int len, tzdir_len;
133       char *new, *tmp;
134
135       tzdir = getenv ("TZDIR");
136       if (tzdir == NULL || *tzdir == '\0')
137         {
138           tzdir = default_tzdir;
139           tzdir_len = sizeof (default_tzdir) - 1;
140         }
141       else
142         tzdir_len = strlen (tzdir);
143       len = strlen (file) + 1;
144       new = (char *) __alloca (tzdir_len + 1 + len);
145       tmp = __mempcpy (new, tzdir, tzdir_len);
146       *tmp++ = '/';
147       memcpy (tmp, file, len);
148       file = new;
149     }
150
151   /* Note the file is opened with cancellation in the I/O functions
152      disabled.  */
153   f = fopen (file, "rc");
154   if (f == NULL)
155     return;
156
157   /* No threads reading this stream.  */
158   __fsetlocking (f, FSETLOCKING_BYCALLER);
159
160   if (__builtin_expect (fread_unlocked ((void *) &tzhead, sizeof (tzhead),
161                                         1, f) != 1, 0))
162     goto lose;
163
164   num_transitions = (size_t) decode (tzhead.tzh_timecnt);
165   num_types = (size_t) decode (tzhead.tzh_typecnt);
166   chars = (size_t) decode (tzhead.tzh_charcnt);
167   num_leaps = (size_t) decode (tzhead.tzh_leapcnt);
168   num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
169   num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
170
171   total_size = num_transitions * (sizeof (time_t) + 1);
172   total_size = ((total_size + __alignof__ (struct ttinfo) - 1)
173                 & ~(__alignof__ (struct ttinfo) - 1));
174   types_idx = total_size;
175   total_size += num_types * sizeof (struct ttinfo) + chars;
176   total_size = ((total_size + __alignof__ (struct leap) - 1)
177                 & ~(__alignof__ (struct leap) - 1));
178   leaps_idx = total_size;
179   total_size += num_leaps * sizeof (struct leap);
180   /* This is for the extra memory required by the caller.  */
181   total_size += extra;
182
183   transitions = (time_t *) malloc (total_size);
184   if (transitions == NULL)
185     goto lose;
186
187   type_idxs = (unsigned char *) transitions + (num_transitions
188                                                * sizeof (time_t));
189   types = (struct ttinfo *) ((char *) transitions + types_idx);
190   zone_names = (char *) types + num_types * sizeof (struct ttinfo);
191   leaps = (struct leap *) ((char *) transitions + leaps_idx);
192   if (extra > 0)
193     *extrap = (char *) &leaps[num_leaps];
194
195   if (sizeof (time_t) < 4)
196     abort ();
197
198   if (sizeof (time_t) == 4)
199     {
200       if (__builtin_expect (fread_unlocked (transitions, 1,
201                                             (4 + 1) * num_transitions, f)
202                             != (4 + 1) * num_transitions, 0))
203         goto lose;
204     }
205   else
206     {
207       if (__builtin_expect (fread_unlocked (transitions, 4, num_transitions, f)
208                             != num_transitions, 0)
209           || __builtin_expect (fread_unlocked (type_idxs, 1, num_transitions,
210                                                f) != num_transitions, 0))
211         goto lose;
212     }
213
214   /* Check for bogus indices in the data file, so we can hereafter
215      safely use type_idxs[T] as indices into `types' and never crash.  */
216   for (i = 0; i < num_transitions; ++i)
217     if (__builtin_expect (type_idxs[i] >= num_types, 0))
218       goto lose;
219
220   if (BYTE_ORDER != BIG_ENDIAN || sizeof (time_t) != 4)
221     {
222       /* Decode the transition times, stored as 4-byte integers in
223          network (big-endian) byte order.  We work from the end of
224          the array so as not to clobber the next element to be
225          processed when sizeof (time_t) > 4.  */
226       i = num_transitions;
227       while (i-- > 0)
228         transitions[i] = decode ((char *) transitions + i * 4);
229     }
230
231   for (i = 0; i < num_types; ++i)
232     {
233       unsigned char x[4];
234       int c;
235       if (__builtin_expect (fread_unlocked (x, 1, sizeof (x), f) != sizeof (x),
236                             0))
237         goto lose;
238       c = getc_unlocked (f);
239       if (__builtin_expect ((unsigned int) c > 1u, 0))
240         goto lose;
241       types[i].isdst = c;
242       c = getc_unlocked (f);
243       if (__builtin_expect ((size_t) c > chars, 0))
244         /* Bogus index in data file.  */
245         goto lose;
246       types[i].idx = c;
247       types[i].offset = (long int) decode (x);
248     }
249
250   if (__builtin_expect (fread_unlocked (zone_names, 1, chars, f) != chars, 0))
251     goto lose;
252
253   for (i = 0; i < num_leaps; ++i)
254     {
255       unsigned char x[4];
256       if (__builtin_expect (fread_unlocked (x, 1, sizeof (x), f) != sizeof (x),
257                             0))
258         goto lose;
259       leaps[i].transition = (time_t) decode (x);
260       if (__builtin_expect (fread_unlocked (x, 1, sizeof (x), f) != sizeof (x),
261                             0))
262         goto lose;
263       leaps[i].change = (long int) decode (x);
264     }
265
266   for (i = 0; i < num_isstd; ++i)
267     {
268       int c = getc_unlocked (f);
269       if (__builtin_expect (c == EOF, 0))
270         goto lose;
271       types[i].isstd = c != 0;
272     }
273   while (i < num_types)
274     types[i++].isstd = 0;
275
276   for (i = 0; i < num_isgmt; ++i)
277     {
278       int c = getc_unlocked (f);
279       if (__builtin_expect (c == EOF, 0))
280         goto lose;
281       types[i].isgmt = c != 0;
282     }
283   while (i < num_types)
284     types[i++].isgmt = 0;
285
286   fclose (f);
287
288   /* First "register" all timezone names.  */
289   for (i = 0; i < num_types; ++i)
290     (void) __tzstring (&zone_names[types[i].idx]);
291
292   /* Find the standard and daylight time offsets used by the rule file.
293      We choose the offsets in the types of each flavor that are
294      transitioned to earliest in time.  */
295   __tzname[0] = NULL;
296   __tzname[1] = NULL;
297   for (i = num_transitions; i > 0; )
298     {
299       int type = type_idxs[--i];
300       int dst = types[type].isdst;
301
302       if (__tzname[dst] == NULL)
303         {
304           int idx = types[type].idx;
305
306           __tzname[dst] = __tzstring (&zone_names[idx]);
307
308           if (__tzname[1 - dst] != NULL)
309             break;
310         }
311     }
312   if (__tzname[0] == NULL)
313     {
314       /* This should only happen if there are no transition rules.
315          In this case there should be only one single type.  */
316       assert (num_types == 1);
317       __tzname[0] = __tzstring (zone_names);
318     }
319   if (__tzname[1] == NULL)
320     __tzname[1] = __tzname[0];
321
322   compute_tzname_max (chars);
323
324   if (num_transitions == 0)
325     /* Use the first rule (which should also be the only one).  */
326     rule_stdoff = rule_dstoff = types[0].offset;
327   else
328     {
329       int stdoff_set = 0, dstoff_set = 0;
330       rule_stdoff = rule_dstoff = 0;
331       i = num_transitions - 1;
332       do
333         {
334           if (!stdoff_set && !types[type_idxs[i]].isdst)
335             {
336               stdoff_set = 1;
337               rule_stdoff = types[type_idxs[i]].offset;
338             }
339           else if (!dstoff_set && types[type_idxs[i]].isdst)
340             {
341               dstoff_set = 1;
342               rule_dstoff = types[type_idxs[i]].offset;
343             }
344           if (stdoff_set && dstoff_set)
345             break;
346         }
347       while (i-- > 0);
348
349       if (!dstoff_set)
350         rule_dstoff = rule_stdoff;
351     }
352
353   __daylight = rule_stdoff != rule_dstoff;
354   __timezone = -rule_stdoff;
355
356   __use_tzfile = 1;
357   return;
358
359  lose:
360   fclose (f);
361 }
362 \f
363 /* The user specified a hand-made timezone, but not its DST rules.
364    We will use the names and offsets from the user, and the rules
365    from the TZDEFRULES file.  */
366
367 void
368 __tzfile_default (const char *std, const char *dst,
369                   long int stdoff, long int dstoff)
370 {
371   size_t stdlen = strlen (std) + 1;
372   size_t dstlen = strlen (dst) + 1;
373   size_t i;
374   int isdst;
375   char *cp;
376
377   __tzfile_read (TZDEFRULES, stdlen + dstlen, &cp);
378   if (!__use_tzfile)
379     return;
380
381   if (num_types < 2)
382     {
383       __use_tzfile = 0;
384       return;
385     }
386
387   /* Ignore the zone names read from the file and use the given ones
388      instead.  */
389   __mempcpy (__mempcpy (cp, std, stdlen), dst, dstlen);
390   zone_names = cp;
391
392   /* Now there are only two zones, regardless of what the file contained.  */
393   num_types = 2;
394
395   /* Now correct the transition times for the user-specified standard and
396      daylight offsets from GMT.  */
397   isdst = 0;
398   for (i = 0; i < num_transitions; ++i)
399     {
400       struct ttinfo *trans_type = &types[type_idxs[i]];
401
402       /* We will use only types 0 (standard) and 1 (daylight).
403          Fix up this transition to point to whichever matches
404          the flavor of its original type.  */
405       type_idxs[i] = trans_type->isdst;
406
407       if (trans_type->isgmt)
408         /* The transition time is in GMT.  No correction to apply.  */ ;
409       else if (isdst && !trans_type->isstd)
410         /* The type says this transition is in "local wall clock time", and
411            wall clock time as of the previous transition was DST.  Correct
412            for the difference between the rule's DST offset and the user's
413            DST offset.  */
414         transitions[i] += dstoff - rule_dstoff;
415       else
416         /* This transition is in "local wall clock time", and wall clock
417            time as of this iteration is non-DST.  Correct for the
418            difference between the rule's standard offset and the user's
419            standard offset.  */
420         transitions[i] += stdoff - rule_stdoff;
421
422       /* The DST state of "local wall clock time" for the next iteration is
423          as specified by this transition.  */
424       isdst = trans_type->isdst;
425     }
426
427   /* Reset types 0 and 1 to describe the user's settings.  */
428   types[0].idx = 0;
429   types[0].offset = stdoff;
430   types[0].isdst = 0;
431   types[1].idx = stdlen;
432   types[1].offset = dstoff;
433   types[1].isdst = 1;
434
435   /* Reset the zone names to point to the user's names.  */
436   __tzname[0] = (char *) std;
437   __tzname[1] = (char *) dst;
438
439   /* Set the timezone.  */
440   __timezone = -types[0].offset;
441
442   compute_tzname_max (stdlen + dstlen);
443 }
444 \f
445 static struct ttinfo *
446 internal_function
447 find_transition (time_t timer)
448 {
449   size_t i;
450
451   if (num_transitions == 0 || timer < transitions[0])
452     {
453       /* TIMER is before any transition (or there are no transitions).
454          Choose the first non-DST type
455          (or the first if they're all DST types).  */
456       i = 0;
457       while (i < num_types && types[i].isdst)
458         ++i;
459       if (i == num_types)
460         i = 0;
461     }
462   else
463     {
464       /* Find the first transition after TIMER, and
465          then pick the type of the transition before it.  */
466       for (i = 1; i < num_transitions; ++i)
467         if (timer < transitions[i])
468           break;
469       i = type_idxs[i - 1];
470     }
471
472   return &types[i];
473 }
474 \f
475 void
476 __tzfile_compute (time_t timer, int use_localtime,
477                   long int *leap_correct, int *leap_hit,
478                   struct tm *tp)
479 {
480   register size_t i;
481
482   if (use_localtime)
483     {
484       struct ttinfo *info = find_transition (timer);
485       __daylight = rule_stdoff != rule_dstoff;
486       __timezone = -rule_stdoff;
487       __tzname[0] = NULL;
488       __tzname[1] = NULL;
489       for (i = num_transitions; i > 0; )
490         {
491           int type = type_idxs[--i];
492           int dst = types[type].isdst;
493           int idx = types[type].idx;
494
495           if (__tzname[dst] == NULL)
496             {
497               __tzname[dst] = __tzstring (&zone_names[idx]);
498
499               if (__tzname[1 - dst] != NULL)
500                 break;
501             }
502         }
503       if (__tzname[0] == NULL)
504         {
505           /* This should only happen if there are no transition rules.
506              In this case there should be only one single type.  */
507           assert (num_types == 1);
508           __tzname[0] = __tzstring (zone_names);
509         }
510       if (__tzname[1] == NULL)
511         /* There is no daylight saving time.  */
512         __tzname[1] = __tzname[0];
513       tp->tm_isdst = info->isdst;
514       tp->tm_zone = __tzstring (&zone_names[info->idx]);
515       tp->tm_gmtoff = info->offset;
516     }
517
518   *leap_correct = 0L;
519   *leap_hit = 0;
520
521   /* Find the last leap second correction transition time before TIMER.  */
522   i = num_leaps;
523   do
524     if (i-- == 0)
525       return;
526   while (timer < leaps[i].transition);
527
528   /* Apply its correction.  */
529   *leap_correct = leaps[i].change;
530
531   if (timer == leaps[i].transition && /* Exactly at the transition time.  */
532       ((i == 0 && leaps[i].change > 0) ||
533        leaps[i].change > leaps[i - 1].change))
534     {
535       *leap_hit = 1;
536       while (i > 0
537              && leaps[i].transition == leaps[i - 1].transition + 1
538              && leaps[i].change == leaps[i - 1].change + 1)
539         {
540           ++*leap_hit;
541           --i;
542         }
543     }
544 }
545 \f
546 static void
547 internal_function
548 compute_tzname_max (size_t chars)
549 {
550   const char *p;
551
552   p = zone_names;
553   do
554     {
555       const char *start = p;
556       while (*p != '\0')
557         ++p;
558       if ((size_t) (p - start) > __tzname_cur_max)
559         __tzname_cur_max = p - start;
560     }
561   while (++p < &zone_names[chars]);
562 }