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