1 | /*
|
---|
2 | * misc - data and miscellaneous routines
|
---|
3 | */
|
---|
4 | /* $Header: /cvsup/minix/src/lib/ansi/misc.c,v 1.2 2005/06/17 13:00:04 jnherder Exp $ */
|
---|
5 |
|
---|
6 | #include <ctype.h>
|
---|
7 | #include <time.h>
|
---|
8 | #include <stdlib.h>
|
---|
9 | #include <string.h>
|
---|
10 |
|
---|
11 | #if defined(__BSD4_2)
|
---|
12 |
|
---|
13 | struct timeval {
|
---|
14 | long tv_sec; /* seconds */
|
---|
15 | long tv_usec; /* and microseconds */
|
---|
16 | };
|
---|
17 |
|
---|
18 | struct timezone {
|
---|
19 | int tz_minuteswest; /* minutes west of Greenwich */
|
---|
20 | int tz_dsttime; /* type of dst correction */
|
---|
21 | };
|
---|
22 |
|
---|
23 | int _gettimeofday(struct timeval *tp, struct timezone *tzp);
|
---|
24 |
|
---|
25 | #elif !defined(_POSIX_SOURCE) && !defined(__USG)
|
---|
26 | #if !defined(_MINIX) /* MINIX has no ftime() */
|
---|
27 | struct timeb {
|
---|
28 | long time;
|
---|
29 | unsigned short millitm;
|
---|
30 | short timezone;
|
---|
31 | short dstflag;
|
---|
32 | };
|
---|
33 | void _ftime(struct timeb *bp);
|
---|
34 | #endif
|
---|
35 | #endif
|
---|
36 |
|
---|
37 | #include "loc_time.h"
|
---|
38 |
|
---|
39 | #define RULE_LEN 120
|
---|
40 | #define TZ_LEN 10
|
---|
41 |
|
---|
42 | /* Make sure that the strings do not end up in ROM.
|
---|
43 | * These strings probably contain the wrong value, and we cannot obtain the
|
---|
44 | * right value from the system. TZ is the only help.
|
---|
45 | */
|
---|
46 | static char ntstr[TZ_LEN + 1] = "GMT"; /* string for normal time */
|
---|
47 | static char dststr[TZ_LEN + 1] = "GDT"; /* string for daylight saving */
|
---|
48 |
|
---|
49 | long _timezone = 0;
|
---|
50 | long _dst_off = 60 * 60;
|
---|
51 | int _daylight = 0;
|
---|
52 | char *_tzname[2] = {ntstr, dststr};
|
---|
53 |
|
---|
54 | #if defined(__USG) || defined(_POSIX_SOURCE)
|
---|
55 | char *tzname[2] = {ntstr, dststr};
|
---|
56 |
|
---|
57 | #if defined(__USG)
|
---|
58 | long timezone = 0;
|
---|
59 | int daylight = 0;
|
---|
60 | #endif
|
---|
61 | #endif
|
---|
62 |
|
---|
63 | static struct dsttype {
|
---|
64 | char ds_type; /* Unknown, Julian, Zero-based or M */
|
---|
65 | int ds_date[3]; /* months, weeks, days */
|
---|
66 | long ds_sec; /* usually 02:00:00 */
|
---|
67 | } dststart = { 'U', { 0, 0, 0 }, 2 * 60 * 60 }
|
---|
68 | , dstend = { 'U', { 0, 0, 0 }, 2 * 60 * 60 };
|
---|
69 |
|
---|
70 | const char *_days[] = {
|
---|
71 | "Sunday", "Monday", "Tuesday", "Wednesday",
|
---|
72 | "Thursday", "Friday", "Saturday"
|
---|
73 | };
|
---|
74 |
|
---|
75 | const char *_months[] = {
|
---|
76 | "January", "February", "March",
|
---|
77 | "April", "May", "June",
|
---|
78 | "July", "August", "September",
|
---|
79 | "October", "November", "December"
|
---|
80 | };
|
---|
81 |
|
---|
82 | const int _ytab[2][12] = {
|
---|
83 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
---|
84 | { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
---|
85 | };
|
---|
86 |
|
---|
87 | static const char *
|
---|
88 | parseZoneName(register char *buf, register const char *p)
|
---|
89 | {
|
---|
90 | register int n = 0;
|
---|
91 |
|
---|
92 | if (*p == ':') return NULL;
|
---|
93 | while (*p && !isdigit(*p) && *p != ',' && *p != '-' && *p != '+') {
|
---|
94 | if (n < TZ_LEN)
|
---|
95 | *buf++ = *p;
|
---|
96 | p++;
|
---|
97 | n++;
|
---|
98 | }
|
---|
99 | if (n < 3) return NULL; /* error */
|
---|
100 | *buf = '\0';
|
---|
101 | return p;
|
---|
102 | }
|
---|
103 |
|
---|
104 | static const char *
|
---|
105 | parseTime(register long *tm, const char *p, register struct dsttype *dst)
|
---|
106 | {
|
---|
107 | register int n = 0;
|
---|
108 | register const char *q = p;
|
---|
109 | char ds_type = (dst ? dst->ds_type : '\0');
|
---|
110 |
|
---|
111 | if (dst) dst->ds_type = 'U';
|
---|
112 |
|
---|
113 | *tm = 0;
|
---|
114 | while(*p >= '0' && *p <= '9') {
|
---|
115 | n = 10 * n + (*p++ - '0');
|
---|
116 | }
|
---|
117 | if (q == p) return NULL; /* "The hour shall be required" */
|
---|
118 | if (n < 0 || n >= 24) return NULL;
|
---|
119 | *tm = n * 60 * 60;
|
---|
120 | if (*p == ':') {
|
---|
121 | p++;
|
---|
122 | n = 0;
|
---|
123 | while(*p >= '0' && *p <= '9') {
|
---|
124 | n = 10 * n + (*p++ - '0');
|
---|
125 | }
|
---|
126 | if (q == p) return NULL; /* format error */
|
---|
127 | if (n < 0 || n >= 60) return NULL;
|
---|
128 | *tm += n * 60;
|
---|
129 | if (*p == ':') {
|
---|
130 | p++;
|
---|
131 | n = 0;
|
---|
132 | while(*p >= '0' && *p <= '9') {
|
---|
133 | n = 10 * n + (*p++ - '0');
|
---|
134 | }
|
---|
135 | if (q == p) return NULL; /* format error */
|
---|
136 | if (n < 0 || n >= 60) return NULL;
|
---|
137 | *tm += n;
|
---|
138 | }
|
---|
139 | }
|
---|
140 | if (dst) {
|
---|
141 | dst->ds_type = ds_type;
|
---|
142 | dst->ds_sec = *tm;
|
---|
143 | }
|
---|
144 | return p;
|
---|
145 | }
|
---|
146 |
|
---|
147 | static const char *
|
---|
148 | parseDate(register char *buf, register const char *p, struct dsttype *dstinfo)
|
---|
149 | {
|
---|
150 | register const char *q;
|
---|
151 | register int n = 0;
|
---|
152 | int cnt = 0;
|
---|
153 | const int bnds[3][2] = { { 1, 12 },
|
---|
154 | { 1, 5 },
|
---|
155 | { 0, 6}
|
---|
156 | };
|
---|
157 | char ds_type;
|
---|
158 |
|
---|
159 | if (*p != 'M') {
|
---|
160 | if (*p == 'J') {
|
---|
161 | *buf++ = *p++;
|
---|
162 | ds_type = 'J';
|
---|
163 | }
|
---|
164 | else ds_type = 'Z';
|
---|
165 | q = p;
|
---|
166 | while(*p >= '0' && *p <= '9') {
|
---|
167 | n = 10 * n + (*p - '0');
|
---|
168 | *buf++ = *p++;
|
---|
169 | }
|
---|
170 | if (q == p) return NULL; /* format error */
|
---|
171 | if (n < (ds_type == 'J') || n > 365) return NULL;
|
---|
172 | dstinfo->ds_type = ds_type;
|
---|
173 | dstinfo->ds_date[0] = n;
|
---|
174 | return p;
|
---|
175 | }
|
---|
176 | ds_type = 'M';
|
---|
177 | do {
|
---|
178 | *buf++ = *p++;
|
---|
179 | q = p;
|
---|
180 | n = 0;
|
---|
181 | while(*p >= '0' && *p <= '9') {
|
---|
182 | n = 10 * n + (*p - '0');
|
---|
183 | *buf++ = *p++;
|
---|
184 | }
|
---|
185 | if (q == p) return NULL; /* format error */
|
---|
186 | if (n < bnds[cnt][0] || n > bnds[cnt][1]) return NULL;
|
---|
187 | dstinfo->ds_date[cnt] = n;
|
---|
188 | cnt++;
|
---|
189 | } while (cnt < 3 && *p == '.');
|
---|
190 | if (cnt != 3) return NULL;
|
---|
191 | *buf = '\0';
|
---|
192 | dstinfo->ds_type = ds_type;
|
---|
193 | return p;
|
---|
194 | }
|
---|
195 |
|
---|
196 | static const char *
|
---|
197 | parseRule(register char *buf, register const char *p)
|
---|
198 | {
|
---|
199 | long time;
|
---|
200 | register const char *q;
|
---|
201 |
|
---|
202 | if (!(p = parseDate(buf, p, &dststart))) return NULL;
|
---|
203 | buf += strlen(buf);
|
---|
204 | if (*p == '/') {
|
---|
205 | q = ++p;
|
---|
206 | if (!(p = parseTime(&time, p, &dststart))) return NULL;
|
---|
207 | while( p != q) *buf++ = *q++;
|
---|
208 | }
|
---|
209 | if (*p != ',') return NULL;
|
---|
210 | p++;
|
---|
211 | if (!(p = parseDate(buf, p, &dstend))) return NULL;
|
---|
212 | buf += strlen(buf);
|
---|
213 | if (*p == '/') {
|
---|
214 | q = ++p;
|
---|
215 | if (!(p = parseTime(&time, p, &dstend))) return NULL;
|
---|
216 | while(*buf++ = *q++);
|
---|
217 | }
|
---|
218 | if (*p) return NULL;
|
---|
219 | return p;
|
---|
220 | }
|
---|
221 |
|
---|
222 | /* The following routine parses timezone information in POSIX-format. For
|
---|
223 | * the requirements, see IEEE Std 1003.1-1988 section 8.1.1.
|
---|
224 | * The function returns as soon as it spots an error.
|
---|
225 | */
|
---|
226 | static void
|
---|
227 | parseTZ(const char *p)
|
---|
228 | {
|
---|
229 | long tz, dst = 60 * 60, sign = 1;
|
---|
230 | static char lastTZ[2 * RULE_LEN];
|
---|
231 | static char buffer[RULE_LEN];
|
---|
232 |
|
---|
233 | if (!p) return;
|
---|
234 |
|
---|
235 | if (*p == ':') {
|
---|
236 | /*
|
---|
237 | * According to POSIX, this is implementation defined.
|
---|
238 | * Since it depends on the particular operating system, we
|
---|
239 | * can do nothing.
|
---|
240 | */
|
---|
241 | return;
|
---|
242 | }
|
---|
243 |
|
---|
244 | if (!strcmp(lastTZ, p)) return; /* nothing changed */
|
---|
245 |
|
---|
246 | *_tzname[0] = '\0';
|
---|
247 | *_tzname[1] = '\0';
|
---|
248 | dststart.ds_type = 'U';
|
---|
249 | dststart.ds_sec = 2 * 60 * 60;
|
---|
250 | dstend.ds_type = 'U';
|
---|
251 | dstend.ds_sec = 2 * 60 * 60;
|
---|
252 |
|
---|
253 | if (strlen(p) > 2 * RULE_LEN) return;
|
---|
254 | strcpy(lastTZ, p);
|
---|
255 |
|
---|
256 | if (!(p = parseZoneName(buffer, p))) return;
|
---|
257 |
|
---|
258 | if (*p == '-') {
|
---|
259 | sign = -1;
|
---|
260 | p++;
|
---|
261 | } else if (*p == '+') p++;
|
---|
262 |
|
---|
263 | if (!(p = parseTime(&tz, p, NULL))) return;
|
---|
264 | tz *= sign;
|
---|
265 | _timezone = tz;
|
---|
266 | strncpy(_tzname[0], buffer, TZ_LEN);
|
---|
267 |
|
---|
268 | if (!(_daylight = (*p != '\0'))) return;
|
---|
269 |
|
---|
270 | buffer[0] = '\0';
|
---|
271 | if (!(p = parseZoneName(buffer, p))) return;
|
---|
272 | strncpy(_tzname[1], buffer, TZ_LEN);
|
---|
273 |
|
---|
274 | buffer[0] = '\0';
|
---|
275 | if (*p && (*p != ','))
|
---|
276 | if (!(p = parseTime(&dst, p, NULL))) return;
|
---|
277 | _dst_off = dst; /* dst was initialized to 1 hour */
|
---|
278 | if (*p) {
|
---|
279 | if (*p != ',') return;
|
---|
280 | p++;
|
---|
281 | if (strlen(p) > RULE_LEN) return;
|
---|
282 | if (!(p = parseRule(buffer, p))) return;
|
---|
283 | }
|
---|
284 | }
|
---|
285 |
|
---|
286 | void
|
---|
287 | _tzset(void)
|
---|
288 | {
|
---|
289 | #if defined(__BSD4_2)
|
---|
290 |
|
---|
291 | struct timeval tv;
|
---|
292 | struct timezone tz;
|
---|
293 |
|
---|
294 | _gettimeofday(&tv, &tz);
|
---|
295 | _daylight = tz.tz_dsttime;
|
---|
296 | _timezone = tz.tz_minuteswest * 60L;
|
---|
297 |
|
---|
298 | #elif !defined(_POSIX_SOURCE) && !defined(__USG)
|
---|
299 |
|
---|
300 | #if !defined(_MINIX) /* MINIX has no ftime() */
|
---|
301 | struct timeb time;
|
---|
302 |
|
---|
303 | _ftime(&time);
|
---|
304 | _timezone = time.timezone * 60L;
|
---|
305 | _daylight = time.dstflag;
|
---|
306 | #endif
|
---|
307 |
|
---|
308 | #endif /* !_POSIX_SOURCE && !__USG */
|
---|
309 |
|
---|
310 | parseTZ(getenv("TZ")); /* should go inside #if */
|
---|
311 |
|
---|
312 | #if defined(__USG) || defined(_POSIX_SOURCE)
|
---|
313 | tzname[0] = _tzname[0];
|
---|
314 | tzname[1] = _tzname[1];
|
---|
315 | #if defined(__USG)
|
---|
316 | timezone = _timezone;
|
---|
317 | daylight = _daylight;
|
---|
318 | #endif
|
---|
319 | #endif /* __USG || _POSIX_SOURCE */
|
---|
320 | }
|
---|
321 |
|
---|
322 | static int
|
---|
323 | last_sunday(register int day, register struct tm *timep)
|
---|
324 | {
|
---|
325 | int first = FIRSTSUNDAY(timep);
|
---|
326 |
|
---|
327 | if (day >= 58 && LEAPYEAR(YEAR0 + timep->tm_year)) day++;
|
---|
328 | if (day < first) return first;
|
---|
329 | return day - (day - first) % 7;
|
---|
330 | }
|
---|
331 |
|
---|
332 | static int
|
---|
333 | date_of(register struct dsttype *dst, struct tm *timep)
|
---|
334 | {
|
---|
335 | int leap = LEAPYEAR(YEAR0 + timep->tm_year);
|
---|
336 | int firstday, tmpday;
|
---|
337 | register int day, month;
|
---|
338 |
|
---|
339 | if (dst->ds_type != 'M') {
|
---|
340 | return dst->ds_date[0] -
|
---|
341 | (dst->ds_type == 'J'
|
---|
342 | && leap
|
---|
343 | && dst->ds_date[0] < 58);
|
---|
344 | }
|
---|
345 | day = 0;
|
---|
346 | month = 1;
|
---|
347 | while (month < dst->ds_date[0]) {
|
---|
348 | day += _ytab[leap][month - 1];
|
---|
349 | month++;
|
---|
350 | }
|
---|
351 | firstday = (day + FIRSTDAYOF(timep)) % 7;
|
---|
352 | tmpday = day;
|
---|
353 | day += (dst->ds_date[2] - firstday + 7) % 7
|
---|
354 | + 7 * (dst->ds_date[1] - 1);
|
---|
355 | if (day >= tmpday + _ytab[leap][month-1]) day -= 7;
|
---|
356 | return day;
|
---|
357 | }
|
---|
358 |
|
---|
359 | /*
|
---|
360 | * The default dst transitions are those for Western Europe (except Great
|
---|
361 | * Britain).
|
---|
362 | */
|
---|
363 | unsigned
|
---|
364 | _dstget(register struct tm *timep)
|
---|
365 | {
|
---|
366 | int begindst, enddst;
|
---|
367 | register struct dsttype *dsts = &dststart, *dste = &dstend;
|
---|
368 | int do_dst = 0;
|
---|
369 |
|
---|
370 | if (_daylight == -1)
|
---|
371 | _tzset();
|
---|
372 |
|
---|
373 | timep->tm_isdst = _daylight;
|
---|
374 | if (!_daylight) return 0;
|
---|
375 |
|
---|
376 | if (dsts->ds_type != 'U')
|
---|
377 | begindst = date_of(dsts, timep);
|
---|
378 | else begindst = last_sunday(89, timep); /* last Sun before Apr */
|
---|
379 | if (dste->ds_type != 'U')
|
---|
380 | enddst = date_of(dste, timep);
|
---|
381 | else enddst = last_sunday(272, timep); /* last Sun in Sep */
|
---|
382 |
|
---|
383 | /* assume begindst != enddst (otherwise it would be no use) */
|
---|
384 | if (begindst < enddst) { /* northern hemisphere */
|
---|
385 | if (timep->tm_yday > begindst && timep->tm_yday < enddst)
|
---|
386 | do_dst = 1;
|
---|
387 | } else { /* southern hemisphere */
|
---|
388 | if (timep->tm_yday > begindst || timep->tm_yday < enddst)
|
---|
389 | do_dst = 1;
|
---|
390 | }
|
---|
391 |
|
---|
392 | if (!do_dst
|
---|
393 | && (timep->tm_yday == begindst || timep->tm_yday == enddst)) {
|
---|
394 | long dsttranssec; /* transition when day is this old */
|
---|
395 | long cursec;
|
---|
396 |
|
---|
397 | if (timep->tm_yday == begindst)
|
---|
398 | dsttranssec = dsts->ds_sec;
|
---|
399 | else dsttranssec = dste->ds_sec;
|
---|
400 | cursec = ((timep->tm_hour * 60) + timep->tm_min) * 60L
|
---|
401 | + timep->tm_sec;
|
---|
402 |
|
---|
403 | if ((timep->tm_yday == begindst && cursec >= dsttranssec)
|
---|
404 | || (timep->tm_yday == enddst && cursec < dsttranssec))
|
---|
405 | do_dst = 1;
|
---|
406 | }
|
---|
407 | if (do_dst) return _dst_off;
|
---|
408 | timep->tm_isdst = 0;
|
---|
409 | return 0;
|
---|
410 | }
|
---|