update from main archive 970202
[kopensolaris-gnu/glibc.git] / time / tzfile.c
1 /* Copyright (C) 1991, 92, 93, 95, 96, 97 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 Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    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    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <time.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <unistd.h>
25
26 #define NOID
27 #include <tzfile.h>
28
29 int __use_tzfile = 0;
30
31 struct ttinfo
32   {
33     long int offset;            /* Seconds east of GMT.  */
34     unsigned char isdst;        /* Used to set tm_isdst.  */
35     unsigned char idx;          /* Index into `zone_names'.  */
36     unsigned char isstd;        /* Transition times are in standard time.  */
37     unsigned char isgmt;        /* Transition times are in GMT.  */
38   };
39
40 struct leap
41   {
42     time_t transition;          /* Time the transition takes effect.  */
43     long int change;            /* Seconds of correction to apply.  */
44   };
45
46 static struct ttinfo *find_transition (time_t timer);
47 static void compute_tzname_max (size_t);
48
49 static size_t num_transitions;
50 static time_t *transitions = NULL;
51 static unsigned char *type_idxs = NULL;
52 static size_t num_types;
53 static struct ttinfo *types = NULL;
54 static char *zone_names = NULL;
55 static size_t num_leaps;
56 static struct leap *leaps = NULL;
57
58 #include <endian.h>
59
60 /* Decode the four bytes at PTR as a signed integer in network byte order.  */
61 static inline int
62 decode (const void *ptr)
63 {
64   if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
65     return *(const int *) ptr;
66   else
67     {
68       const unsigned char *p = ptr;
69       int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
70
71       result = (result << 8) | *p++;
72       result = (result << 8) | *p++;
73       result = (result << 8) | *p++;
74       result = (result << 8) | *p++;
75
76       return result;
77     }
78 }
79
80 void
81 __tzfile_read (const char *file)
82 {
83   static const char default_tzdir[] = TZDIR;
84   size_t num_isstd, num_isgmt;
85   register FILE *f;
86   struct tzhead tzhead;
87   size_t chars;
88   register size_t i;
89   struct ttinfo *info;
90
91   __use_tzfile = 0;
92
93   if (transitions != NULL)
94     free ((void *) transitions);
95   transitions = NULL;
96   if (type_idxs != NULL)
97     free ((void *) type_idxs);
98   type_idxs = NULL;
99   if (types != NULL)
100     free ((void *) types);
101   types = NULL;
102   if (zone_names != NULL)
103     free ((void *) zone_names);
104   zone_names = NULL;
105   if (leaps != NULL)
106     free ((void *) leaps);
107   leaps = NULL;
108
109   if (file == NULL)
110     /* No user specification; use the site-wide default.  */
111     file = TZDEFAULT;
112   else if (*file == '\0')
113     /* User specified the empty string; use UTC explicitly.  */
114     file = "Universal";
115   else
116     {
117       /* We must not allow to read an arbitrary file in a setuid
118          program.  So we fail for any file which is not in the
119          directory hierachy starting at TZDIR.  */
120       if (__libc_enable_secure
121           && ((*file == '/'
122                && memcmp (file, default_tzdir, sizeof (default_tzdir) - 1))
123               || strstr (file, "../") != NULL))
124         /* This test a certainly a bit too restrictive but it should
125            catch all critical case.  */
126         return;
127     }
128
129   if (*file != '/')
130     {
131       const char *tzdir;
132       unsigned int len, tzdir_len;
133       char *new;
134
135       tzdir = __secure_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       memcpy (new, tzdir, tzdir_len);
146       new[tzdir_len] = '/';
147       memcpy (&new[tzdir_len + 1], file, len);
148       file = new;
149     }
150
151   f = fopen (file, "r");
152   if (f == NULL)
153     return;
154
155   if (fread ((void *) &tzhead, sizeof (tzhead), 1, f) != 1)
156     goto lose;
157
158   num_transitions = (size_t) decode (tzhead.tzh_timecnt);
159   num_types = (size_t) decode (tzhead.tzh_typecnt);
160   chars = (size_t) decode (tzhead.tzh_charcnt);
161   num_leaps = (size_t) decode (tzhead.tzh_leapcnt);
162   num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
163   num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
164
165   if (num_transitions > 0)
166     {
167       transitions = (time_t *) malloc (num_transitions * sizeof(time_t));
168       if (transitions == NULL)
169         goto lose;
170       type_idxs = (unsigned char *) malloc (num_transitions);
171       if (type_idxs == NULL)
172         goto lose;
173     }
174   if (num_types > 0)
175     {
176       types = (struct ttinfo *) malloc (num_types * sizeof (struct ttinfo));
177       if (types == NULL)
178         goto lose;
179     }
180   if (chars > 0)
181     {
182       zone_names = (char *) malloc (chars);
183       if (zone_names == NULL)
184         goto lose;
185     }
186   if (num_leaps > 0)
187     {
188       leaps = (struct leap *) malloc (num_leaps * sizeof (struct leap));
189       if (leaps == NULL)
190         goto lose;
191     }
192
193   if (sizeof (time_t) < 4)
194       abort ();
195
196   if (fread(transitions, 4, num_transitions, f) != num_transitions ||
197       fread(type_idxs, 1, num_transitions, f) != num_transitions)
198     goto lose;
199
200   /* Check for bogus indices in the data file, so we can hereafter
201      safely use type_idxs[T] as indices into `types' and never crash.  */
202   for (i = 0; i < num_transitions; ++i)
203     if (type_idxs[i] >= num_types)
204       goto lose;
205
206   if (BYTE_ORDER != BIG_ENDIAN || sizeof (time_t) != 4)
207     {
208       /* Decode the transition times, stored as 4-byte integers in
209          network (big-endian) byte order.  We work from the end of
210          the array so as not to clobber the next element to be
211          processed when sizeof (time_t) > 4.  */
212       i = num_transitions;
213       while (i-- > 0)
214         transitions[i] = decode ((char *) transitions + i*4);
215     }
216
217   for (i = 0; i < num_types; ++i)
218     {
219       unsigned char x[4];
220       if (fread (x, 1, 4, f) != 4 ||
221           fread (&types[i].isdst, 1, 1, f) != 1 ||
222           fread (&types[i].idx, 1, 1, f) != 1)
223         goto lose;
224       if (types[i].idx >= chars) /* Bogus index in data file.  */
225         goto lose;
226       types[i].offset = (long int) decode (x);
227     }
228
229   if (fread (zone_names, 1, chars, f) != chars)
230     goto lose;
231
232   for (i = 0; i < num_leaps; ++i)
233     {
234       unsigned char x[4];
235       if (fread (x, 1, sizeof (x), f) != sizeof (x))
236         goto lose;
237       leaps[i].transition = (time_t) decode (x);
238       if (fread (x, 1, sizeof (x), f) != sizeof (x))
239         goto lose;
240       leaps[i].change = (long int) decode (x);
241     }
242
243   for (i = 0; i < num_isstd; ++i)
244     {
245       char c = getc (f);
246       if (c == EOF)
247         goto lose;
248       types[i].isstd = c != 0;
249     }
250   while (i < num_types)
251     types[i++].isstd = 0;
252
253   for (i = 0; i < num_isgmt; ++i)
254     {
255       char c = getc (f);
256       if (c == EOF)
257         goto lose;
258       types[i].isgmt = c != 0;
259     }
260   while (i < num_types)
261     types[i++].isgmt = 0;
262
263   fclose (f);
264
265   info = find_transition (0);
266   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
267        ++i)
268     __tzname[types[i].isdst] = &zone_names[types[i].idx];
269   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
270     __tzname[info->isdst] = &zone_names[info->idx];
271
272   compute_tzname_max (chars);
273
274   __use_tzfile = 1;
275   return;
276
277  lose:;
278   fclose(f);
279 }
280 \f
281 /* The user specified a hand-made timezone, but not its DST rules.
282    We will use the names and offsets from the user, and the rules
283    from the TZDEFRULES file.  */
284
285 void
286 __tzfile_default (char *std, char *dst, long int stdoff, long int dstoff)
287 {
288   size_t stdlen, dstlen, i;
289   long int rule_offset, rule_stdoff, rule_dstoff;
290   int isdst;
291
292   __tzfile_read (TZDEFRULES);
293   if (!__use_tzfile)
294     return;
295
296   if (num_types < 2)
297     {
298       __use_tzfile = 0;
299       return;
300     }
301
302   /* Ignore the zone names read from the file.  */
303   free (zone_names);
304
305   /* Use the names the user specified.  */
306   stdlen = strlen (std) + 1;
307   dstlen = strlen (dst) + 1;
308   zone_names = malloc (stdlen + dstlen);
309   if (zone_names == NULL)
310     {
311       __use_tzfile = 0;
312       return;
313     }
314   memcpy (zone_names, std, stdlen);
315   memcpy (&zone_names[stdlen], dst, dstlen);
316
317   /* Find the standard and daylight time offsets used by the rule file.
318      We choose the offsets in the types of each flavor that are
319      transitioned to earliest in time.  */
320   rule_stdoff = rule_dstoff = 0;
321   for (i = 0; i < num_transitions; ++i)
322     {
323       if (!rule_stdoff && !types[type_idxs[i]].isdst)
324         rule_stdoff = types[type_idxs[i]].offset;
325       if (!rule_dstoff && types[type_idxs[i]].isdst)
326         rule_dstoff = types[type_idxs[i]].offset;
327       if (rule_stdoff && rule_dstoff)
328         break;
329     }
330
331   /* Now correct the transition times for the user-specified standard and
332      daylight offsets from GMT.  */
333   isdst = 0;
334   rule_offset = rule_offset;
335   for (i = 0; i < num_transitions; ++i)
336     {
337       struct ttinfo *trans_type = &types[type_idxs[i]];
338
339       /* We will use only types 0 (standard) and 1 (daylight).
340          Fix up this transition to point to whichever matches
341          the flavor of its original type.  */
342       type_idxs[i] = trans_type->isdst;
343
344       if (trans_type->isgmt)
345         /* The transition time is in GMT.  No correction to apply.  */ ;
346       else if (isdst && !trans_type->isstd)
347         /* The type says this transition is in "local wall clock time", and
348            wall clock time as of the previous transition was DST.  Correct
349            for the difference between the rule's DST offset and the user's
350            DST offset.  */
351         transitions[i] += dstoff - rule_dstoff;
352       else
353         /* This transition is in "local wall clock time", and wall clock
354            time as of this iteration is non-DST.  Correct for the
355            difference between the rule's standard offset and the user's
356            standard offset.  */
357         transitions[i] += stdoff - rule_stdoff;
358
359       /* The DST state of "local wall clock time" for the next iteration is
360          as specified by this transition.  */
361       isdst = trans_type->isdst;
362     }
363
364   /* Reset types 0 and 1 to describe the user's settings.  */
365   types[0].idx = 0;
366   types[0].offset = stdoff;
367   types[0].isdst = 0;
368   types[1].idx = stdlen;
369   types[1].offset = dstoff;
370   types[1].isdst = 1;
371
372   /* Reset the zone names to point to the user's names.  */
373   __tzname[0] = &zone_names[0];
374   __tzname[1] = &zone_names[stdlen];
375
376   compute_tzname_max (stdlen + dstlen);
377 }
378 \f
379 static struct ttinfo *
380 find_transition (time_t timer)
381 {
382   size_t i;
383
384   if (num_transitions == 0 || timer < transitions[0])
385     {
386       /* TIMER is before any transition (or there are no transitions).
387          Choose the first non-DST type
388          (or the first if they're all DST types).  */
389       i = 0;
390       while (i < num_types && types[i].isdst)
391         ++i;
392       if (i == num_types)
393         i = 0;
394     }
395   else
396     {
397       /* Find the first transition after TIMER, and
398          then pick the type of the transition before it.  */
399       for (i = 1; i < num_transitions; ++i)
400         if (timer < transitions[i])
401           break;
402       i = type_idxs[i - 1];
403     }
404
405   return &types[i];
406 }
407 \f
408 int
409 __tzfile_compute (time_t timer, long int *leap_correct, int *leap_hit)
410 {
411   struct ttinfo *info;
412   register size_t i;
413
414   info = find_transition (timer);
415   __daylight = info->isdst;
416   __timezone = info->offset;
417   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
418        ++i)
419     __tzname[types[i].isdst] = &zone_names[types[i].idx];
420   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
421     __tzname[info->isdst] = &zone_names[info->idx];
422
423   *leap_correct = 0L;
424   *leap_hit = 0;
425
426   /* Find the last leap second correction transition time before TIMER.  */
427   i = num_leaps;
428   do
429     if (i-- == 0)
430       return 1;
431   while (timer < leaps[i].transition);
432
433   /* Apply its correction.  */
434   *leap_correct = leaps[i].change;
435
436   if (timer == leaps[i].transition && /* Exactly at the transition time.  */
437       ((i == 0 && leaps[i].change > 0) ||
438        leaps[i].change > leaps[i - 1].change))
439     {
440       *leap_hit = 1;
441       while (i > 0 &&
442              leaps[i].transition == leaps[i - 1].transition + 1 &&
443              leaps[i].change == leaps[i - 1].change + 1)
444         {
445           ++*leap_hit;
446           --i;
447         }
448     }
449
450   return 1;
451 }
452 \f
453 void
454 compute_tzname_max (size_t chars)
455 {
456   extern size_t __tzname_cur_max; /* Defined in __tzset.c. */
457
458   const char *p;
459
460   p = zone_names;
461   do
462     {
463       const char *start = p;
464       while (*p != '\0')
465         ++p;
466       if ((size_t) (p - start) > __tzname_cur_max)
467         __tzname_cur_max = p - start;
468     } while (++p < &zone_names[chars]);
469 }