1: /*
   2:  * misc - data and miscellaneous routines
   3:  */
   4: /* $Header: misc.c,v 1.11 91/04/22 13:20:45 ceriel 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]) 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: }