update from main archive 970122
[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
116   /* We must not allow to read an arbitrary file in a setuid program.
117      So we fail for any file which is not in the directory hierachy
118      starting at TZDIR.  */
119   if (__libc_enable_secure
120       && ((*file == '/'
121            && memcmp (file, default_tzdir, sizeof (default_tzdir) - 1) != 0)
122           || strstr (file, "../") != NULL))
123     /* This test a certainly a bit too restrictive but it should catch all
124        critical case.  */
125     return;
126
127   if (*file != '/')
128     {
129       const char *tzdir;
130       unsigned int len, tzdir_len;
131       char *new;
132
133       tzdir = __secure_getenv ("TZDIR");
134       if (tzdir == NULL || *tzdir == '\0')
135         {
136           tzdir = default_tzdir;
137           tzdir_len = sizeof (default_tzdir) - 1;
138         }
139       else
140         tzdir_len = strlen (tzdir);
141       len = strlen (file) + 1;
142       new = (char *) __alloca (tzdir_len + 1 + len);
143       memcpy (new, tzdir, tzdir_len);
144       new[tzdir_len] = '/';
145       memcpy (&new[tzdir_len + 1], file, len);
146       file = new;
147     }
148
149   f = fopen (file, "r");
150   if (f == NULL)
151     return;
152
153   if (fread ((void *) &tzhead, sizeof (tzhead), 1, f) != 1)
154     goto lose;
155
156   num_transitions = (size_t) decode (tzhead.tzh_timecnt);
157   num_types = (size_t) decode (tzhead.tzh_typecnt);
158   chars = (size_t) decode (tzhead.tzh_charcnt);
159   num_leaps = (size_t) decode (tzhead.tzh_leapcnt);
160   num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
161   num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
162
163   if (num_transitions > 0)
164     {
165       transitions = (time_t *) malloc (num_transitions * sizeof(time_t));
166       if (transitions == NULL)
167         goto lose;
168       type_idxs = (unsigned char *) malloc (num_transitions);
169       if (type_idxs == NULL)
170         goto lose;
171     }
172   if (num_types > 0)
173     {
174       types = (struct ttinfo *) malloc (num_types * sizeof (struct ttinfo));
175       if (types == NULL)
176         goto lose;
177     }
178   if (chars > 0)
179     {
180       zone_names = (char *) malloc (chars);
181       if (zone_names == NULL)
182         goto lose;
183     }
184   if (num_leaps > 0)
185     {
186       leaps = (struct leap *) malloc (num_leaps * sizeof (struct leap));
187       if (leaps == NULL)
188         goto lose;
189     }
190
191   if (sizeof (time_t) < 4)
192       abort ();
193
194   if (fread(transitions, 4, num_transitions, f) != num_transitions ||
195       fread(type_idxs, 1, num_transitions, f) != num_transitions)
196     goto lose;
197
198   /* Check for bogus indices in the data file, so we can hereafter
199      safely use type_idxs[T] as indices into `types' and never crash.  */
200   for (i = 0; i < num_transitions; ++i)
201     if (type_idxs[i] >= num_types)
202       goto lose;
203
204   if (BYTE_ORDER != BIG_ENDIAN || sizeof (time_t) != 4)
205     {
206       /* Decode the transition times, stored as 4-byte integers in
207          network (big-endian) byte order.  We work from the end of
208          the array so as not to clobber the next element to be
209          processed when sizeof (time_t) > 4.  */
210       i = num_transitions;
211       while (i-- > 0)
212         transitions[i] = decode ((char *) transitions + i*4);
213     }
214
215   for (i = 0; i < num_types; ++i)
216     {
217       unsigned char x[4];
218       if (fread (x, 1, 4, f) != 4 ||
219           fread (&types[i].isdst, 1, 1, f) != 1 ||
220           fread (&types[i].idx, 1, 1, f) != 1)
221         goto lose;
222       if (types[i].idx >= chars) /* Bogus index in data file.  */
223         goto lose;
224       types[i].offset = (long int) decode (x);
225     }
226
227   if (fread (zone_names, 1, chars, f) != chars)
228     goto lose;
229
230   for (i = 0; i < num_leaps; ++i)
231     {
232       unsigned char x[4];
233       if (fread (x, 1, sizeof (x), f) != sizeof (x))
234         goto lose;
235       leaps[i].transition = (time_t) decode (x);
236       if (fread (x, 1, sizeof (x), f) != sizeof (x))
237         goto lose;
238       leaps[i].change = (long int) decode (x);
239     }
240
241   for (i = 0; i < num_isstd; ++i)
242     {
243       char c = getc (f);
244       if (c == EOF)
245         goto lose;
246       types[i].isstd = c != 0;
247     }
248   while (i < num_types)
249     types[i++].isstd = 0;
250
251   for (i = 0; i < num_isgmt; ++i)
252     {
253       char c = getc (f);
254       if (c == EOF)
255         goto lose;
256       types[i].isgmt = c != 0;
257     }
258   while (i < num_types)
259     types[i++].isgmt = 0;
260
261   fclose (f);
262
263   info = find_transition (0);
264   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
265        ++i)
266     __tzname[types[i].isdst] = &zone_names[types[i].idx];
267   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
268     __tzname[info->isdst] = &zone_names[info->idx];
269
270   compute_tzname_max (chars);
271
272   __use_tzfile = 1;
273   return;
274
275  lose:;
276   fclose(f);
277 }
278 \f
279 /* The user specified a hand-made timezone, but not its DST rules.
280    We will use the names and offsets from the user, and the rules
281    from the TZDEFRULES file.  */
282
283 void
284 __tzfile_default (char *std, char *dst, long int stdoff, long int dstoff)
285 {
286   size_t stdlen, dstlen, i;
287   long int rule_offset, rule_stdoff, rule_dstoff;
288   int isdst;
289
290   __tzfile_read (TZDEFRULES);
291   if (!__use_tzfile)
292     return;
293
294   if (num_types < 2)
295     {
296       __use_tzfile = 0;
297       return;
298     }
299
300   /* Ignore the zone names read from the file.  */
301   free (zone_names);
302
303   /* Use the names the user specified.  */
304   stdlen = strlen (std) + 1;
305   dstlen = strlen (dst) + 1;
306   zone_names = malloc (stdlen + dstlen);
307   if (zone_names == NULL)
308     {
309       __use_tzfile = 0;
310       return;
311     }
312   memcpy (zone_names, std, stdlen);
313   memcpy (&zone_names[stdlen], dst, dstlen);
314
315   /* Find the standard and daylight time offsets used by the rule file.
316      We choose the offsets in the types of each flavor that are
317      transitioned to earliest in time.  */
318   rule_stdoff = rule_dstoff = 0;
319   for (i = 0; i < num_transitions; ++i)
320     {
321       if (!rule_stdoff && !types[type_idxs[i]].isdst)
322         rule_stdoff = types[type_idxs[i]].offset;
323       if (!rule_dstoff && types[type_idxs[i]].isdst)
324         rule_dstoff = types[type_idxs[i]].offset;
325       if (rule_stdoff && rule_dstoff)
326         break;
327     }
328
329   /* Now correct the transition times for the user-specified standard and
330      daylight offsets from GMT.  */
331   isdst = 0;
332   rule_offset = rule_offset;
333   for (i = 0; i < num_transitions; ++i)
334     {
335       struct ttinfo *trans_type = &types[type_idxs[i]];
336
337       /* We will use only types 0 (standard) and 1 (daylight).
338          Fix up this transition to point to whichever matches
339          the flavor of its original type.  */
340       type_idxs[i] = trans_type->isdst;
341
342       if (trans_type->isgmt)
343         /* The transition time is in GMT.  No correction to apply.  */ ;
344       else if (isdst && !trans_type->isstd)
345         /* The type says this transition is in "local wall clock time", and
346            wall clock time as of the previous transition was DST.  Correct
347            for the difference between the rule's DST offset and the user's
348            DST offset.  */
349         transitions[i] += dstoff - rule_dstoff;
350       else
351         /* This transition is in "local wall clock time", and wall clock
352            time as of this iteration is non-DST.  Correct for the
353            difference between the rule's standard offset and the user's
354            standard offset.  */
355         transitions[i] += stdoff - rule_stdoff;
356
357       /* The DST state of "local wall clock time" for the next iteration is
358          as specified by this transition.  */
359       isdst = trans_type->isdst;
360     }
361
362   /* Reset types 0 and 1 to describe the user's settings.  */
363   types[0].idx = 0;
364   types[0].offset = stdoff;
365   types[0].isdst = 0;
366   types[1].idx = stdlen;
367   types[1].offset = dstoff;
368   types[1].isdst = 1;
369
370   /* Reset the zone names to point to the user's names.  */
371   __tzname[0] = &zone_names[0];
372   __tzname[1] = &zone_names[stdlen];
373
374   compute_tzname_max (stdlen + dstlen);
375 }
376 \f
377 static struct ttinfo *
378 find_transition (time_t timer)
379 {
380   size_t i;
381
382   if (num_transitions == 0 || timer < transitions[0])
383     {
384       /* TIMER is before any transition (or there are no transitions).
385          Choose the first non-DST type
386          (or the first if they're all DST types).  */
387       i = 0;
388       while (i < num_types && types[i].isdst)
389         ++i;
390       if (i == num_types)
391         i = 0;
392     }
393   else
394     {
395       /* Find the first transition after TIMER, and
396          then pick the type of the transition before it.  */
397       for (i = 1; i < num_transitions; ++i)
398         if (timer < transitions[i])
399           break;
400       i = type_idxs[i - 1];
401     }
402
403   return &types[i];
404 }
405 \f
406 int
407 __tzfile_compute (time_t timer, long int *leap_correct, int *leap_hit)
408 {
409   struct ttinfo *info;
410   register size_t i;
411
412   info = find_transition (timer);
413   __daylight = info->isdst;
414   __timezone = info->offset;
415   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
416        ++i)
417     __tzname[types[i].isdst] = &zone_names[types[i].idx];
418   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
419     __tzname[info->isdst] = &zone_names[info->idx];
420
421   *leap_correct = 0L;
422   *leap_hit = 0;
423
424   /* Find the last leap second correction transition time before TIMER.  */
425   i = num_leaps;
426   do
427     if (i-- == 0)
428       return 1;
429   while (timer < leaps[i].transition);
430
431   /* Apply its correction.  */
432   *leap_correct = leaps[i].change;
433
434   if (timer == leaps[i].transition && /* Exactly at the transition time.  */
435       ((i == 0 && leaps[i].change > 0) ||
436        leaps[i].change > leaps[i - 1].change))
437     {
438       *leap_hit = 1;
439       while (i > 0 &&
440              leaps[i].transition == leaps[i - 1].transition + 1 &&
441              leaps[i].change == leaps[i - 1].change + 1)
442         {
443           ++*leap_hit;
444           --i;
445         }
446     }
447
448   return 1;
449 }
450 \f
451 void
452 compute_tzname_max (size_t chars)
453 {
454   extern size_t __tzname_cur_max; /* Defined in __tzset.c. */
455
456   const char *p;
457
458   p = zone_names;
459   do
460     {
461       const char *start = p;
462       while (*p != '\0')
463         ++p;
464       if ((size_t) (p - start) > __tzname_cur_max)
465         __tzname_cur_max = p - start;
466     } while (++p < &zone_names[chars]);
467 }