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