(struct ttinfo): New member `isgmt'. (__tzfile_read): Read isgmt flags for each type...
[platform/upstream/glibc.git] / time / tzfile.c
1 /* Copyright (C) 1991, 1992, 1993, 1995 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 #define uc2ul(x)        _uc2ul((unsigned char *) (x))
58 #define _uc2ul(x)                                                             \
59   ((x)[3] + ((x)[2] << CHAR_BIT) + ((x)[1] << (2 * CHAR_BIT)) +               \
60    ((x)[0] << (3 * CHAR_BIT)))
61
62 void
63 DEFUN(__tzfile_read, (file), CONST char *file)
64 {
65   size_t num_isstd, num_isgmt;
66   register FILE *f;
67   struct tzhead tzhead;
68   size_t chars;
69   register size_t i;
70
71   __use_tzfile = 0;
72
73   if (transitions != NULL)
74     free((PTR) transitions);
75   transitions = NULL;
76   if (type_idxs != NULL)
77     free((PTR) type_idxs);
78   type_idxs = NULL;
79   if (types != NULL)
80     free((PTR) types);
81   types = NULL;
82   if (zone_names != NULL)
83     free((PTR) zone_names);
84   zone_names = NULL;
85   if (leaps != NULL)
86     free((PTR) leaps);
87   leaps = NULL;
88
89   if (file == NULL || *file == '\0')
90     file = TZDEFAULT;
91
92   if (*file != '/')
93     {
94       static CONST char tzdir[] = TZDIR;
95       register CONST unsigned int len = strlen(file) + 1;
96       char *new = (char *) __alloca(sizeof(tzdir) + len);
97       memcpy(new, tzdir, sizeof(tzdir) - 1);
98       new[sizeof(tzdir) - 1] = '/';
99       memcpy(&new[sizeof(tzdir)], file, len);
100       file = new;
101     }
102
103   f = fopen(file, "r");
104   if (f == NULL)
105     return;
106
107   if (fread((PTR) &tzhead, sizeof(tzhead), 1, f) != 1)
108     goto lose;
109
110   num_transitions = (size_t) uc2ul(tzhead.tzh_timecnt);
111   num_types = (size_t) uc2ul (tzhead.tzh_typecnt);
112   chars = (size_t) uc2ul (tzhead.tzh_charcnt);
113   num_leaps = (size_t) uc2ul (tzhead.tzh_leapcnt);
114   num_isstd = (size_t) uc2ul (tzhead.tzh_ttisstdcnt);
115   num_isgmt = (size_t) uc2ul (tzhead.tzh_ttisgmtcnt);
116
117   if (num_transitions > 0)
118     {
119       transitions = (time_t *) malloc (num_transitions * sizeof(time_t));
120       if (transitions == NULL)
121         goto lose;
122       type_idxs = (unsigned char *) malloc (num_transitions);
123       if (type_idxs == NULL)
124         goto lose;
125     }
126   if (num_types > 0)
127     {
128       types = (struct ttinfo *) malloc (num_types * sizeof (struct ttinfo));
129       if (types == NULL)
130         goto lose;
131     }
132   if (chars > 0)
133     {
134       zone_names = (char *) malloc (chars);
135       if (zone_names == NULL)
136         goto lose;
137     }
138   if (num_leaps > 0)
139     {
140       leaps = (struct leap *) malloc (num_leaps * sizeof (struct leap));
141       if (leaps == NULL)
142         goto lose;
143     }
144
145   if (fread((PTR) transitions, sizeof(time_t),
146             num_transitions, f) != num_transitions ||
147       fread((PTR) type_idxs, 1, num_transitions, f) != num_transitions)
148     goto lose;
149
150   for (i = 0; i < num_transitions; ++i)
151     transitions[i] = uc2ul (&transitions[i]);
152
153   for (i = 0; i < num_types; ++i)
154     {
155       unsigned char x[4];
156       if (fread((PTR) x, 1, 4, f) != 4 ||
157           fread((PTR) &types[i].isdst, 1, 1, f) != 1 ||
158           fread((PTR) &types[i].idx, 1, 1, f) != 1)
159         goto lose;
160       types[i].offset = (long int) uc2ul(x);
161     }
162
163   if (fread((PTR) zone_names, 1, chars, f) != chars)
164     goto lose;
165
166   for (i = 0; i < num_leaps; ++i)
167     {
168       unsigned char x[4];
169       if (fread((PTR) x, 1, sizeof(x), f) != sizeof(x))
170         goto lose;
171       leaps[i].transition = (time_t) uc2ul(x);
172       if (fread((PTR) x, 1, sizeof(x), f) != sizeof(x))
173         goto lose;
174       leaps[i].change = (long int) uc2ul(x);
175     }
176
177   for (i = 0; i < num_isstd; ++i)
178     {
179       char c = getc(f);
180       if (c == EOF)
181         goto lose;
182       types[i].isstd = c != 0;
183     }
184   while (i < num_types)
185     types[i++].isstd = 0;
186
187   for (i = 0; i < num_isgmt; ++i)
188     {
189       char c = getc(f);
190       if (c == EOF)
191         goto lose;
192       types[i].isgmt = c != 0;
193     }
194   while (i < num_types)
195     types[i++].isgmt = 0;
196
197   (void) fclose(f);
198
199   compute_tzname_max (chars);
200   
201   __use_tzfile = 1;
202   return;
203
204  lose:;
205   (void) fclose(f);
206 }
207 \f
208 /* The user specified a hand-made timezone, but not its DST rules.
209    We will use the names and offsets from the user, and the rules
210    from the TZDEFRULES file.  */
211
212 void
213 DEFUN(__tzfile_default, (std, dst, stdoff, dstoff),
214       char *std AND char *dst AND
215       long int stdoff AND long int dstoff)
216 {
217   size_t stdlen, dstlen, i;
218   long int rule_offset, rule_stdoff, rule_dstoff;
219   int isdst;
220
221   __tzfile_read (TZDEFRULES);
222   if (!__use_tzfile)
223     return;
224
225   if (num_types < 2)
226     {
227       __use_tzfile = 0;
228       return;
229     }
230
231   /* Ignore the zone names read from the file.  */
232   free (zone_names);
233
234   /* Use the names the user specified.  */
235   stdlen = strlen (std) + 1;
236   dstlen = strlen (dst) + 1;
237   zone_names = malloc (stdlen + dstlen);
238   if (zone_names == NULL)
239     {
240       __use_tzfile = 0;
241       return;
242     }
243   memcpy (zone_names, std, stdlen);
244   memcpy (&zone_names[stdlen], dst, dstlen);
245
246   /* Find the standard and daylight time offsets used by the rule file.
247      We choose the offsets in the types of each flavor that are
248      transitioned to earliest in time.  */
249   rule_dstoff = 0;
250   for (i = 0; i < num_transitions; ++i)
251     {
252       if (!rule_stdoff && !types[type_idxs[i]].isdst)
253         rule_stdoff = types[type_idxs[i]].offset;
254       if (!rule_dstoff && types[type_idxs[i]].isdst)
255         rule_dstoff = types[type_idxs[i]].offset;
256       if (rule_stdoff && rule_dstoff)
257         break;
258     }
259
260   /* Now correct the transition times for the user-specified standard and
261      daylight offsets from GMT.  */
262   isdst = 0;
263   rule_offset = rule_offset;
264   for (i = 0; i < num_transitions; ++i)
265     {
266       struct ttinfo *trans_type = &types[type_idxs[i]];
267
268       /* We will use only types 0 (standard) and 1 (daylight).
269          Fix up this transition to point to whichever matches
270          the flavor of its original type.  */
271       type_idxs[i] = trans_type->isdst;
272
273       if (trans_type->isgmt)
274         /* The transition time is in GMT.  No correction to apply.  */ ;
275       else if (isdst && !trans_type->isstd)
276         /* The type says this transition is in "local wall clock time", and
277            wall clock time as of the previous transition was DST.  Correct
278            for the difference between the rule's DST offset and the user's
279            DST offset.  */
280         transitions[i] += dstoff - rule_dstoff;
281       else
282         /* This transition is in "local wall clock time", and wall clock
283            time as of this iteration is non-DST.  Correct for the
284            difference between the rule's standard offset and the user's
285            standard offset.  */
286         transitions[i] += stdoff - rule_stdoff;
287
288       /* The DST state of "local wall clock time" for the next iteration is
289          as specified by this transition.  */
290       isdst = trans_type->isdst;
291     }
292
293   /* Reset types 0 and 1 to describe the user's settings.  */
294   types[0].idx = 0;
295   types[0].offset = stdoff;
296   types[0].isdst = 0;
297   types[1].idx = stdlen;
298   types[1].offset = dstoff;
299   types[1].isdst = 1;
300
301   compute_tzname_max (stdlen + dstlen);
302 }
303 \f
304 int
305 DEFUN(__tzfile_compute, (timer, leap_correct, leap_hit),
306       time_t timer AND long int *leap_correct AND int *leap_hit)
307 {
308   struct ttinfo *info;
309   register size_t i;
310
311   if (num_transitions == 0 || timer < transitions[0])
312     {
313       /* TIMER is before any transition (or there are no transitions).
314          Choose the first non-DST type
315          (or the first if they're all DST types).  */
316       i = 0;
317       while (i < num_types && types[i].isdst)
318         ++i;
319       if (i == num_types)
320         i = 0;
321     }
322   else
323     {
324       /* Find the first transition after TIMER, and
325          then pick the type of the transition before it.  */
326       for (i = 1; i < num_transitions; ++i)
327         if (timer < transitions[i])
328           break;
329       i = type_idxs[i - 1];
330     }
331
332   info = &types[i];
333   __daylight = info->isdst;
334   __timezone = info->offset;
335   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
336        ++i)
337     __tzname[types[i].isdst] = &zone_names[types[i].idx];
338   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
339     __tzname[info->isdst] = &zone_names[info->idx];
340
341   *leap_correct = 0L;
342   *leap_hit = 0;
343
344   /* Find the last leap second correction transition time before TIMER.  */
345   i = num_leaps;
346   do
347     if (i-- == 0)
348       return 1;
349   while (timer < leaps[i].transition);
350
351   /* Apply its correction.  */
352   *leap_correct = leaps[i].change;
353
354   if (timer == leaps[i].transition && /* Exactly at the transition time.  */
355       ((i == 0 && leaps[i].change > 0) ||
356        leaps[i].change > leaps[i - 1].change))
357     {
358       *leap_hit = 1;
359       while (i > 0 &&
360              leaps[i].transition == leaps[i - 1].transition + 1 &&
361              leaps[i].change == leaps[i - 1].change + 1)
362         {
363           ++*leap_hit;
364           --i;
365         }
366     }
367
368   return 1;
369 }
370 \f
371 void
372 DEFUN(compute_tzname_max, (chars), size_t chars)
373 {
374   extern long int __tzname_cur_max; /* Defined in __tzset.c. */
375
376   const char *p;
377
378   p = zone_names;
379   do
380     {
381       const char *start = p;
382       while (*p != '\0')
383         ++p;
384       if (p - start > __tzname_cur_max)
385         __tzname_cur_max = p - start;
386     } while (++p < &zone_names[chars]);
387 }