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