home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume4 / settz / tzcomp.c < prev    next >
C/C++ Source or Header  |  1986-11-30  |  25KB  |  1,204 lines

  1. #
  2.  
  3. #include "stdio.h"
  4.  
  5. #ifdef OBJECTID
  6. static char    sccsid[] = "@(#)tzcomp.c    2.1";
  7. #endif
  8.  
  9. #include "tzfile.h"
  10. #include "ctype.h"
  11.  
  12. #ifndef alloc_t
  13. #define alloc_t    unsigned
  14. #endif
  15.  
  16. #ifndef MAL
  17. #define MAL    NULL
  18. #endif
  19.  
  20. #ifndef BUFSIZ
  21. #define BUFSIZ    1024
  22. #endif
  23.  
  24. #ifndef TRUE
  25. #define TRUE    1
  26. #define FALSE    0
  27. #endif
  28.  
  29. #ifdef lint
  30. #define scheck(string, format)    (format)
  31. #endif
  32. #ifndef lint
  33. extern char *    scheck();
  34. #endif
  35.  
  36. extern char *    calloc();
  37. extern char *    malloc();
  38. extern char *    optarg;
  39. extern int    optind;
  40. extern FILE *    popen();
  41. extern char *    realloc();
  42. extern char *    sprintf();
  43. extern char *    strcat();
  44. extern char *    strchr();
  45. extern char *    strcpy();
  46.  
  47. static int    errors;
  48. static char *    filename;
  49. static char **    getfields();
  50. static int    linenum;
  51. static char *    progname;
  52. static long    rpytime();
  53. static long    tadd();
  54.  
  55. #define    SECS_PER_MIN    60L
  56. #define MINS_PER_HOUR    60L
  57. #define HOURS_PER_DAY    24L
  58. #define DAYS_PER_YEAR    365L    /* Except in leap years */
  59. #define    SECS_PER_HOUR    (SECS_PER_MIN * MINS_PER_HOUR)
  60. #define SECS_PER_DAY    (SECS_PER_HOUR * HOURS_PER_DAY)
  61. #define SECS_PER_YEAR    (SECS_PER_DAY * DAYS_PER_YEAR)
  62.  
  63. #define EPOCH_YEAR    1970
  64. #define EPOCH_WDAY    TM_THURSDAY
  65.  
  66. /*
  67. ** Values a la localtime(3)
  68. */
  69.  
  70. #define TM_JANUARY    0
  71. #define TM_FEBRUARY    1
  72. #define TM_MARCH    2
  73. #define TM_APRIL    3
  74. #define TM_MAY        4
  75. #define TM_JUNE        5
  76. #define TM_JULY        6
  77. #define TM_AUGUST    7
  78. #define TM_SEPTEMBER    8
  79. #define TM_OCTOBER    9
  80. #define TM_NOVEMBER    10
  81. #define TM_DECEMBER    11
  82.  
  83. #define TM_SUNDAY    0
  84. #define TM_MONDAY    1
  85. #define TM_TUESDAY    2
  86. #define TM_WEDNESDAY    3
  87. #define TM_THURSDAY    4
  88. #define TM_FRIDAY    5
  89. #define TM_SATURDAY    6
  90.  
  91. /*
  92. ** Line codes.
  93. */
  94.  
  95. #define LC_RULE        0
  96. #define LC_ZONE        1
  97. #define LC_LINK        2
  98.  
  99. /*
  100. ** Which fields are which on a Zone line.
  101. */
  102.  
  103. #define ZF_NAME        1
  104. #define ZF_GMTOFF    2
  105. #define ZF_RULE        3
  106. #define ZF_FORMAT    4
  107. #define ZONE_FIELDS    5
  108.  
  109. /*
  110. ** Which files are which on a Rule line.
  111. */
  112.  
  113. #define    RF_NAME        1
  114. #define RF_LOYEAR    2
  115. #define RF_HIYEAR    3
  116. #define RF_COMMAND    4
  117. #define RF_MONTH    5
  118. #define RF_DAY        6
  119. #define RF_TOD        7
  120. #define RF_STDOFF    8
  121. #define RF_ABBRVAR    9
  122. #define RULE_FIELDS    10
  123.  
  124. /*
  125. ** Which fields are which on a Link line.
  126. */
  127.  
  128. #define LF_FROM        1
  129. #define LF_TO        2
  130. #define LINK_FIELDS    3
  131.  
  132. struct rule {
  133.     char *    r_filename;
  134.     int    r_linenum;
  135.     char *    r_name;
  136.  
  137.     long    r_loyear;    /* for example, 1986 */
  138.     long    r_hiyear;    /* for example, 1986 */
  139.     char *    r_yrtype;
  140.  
  141.     long    r_month;    /* 0..11 */
  142.  
  143.     int    r_dycode;    /* see below */
  144.     long    r_dayofmonth;
  145.     long    r_wday;
  146.  
  147.     long    r_tod;        /* time from midnight */
  148.     int    r_todisstd;    /* above is standard time if TRUE */
  149.                 /* above is wall clock time if FALSE */
  150.     long    r_stdoff;    /* offset from standard time */
  151.     char *    r_abbrvar;    /* variable part of time zone abbreviation */
  152.  
  153.     int    r_type;    /* used when creating output files */
  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. static struct rule *    rules;
  164. static int        nrules;    /* number of rules */
  165.  
  166. struct zone {
  167.     char *        z_filename;
  168.     int        z_linenum;
  169.  
  170.     char *        z_name;
  171.     long        z_gmtoff;
  172.     char *        z_rule;
  173.     char *        z_format;
  174.  
  175.     struct rule *    z_rules;
  176.     int        z_nrules;
  177. };
  178.  
  179. static struct zone *    zones;
  180. static int        nzones;    /* number of zones */
  181.  
  182. struct link {
  183.     char *        l_filename;
  184.     int        l_linenum;
  185.     char *        l_from;
  186.     char *        l_to;
  187. };
  188.  
  189. static struct link *    links;
  190. static int        nlinks;
  191.  
  192. struct lookup {
  193.     char *        l_word;
  194.     long        l_value;
  195. };
  196.  
  197. static struct lookup *    byword();
  198.  
  199. static struct lookup    line_codes[] = {
  200.     "Rule",        LC_RULE,
  201.     "Zone",        LC_ZONE,
  202.     "Link",        LC_LINK,
  203.     NULL,        0
  204. };
  205.  
  206. static struct lookup    mon_names[] = {
  207.     "January",    TM_JANUARY,
  208.     "February",    TM_FEBRUARY,
  209.     "March",    TM_MARCH,
  210.     "April",    TM_APRIL,
  211.     "May",        TM_MAY,
  212.     "June",        TM_JUNE,
  213.     "July",        TM_JULY,
  214.     "August",    TM_AUGUST,
  215.     "September",    TM_SEPTEMBER,
  216.     "October",    TM_OCTOBER,
  217.     "November",    TM_NOVEMBER,
  218.     "December",    TM_DECEMBER,
  219.     NULL,        0
  220. };
  221.  
  222. static struct lookup    wday_names[] = {
  223.     "Sunday",    TM_SUNDAY,
  224.     "Monday",    TM_MONDAY,
  225.     "Tuesday",    TM_TUESDAY,
  226.     "Wednesday",    TM_WEDNESDAY,
  227.     "Thursday",    TM_THURSDAY,
  228.     "Friday",    TM_FRIDAY,
  229.     "Saturday",    TM_SATURDAY,
  230.     NULL,        0
  231. };
  232.  
  233. static struct lookup    lasts[] = {
  234.     "last-Sunday",        TM_SUNDAY,
  235.     "last-Monday",        TM_MONDAY,
  236.     "last-Tuesday",        TM_TUESDAY,
  237.     "last-Wednesday",    TM_WEDNESDAY,
  238.     "last-Thursday",    TM_THURSDAY,
  239.     "last-Friday",        TM_FRIDAY,
  240.     "last-Saturday",    TM_SATURDAY,
  241.     NULL,            0
  242. };
  243.  
  244. static long    mon_lengths[] = {    /* ". . .knuckles are 31. . ." */
  245.     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  246. };
  247.  
  248. static struct tzhead    h;
  249. static long        ats[TZ_MAX_TIMES];
  250. static unsigned char    types[TZ_MAX_TIMES];
  251. static struct ttinfo    ttis[TZ_MAX_TYPES];
  252. static char        chars[TZ_MAX_CHARS];
  253.  
  254. /*
  255. ** Exits.
  256. */
  257.  
  258. static
  259. tameexit()
  260. {
  261.     exit(0);
  262. }
  263.  
  264. static
  265. wild2exit(part1, part2)
  266. char *    part1;
  267. char *    part2;
  268. {
  269.     register char *    between;
  270.  
  271.     if (part1 == NULL)
  272.         part1 = "";
  273.     if (part2 == NULL)
  274.         part2 = "";
  275.     between = (*part1 == '\0' || *part2 == '\0') ? "" : " ";
  276.     (void) fprintf(stderr, "%s: wild %s%s%s\n",
  277.         progname, part1, between, part2);
  278.     exit(1);
  279. }
  280.  
  281. static
  282. wildexit(string)
  283. char *    string;
  284. {
  285.     wild2exit(string, (char *) NULL);
  286. }
  287.  
  288. static
  289. wildrexit(string)
  290. char *    string;
  291. {
  292.     wild2exit("result from", string);
  293. }
  294.  
  295. /*
  296. ** Memory allocation.
  297. */
  298.  
  299. static char *
  300. emalloc(size)
  301. {
  302.     register char *    cp;
  303.  
  304.     if ((cp = malloc((alloc_t) size)) == NULL || cp == MAL)
  305.         wildrexit("malloc");
  306.     return cp;
  307. }
  308.  
  309. static char *
  310. erealloc(ptr, size)
  311. char *    ptr;
  312. {
  313.     register char *    cp;
  314.  
  315.     if ((cp = realloc(ptr, (alloc_t) size)) == NULL)
  316.         wildrexit("realloc");
  317.     return cp;
  318. }
  319.  
  320. static char *
  321. eallocpy(old)
  322. char *    old;
  323. {
  324.     register char *    new;
  325.  
  326.     if (old == NULL)
  327.         old = "";
  328.     new = emalloc(strlen(old) + 1);
  329.     (void) strcpy(new, old);
  330.     return new;
  331. }
  332.  
  333. static
  334. usage()
  335. {
  336.     (void) fprintf(stderr,
  337.     "%s: usage is %s [ -l localtime ] [ -d directory ] [ filename ... ]\n",
  338.         progname, progname);
  339.     exit(1);
  340. }
  341.  
  342. static char *    localtime = NULL;
  343. static char *    directory = NULL;
  344.  
  345. main(argc, argv)
  346. int    argc;
  347. char *    argv[];
  348. {
  349.     register int    i;
  350.     register int    c;
  351.  
  352.     progname = argv[0];
  353.     while ((c = getopt(argc, argv, "d:l:")) != EOF)
  354.         switch (c) {
  355.             default:
  356.                 usage();
  357.             case 'd':
  358.                 if (directory == NULL)
  359.                     directory = optarg;
  360.                 else    wildexit("multiple command line -d's");
  361.                 break;
  362.             case 'l':
  363.                 if (localtime == NULL)
  364.                     localtime = optarg;
  365.                 else    wildexit("multiple command line -l's");
  366.         }
  367.     if (directory == NULL)
  368.         directory = TZDIR;
  369.     if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
  370.         usage();    /* usage message by request */
  371.     zones = (struct zone *) emalloc(0);
  372.     rules = (struct rule *) emalloc(0);
  373.     links = (struct link *) emalloc(0);
  374.     for (i = optind; i < argc; ++i)
  375.         infile(argv[i]);
  376.     if (errors)
  377.         wildexit("input data");
  378.     associate();
  379.     for (i = 0; i < nzones; ++i)
  380.         outzone(&zones[i]);
  381.     /*
  382.     ** We'll take the easy way out on this last part.
  383.     */
  384.     if (chdir(directory) != 0)
  385.         wild2exit("result from chdir to", directory);
  386.     for (i = 0; i < nlinks; ++i) {
  387.         (void) unlink(links[i].l_to);
  388.         if (link(links[i].l_from, links[i].l_to) != 0)
  389.             wild2exit("result creating", links[i].l_to);
  390.     }
  391.     if (localtime != NULL) {
  392.         (void) unlink(TZDEFAULT);
  393.         if (link(localtime, TZDEFAULT) != 0)
  394.             wild2exit("result creating", TZDEFAULT);
  395.     }
  396.     tameexit();
  397. }
  398.  
  399. /*
  400. ** Associate sets of rules with zones.
  401. */
  402.  
  403. /*
  404. ** Sort by rule name, and by magnitude of standard time offset for rules of
  405. ** the same name.  The second sort gets standard time entries to the start
  406. ** of the dsinfo table (and we want a standard time entry at the start of
  407. ** the table, since the first entry gets used for times not covered by the
  408. ** rules).
  409. */
  410.  
  411. static
  412. rcomp(cp1, cp2)
  413. char *    cp1;
  414. char *    cp2;
  415. {
  416.     register struct rule *    rp1;
  417.     register struct rule *    rp2;
  418.     register long        l1, l2;
  419.     register int        diff;
  420.  
  421.     rp1 = (struct rule *) cp1;
  422.     rp2 = (struct rule *) cp2;
  423.     if ((diff = strcmp(rp1->r_name, rp2->r_name)) != 0)
  424.         return diff;
  425.     if ((l1 = rp1->r_stdoff) < 0)
  426.         l1 = -l1;
  427.     if ((l2 = rp2->r_stdoff) < 0)
  428.         l2 = -l2;
  429.     if (l1 > l2)
  430.         return 1;
  431.     else if (l1 < l2)
  432.         return -1;
  433.     else    return 0;
  434. }
  435.  
  436. static
  437. associate()
  438. {
  439.     register struct zone *    zp;
  440.     register struct rule *    rp;
  441.     register int        base, out;
  442.     register int        i;
  443.  
  444.     if (nrules != 0)
  445.         (void) qsort((char *) rules, nrules, sizeof *rules, rcomp);
  446.     base = 0;
  447.     for (i = 0; i < nzones; ++i) {
  448.         zp = &zones[i];
  449.         zp->z_rules = NULL;
  450.         zp->z_nrules = 0;
  451.     }
  452.     while (base < nrules) {
  453.         rp = &rules[base];
  454.         for (out = base + 1; out < nrules; ++out)
  455.             if (strcmp(rp->r_name, rules[out].r_name) != 0)
  456.                 break;
  457.         for (i = 0; i < nzones; ++i) {
  458.             zp = &zones[i];
  459.             if (strcmp(zp->z_rule, rp->r_name) != 0)
  460.                 continue;
  461.             zp->z_rules = rp;
  462.             zp->z_nrules = out - base;
  463.         }
  464.         base = out;
  465.     }
  466.     for (i = 0; i < nzones; ++i) {
  467.         zp = &zones[i];
  468.         if (*zp->z_rule != '\0' && zp->z_nrules == 0) {
  469.             filename = zp->z_filename;
  470.             linenum = zp->z_linenum;
  471.             error("unruly zone");
  472.         }
  473.     }
  474.     if (errors)
  475.         wildexit("unruly zone(s)");
  476. }
  477.  
  478. static
  479. error(string)
  480. char *    string;
  481. {
  482.     (void) fprintf(stderr, "%s: file \"%s\", line %d: wild %s\n",
  483.         progname, filename, linenum, string);
  484.     ++errors;
  485. }
  486.  
  487. static
  488. infile(name)
  489. char *    name;
  490. {
  491.     register FILE *            fp;
  492.     register char **        fields;
  493.     register char *            cp;
  494.     register struct lookup *    lp;
  495.     register int            nfields;
  496.     char                buf[BUFSIZ];
  497.  
  498.     if (strcmp(name, "-") == 0) {
  499.         name = "standard input";
  500.         fp = stdin;
  501.     } else if ((fp = fopen(name, "r")) == NULL)
  502.         wild2exit("result opening", name);
  503.     filename = eallocpy(name);
  504.     for (linenum = 1; ; ++linenum) {
  505.         if (fgets(buf, sizeof buf, fp) != buf)
  506.             break;
  507.         cp = strchr(buf, '\n');
  508.         if (cp == NULL) {
  509.             error("long line");
  510.             wildexit("input data");
  511.         }
  512.         *cp = '\0';
  513.         if ((fields = getfields(buf)) == NULL)
  514.             wildrexit("getfields");
  515.         nfields = 0;
  516.         while (fields[nfields] != NULL) {
  517.             if (ciequal(fields[nfields], "-"))
  518.                 fields[nfields] = "";
  519.             ++nfields;
  520.         }
  521.         if (nfields > 0)    /* non-blank line */
  522.             if ((lp = byword(fields[0], line_codes)) == NULL)
  523.                 error("input line of unknown type");
  524.             else switch (lp->l_value) {
  525.                 case LC_RULE:
  526.                     inrule(fields, nfields);
  527.                     break;
  528.                 case LC_ZONE:
  529.                     inzone(fields, nfields);
  530.                     break;
  531.                 case LC_LINK:
  532.                     inlink(fields, nfields);
  533.                     break;
  534.                 default:    /* "cannot happen" */
  535.                     wildrexit("lookup");
  536.             }
  537.         free((char *) fields);
  538.     }
  539.     if (ferror(fp))
  540.         wild2exit("result reading", filename);
  541.     if (fp != stdin && fclose(fp))
  542.         wild2exit("result closing", filename);
  543. }
  544.  
  545. /*
  546. ** Convert a string of one of the forms
  547. **    h    -h     hh:mm    -hh:mm    hh:mm:ss    -hh:mm:ss
  548. ** into a number of seconds. 
  549. ** Call error with errstring and return zero on errors.
  550. */
  551.  
  552. static long
  553. getoff(string, errstring)
  554. char *    string;
  555. char *    errstring;
  556. {
  557.     long    hh, mm, ss, sign;
  558.  
  559.     if (*string == '-') {
  560.         sign = -1;
  561.         ++string;
  562.     } else    sign = 1;
  563.     if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
  564.         mm = ss = 0;
  565.     else if (sscanf(string, scheck(string, "%ld:%ld"), &hh, &mm) == 2)
  566.         ss = 0;
  567.     else if (sscanf(string, scheck(string, "%ld:%ld:%ld"),
  568.         &hh, &mm, &ss) != 3) {
  569.             error(errstring);
  570.             return 0;
  571.     }
  572.     if (hh < 0 || hh >= HOURS_PER_DAY ||
  573.         mm < 0 || mm >= MINS_PER_HOUR ||
  574.         ss < 0 || ss >= SECS_PER_MIN) {
  575.             error(errstring);
  576.             return 0;
  577.     }
  578.     return (long) sign * (((hh * MINS_PER_HOUR) + mm) * SECS_PER_MIN + ss);
  579. }
  580.  
  581. static
  582. inrule(fields, nfields)
  583. char **    fields;
  584. {
  585.     register struct lookup *    lp;
  586.     register char *            cp;
  587.     struct rule            r;
  588.  
  589.     if (nfields != RULE_FIELDS) {
  590.         error("number of fields on Rule line");
  591.         return;
  592.     }
  593.     r.r_filename = filename;
  594.     r.r_linenum = linenum;
  595.     if ((lp = byword(fields[RF_MONTH], mon_names)) == NULL) {
  596.         error("month name");
  597.         return;
  598.     }
  599.     r.r_month = lp->l_value;
  600.     r.r_todisstd = FALSE;
  601.     cp = fields[RF_TOD];
  602.     if (strlen(cp) > 0) {
  603.         cp += strlen(cp) - 1;
  604.         switch (lowerit(*cp)) {
  605.             case 's':
  606.                 r.r_todisstd = TRUE;
  607.                 *cp = '\0';
  608.                 break;
  609.             case 'w':
  610.                 r.r_todisstd = FALSE;
  611.                 *cp = '\0';
  612.                 break;
  613.         }
  614.     }
  615.     r.r_tod = getoff(fields[RF_TOD], "time of day");
  616.     r.r_stdoff = getoff(fields[RF_STDOFF], "Standard Time offset");
  617.     /*
  618.     ** Year work.
  619.     */
  620.     cp = fields[RF_LOYEAR];
  621.     if (sscanf(cp, scheck(cp, "%ld"), &r.r_loyear) != 1 ||
  622.         r.r_loyear <= 0) {
  623.             error("low year");
  624.             return;
  625.     }
  626.     cp = fields[RF_HIYEAR];
  627.     if (*cp == '\0' || ciequal(cp, "only"))
  628.         r.r_hiyear = r.r_loyear;
  629.     else if (sscanf(cp, scheck(cp, "%ld"), &r.r_hiyear) != 1 ||
  630.         r.r_hiyear <= 0) {
  631.             error("high year");
  632.             return;
  633.     }
  634.     if (r.r_loyear > r.r_hiyear) {
  635.         error("low year (greater than high year)");
  636.         return;
  637.     }
  638.     if (*fields[RF_COMMAND] == '\0')
  639.         r.r_yrtype = NULL;
  640.     else {
  641.         if (r.r_loyear == r.r_hiyear) {
  642.             error("typed single year");
  643.             return;
  644.         }
  645.         r.r_yrtype = eallocpy(fields[RF_COMMAND]);
  646.     }
  647.     /*
  648.     ** Day work.
  649.     ** Accept things such as:
  650.     **    1
  651.     **    last-Sunday
  652.     **    Sun<=20
  653.     **    Sun>=7
  654.     */
  655.     cp = fields[RF_DAY];
  656.     if ((lp = byword(cp, lasts)) != NULL) {
  657.         r.r_dycode = DC_DOWLEQ;
  658.         r.r_wday = lp->l_value;
  659.         r.r_dayofmonth = mon_lengths[r.r_month];
  660.         if (r.r_month == TM_FEBRUARY)
  661.             ++r.r_dayofmonth;
  662.     } else {
  663.         if ((cp = strchr(fields[RF_DAY], '<')) != 0)
  664.             r.r_dycode = DC_DOWLEQ;
  665.         else if ((cp = strchr(fields[RF_DAY], '>')) != 0)
  666.             r.r_dycode = DC_DOWGEQ;
  667.         else {
  668.             cp = fields[RF_DAY];
  669.             r.r_dycode = DC_DOM;
  670.         }
  671.         if (r.r_dycode != DC_DOM) {
  672.             *cp++ = 0;
  673.             if (*cp++ != '=') {
  674.                 error("day of month");
  675.                 return;
  676.             }
  677.             if ((lp = byword(fields[RF_DAY], wday_names)) == NULL) {
  678.                 error("weekday name");
  679.                 return;
  680.             }
  681.             r.r_wday = lp->l_value;
  682.         }
  683.         if (sscanf(cp, scheck(cp, "%ld"), &r.r_dayofmonth) != 1 ||
  684.             r.r_dayofmonth <= 0 ||
  685.             (r.r_dayofmonth > mon_lengths[r.r_month] &&
  686.             r.r_month != TM_FEBRUARY && r.r_dayofmonth != 29)) {
  687.                 error("day of month");
  688.                 return;
  689.         }
  690.     }
  691.     if (*fields[RF_NAME] == '\0') {
  692.         error("nameless rule");
  693.         return;
  694.     }
  695.     r.r_name = eallocpy(fields[RF_NAME]);
  696.     r.r_abbrvar = eallocpy(fields[RF_ABBRVAR]);
  697.     rules = (struct rule *) erealloc((char *) rules,
  698.         (nrules + 1) * sizeof *rules);
  699.     rules[nrules++] = r;
  700. }
  701.  
  702. static
  703. inzone(fields, nfields)
  704. char **    fields;
  705. {
  706.     register char *    cp;
  707.     register int    i;
  708.     struct zone    z;
  709.     char        buf[132];
  710.  
  711.     if (nfields != ZONE_FIELDS) {
  712.         error("number of fields on Zone line");
  713.         return;
  714.     }
  715.     z.z_filename = filename;
  716.     z.z_linenum = linenum;
  717.     for (i = 0; i < nzones; ++i)
  718.         if (strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
  719.             (void) sprintf(buf,
  720.                 "duplicate zone name %s (file \"%s\", line %d)",
  721.                 fields[ZF_NAME],
  722.                 zones[i].z_filename,
  723.                 zones[i].z_linenum);
  724.             error(buf);
  725.             return;
  726.         }
  727.     z.z_gmtoff = getoff(fields[ZF_GMTOFF], "GMT offset");
  728.     if ((cp = strchr(fields[ZF_FORMAT], '%')) != 0) {
  729.         if (*++cp != 's' || strchr(cp, '%') != 0) {
  730.             error("format");
  731.             return;
  732.         }
  733.     }
  734.     z.z_name = eallocpy(fields[ZF_NAME]);
  735.     z.z_rule = eallocpy(fields[ZF_RULE]);
  736.     z.z_format = eallocpy(fields[ZF_FORMAT]);
  737.     zones = (struct zone *) erealloc((char *) zones,
  738.         (nzones + 1) * sizeof *zones);
  739.     zones[nzones++] = z;
  740. }
  741.  
  742. static
  743. inlink(fields, nfields)
  744. char **    fields;
  745. {
  746.     struct link    l;
  747.  
  748.     if (nfields != LINK_FIELDS) {
  749.         error("number of fields on Link line");
  750.         return;
  751.     }
  752.     if (*fields[LF_FROM] == '\0') {
  753.         error("blank FROM field on Link line");
  754.         return;
  755.     }
  756.     if (*fields[LF_TO] == '\0') {
  757.         error("blank TO field on Link line");
  758.         return;
  759.     }
  760.     l.l_filename = filename;
  761.     l.l_linenum = linenum;
  762.     l.l_from = eallocpy(fields[LF_FROM]);
  763.     l.l_to = eallocpy(fields[LF_TO]);
  764.     links = (struct link *) erealloc((char *) links,
  765.         (nlinks + 1) * sizeof *links);
  766.     links[nlinks++] = l;
  767. }
  768.  
  769. static
  770. writezone(name)
  771. char *    name;
  772. {
  773.     register FILE *    fp;
  774.     register int    i;
  775.     char        fullname[BUFSIZ];
  776.  
  777.     if (strlen(directory) + 1 + strlen(name) >= BUFSIZ)
  778.         wild2exit("long directory/file", filename);
  779.     (void) sprintf(fullname, "%s/%s", directory, name);
  780.     if ((fp = fopen(fullname, "w")) == NULL) {
  781.         mkdirs(fullname);
  782.         if ((fp = fopen(fullname, "w")) == NULL)
  783.             wild2exit("result creating", fullname);
  784.     }
  785.     if (fwrite((char *) &h, sizeof h, 1, fp) != 1)
  786.         goto wreck;
  787.     if ((i = h.tzh_timecnt) != 0) {
  788.         if (fwrite((char *) ats, sizeof ats[0], i, fp) != i)
  789.             goto wreck;
  790.         if (fwrite((char *) types, sizeof types[0], i, fp) != i)
  791.             goto wreck;
  792.     }
  793.     if ((i = h.tzh_typecnt) != 0)
  794.         if (fwrite((char *) ttis, sizeof ttis[0], i, fp) != i)
  795.             goto wreck;
  796.     if ((i = h.tzh_charcnt) != 0)
  797.         if (fwrite(chars, sizeof chars[0], i, fp) != i)
  798.             goto wreck;
  799.     if (fclose(fp))
  800.         wild2exit("result closing", fullname);
  801.     return;
  802. wreck:
  803.     wild2exit("result writing", fullname);
  804. }
  805.  
  806. struct temp {
  807.     long        t_time;
  808.     struct rule *    t_rp;
  809. };
  810.  
  811. static struct temp    temps[TZ_MAX_TIMES];
  812. static int        ntemps;
  813.  
  814. static
  815. tcomp(cp1, cp2)
  816. char *    cp1;
  817. char *    cp2;
  818. {
  819.     register struct temp *    tp1;
  820.     register struct temp *    tp2;
  821.     register char *        cp;
  822.     register long        diff;
  823.  
  824.     tp1 = (struct temp *) cp1;
  825.     tp2 = (struct temp *) cp2;
  826.     if (tp1->t_time > 0 && tp2->t_time <= 0)
  827.         return 1;
  828.     if (tp1->t_time <= 0 && tp2->t_time > 0)
  829.         return -1;
  830.     if ((diff = tp1->t_time - tp2->t_time) > 0)
  831.         return 1;
  832.     else if (diff < 0)
  833.         return -1;
  834.     /*
  835.     ** Oops!
  836.     */
  837.     if (tp1->t_rp->r_type == tp2->t_rp->r_type)
  838.         cp = "duplicate rule?!";
  839.     else    cp = "inconsistent rules?!";
  840.     filename = tp1->t_rp->r_filename;
  841.     linenum = tp1->t_rp->r_linenum;
  842.     error(cp);
  843.     filename = tp2->t_rp->r_filename;
  844.     linenum = tp2->t_rp->r_linenum;
  845.     error(cp);
  846.     wildexit(cp);
  847.     /*NOTREACHED*/
  848. }
  849.  
  850. static
  851. addrule(rp, y)
  852. register struct rule *    rp;
  853. long            y;
  854. {
  855.     if (ntemps >= TZ_MAX_TIMES) {
  856.         error("too many transitions?!");
  857.         wildexit("large number of transitions");
  858.     }
  859.     temps[ntemps].t_time = rpytime(rp, y);
  860.     temps[ntemps].t_rp = rp;
  861.     ++ntemps;
  862. }
  863.  
  864. static
  865. outzone(zp)
  866. register struct zone *    zp;
  867. {
  868.     register struct rule *        rp;
  869.     register int            i, j;
  870.     register long            y;
  871.     register long            gmtoff;
  872.     char                buf[BUFSIZ];
  873.  
  874.     h.tzh_timecnt = 0;
  875.     h.tzh_typecnt = 0;
  876.     h.tzh_charcnt = 0;
  877.     if (zp->z_nrules == 0) {    /* Piece of cake! */
  878.         h.tzh_timecnt = 0;
  879.         h.tzh_typecnt = 1;
  880.         ttis[0].tt_gmtoff = zp->z_gmtoff;
  881.         ttis[0].tt_isdst = 0;
  882.         ttis[0].tt_abbrind = 0;
  883.         newabbr(zp->z_format);
  884.         writezone(zp->z_name);
  885.         return;
  886.     }
  887.     /*
  888.     ** See what the different local time types are.
  889.     ** Plug the indices into the rules.
  890.     */
  891.     for (i = 0; i < zp->z_nrules; ++i) {
  892.         rp = &zp->z_rules[i];
  893.         (void) sprintf(buf, zp->z_format, rp->r_abbrvar);
  894.         gmtoff = tadd(zp->z_gmtoff, rp->r_stdoff);
  895.         for (j = 0; j < h.tzh_typecnt; ++j) {
  896.             if (gmtoff == ttis[j].tt_gmtoff &&
  897.                 strcmp(buf, &chars[ttis[j].tt_abbrind]) == 0)
  898.                     break;
  899.         }
  900.         if (j >= h.tzh_typecnt) {
  901.             if (h.tzh_typecnt >= TZ_MAX_TYPES) {
  902.                 filename = zp->z_filename;
  903.                 linenum = zp->z_linenum;
  904.                 error("large number of local time types");
  905.                 wildexit("input data");
  906.             }
  907.             ttis[j].tt_gmtoff = gmtoff;
  908.             ttis[j].tt_isdst = rp->r_stdoff != 0;
  909.             ttis[j].tt_abbrind = h.tzh_charcnt;
  910.             newabbr(buf);
  911.             ++h.tzh_typecnt;
  912.         }
  913.         rp->r_type = j;
  914.     }
  915.     /*
  916.     ** Now. . .finally. . .generate some useable data!
  917.     */
  918.     ntemps = 0;
  919.     for (i = 0; i < zp->z_nrules; ++i) {
  920.         rp = &zp->z_rules[i];
  921.         filename = rp->r_filename;
  922.         linenum = rp->r_linenum;
  923.         if (rp->r_yrtype != NULL && *rp->r_yrtype != '\0')
  924.             hard(rp);
  925.         else for (y = rp->r_loyear; y <= rp->r_hiyear; ++y)
  926.             addrule(rp, y);
  927.     }
  928.     h.tzh_timecnt = ntemps;
  929.     (void) qsort((char *) temps, ntemps, sizeof *temps, tcomp);
  930.     for (i = 0; i < ntemps; ++i) {
  931.         rp = temps[i].t_rp;
  932.         filename = rp->r_filename;
  933.         linenum = rp->r_linenum;
  934.         types[i] = rp->r_type;
  935.         ats[i] = tadd(temps[i].t_time, -zp->z_gmtoff);
  936.         if (!rp->r_todisstd) {
  937.             /*
  938.             ** Credit to munnari!kre for pointing out the need for
  939.             ** the following.  (This can still mess up on the
  940.             ** earliest rule; who's got the solution?  It can also
  941.             ** mess up if a time switch results in a day switch;
  942.             ** this is left as an exercise for the reader.)
  943.             */
  944.             if (i == 0) {
  945.                 /*
  946.                 ** Kludge--not guaranteed to work.
  947.                 */
  948.                 if (ntemps > 1)
  949.                     ats[0] = tadd(ats[0],
  950.                         -temps[1].t_rp->r_stdoff);
  951.             } else    ats[i] = tadd(ats[i],
  952.                 -temps[i - 1].t_rp->r_stdoff);
  953.         }
  954.     }
  955.     writezone(zp->z_name);
  956.     return;
  957. }
  958.  
  959. static
  960. hard(rp)
  961. register struct rule *    rp;
  962. {
  963.     register FILE *    fp;
  964.     register int    n;
  965.     long        y;
  966.     char        buf[BUFSIZ];
  967.     char        command[BUFSIZ];
  968.  
  969.     (void) sprintf(command, "years %ld %ld %s",
  970.         rp->r_loyear, rp->r_hiyear, rp->r_yrtype);
  971.     if ((fp = popen(command, "r")) == NULL)
  972.         wild2exit("result opening pipe to", command);
  973.     for (n = 0; fgets(buf, sizeof buf, fp) == buf; ++n) {
  974.         if (strchr(buf, '\n') == 0)
  975.             wildrexit(command);
  976.         *strchr(buf, '\n') = '\0';
  977.         if (sscanf(buf, scheck(buf, "%ld"), &y) != 1)
  978.             wildrexit(command);
  979.         if (y < rp->r_loyear || y > rp->r_hiyear)
  980.             wildrexit(command);
  981.         addrule(rp, y);
  982.     }
  983.     if (ferror(fp))
  984.         wild2exit("result reading from", command);
  985.     if (pclose(fp))
  986.         wild2exit("result closing pipe to", command);
  987.     if (n == 0) {
  988.         error("no year in range matches type");
  989.         wildexit("input data");
  990.     }
  991. }
  992.  
  993. static
  994. lowerit(a)
  995. {
  996.     return (isascii(a) && isupper(a)) ? tolower(a) : a;
  997. }
  998.  
  999. static
  1000. ciequal(ap, bp)        /* case-insensitive equality */
  1001. register char *    ap;
  1002. register char *    bp;
  1003. {
  1004.     while (lowerit(*ap) == lowerit(*bp++))
  1005.         if (*ap++ == '\0')
  1006.             return TRUE;
  1007.     return FALSE;
  1008. }
  1009.  
  1010. static
  1011. isabbr(abbr, word)
  1012. register char *        abbr;
  1013. register char *        word;
  1014. {
  1015.     if (lowerit(*abbr) != lowerit(*word))
  1016.         return FALSE;
  1017.     ++word;
  1018.     while (*++abbr != '\0')
  1019.         do if (*word == '\0')
  1020.             return FALSE;
  1021.                 while (lowerit(*word++) != lowerit(*abbr));
  1022.     return TRUE;
  1023. }
  1024.  
  1025. static struct lookup *
  1026. byword(word, table)
  1027. register char *            word;
  1028. register struct lookup *    table;
  1029. {
  1030.     register struct lookup *    foundlp;
  1031.     register struct lookup *    lp;
  1032.  
  1033.     if (word == NULL || table == NULL)
  1034.         return NULL;
  1035.     foundlp = NULL;
  1036.     for (lp = table; lp->l_word != NULL; ++lp)
  1037.         if (ciequal(word, lp->l_word))        /* "exact" match */
  1038.             return lp;
  1039.         else if (!isabbr(word, lp->l_word))
  1040.             continue;
  1041.         else if (foundlp == NULL)
  1042.             foundlp = lp;
  1043.         else    return NULL;        /* two inexact matches */
  1044.     return foundlp;
  1045. }
  1046.  
  1047. static char **
  1048. getfields(cp)
  1049. register char *    cp;
  1050. {
  1051.     register char *        dp;
  1052.     register char **    array;
  1053.     register int        nsubs;
  1054.  
  1055.     if (cp == NULL)
  1056.         return NULL;
  1057.     array = (char **) emalloc((strlen(cp) + 1) * sizeof *array);
  1058.     nsubs = 0;
  1059.     for ( ; ; ) {
  1060.         while (isascii(*cp) && isspace(*cp))
  1061.             ++cp;
  1062.         if (*cp == '\0' || *cp == '#')
  1063.             break;
  1064.         array[nsubs++] = dp = cp;
  1065.         do {
  1066.             if ((*dp = *cp++) != '"')
  1067.                 ++dp;
  1068.             else while ((*dp = *cp++) != '"')
  1069.                 if (*dp != '\0')
  1070.                     ++dp;
  1071.                 else    error("Odd number of quotation marks");
  1072.         } while (*cp != '\0' && *cp != '#' &&
  1073.             (!isascii(*cp) || !isspace(*cp)));
  1074.         if (isascii(*cp) && isspace(*cp))
  1075.             ++cp;
  1076.         *dp++ = '\0';
  1077.     }
  1078.     array[nsubs] = NULL;
  1079.     return array;
  1080. }
  1081.  
  1082. static long
  1083. tadd(t1, t2)
  1084. long    t1;
  1085. long    t2;
  1086. {
  1087.     register long    t;
  1088.  
  1089.     t = t1 + t2;
  1090.     if (t1 > 0 && t2 > 0 && t <= 0 || t1 < 0 && t2 < 0 && t >= 0) {
  1091.         error("time overflow");
  1092.         wildexit("time overflow");
  1093.     }
  1094.     return t;
  1095. }
  1096.  
  1097. static
  1098. isleap(y)
  1099. long    y;
  1100. {
  1101.     return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
  1102. }
  1103.  
  1104. static long
  1105. rpytime(rp, wantedy)
  1106. register struct rule *    rp;
  1107. register long        wantedy;
  1108. {
  1109.     register long    i, y, wday, t, m;
  1110.     register long    dayoff;            /* with a nod to Margaret O. */
  1111.  
  1112.     dayoff = 0;
  1113.     m = TM_JANUARY;
  1114.     y = EPOCH_YEAR;
  1115.     while (wantedy != y) {
  1116.         if (wantedy > y) {
  1117.             i = DAYS_PER_YEAR;
  1118.             if (isleap(y))
  1119.                 ++i;
  1120.             ++y;
  1121.         } else {
  1122.             --y;
  1123.             i = -DAYS_PER_YEAR;
  1124.             if (isleap(y))
  1125.                 --i;
  1126.         }
  1127.         dayoff = tadd(dayoff, i);
  1128.     }
  1129.     while (m != rp->r_month) {
  1130.         i = mon_lengths[m];
  1131.         if (m == TM_FEBRUARY && isleap(y))
  1132.             ++i;
  1133.         dayoff = tadd(dayoff, i);
  1134.         ++m;
  1135.     }
  1136.     i = rp->r_dayofmonth;
  1137.     if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
  1138.         if (rp->r_dycode == DC_DOWLEQ)
  1139.             --i;
  1140.         else {
  1141.             error("use of 2/29 in non leap-year");
  1142.             for ( ; ; )
  1143.                 wildexit("data");
  1144.         }
  1145.     }
  1146.     --i;
  1147.     dayoff = tadd(dayoff, i);
  1148.     if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
  1149.         wday = EPOCH_WDAY;
  1150.         /*
  1151.         ** Don't trust mod of negative numbers.
  1152.         */
  1153.         if (dayoff >= 0)
  1154.             wday = (wday + dayoff) % 7;
  1155.         else {
  1156.             wday -= ((-dayoff) % 7);
  1157.             if (wday < 0)
  1158.                 wday += 7;
  1159.         }
  1160.         while (wday != rp->r_wday) {
  1161.             if (rp->r_dycode == DC_DOWGEQ)
  1162.                 i = 1;
  1163.             else    i = -1;
  1164.             dayoff = tadd(dayoff, i);
  1165.             wday = (wday + i + 7) % 7;
  1166.         }
  1167.     }
  1168.     t = dayoff * SECS_PER_DAY;
  1169.     /*
  1170.     ** Cheap overflow check.
  1171.     */
  1172.     if (t / SECS_PER_DAY != dayoff)
  1173.         error("time overflow");
  1174.     return tadd(t, rp->r_tod);
  1175. }
  1176.  
  1177. static
  1178. newabbr(string)
  1179. char *    string;
  1180. {
  1181.     register int    i;
  1182.  
  1183.     i = strlen(string);
  1184.     if (h.tzh_charcnt + i >= TZ_MAX_CHARS)
  1185.         error("long time zone abbreviations");
  1186.     (void) strcpy(&chars[h.tzh_charcnt], string);
  1187.     h.tzh_charcnt += i + 1;
  1188. }
  1189.  
  1190. static
  1191. mkdirs(name)
  1192. char *    name;
  1193. {
  1194.     register char *    cp;
  1195.  
  1196.     if ((cp = name) == NULL || *cp == '\0')
  1197.         return;
  1198.     while ((cp = strchr(cp + 1, '/')) != 0) {
  1199.         *cp = '\0';
  1200.         (void) mkdir(name);
  1201.         *cp = '/';
  1202.     }
  1203. }
  1204.