[9] | 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 | }
|
---|