Sun Jun 16 00:40:20 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ansidecl.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include <string.h>
24 #include <limits.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 static void compute_tzname_max __P ((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 DEFUN(__tzfile_read, (file), 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
88   __use_tzfile = 0;
89
90   if (transitions != NULL)
91     free((PTR) transitions);
92   transitions = NULL;
93   if (type_idxs != NULL)
94     free((PTR) type_idxs);
95   type_idxs = NULL;
96   if (types != NULL)
97     free((PTR) types);
98   types = NULL;
99   if (zone_names != NULL)
100     free((PTR) zone_names);
101   zone_names = NULL;
102   if (leaps != NULL)
103     free((PTR) leaps);
104   leaps = NULL;
105
106   if (file == NULL)
107     /* No user specification; use the site-wide default.  */
108     file = TZDEFAULT;
109   else if (*file == '\0')
110     /* User specified the empty string; use UTC explicitly.  */
111     file = "Universal";
112
113   if (*file != '/')
114     {
115       static CONST char tzdir[] = TZDIR;
116       register CONST unsigned int len = strlen(file) + 1;
117       char *new = (char *) __alloca(sizeof(tzdir) + len);
118       memcpy(new, tzdir, sizeof(tzdir) - 1);
119       new[sizeof(tzdir) - 1] = '/';
120       memcpy(&new[sizeof(tzdir)], file, len);
121       file = new;
122     }
123
124   f = fopen(file, "r");
125   if (f == NULL)
126     return;
127
128   if (fread((PTR) &tzhead, sizeof(tzhead), 1, f) != 1)
129     goto lose;
130
131   num_transitions = (size_t) decode (tzhead.tzh_timecnt);
132   num_types = (size_t) decode (tzhead.tzh_typecnt);
133   chars = (size_t) decode (tzhead.tzh_charcnt);
134   num_leaps = (size_t) decode (tzhead.tzh_leapcnt);
135   num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
136   num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
137
138   if (num_transitions > 0)
139     {
140       transitions = (time_t *) malloc (num_transitions * sizeof(time_t));
141       if (transitions == NULL)
142         goto lose;
143       type_idxs = (unsigned char *) malloc (num_transitions);
144       if (type_idxs == NULL)
145         goto lose;
146     }
147   if (num_types > 0)
148     {
149       types = (struct ttinfo *) malloc (num_types * sizeof (struct ttinfo));
150       if (types == NULL)
151         goto lose;
152     }
153   if (chars > 0)
154     {
155       zone_names = (char *) malloc (chars);
156       if (zone_names == NULL)
157         goto lose;
158     }
159   if (num_leaps > 0)
160     {
161       leaps = (struct leap *) malloc (num_leaps * sizeof (struct leap));
162       if (leaps == NULL)
163         goto lose;
164     }
165
166   if (sizeof (time_t) < 4)
167       abort ();
168
169   if (fread((PTR) transitions, 4, num_transitions, f) != num_transitions ||
170       fread((PTR) type_idxs, 1, num_transitions, f) != num_transitions)
171     goto lose;
172
173   if (BYTE_ORDER != BIG_ENDIAN || sizeof (time_t) != 4)
174     {
175       /* Decode the transition times, stored as 4-byte integers in
176          network (big-endian) byte order.  We work from the end of
177          the array so as not to clobber the next element to be
178          processed when sizeof (time_t) > 4.  */
179       i = num_transitions;
180       while (i-- > 0)
181         transitions[i] = decode ((char *) transitions + i*4);
182     }
183
184   for (i = 0; i < num_types; ++i)
185     {
186       unsigned char x[4];
187       if (fread((PTR) x, 1, 4, f) != 4 ||
188           fread((PTR) &types[i].isdst, 1, 1, f) != 1 ||
189           fread((PTR) &types[i].idx, 1, 1, f) != 1)
190         goto lose;
191       types[i].offset = (long int) decode (x);
192     }
193
194   if (fread((PTR) zone_names, 1, chars, f) != chars)
195     goto lose;
196
197   for (i = 0; i < num_leaps; ++i)
198     {
199       unsigned char x[4];
200       if (fread((PTR) x, 1, sizeof(x), f) != sizeof(x))
201         goto lose;
202       leaps[i].transition = (time_t) decode (x);
203       if (fread((PTR) x, 1, sizeof(x), f) != sizeof(x))
204         goto lose;
205       leaps[i].change = (long int) decode (x);
206     }
207
208   for (i = 0; i < num_isstd; ++i)
209     {
210       char c = getc(f);
211       if (c == EOF)
212         goto lose;
213       types[i].isstd = c != 0;
214     }
215   while (i < num_types)
216     types[i++].isstd = 0;
217
218   for (i = 0; i < num_isgmt; ++i)
219     {
220       char c = getc(f);
221       if (c == EOF)
222         goto lose;
223       types[i].isgmt = c != 0;
224     }
225   while (i < num_types)
226     types[i++].isgmt = 0;
227
228   (void) fclose(f);
229
230   compute_tzname_max (chars);
231
232   __use_tzfile = 1;
233   return;
234
235  lose:;
236   (void) fclose(f);
237 }
238 \f
239 /* The user specified a hand-made timezone, but not its DST rules.
240    We will use the names and offsets from the user, and the rules
241    from the TZDEFRULES file.  */
242
243 void
244 DEFUN(__tzfile_default, (std, dst, stdoff, dstoff),
245       char *std AND char *dst AND
246       long int stdoff AND long int dstoff)
247 {
248   size_t stdlen, dstlen, i;
249   long int rule_offset, rule_stdoff, rule_dstoff;
250   int isdst;
251
252   __tzfile_read (TZDEFRULES);
253   if (!__use_tzfile)
254     return;
255
256   if (num_types < 2)
257     {
258       __use_tzfile = 0;
259       return;
260     }
261
262   /* Ignore the zone names read from the file.  */
263   free (zone_names);
264
265   /* Use the names the user specified.  */
266   stdlen = strlen (std) + 1;
267   dstlen = strlen (dst) + 1;
268   zone_names = malloc (stdlen + dstlen);
269   if (zone_names == NULL)
270     {
271       __use_tzfile = 0;
272       return;
273     }
274   memcpy (zone_names, std, stdlen);
275   memcpy (&zone_names[stdlen], dst, dstlen);
276
277   /* Find the standard and daylight time offsets used by the rule file.
278      We choose the offsets in the types of each flavor that are
279      transitioned to earliest in time.  */
280   rule_stdoff = rule_dstoff = 0;
281   for (i = 0; i < num_transitions; ++i)
282     {
283       if (!rule_stdoff && !types[type_idxs[i]].isdst)
284         rule_stdoff = types[type_idxs[i]].offset;
285       if (!rule_dstoff && types[type_idxs[i]].isdst)
286         rule_dstoff = types[type_idxs[i]].offset;
287       if (rule_stdoff && rule_dstoff)
288         break;
289     }
290
291   /* Now correct the transition times for the user-specified standard and
292      daylight offsets from GMT.  */
293   isdst = 0;
294   rule_offset = rule_offset;
295   for (i = 0; i < num_transitions; ++i)
296     {
297       struct ttinfo *trans_type = &types[type_idxs[i]];
298
299       /* We will use only types 0 (standard) and 1 (daylight).
300          Fix up this transition to point to whichever matches
301          the flavor of its original type.  */
302       type_idxs[i] = trans_type->isdst;
303
304       if (trans_type->isgmt)
305         /* The transition time is in GMT.  No correction to apply.  */ ;
306       else if (isdst && !trans_type->isstd)
307         /* The type says this transition is in "local wall clock time", and
308            wall clock time as of the previous transition was DST.  Correct
309            for the difference between the rule's DST offset and the user's
310            DST offset.  */
311         transitions[i] += dstoff - rule_dstoff;
312       else
313         /* This transition is in "local wall clock time", and wall clock
314            time as of this iteration is non-DST.  Correct for the
315            difference between the rule's standard offset and the user's
316            standard offset.  */
317         transitions[i] += stdoff - rule_stdoff;
318
319       /* The DST state of "local wall clock time" for the next iteration is
320          as specified by this transition.  */
321       isdst = trans_type->isdst;
322     }
323
324   /* Reset types 0 and 1 to describe the user's settings.  */
325   types[0].idx = 0;
326   types[0].offset = stdoff;
327   types[0].isdst = 0;
328   types[1].idx = stdlen;
329   types[1].offset = dstoff;
330   types[1].isdst = 1;
331
332   compute_tzname_max (stdlen + dstlen);
333 }
334 \f
335 int
336 DEFUN(__tzfile_compute, (timer, leap_correct, leap_hit),
337       time_t timer AND long int *leap_correct AND int *leap_hit)
338 {
339   struct ttinfo *info;
340   register size_t i;
341
342   if (num_transitions == 0 || timer < transitions[0])
343     {
344       /* TIMER is before any transition (or there are no transitions).
345          Choose the first non-DST type
346          (or the first if they're all DST types).  */
347       i = 0;
348       while (i < num_types && types[i].isdst)
349         ++i;
350       if (i == num_types)
351         i = 0;
352     }
353   else
354     {
355       /* Find the first transition after TIMER, and
356          then pick the type of the transition before it.  */
357       for (i = 1; i < num_transitions; ++i)
358         if (timer < transitions[i])
359           break;
360       i = type_idxs[i - 1];
361     }
362
363   info = &types[i];
364   __daylight = info->isdst;
365   __timezone = info->offset;
366   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
367        ++i)
368     __tzname[types[i].isdst] = &zone_names[types[i].idx];
369   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
370     __tzname[info->isdst] = &zone_names[info->idx];
371
372   *leap_correct = 0L;
373   *leap_hit = 0;
374
375   /* Find the last leap second correction transition time before TIMER.  */
376   i = num_leaps;
377   do
378     if (i-- == 0)
379       return 1;
380   while (timer < leaps[i].transition);
381
382   /* Apply its correction.  */
383   *leap_correct = leaps[i].change;
384
385   if (timer == leaps[i].transition && /* Exactly at the transition time.  */
386       ((i == 0 && leaps[i].change > 0) ||
387        leaps[i].change > leaps[i - 1].change))
388     {
389       *leap_hit = 1;
390       while (i > 0 &&
391              leaps[i].transition == leaps[i - 1].transition + 1 &&
392              leaps[i].change == leaps[i - 1].change + 1)
393         {
394           ++*leap_hit;
395           --i;
396         }
397     }
398
399   return 1;
400 }
401 \f
402 void
403 DEFUN(compute_tzname_max, (chars), size_t chars)
404 {
405   extern long int __tzname_cur_max; /* Defined in __tzset.c. */
406
407   const char *p;
408
409   p = zone_names;
410   do
411     {
412       const char *start = p;
413       while (*p != '\0')
414         ++p;
415       if (p - start > __tzname_cur_max)
416         __tzname_cur_max = p - start;
417     } while (++p < &zone_names[chars]);
418 }