1: /* 2: * @(#)zic.c 1.1 zic.c 3/4/87 3: */ 4: 5: #include "stdio.h" 6: #include "ctype.h" 7: #include "sys/types.h" 8: #include "sys/stat.h" 9: #include "sys/file.h" 10: #include "strings.h" 11: #include "time.h" 12: #include "tzfile.h" 13: 14: #ifndef BUFSIZ 15: #define BUFSIZ 1024 16: #endif 17: 18: #ifndef TRUE 19: #define TRUE 1 20: #define FALSE 0 21: #endif 22: 23: extern char * icpyalloc(); 24: extern char * imalloc(); 25: extern char * irealloc(); 26: extern char * optarg; 27: extern int optind; 28: extern char * scheck(); 29: extern char * sprintf(); 30: 31: static addtt(); 32: static addtype(); 33: static associate(); 34: static int charcnt; 35: static ciequal(); 36: static long eitol(); 37: static int errors; 38: static char * filename; 39: static char ** getfields(); 40: static long gethms(); 41: static infile(); 42: static inlink(); 43: static inrule(); 44: static inzcont(); 45: static inzone(); 46: static inzsub(); 47: static int linenum; 48: static lowerit(); 49: static time_t max_time; 50: static int max_year; 51: static time_t min_time; 52: static int min_year; 53: static mkdirs(); 54: static newabbr(); 55: static int noise; 56: static nondunlink(); 57: static long oadd(); 58: static outzone(); 59: static char * progname; 60: static char * rfilename; 61: static int rlinenum; 62: static time_t rpytime(); 63: static rulesub(); 64: static setboundaries(); 65: static time_t tadd(); 66: static int timecnt; 67: static int tt_signed; 68: static int typecnt; 69: static yearistype(); 70: 71: /* 72: ** Line codes. 73: */ 74: 75: #define LC_RULE 0 76: #define LC_ZONE 1 77: #define LC_LINK 2 78: 79: /* 80: ** Which fields are which on a Zone line. 81: */ 82: 83: #define ZF_NAME 1 84: #define ZF_GMTOFF 2 85: #define ZF_RULE 3 86: #define ZF_FORMAT 4 87: #define ZF_UNTILYEAR 5 88: #define ZF_UNTILMONTH 6 89: #define ZF_UNTILDAY 7 90: #define ZF_UNTILTIME 8 91: #define ZONE_MINFIELDS 5 92: #define ZONE_MAXFIELDS 9 93: 94: /* 95: ** Which fields are which on a Zone continuation line. 96: */ 97: 98: #define ZFC_GMTOFF 0 99: #define ZFC_RULE 1 100: #define ZFC_FORMAT 2 101: #define ZFC_UNTILYEAR 3 102: #define ZFC_UNTILMONTH 4 103: #define ZFC_UNTILDAY 5 104: #define ZFC_UNTILTIME 6 105: #define ZONEC_MINFIELDS 3 106: #define ZONEC_MAXFIELDS 7 107: 108: /* 109: ** Which files are which on a Rule line. 110: */ 111: 112: #define RF_NAME 1 113: #define RF_LOYEAR 2 114: #define RF_HIYEAR 3 115: #define RF_COMMAND 4 116: #define RF_MONTH 5 117: #define RF_DAY 6 118: #define RF_TOD 7 119: #define RF_STDOFF 8 120: #define RF_ABBRVAR 9 121: #define RULE_FIELDS 10 122: 123: /* 124: ** Which fields are which on a Link line. 125: */ 126: 127: #define LF_FROM 1 128: #define LF_TO 2 129: #define LINK_FIELDS 3 130: 131: struct rule { 132: char * r_filename; 133: int r_linenum; 134: char * r_name; 135: 136: int r_loyear; /* for example, 1986 */ 137: int r_hiyear; /* for example, 1986 */ 138: char * r_yrtype; 139: 140: int r_month; /* 0..11 */ 141: 142: int r_dycode; /* see below */ 143: int r_dayofmonth; 144: int r_wday; 145: 146: long r_tod; /* time from midnight */ 147: int r_todisstd; /* above is standard time if TRUE */ 148: /* above is wall clock time if FALSE */ 149: long r_stdoff; /* offset from standard time */ 150: char * r_abbrvar; /* variable part of time zone abbreviation */ 151: 152: int r_todo; /* a rule to do (used in outzone) */ 153: time_t r_temp; /* used in outzone */ 154: }; 155: 156: /* 157: ** r_dycode r_dayofmonth r_wday 158: */ 159: #define DC_DOM 0 /* 1..31 */ /* unused */ 160: #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ 161: #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ 162: 163: /* 164: ** Year synonyms. 165: */ 166: 167: #define YR_MINIMUM 0 168: #define YR_MAXIMUM 1 169: #define YR_ONLY 2 170: 171: static struct rule * rules; 172: static int nrules; /* number of rules */ 173: 174: struct zone { 175: char * z_filename; 176: int z_linenum; 177: 178: char * z_name; 179: long z_gmtoff; 180: char * z_rule; 181: char * z_format; 182: 183: long z_stdoff; 184: 185: struct rule * z_rules; 186: int z_nrules; 187: 188: struct rule z_untilrule; 189: time_t z_untiltime; 190: }; 191: 192: static struct zone * zones; 193: static int nzones; /* number of zones */ 194: 195: struct link { 196: char * l_filename; 197: int l_linenum; 198: char * l_from; 199: char * l_to; 200: }; 201: 202: static struct link * links; 203: static int nlinks; 204: 205: struct lookup { 206: char * l_word; 207: int l_value; 208: }; 209: 210: static struct lookup * byword(); 211: 212: static struct lookup line_codes[] = { 213: "Rule", LC_RULE, 214: "Zone", LC_ZONE, 215: "Link", LC_LINK, 216: NULL, 0 217: }; 218: 219: static struct lookup mon_names[] = { 220: "January", TM_JANUARY, 221: "February", TM_FEBRUARY, 222: "March", TM_MARCH, 223: "April", TM_APRIL, 224: "May", TM_MAY, 225: "June", TM_JUNE, 226: "July", TM_JULY, 227: "August", TM_AUGUST, 228: "September", TM_SEPTEMBER, 229: "October", TM_OCTOBER, 230: "November", TM_NOVEMBER, 231: "December", TM_DECEMBER, 232: NULL, 0 233: }; 234: 235: static struct lookup wday_names[] = { 236: "Sunday", TM_SUNDAY, 237: "Monday", TM_MONDAY, 238: "Tuesday", TM_TUESDAY, 239: "Wednesday", TM_WEDNESDAY, 240: "Thursday", TM_THURSDAY, 241: "Friday", TM_FRIDAY, 242: "Saturday", TM_SATURDAY, 243: NULL, 0 244: }; 245: 246: static struct lookup lasts[] = { 247: "last-Sunday", TM_SUNDAY, 248: "last-Monday", TM_MONDAY, 249: "last-Tuesday", TM_TUESDAY, 250: "last-Wednesday", TM_WEDNESDAY, 251: "last-Thursday", TM_THURSDAY, 252: "last-Friday", TM_FRIDAY, 253: "last-Saturday", TM_SATURDAY, 254: NULL, 0 255: }; 256: 257: static struct lookup begin_years[] = { 258: "minimum", YR_MINIMUM, 259: "maximum", YR_MAXIMUM, 260: NULL, 0 261: }; 262: 263: static struct lookup end_years[] = { 264: "minimum", YR_MINIMUM, 265: "maximum", YR_MAXIMUM, 266: "only", YR_ONLY, 267: NULL, 0 268: }; 269: 270: static int len_months[2][MONS_PER_YEAR] = { 271: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 272: 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 273: }; 274: 275: static int len_years[2] = { 276: DAYS_PER_NYEAR, DAYS_PER_LYEAR 277: }; 278: 279: static time_t ats[TZ_MAX_TIMES]; 280: static unsigned char types[TZ_MAX_TIMES]; 281: static long gmtoffs[TZ_MAX_TYPES]; 282: static char isdsts[TZ_MAX_TYPES]; 283: static char abbrinds[TZ_MAX_TYPES]; 284: static char chars[TZ_MAX_CHARS]; 285: 286: /* 287: ** Memory allocation. 288: */ 289: 290: static char * 291: memcheck(ptr) 292: char * ptr; 293: { 294: if (ptr == NULL) { 295: perror(progname); 296: exit(1); 297: } 298: return ptr; 299: } 300: 301: #define emalloc(size) memcheck(imalloc(size)) 302: #define erealloc(ptr, size) memcheck(irealloc(ptr, size)) 303: #define ecpyalloc(ptr) memcheck(icpyalloc(ptr)) 304: 305: /* 306: ** Error handling. 307: */ 308: 309: static 310: eats(name, num, rname, rnum) 311: char * name; 312: char * rname; 313: { 314: filename = name; 315: linenum = num; 316: rfilename = rname; 317: rlinenum = rnum; 318: } 319: 320: static 321: eat(name, num) 322: char * name; 323: { 324: eats(name, num, (char *) NULL, -1); 325: } 326: 327: static 328: error(string) 329: char * string; 330: { 331: /* 332: ** Match the format of "cc" to allow sh users to 333: ** zic ... 2>&1 | error -t "*" -v 334: ** on BSD systems. 335: */ 336: (void) fprintf(stderr, "\"%s\", line %d: %s", 337: filename, linenum, string); 338: if (rfilename != NULL) 339: (void) fprintf(stderr, " (rule from \"%s\", line %d)", 340: rfilename, rlinenum); 341: (void) fprintf(stderr, "\n"); 342: ++errors; 343: } 344: 345: static 346: usage() 347: { 348: (void) fprintf(stderr, 349: "%s: usage is %s [ -v ] [ -l localtime ] [ -d directory ] [ filename ... ]\n", 350: progname, progname); 351: exit(1); 352: } 353: 354: static char * lcltime = NULL; 355: static char * directory = NULL; 356: 357: main(argc, argv) 358: int argc; 359: char * argv[]; 360: { 361: register int i, j; 362: register int c; 363: 364: #ifdef unix 365: umask(umask(022) | 022); 366: #endif 367: progname = argv[0]; 368: while ((c = getopt(argc, argv, "d:l:v")) != EOF) 369: switch (c) { 370: default: 371: usage(); 372: case 'd': 373: if (directory == NULL) 374: directory = optarg; 375: else { 376: (void) fprintf(stderr, 377: "%s: More than one -d option specified\n", 378: progname); 379: exit(1); 380: } 381: break; 382: case 'l': 383: if (lcltime == NULL) 384: lcltime = optarg; 385: else { 386: (void) fprintf(stderr, 387: "%s: More than one -l option specified\n", 388: progname); 389: exit(1); 390: } 391: break; 392: case 'v': 393: noise = TRUE; 394: break; 395: } 396: if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) 397: usage(); /* usage message by request */ 398: if (directory == NULL) 399: directory = TZDIR; 400: 401: setboundaries(); 402: 403: zones = (struct zone *) emalloc(0); 404: rules = (struct rule *) emalloc(0); 405: links = (struct link *) emalloc(0); 406: for (i = optind; i < argc; ++i) 407: infile(argv[i]); 408: if (errors) 409: exit(1); 410: associate(); 411: for (i = 0; i < nzones; i = j) { 412: /* 413: * Find the next non-continuation zone entry. 414: */ 415: for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) 416: ; 417: outzone(&zones[i], j - i); 418: } 419: /* 420: ** We'll take the easy way out on this last part. 421: */ 422: if (chdir(directory) != 0) { 423: (void) fprintf(stderr, "%s: Can't chdir to ", progname); 424: perror(directory); 425: exit(1); 426: } 427: for (i = 0; i < nlinks; ++i) { 428: nondunlink(links[i].l_to); 429: if (link(links[i].l_from, links[i].l_to) != 0) { 430: (void) fprintf(stderr, "%s: Can't link %s to ", 431: progname, links[i].l_from); 432: perror(links[i].l_to); 433: exit(1); 434: } 435: } 436: if (lcltime != NULL) { 437: nondunlink(TZDEFAULT); 438: if (link(lcltime, TZDEFAULT) != 0) { 439: (void) fprintf(stderr, "%s: Can't link %s to ", 440: progname, lcltime); 441: perror(TZDEFAULT); 442: exit(1); 443: } 444: } 445: exit((errors == 0) ? 0 : 1); 446: } 447: 448: static 449: setboundaries() 450: { 451: register time_t bit; 452: 453: for (bit = 1; bit > 0; bit <<= 1) 454: ; 455: if (bit == 0) { /* time_t is an unsigned type */ 456: tt_signed = FALSE; 457: min_time = 0; 458: max_time = ~(time_t) 0; 459: } else { 460: tt_signed = TRUE; 461: min_time = bit; 462: max_time = bit; 463: ++max_time; 464: max_time = -max_time; 465: } 466: min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year; 467: max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year; 468: } 469: 470: /* 471: ** We get to be careful here since there's a fair chance of root running us. 472: */ 473: 474: static 475: nondunlink(name) 476: char * name; 477: { 478: struct stat s; 479: 480: if (stat(name, &s) != 0) 481: return; 482: if ((s.st_mode & S_IFMT) == S_IFDIR) 483: return; 484: (void) unlink(name); 485: } 486: 487: /* 488: ** Associate sets of rules with zones. 489: */ 490: 491: /* 492: ** Sort by rule name. 493: */ 494: 495: static 496: rcomp(cp1, cp2) 497: char * cp1; 498: char * cp2; 499: { 500: return strcmp(((struct rule *) cp1)->r_name, 501: ((struct rule *) cp2)->r_name); 502: } 503: 504: static 505: associate() 506: { 507: register struct zone * zp; 508: register struct rule * rp; 509: register int base, out; 510: register int i; 511: 512: if (nrules != 0) 513: (void) qsort((char *) rules, nrules, sizeof *rules, rcomp); 514: for (i = 0; i < nzones; ++i) { 515: zp = &zones[i]; 516: zp->z_rules = NULL; 517: zp->z_nrules = 0; 518: } 519: for (base = 0; base < nrules; base = out) { 520: rp = &rules[base]; 521: for (out = base + 1; out < nrules; ++out) 522: if (strcmp(rp->r_name, rules[out].r_name) != 0) 523: break; 524: for (i = 0; i < nzones; ++i) { 525: zp = &zones[i]; 526: if (strcmp(zp->z_rule, rp->r_name) != 0) 527: continue; 528: zp->z_rules = rp; 529: zp->z_nrules = out - base; 530: } 531: } 532: for (i = 0; i < nzones; ++i) { 533: zp = &zones[i]; 534: if (zp->z_nrules == 0) { 535: /* 536: ** Maybe we have a local standard time offset. 537: */ 538: eat(zp->z_filename, zp->z_linenum); 539: zp->z_stdoff = gethms(zp->z_rule, "unruly zone", TRUE); 540: /* 541: ** Note, though, that if there's no rule, 542: ** a '%s' in the format is a bad thing. 543: */ 544: if (index(zp->z_format, '%') != 0) 545: error("%s in ruleless zone"); 546: } 547: } 548: if (errors) 549: exit(1); 550: } 551: 552: static 553: infile(name) 554: char * name; 555: { 556: register FILE * fp; 557: register char ** fields; 558: register char * cp; 559: register struct lookup * lp; 560: register int nfields; 561: register int wantcont; 562: register int num; 563: char buf[BUFSIZ]; 564: 565: if (strcmp(name, "-") == 0) { 566: name = "standard input"; 567: fp = stdin; 568: } else if ((fp = fopen(name, "r")) == NULL) { 569: (void) fprintf(stderr, "%s: Can't open ", progname); 570: perror(name); 571: exit(1); 572: } 573: wantcont = FALSE; 574: for (num = 1; ; ++num) { 575: eat(name, num); 576: if (fgets(buf, sizeof buf, fp) != buf) 577: break; 578: cp = index(buf, '\n'); 579: if (cp == NULL) { 580: error("line too long"); 581: exit(1); 582: } 583: *cp = '\0'; 584: fields = getfields(buf); 585: nfields = 0; 586: while (fields[nfields] != NULL) { 587: if (ciequal(fields[nfields], "-")) 588: fields[nfields] = ""; 589: ++nfields; 590: } 591: if (nfields == 0) { 592: /* nothing to do */ 593: } else if (wantcont) { 594: wantcont = inzcont(fields, nfields); 595: } else { 596: lp = byword(fields[0], line_codes); 597: if (lp == NULL) 598: error("input line of unknown type"); 599: else switch ((int) (lp->l_value)) { 600: case LC_RULE: 601: inrule(fields, nfields); 602: wantcont = FALSE; 603: break; 604: case LC_ZONE: 605: wantcont = inzone(fields, nfields); 606: break; 607: case LC_LINK: 608: inlink(fields, nfields); 609: wantcont = FALSE; 610: break; 611: default: /* "cannot happen" */ 612: (void) fprintf(stderr, 613: "%s: panic: Invalid l_value %d\n", 614: progname, lp->l_value); 615: exit(1); 616: } 617: } 618: free((char *) fields); 619: } 620: if (ferror(fp)) { 621: (void) fprintf(stderr, "%s: Error reading ", progname); 622: perror(filename); 623: exit(1); 624: } 625: if (fp != stdin && fclose(fp)) { 626: (void) fprintf(stderr, "%s: Error closing ", progname); 627: perror(filename); 628: exit(1); 629: } 630: if (wantcont) 631: error("expected continuation line not found"); 632: } 633: 634: /* 635: ** Convert a string of one of the forms 636: ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss 637: ** into a number of seconds. 638: ** A null string maps to zero. 639: ** Call error with errstring and return zero on errors. 640: */ 641: 642: static long 643: gethms(string, errstring, signable) 644: char * string; 645: char * errstring; 646: { 647: int hh, mm, ss, sign; 648: 649: if (string == NULL || *string == '\0') 650: return 0; 651: if (!signable) 652: sign = 1; 653: else if (*string == '-') { 654: sign = -1; 655: ++string; 656: } else sign = 1; 657: if (sscanf(string, scheck(string, "%d"), &hh) == 1) 658: mm = ss = 0; 659: else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2) 660: ss = 0; 661: else if (sscanf(string, scheck(string, "%d:%d:%d"), 662: &hh, &mm, &ss) != 3) { 663: error(errstring); 664: return 0; 665: } 666: if (hh < 0 || hh >= HOURS_PER_DAY || 667: mm < 0 || mm >= MINS_PER_HOUR || 668: ss < 0 || ss >= SECS_PER_MIN) { 669: error(errstring); 670: return 0; 671: } 672: return eitol(sign) * 673: (eitol(hh * MINS_PER_HOUR + mm) * 674: eitol(SECS_PER_MIN) + eitol(ss)); 675: } 676: 677: static 678: inrule(fields, nfields) 679: register char ** fields; 680: { 681: struct rule r; 682: 683: if (nfields != RULE_FIELDS) { 684: error("wrong number of fields on Rule line"); 685: return; 686: } 687: if (*fields[RF_NAME] == '\0') { 688: error("nameless rule"); 689: return; 690: } 691: r.r_filename = filename; 692: r.r_linenum = linenum; 693: r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE); 694: rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], 695: fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); 696: r.r_name = ecpyalloc(fields[RF_NAME]); 697: r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); 698: rules = (struct rule *) erealloc((char *) rules, 699: (nrules + 1) * sizeof *rules); 700: rules[nrules++] = r; 701: } 702: 703: static 704: inzone(fields, nfields) 705: register char ** fields; 706: { 707: register int i; 708: char buf[132]; 709: 710: if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { 711: error("wrong number of fields on Zone line"); 712: return FALSE; 713: } 714: if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { 715: (void) sprintf(buf, 716: "\"Zone %s\" line and -l option are mutually exclusive", 717: TZDEFAULT); 718: error(buf); 719: return FALSE; 720: } 721: for (i = 0; i < nzones; ++i) 722: if (zones[i].z_name != NULL && 723: strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { 724: (void) sprintf(buf, 725: "duplicate zone name %s (file \"%s\", line %d)", 726: fields[ZF_NAME], 727: zones[i].z_filename, 728: zones[i].z_linenum); 729: error(buf); 730: return FALSE; 731: } 732: return inzsub(fields, nfields, FALSE); 733: } 734: 735: static 736: inzcont(fields, nfields) 737: register char ** fields; 738: { 739: if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { 740: error("wrong number of fields on Zone continuation line"); 741: return FALSE; 742: } 743: return inzsub(fields, nfields, TRUE); 744: } 745: 746: static 747: inzsub(fields, nfields, iscont) 748: register char ** fields; 749: { 750: register char * cp; 751: struct zone z; 752: register int i_gmtoff, i_rule, i_format; 753: register int i_untilyear, i_untilmonth; 754: register int i_untilday, i_untiltime; 755: register int hasuntil; 756: 757: if (iscont) { 758: i_gmtoff = ZFC_GMTOFF; 759: i_rule = ZFC_RULE; 760: i_format = ZFC_FORMAT; 761: i_untilyear = ZFC_UNTILYEAR; 762: i_untilmonth = ZFC_UNTILMONTH; 763: i_untilday = ZFC_UNTILDAY; 764: i_untiltime = ZFC_UNTILTIME; 765: z.z_name = NULL; 766: } else { 767: i_gmtoff = ZF_GMTOFF; 768: i_rule = ZF_RULE; 769: i_format = ZF_FORMAT; 770: i_untilyear = ZF_UNTILYEAR; 771: i_untilmonth = ZF_UNTILMONTH; 772: i_untilday = ZF_UNTILDAY; 773: i_untiltime = ZF_UNTILTIME; 774: z.z_name = ecpyalloc(fields[ZF_NAME]); 775: } 776: z.z_filename = filename; 777: z.z_linenum = linenum; 778: z.z_gmtoff = gethms(fields[i_gmtoff], "invalid GMT offset", TRUE); 779: if ((cp = index(fields[i_format], '%')) != 0) { 780: if (*++cp != 's' || index(cp, '%') != 0) { 781: error("invalid abbreviation format"); 782: return FALSE; 783: } 784: } 785: z.z_rule = ecpyalloc(fields[i_rule]); 786: z.z_format = ecpyalloc(fields[i_format]); 787: hasuntil = nfields > i_untilyear; 788: if (hasuntil) { 789: z.z_untilrule.r_filename = filename; 790: z.z_untilrule.r_linenum = linenum; 791: rulesub(&z.z_untilrule, 792: fields[i_untilyear], 793: "only", 794: "", 795: (nfields > i_untilmonth) ? fields[i_untilmonth] : "Jan", 796: (nfields > i_untilday) ? fields[i_untilday] : "1", 797: (nfields > i_untiltime) ? fields[i_untiltime] : "0"); 798: z.z_untiltime = rpytime(&z.z_untilrule, z.z_untilrule.r_loyear); 799: if (iscont && nzones > 0 && z.z_untiltime < max_time && 800: z.z_untiltime > min_time && 801: zones[nzones - 1].z_untiltime >= z.z_untiltime) { 802: error("Zone continuation line end time is not after end time of previous line"); 803: return FALSE; 804: } 805: } 806: zones = (struct zone *) erealloc((char *) zones, 807: (nzones + 1) * sizeof *zones); 808: zones[nzones++] = z; 809: /* 810: ** If there was an UNTIL field on this line, 811: ** there's more information about the zone on the next line. 812: */ 813: return hasuntil; 814: } 815: 816: static 817: inlink(fields, nfields) 818: register char ** fields; 819: { 820: struct link l; 821: 822: if (nfields != LINK_FIELDS) { 823: error("wrong number of fields on Link line"); 824: return; 825: } 826: if (*fields[LF_FROM] == '\0') { 827: error("blank FROM field on Link line"); 828: return; 829: } 830: if (*fields[LF_TO] == '\0') { 831: error("blank TO field on Link line"); 832: return; 833: } 834: l.l_filename = filename; 835: l.l_linenum = linenum; 836: l.l_from = ecpyalloc(fields[LF_FROM]); 837: l.l_to = ecpyalloc(fields[LF_TO]); 838: links = (struct link *) erealloc((char *) links, 839: (nlinks + 1) * sizeof *links); 840: links[nlinks++] = l; 841: } 842: 843: static 844: rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep) 845: register struct rule * rp; 846: char * loyearp; 847: char * hiyearp; 848: char * typep; 849: char * monthp; 850: char * dayp; 851: char * timep; 852: { 853: register struct lookup * lp; 854: register char * cp; 855: 856: if ((lp = byword(monthp, mon_names)) == NULL) { 857: error("invalid month name"); 858: return; 859: } 860: rp->r_month = lp->l_value; 861: rp->r_todisstd = FALSE; 862: cp = timep; 863: if (*cp != '\0') { 864: cp += strlen(cp) - 1; 865: switch (lowerit(*cp)) { 866: case 's': 867: rp->r_todisstd = TRUE; 868: *cp = '\0'; 869: break; 870: case 'w': 871: rp->r_todisstd = FALSE; 872: *cp = '\0'; 873: break; 874: } 875: } 876: rp->r_tod = gethms(timep, "invalid time of day", FALSE); 877: /* 878: ** Year work. 879: */ 880: cp = loyearp; 881: if ((lp = byword(cp, begin_years)) != NULL) switch ((int) lp->l_value) { 882: case YR_MINIMUM: 883: rp->r_loyear = min_year; 884: break; 885: case YR_MAXIMUM: 886: rp->r_loyear = max_year; 887: break; 888: default: /* "cannot happen" */ 889: (void) fprintf(stderr, 890: "%s: panic: Invalid l_value %d\n", 891: progname, lp->l_value); 892: exit(1); 893: } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1 || 894: rp->r_loyear < min_year || rp->r_loyear > max_year) { 895: if (noise) 896: error("invalid starting year"); 897: if (rp->r_loyear > max_year) 898: return; 899: } 900: cp = hiyearp; 901: if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) { 902: case YR_MINIMUM: 903: rp->r_hiyear = min_year; 904: break; 905: case YR_MAXIMUM: 906: rp->r_hiyear = max_year; 907: break; 908: case YR_ONLY: 909: rp->r_hiyear = rp->r_loyear; 910: break; 911: default: /* "cannot happen" */ 912: (void) fprintf(stderr, 913: "%s: panic: Invalid l_value %d\n", 914: progname, lp->l_value); 915: exit(1); 916: } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1 || 917: rp->r_hiyear < min_year || rp->r_hiyear > max_year) { 918: if (noise) 919: error("invalid ending year"); 920: if (rp->r_hiyear < min_year) 921: return; 922: } 923: if (rp->r_hiyear < min_year) 924: return; 925: if (rp->r_loyear < min_year) 926: rp->r_loyear = min_year; 927: if (rp->r_hiyear > max_year) 928: rp->r_hiyear = max_year; 929: if (rp->r_loyear > rp->r_hiyear) { 930: error("starting year greater than ending year"); 931: return; 932: } 933: if (*typep == '\0') 934: rp->r_yrtype = NULL; 935: else { 936: if (rp->r_loyear == rp->r_hiyear) { 937: error("typed single year"); 938: return; 939: } 940: rp->r_yrtype = ecpyalloc(typep); 941: } 942: /* 943: ** Day work. 944: ** Accept things such as: 945: ** 1 946: ** last-Sunday 947: ** Sun<=20 948: ** Sun>=7 949: */ 950: if ((lp = byword(dayp, lasts)) != NULL) { 951: rp->r_dycode = DC_DOWLEQ; 952: rp->r_wday = lp->l_value; 953: rp->r_dayofmonth = len_months[1][rp->r_month]; 954: } else { 955: if ((cp = index(dayp, '<')) != 0) 956: rp->r_dycode = DC_DOWLEQ; 957: else if ((cp = index(dayp, '>')) != 0) 958: rp->r_dycode = DC_DOWGEQ; 959: else { 960: cp = dayp; 961: rp->r_dycode = DC_DOM; 962: } 963: if (rp->r_dycode != DC_DOM) { 964: *cp++ = 0; 965: if (*cp++ != '=') { 966: error("invalid day of month"); 967: return; 968: } 969: if ((lp = byword(dayp, wday_names)) == NULL) { 970: error("invalid weekday name"); 971: return; 972: } 973: rp->r_wday = lp->l_value; 974: } 975: if (sscanf(cp, scheck(cp, "%d"), &rp->r_dayofmonth) != 1 || 976: rp->r_dayofmonth <= 0 || 977: (rp->r_dayofmonth > len_months[1][rp->r_month])) { 978: error("invalid day of month"); 979: return; 980: } 981: } 982: } 983: 984: static 985: puttzcode(val, fp) 986: long val; 987: FILE * fp; 988: { 989: register int c; 990: register int shift; 991: 992: for (shift = 24; shift >= 0; shift -= 8) { 993: c = val >> shift; 994: (void) putc(c, fp); 995: } 996: } 997: 998: static 999: writezone(name) 1000: char * name; 1001: { 1002: register FILE * fp; 1003: register int i; 1004: char fullname[BUFSIZ]; 1005: 1006: if (strlen(directory) + 1 + strlen(name) >= sizeof fullname) { 1007: (void) fprintf(stderr, 1008: "%s: File name %s/%s too long\n", progname, 1009: directory, name); 1010: exit(1); 1011: } 1012: (void) sprintf(fullname, "%s/%s", directory, name); 1013: if ((fp = fopen(fullname, "w")) == NULL) { 1014: if (mkdirs(fullname) != 0) 1015: exit(1); 1016: if ((fp = fopen(fullname, "w")) == NULL) { 1017: (void) fprintf(stderr, "%s: Can't create ", progname); 1018: perror(fullname); 1019: exit(1); 1020: } 1021: } 1022: (void) fseek(fp, (long) sizeof ((struct tzhead *) 0)->tzh_reserved, 0); 1023: puttzcode(eitol(timecnt), fp); 1024: puttzcode(eitol(typecnt), fp); 1025: puttzcode(eitol(charcnt), fp); 1026: for (i = 0; i < timecnt; ++i) 1027: puttzcode((long) ats[i], fp); 1028: if (timecnt > 0) 1029: (void) fwrite((char *) types, sizeof types[0], 1030: (int) timecnt, fp); 1031: for (i = 0; i < typecnt; ++i) { 1032: puttzcode((long) gmtoffs[i], fp); 1033: (void) putc(isdsts[i], fp); 1034: (void) putc(abbrinds[i], fp); 1035: } 1036: if (charcnt != 0) 1037: (void) fwrite(chars, sizeof chars[0], (int) charcnt, fp); 1038: if (ferror(fp) || fclose(fp)) { 1039: (void) fprintf(stderr, "%s: Write error on ", progname); 1040: perror(fullname); 1041: exit(1); 1042: } 1043: } 1044: 1045: static 1046: outzone(zpfirst, zonecount) 1047: struct zone * zpfirst; 1048: { 1049: register struct zone * zp; 1050: register struct rule * rp; 1051: register int i, j; 1052: register int usestart, useuntil; 1053: register time_t starttime, untiltime; 1054: register long gmtoff; 1055: register long stdoff; 1056: register int year; 1057: register long startoff; 1058: register int startisdst; 1059: register int type; 1060: char startbuf[BUFSIZ]; 1061: 1062: /* 1063: ** Now. . .finally. . .generate some useful data! 1064: */ 1065: timecnt = 0; 1066: typecnt = 0; 1067: charcnt = 0; 1068: /* 1069: ** Two guesses. . .the second may well be corrected later. 1070: */ 1071: gmtoff = zpfirst->z_gmtoff; 1072: stdoff = 0; 1073: for (i = 0; i < zonecount; ++i) { 1074: usestart = i > 0; 1075: useuntil = i < (zonecount - 1); 1076: zp = &zpfirst[i]; 1077: eat(zp->z_filename, zp->z_linenum); 1078: startisdst = -1; 1079: if (zp->z_nrules == 0) { 1080: type = addtype(oadd(zp->z_gmtoff, zp->z_stdoff), 1081: zp->z_format, zp->z_stdoff != 0); 1082: if (usestart) 1083: addtt(starttime, type); 1084: gmtoff = zp->z_gmtoff; 1085: stdoff = zp->z_stdoff; 1086: } else for (year = min_year; year <= max_year; ++year) { 1087: if (useuntil && year > zp->z_untilrule.r_hiyear) 1088: break; 1089: /* 1090: ** Mark which rules to do in the current year. 1091: ** For those to do, calculate rpytime(rp, year); 1092: */ 1093: for (j = 0; j < zp->z_nrules; ++j) { 1094: rp = &zp->z_rules[j]; 1095: eats(zp->z_filename, zp->z_linenum, 1096: rp->r_filename, rp->r_linenum); 1097: rp->r_todo = year >= rp->r_loyear && 1098: year <= rp->r_hiyear && 1099: yearistype(year, rp->r_yrtype); 1100: if (rp->r_todo) 1101: rp->r_temp = rpytime(rp, year); 1102: } 1103: for ( ; ; ) { 1104: register int k; 1105: register time_t jtime, ktime; 1106: register long offset; 1107: char buf[BUFSIZ]; 1108: 1109: if (useuntil) { 1110: /* 1111: ** Turn untiltime into GMT 1112: ** assuming the current gmtoff and 1113: ** stdoff values. 1114: */ 1115: offset = gmtoff; 1116: if (!zp->z_untilrule.r_todisstd) 1117: offset = oadd(offset, stdoff); 1118: untiltime = tadd(zp->z_untiltime, 1119: -offset); 1120: } 1121: /* 1122: ** Find the rule (of those to do, if any) 1123: ** that takes effect earliest in the year. 1124: */ 1125: k = -1; 1126: for (j = 0; j < zp->z_nrules; ++j) { 1127: rp = &zp->z_rules[j]; 1128: if (!rp->r_todo) 1129: continue; 1130: eats(zp->z_filename, zp->z_linenum, 1131: rp->r_filename, rp->r_linenum); 1132: offset = gmtoff; 1133: if (!rp->r_todisstd) 1134: offset = oadd(offset, stdoff); 1135: jtime = rp->r_temp; 1136: if (jtime == min_time || 1137: jtime == max_time) 1138: continue; 1139: jtime = tadd(jtime, -offset); 1140: if (k < 0 || jtime < ktime) { 1141: k = j; 1142: ktime = jtime; 1143: } 1144: } 1145: if (k < 0) 1146: break; /* go on to next year */ 1147: rp = &zp->z_rules[k]; 1148: rp->r_todo = FALSE; 1149: if (useuntil && ktime >= untiltime) 1150: break; 1151: if (usestart) { 1152: if (ktime < starttime) { 1153: stdoff = rp->r_stdoff; 1154: startoff = oadd(zp->z_gmtoff, 1155: rp->r_stdoff); 1156: (void) sprintf(startbuf, 1157: zp->z_format, 1158: rp->r_abbrvar); 1159: startisdst = 1160: rp->r_stdoff != 0; 1161: continue; 1162: } 1163: if (ktime != starttime && 1164: startisdst >= 0) 1165: addtt(starttime, addtype(startoff, startbuf, startisdst)); 1166: usestart = FALSE; 1167: } 1168: eats(zp->z_filename, zp->z_linenum, 1169: rp->r_filename, rp->r_linenum); 1170: (void) sprintf(buf, zp->z_format, 1171: rp->r_abbrvar); 1172: offset = oadd(zp->z_gmtoff, rp->r_stdoff); 1173: type = addtype(offset, buf, rp->r_stdoff != 0); 1174: if (timecnt != 0 || rp->r_stdoff != 0) 1175: addtt(ktime, type); 1176: gmtoff = zp->z_gmtoff; 1177: stdoff = rp->r_stdoff; 1178: } 1179: } 1180: /* 1181: ** Now we may get to set starttime for the next zone line. 1182: */ 1183: if (useuntil) 1184: starttime = tadd(zp->z_untiltime, 1185: -gmtoffs[types[timecnt - 1]]); 1186: } 1187: writezone(zpfirst->z_name); 1188: } 1189: 1190: static 1191: addtt(starttime, type) 1192: time_t starttime; 1193: { 1194: if (timecnt != 0 && type == types[timecnt - 1]) 1195: return; /* easy enough! */ 1196: if (timecnt >= TZ_MAX_TIMES) { 1197: error("too many transitions?!"); 1198: exit(1); 1199: } 1200: ats[timecnt] = starttime; 1201: types[timecnt] = type; 1202: ++timecnt; 1203: } 1204: 1205: static 1206: addtype(gmtoff, abbr, isdst) 1207: long gmtoff; 1208: char * abbr; 1209: { 1210: register int i, j; 1211: 1212: /* 1213: ** See if there's already an entry for this zone type. 1214: ** If so, just return its index. 1215: */ 1216: for (i = 0; i < typecnt; ++i) { 1217: if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && 1218: strcmp(abbr, &chars[abbrinds[i]]) == 0) 1219: return i; 1220: } 1221: /* 1222: ** There isn't one; add a new one, unless there are already too 1223: ** many. 1224: */ 1225: if (typecnt >= TZ_MAX_TYPES) { 1226: error("too many local time types"); 1227: exit(1); 1228: } 1229: gmtoffs[i] = gmtoff; 1230: isdsts[i] = isdst; 1231: 1232: for (j = 0; j < charcnt; ++j) 1233: if (strcmp(&chars[j], abbr) == 0) 1234: break; 1235: if (j == charcnt) 1236: newabbr(abbr); 1237: abbrinds[i] = j; 1238: ++typecnt; 1239: return i; 1240: } 1241: 1242: static 1243: yearistype(year, type) 1244: char * type; 1245: { 1246: char buf[BUFSIZ]; 1247: int result; 1248: 1249: if (type == NULL || *type == '\0') 1250: return TRUE; 1251: if (strcmp(type, "uspres") == 0) 1252: return (year % 4) == 0; 1253: if (strcmp(type, "nonpres") == 0) 1254: return (year % 4) != 0; 1255: (void) sprintf(buf, "yearistype %d %s", year, type); 1256: result = system(buf); 1257: if (result == 0) 1258: return TRUE; 1259: if (result == 1 << 8) 1260: return FALSE; 1261: error("Wild result from command execution"); 1262: (void) fprintf(stderr, "%s: command was '%s', result was %d\n", 1263: progname, buf, result); 1264: for ( ; ; ) 1265: exit(1); 1266: } 1267: 1268: static 1269: lowerit(a) 1270: { 1271: return (isascii(a) && isupper(a)) ? tolower(a) : a; 1272: } 1273: 1274: static 1275: ciequal(ap, bp) /* case-insensitive equality */ 1276: register char * ap; 1277: register char * bp; 1278: { 1279: while (lowerit(*ap) == lowerit(*bp++)) 1280: if (*ap++ == '\0') 1281: return TRUE; 1282: return FALSE; 1283: } 1284: 1285: static 1286: isabbr(abbr, word) 1287: register char * abbr; 1288: register char * word; 1289: { 1290: if (lowerit(*abbr) != lowerit(*word)) 1291: return FALSE; 1292: ++word; 1293: while (*++abbr != '\0') 1294: do if (*word == '\0') 1295: return FALSE; 1296: while (lowerit(*word++) != lowerit(*abbr)); 1297: return TRUE; 1298: } 1299: 1300: static struct lookup * 1301: byword(word, table) 1302: register char * word; 1303: register struct lookup * table; 1304: { 1305: register struct lookup * foundlp; 1306: register struct lookup * lp; 1307: 1308: if (word == NULL || table == NULL) 1309: return NULL; 1310: /* 1311: ** Look for exact match. 1312: */ 1313: for (lp = table; lp->l_word != NULL; ++lp) 1314: if (ciequal(word, lp->l_word)) 1315: return lp; 1316: /* 1317: ** Look for inexact match. 1318: */ 1319: foundlp = NULL; 1320: for (lp = table; lp->l_word != NULL; ++lp) 1321: if (isabbr(word, lp->l_word)) 1322: if (foundlp == NULL) 1323: foundlp = lp; 1324: else return NULL; /* multiple inexact matches */ 1325: return foundlp; 1326: } 1327: 1328: static char ** 1329: getfields(cp) 1330: register char * cp; 1331: { 1332: register char * dp; 1333: register char ** array; 1334: register int nsubs; 1335: 1336: if (cp == NULL) 1337: return NULL; 1338: array = (char **) emalloc((strlen(cp) + 1) * sizeof *array); 1339: nsubs = 0; 1340: for ( ; ; ) { 1341: while (isascii(*cp) && isspace(*cp)) 1342: ++cp; 1343: if (*cp == '\0' || *cp == '#') 1344: break; 1345: array[nsubs++] = dp = cp; 1346: do { 1347: if ((*dp = *cp++) != '"') 1348: ++dp; 1349: else while ((*dp = *cp++) != '"') 1350: if (*dp != '\0') 1351: ++dp; 1352: else error("Odd number of quotation marks"); 1353: } while (*cp != '\0' && *cp != '#' && 1354: (!isascii(*cp) || !isspace(*cp))); 1355: if (isascii(*cp) && isspace(*cp)) 1356: ++cp; 1357: *dp = '\0'; 1358: } 1359: array[nsubs] = NULL; 1360: return array; 1361: } 1362: 1363: static long 1364: oadd(t1, t2) 1365: long t1; 1366: long t2; 1367: { 1368: register long t; 1369: 1370: t = t1 + t2; 1371: if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) { 1372: error("time overflow"); 1373: exit(1); 1374: } 1375: return t; 1376: } 1377: 1378: static time_t 1379: tadd(t1, t2) 1380: time_t t1; 1381: long t2; 1382: { 1383: register time_t t; 1384: 1385: if (t1 == max_time && t2 > 0) 1386: return max_time; 1387: if (t1 == min_time && t2 < 0) 1388: return min_time; 1389: t = t1 + t2; 1390: if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) { 1391: error("time overflow"); 1392: exit(1); 1393: } 1394: return t; 1395: } 1396: 1397: /* 1398: ** Given a rule, and a year, compute the date - in seconds since January 1, 1399: ** 1970, 00:00 LOCAL time - in that year that the rule refers to. 1400: */ 1401: 1402: static time_t 1403: rpytime(rp, wantedy) 1404: register struct rule * rp; 1405: register int wantedy; 1406: { 1407: register int y, m, i; 1408: register long dayoff; /* with a nod to Margaret O. */ 1409: register time_t t; 1410: 1411: dayoff = 0; 1412: m = TM_JANUARY; 1413: y = EPOCH_YEAR; 1414: while (wantedy != y) { 1415: if (wantedy > y) { 1416: i = len_years[isleap(y)]; 1417: ++y; 1418: } else { 1419: --y; 1420: i = -len_years[isleap(y)]; 1421: } 1422: dayoff = oadd(dayoff, eitol(i)); 1423: } 1424: while (m != rp->r_month) { 1425: i = len_months[isleap(y)][m]; 1426: dayoff = oadd(dayoff, eitol(i)); 1427: ++m; 1428: } 1429: i = rp->r_dayofmonth; 1430: if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { 1431: if (rp->r_dycode == DC_DOWLEQ) 1432: --i; 1433: else { 1434: error("use of 2/29 in non leap-year"); 1435: exit(1); 1436: } 1437: } 1438: --i; 1439: dayoff = oadd(dayoff, eitol(i)); 1440: if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { 1441: register long wday; 1442: 1443: #define LDAYS_PER_WEEK ((long) DAYS_PER_WEEK) 1444: wday = eitol(EPOCH_WDAY); 1445: /* 1446: ** Don't trust mod of negative numbers. 1447: */ 1448: if (dayoff >= 0) 1449: wday = (wday + dayoff) % LDAYS_PER_WEEK; 1450: else { 1451: wday -= ((-dayoff) % LDAYS_PER_WEEK); 1452: if (wday < 0) 1453: wday += LDAYS_PER_WEEK; 1454: } 1455: while (wday != eitol(rp->r_wday)) 1456: if (rp->r_dycode == DC_DOWGEQ) { 1457: dayoff = oadd(dayoff, (long) 1); 1458: if (++wday >= LDAYS_PER_WEEK) 1459: wday = 0; 1460: ++i; 1461: } else { 1462: dayoff = oadd(dayoff, (long) -1); 1463: if (--wday < 0) 1464: wday = LDAYS_PER_WEEK; 1465: --i; 1466: } 1467: if (i < 0 || i >= len_months[isleap(y)][m]) { 1468: error("no day in month matches rule"); 1469: exit(1); 1470: } 1471: } 1472: if (dayoff < 0 && !tt_signed) { 1473: if (wantedy == rp->r_loyear) 1474: return min_time; 1475: error("time before zero"); 1476: exit(1); 1477: } 1478: t = (time_t) dayoff * SECS_PER_DAY; 1479: /* 1480: ** Cheap overflow check. 1481: */ 1482: if (t / SECS_PER_DAY != dayoff) { 1483: if (wantedy == rp->r_hiyear) 1484: return max_time; 1485: if (wantedy == rp->r_loyear) 1486: return min_time; 1487: error("time overflow"); 1488: exit(1); 1489: } 1490: return tadd(t, rp->r_tod); 1491: } 1492: 1493: static 1494: newabbr(string) 1495: char * string; 1496: { 1497: register int i; 1498: 1499: i = strlen(string) + 1; 1500: if (charcnt + i >= TZ_MAX_CHARS) { 1501: error("too many, or too long, time zone abbreviations"); 1502: exit(1); 1503: } 1504: (void) strcpy(&chars[charcnt], string); 1505: charcnt += eitol(i); 1506: } 1507: 1508: static 1509: mkdirs(name) 1510: char * name; 1511: { 1512: register char * cp; 1513: 1514: if ((cp = name) == NULL || *cp == '\0') 1515: return 0; 1516: while ((cp = index(cp + 1,'/')) != 0) { 1517: *cp = '\0'; 1518: if (access(name,F_OK) && mkdir(name,0755)) { 1519: perror(name); 1520: return -1; 1521: } 1522: *cp = '/'; 1523: } 1524: return 0; 1525: } 1526: 1527: static long 1528: eitol(i) 1529: { 1530: long l; 1531: 1532: l = i; 1533: if (i < 0 && l >= 0 || i == 0 && l != 0 || i > 0 && l <= 0) { 1534: (void) fprintf(stderr, "%s: %d did not sign extend correctly\n", 1535: progname, i); 1536: exit(1); 1537: } 1538: return l; 1539: } 1540: 1541: /* 1542: ** UNIX is a registered trademark of AT&T. 1543: */