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