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