148425Sbostic /*-
2*63909Sbostic * Copyright (c) 1991, 1993
3*63909Sbostic * The Regents of the University of California. All rights reserved.
448425Sbostic *
548425Sbostic * This code is derived from software contributed to Berkeley by
648425Sbostic * Arthur David Olson of the National Cancer Institute.
748425Sbostic *
848425Sbostic * %sccs.include.redist.c%
948425Sbostic */
1048425Sbostic
1139991Sbostic #ifndef lint
12*63909Sbostic static char sccsid[] = "@(#)zic.c 8.1 (Berkeley) 07/19/93";
1348425Sbostic #endif /* not lint */
1448425Sbostic
1548425Sbostic #ifdef notdef
1639991Sbostic static char elsieid[] = "@(#)zic.c 4.12";
1748425Sbostic #endif
1839991Sbostic
1946793Sbostic #include <sys/types.h>
2046793Sbostic #include <sys/cdefs.h>
2146793Sbostic #include <sys/stat.h>
2246793Sbostic #include <time.h>
2346793Sbostic #include <tzfile.h>
2446793Sbostic #include <stdio.h>
2546793Sbostic #include <ctype.h>
2646793Sbostic #include <string.h>
2746793Sbostic #include <stdlib.h>
2839991Sbostic
2939991Sbostic #ifndef TRUE
3039991Sbostic #define TRUE 1
3139991Sbostic #define FALSE 0
3239991Sbostic #endif /* !defined TRUE */
3339991Sbostic
3439991Sbostic struct rule {
3539991Sbostic const char * r_filename;
3639991Sbostic int r_linenum;
3739991Sbostic const char * r_name;
3839991Sbostic
3939991Sbostic int r_loyear; /* for example, 1986 */
4039991Sbostic int r_hiyear; /* for example, 1986 */
4139991Sbostic const char * r_yrtype;
4239991Sbostic
4339991Sbostic int r_month; /* 0..11 */
4439991Sbostic
4539991Sbostic int r_dycode; /* see below */
4639991Sbostic int r_dayofmonth;
4739991Sbostic int r_wday;
4839991Sbostic
4939991Sbostic long r_tod; /* time from midnight */
5039991Sbostic int r_todisstd; /* above is standard time if TRUE */
5139991Sbostic /* or wall clock time if FALSE */
5239991Sbostic long r_stdoff; /* offset from standard time */
5339991Sbostic const char * r_abbrvar; /* variable part of abbreviation */
5439991Sbostic
5539991Sbostic int r_todo; /* a rule to do (used in outzone) */
5639991Sbostic time_t r_temp; /* used in outzone */
5739991Sbostic };
5839991Sbostic
5939991Sbostic /*
6039991Sbostic ** r_dycode r_dayofmonth r_wday
6139991Sbostic */
6239991Sbostic
6339991Sbostic #define DC_DOM 0 /* 1..31 */ /* unused */
6439991Sbostic #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
6539991Sbostic #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
6639991Sbostic
6739991Sbostic struct zone {
6839991Sbostic const char * z_filename;
6939991Sbostic int z_linenum;
7039991Sbostic
7139991Sbostic const char * z_name;
7239991Sbostic long z_gmtoff;
7339991Sbostic const char * z_rule;
7439991Sbostic const char * z_format;
7539991Sbostic
7639991Sbostic long z_stdoff;
7739991Sbostic
7839991Sbostic struct rule * z_rules;
7939991Sbostic int z_nrules;
8039991Sbostic
8139991Sbostic struct rule z_untilrule;
8239991Sbostic time_t z_untiltime;
8339991Sbostic };
8439991Sbostic
8546793Sbostic extern char * icatalloc __P((char * old, const char * new));
8646793Sbostic extern char * icpyalloc __P((const char * string));
8746793Sbostic extern void ifree __P((char * p));
8846793Sbostic extern char * imalloc __P((int n));
8946793Sbostic extern char * irealloc __P((char * old, int n));
9046793Sbostic extern int link __P((const char * fromname, const char * toname));
9139991Sbostic extern char * optarg;
9239991Sbostic extern int optind;
9346793Sbostic extern void perror __P((const char * string));
9446793Sbostic extern char * scheck __P((const char * string, const char * format));
9546793Sbostic static void addtt __P((time_t starttime, int type));
9646793Sbostic static int addtype
9746793Sbostic __P((long gmtoff, const char * abbr, int isdst,
9846793Sbostic int ttisstd));
9946793Sbostic static void addleap __P((time_t t, int positive, int rolling));
10046793Sbostic static void adjleap __P((void));
10146793Sbostic static void associate __P((void));
10246793Sbostic static int ciequal __P((const char * ap, const char * bp));
10346793Sbostic static void convert __P((long val, char * buf));
10446793Sbostic static void dolink __P((const char * fromfile, const char * tofile));
10546793Sbostic static void eat __P((const char * name, int num));
10646793Sbostic static void eats __P((const char * name, int num,
10746793Sbostic const char * rname, int rnum));
10846793Sbostic static long eitol __P((int i));
10946793Sbostic static void error __P((const char * message));
11046793Sbostic static char ** getfields __P((char * buf));
11146793Sbostic static long gethms __P((char * string, const char * errstrng,
11246793Sbostic int signable));
11346793Sbostic static void infile __P((const char * filename));
11446793Sbostic static void inleap __P((char ** fields, int nfields));
11546793Sbostic static void inlink __P((char ** fields, int nfields));
11646793Sbostic static void inrule __P((char ** fields, int nfields));
11746793Sbostic static int inzcont __P((char ** fields, int nfields));
11846793Sbostic static int inzone __P((char ** fields, int nfields));
11946793Sbostic static int inzsub __P((char ** fields, int nfields, int iscont));
12046793Sbostic static int itsabbr __P((const char * abbr, const char * word));
12146793Sbostic static int itsdir __P((const char * name));
12246793Sbostic static int lowerit __P((int c));
12346793Sbostic static char * memcheck __P((char * tocheck));
12446793Sbostic static int mkdirs __P((char * filename));
12546793Sbostic static void newabbr __P((const char * abbr));
12646793Sbostic static long oadd __P((long t1, long t2));
12746793Sbostic static void outzone __P((const struct zone * zp, int ntzones));
12846793Sbostic static void puttzcode __P((long code, FILE * fp));
12946793Sbostic static int rcomp __P((const void *leftp, const void *rightp));
13046793Sbostic static time_t rpytime __P((const struct rule * rp, int wantedy));
13146793Sbostic static void rulesub __P((struct rule * rp, char * loyearp, char * hiyearp,
13246793Sbostic char * typep, char * monthp, char * dayp, char * timep));
13346793Sbostic static void setboundaries __P((void));
13446793Sbostic static time_t tadd __P((time_t t1, long t2));
13546793Sbostic static void usage __P((void));
13646793Sbostic static void writezone __P((const char * name));
13746793Sbostic static int yearistype __P((int year, const char * type));
13839991Sbostic
13939991Sbostic static int charcnt;
14039991Sbostic static int errors;
14139991Sbostic static const char * filename;
14239991Sbostic static int leapcnt;
14339991Sbostic static int linenum;
14439991Sbostic static time_t max_time;
14539991Sbostic static int max_year;
14639991Sbostic static time_t min_time;
14739991Sbostic static int min_year;
14839991Sbostic static int noise;
14939991Sbostic static const char * rfilename;
15039991Sbostic static int rlinenum;
15139991Sbostic static const char * progname;
15239991Sbostic static int timecnt;
15339991Sbostic static int typecnt;
15439991Sbostic static int tt_signed;
15539991Sbostic
15639991Sbostic /*
15739991Sbostic ** Line codes.
15839991Sbostic */
15939991Sbostic
16039991Sbostic #define LC_RULE 0
16139991Sbostic #define LC_ZONE 1
16239991Sbostic #define LC_LINK 2
16339991Sbostic #define LC_LEAP 3
16439991Sbostic
16539991Sbostic /*
16639991Sbostic ** Which fields are which on a Zone line.
16739991Sbostic */
16839991Sbostic
16939991Sbostic #define ZF_NAME 1
17039991Sbostic #define ZF_GMTOFF 2
17139991Sbostic #define ZF_RULE 3
17239991Sbostic #define ZF_FORMAT 4
17339991Sbostic #define ZF_TILYEAR 5
17439991Sbostic #define ZF_TILMONTH 6
17539991Sbostic #define ZF_TILDAY 7
17639991Sbostic #define ZF_TILTIME 8
17739991Sbostic #define ZONE_MINFIELDS 5
17839991Sbostic #define ZONE_MAXFIELDS 9
17939991Sbostic
18039991Sbostic /*
18139991Sbostic ** Which fields are which on a Zone continuation line.
18239991Sbostic */
18339991Sbostic
18439991Sbostic #define ZFC_GMTOFF 0
18539991Sbostic #define ZFC_RULE 1
18639991Sbostic #define ZFC_FORMAT 2
18739991Sbostic #define ZFC_TILYEAR 3
18839991Sbostic #define ZFC_TILMONTH 4
18939991Sbostic #define ZFC_TILDAY 5
19039991Sbostic #define ZFC_TILTIME 6
19139991Sbostic #define ZONEC_MINFIELDS 3
19239991Sbostic #define ZONEC_MAXFIELDS 7
19339991Sbostic
19439991Sbostic /*
19539991Sbostic ** Which files are which on a Rule line.
19639991Sbostic */
19739991Sbostic
19839991Sbostic #define RF_NAME 1
19939991Sbostic #define RF_LOYEAR 2
20039991Sbostic #define RF_HIYEAR 3
20139991Sbostic #define RF_COMMAND 4
20239991Sbostic #define RF_MONTH 5
20339991Sbostic #define RF_DAY 6
20439991Sbostic #define RF_TOD 7
20539991Sbostic #define RF_STDOFF 8
20639991Sbostic #define RF_ABBRVAR 9
20739991Sbostic #define RULE_FIELDS 10
20839991Sbostic
20939991Sbostic /*
21039991Sbostic ** Which fields are which on a Link line.
21139991Sbostic */
21239991Sbostic
21339991Sbostic #define LF_FROM 1
21439991Sbostic #define LF_TO 2
21539991Sbostic #define LINK_FIELDS 3
21639991Sbostic
21739991Sbostic /*
21839991Sbostic ** Which fields are which on a Leap line.
21939991Sbostic */
22039991Sbostic
22139991Sbostic #define LP_YEAR 1
22239991Sbostic #define LP_MONTH 2
22339991Sbostic #define LP_DAY 3
22439991Sbostic #define LP_TIME 4
22539991Sbostic #define LP_CORR 5
22639991Sbostic #define LP_ROLL 6
22739991Sbostic #define LEAP_FIELDS 7
22839991Sbostic
22939991Sbostic /*
23039991Sbostic ** Year synonyms.
23139991Sbostic */
23239991Sbostic
23339991Sbostic #define YR_MINIMUM 0
23439991Sbostic #define YR_MAXIMUM 1
23539991Sbostic #define YR_ONLY 2
23639991Sbostic
23739991Sbostic static struct rule * rules;
23839991Sbostic static int nrules; /* number of rules */
23939991Sbostic
24039991Sbostic static struct zone * zones;
24139991Sbostic static int nzones; /* number of zones */
24239991Sbostic
24339991Sbostic struct link {
24439991Sbostic const char * l_filename;
24539991Sbostic int l_linenum;
24639991Sbostic const char * l_from;
24739991Sbostic const char * l_to;
24839991Sbostic };
24939991Sbostic
25039991Sbostic static struct link * links;
25139991Sbostic static int nlinks;
25239991Sbostic
25339991Sbostic struct lookup {
25439991Sbostic const char * l_word;
25539991Sbostic const int l_value;
25639991Sbostic };
25739991Sbostic
25846793Sbostic static struct lookup const * byword __P((const char * string,
25939991Sbostic const struct lookup * lp));
26039991Sbostic
26139991Sbostic static struct lookup const line_codes[] = {
26239991Sbostic "Rule", LC_RULE,
26339991Sbostic "Zone", LC_ZONE,
26439991Sbostic "Link", LC_LINK,
26539991Sbostic "Leap", LC_LEAP,
26639991Sbostic NULL, 0
26739991Sbostic };
26839991Sbostic
26939991Sbostic static struct lookup const mon_names[] = {
27039991Sbostic "January", TM_JANUARY,
27139991Sbostic "February", TM_FEBRUARY,
27239991Sbostic "March", TM_MARCH,
27339991Sbostic "April", TM_APRIL,
27439991Sbostic "May", TM_MAY,
27539991Sbostic "June", TM_JUNE,
27639991Sbostic "July", TM_JULY,
27739991Sbostic "August", TM_AUGUST,
27839991Sbostic "September", TM_SEPTEMBER,
27939991Sbostic "October", TM_OCTOBER,
28039991Sbostic "November", TM_NOVEMBER,
28139991Sbostic "December", TM_DECEMBER,
28239991Sbostic NULL, 0
28339991Sbostic };
28439991Sbostic
28539991Sbostic static struct lookup const wday_names[] = {
28639991Sbostic "Sunday", TM_SUNDAY,
28739991Sbostic "Monday", TM_MONDAY,
28839991Sbostic "Tuesday", TM_TUESDAY,
28939991Sbostic "Wednesday", TM_WEDNESDAY,
29039991Sbostic "Thursday", TM_THURSDAY,
29139991Sbostic "Friday", TM_FRIDAY,
29239991Sbostic "Saturday", TM_SATURDAY,
29339991Sbostic NULL, 0
29439991Sbostic };
29539991Sbostic
29639991Sbostic static struct lookup const lasts[] = {
29739991Sbostic "last-Sunday", TM_SUNDAY,
29839991Sbostic "last-Monday", TM_MONDAY,
29939991Sbostic "last-Tuesday", TM_TUESDAY,
30039991Sbostic "last-Wednesday", TM_WEDNESDAY,
30139991Sbostic "last-Thursday", TM_THURSDAY,
30239991Sbostic "last-Friday", TM_FRIDAY,
30339991Sbostic "last-Saturday", TM_SATURDAY,
30439991Sbostic NULL, 0
30539991Sbostic };
30639991Sbostic
30739991Sbostic static struct lookup const begin_years[] = {
30839991Sbostic "minimum", YR_MINIMUM,
30939991Sbostic "maximum", YR_MAXIMUM,
31039991Sbostic NULL, 0
31139991Sbostic };
31239991Sbostic
31339991Sbostic static struct lookup const end_years[] = {
31439991Sbostic "minimum", YR_MINIMUM,
31539991Sbostic "maximum", YR_MAXIMUM,
31639991Sbostic "only", YR_ONLY,
31739991Sbostic NULL, 0
31839991Sbostic };
31939991Sbostic
32039991Sbostic static struct lookup const leap_types[] = {
32139991Sbostic "Rolling", TRUE,
32239991Sbostic "Stationary", FALSE,
32339991Sbostic NULL, 0
32439991Sbostic };
32539991Sbostic
32639991Sbostic static const int len_months[2][MONSPERYEAR] = {
32739991Sbostic 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
32839991Sbostic 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
32939991Sbostic };
33039991Sbostic
33139991Sbostic static const int len_years[2] = {
33239991Sbostic DAYSPERNYEAR, DAYSPERLYEAR
33339991Sbostic };
33439991Sbostic
33539991Sbostic static time_t ats[TZ_MAX_TIMES];
33639991Sbostic static unsigned char types[TZ_MAX_TIMES];
33739991Sbostic static long gmtoffs[TZ_MAX_TYPES];
33839991Sbostic static char isdsts[TZ_MAX_TYPES];
33939991Sbostic static char abbrinds[TZ_MAX_TYPES];
34039991Sbostic static char ttisstds[TZ_MAX_TYPES];
34139991Sbostic static char chars[TZ_MAX_CHARS];
34239991Sbostic static time_t trans[TZ_MAX_LEAPS];
34339991Sbostic static long corr[TZ_MAX_LEAPS];
34439991Sbostic static char roll[TZ_MAX_LEAPS];
34539991Sbostic
34639991Sbostic /*
34739991Sbostic ** Memory allocation.
34839991Sbostic */
34939991Sbostic
35039991Sbostic static char *
memcheck(ptr)35139991Sbostic memcheck(ptr)
35239991Sbostic char * const ptr;
35339991Sbostic {
35439991Sbostic if (ptr == NULL) {
35539991Sbostic (void) perror(progname);
35639991Sbostic (void) exit(EXIT_FAILURE);
35739991Sbostic }
35839991Sbostic return ptr;
35939991Sbostic }
36039991Sbostic
36139991Sbostic #define emalloc(size) memcheck(imalloc(size))
36239991Sbostic #define erealloc(ptr, size) memcheck(irealloc(ptr, size))
36339991Sbostic #define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
36439991Sbostic #define ecatalloc(oldp, newp) memcheck(icatalloc(oldp, newp))
36539991Sbostic
36639991Sbostic /*
36739991Sbostic ** Error handling.
36839991Sbostic */
36939991Sbostic
37039991Sbostic static void
eats(name,num,rname,rnum)37139991Sbostic eats(name, num, rname, rnum)
37239991Sbostic const char * const name;
37339991Sbostic const int num;
37439991Sbostic const char * const rname;
37539991Sbostic const int rnum;
37639991Sbostic {
37739991Sbostic filename = name;
37839991Sbostic linenum = num;
37939991Sbostic rfilename = rname;
38039991Sbostic rlinenum = rnum;
38139991Sbostic }
38239991Sbostic
38339991Sbostic static void
eat(name,num)38439991Sbostic eat(name, num)
38539991Sbostic const char * const name;
38639991Sbostic const int num;
38739991Sbostic {
38839991Sbostic eats(name, num, (char *) NULL, -1);
38939991Sbostic }
39039991Sbostic
39139991Sbostic static void
error(string)39239991Sbostic error(string)
39339991Sbostic const char * const string;
39439991Sbostic {
39539991Sbostic /*
39639991Sbostic ** Match the format of "cc" to allow sh users to
39739991Sbostic ** zic ... 2>&1 | error -t "*" -v
39839991Sbostic ** on BSD systems.
39939991Sbostic */
40039991Sbostic (void) fprintf(stderr, "\"%s\", line %d: %s",
40139991Sbostic filename, linenum, string);
40239991Sbostic if (rfilename != NULL)
40339991Sbostic (void) fprintf(stderr, " (rule from \"%s\", line %d)",
40439991Sbostic rfilename, rlinenum);
40539991Sbostic (void) fprintf(stderr, "\n");
40639991Sbostic ++errors;
40739991Sbostic }
40839991Sbostic
40939991Sbostic static void
usage()41039991Sbostic usage()
41139991Sbostic {
41239991Sbostic (void) fprintf(stderr,
41339991Sbostic "%s: usage is %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n\
41439991Sbostic \t[ -L leapseconds ] [ filename ... ]\n",
41539991Sbostic progname, progname);
41639991Sbostic (void) exit(EXIT_FAILURE);
41739991Sbostic }
41839991Sbostic
41939991Sbostic static const char * psxrules = NULL;
42039991Sbostic static const char * lcltime = NULL;
42139991Sbostic static const char * directory = NULL;
42239991Sbostic static const char * leapsec = NULL;
42339991Sbostic static int sflag = FALSE;
42439991Sbostic
42539991Sbostic int
main(argc,argv)42639991Sbostic main(argc, argv)
42739991Sbostic int argc;
42839991Sbostic char * argv[];
42939991Sbostic {
43039991Sbostic register int i, j;
43139991Sbostic register int c;
43239991Sbostic
43339991Sbostic (void) umask(umask(022) | 022);
43439991Sbostic progname = argv[0];
43539991Sbostic while ((c = getopt(argc, argv, "d:l:p:L:vs")) != EOF)
43639991Sbostic switch (c) {
43739991Sbostic default:
43839991Sbostic usage();
43939991Sbostic case 'd':
44039991Sbostic if (directory == NULL)
44139991Sbostic directory = optarg;
44239991Sbostic else {
44339991Sbostic (void) fprintf(stderr,
44439991Sbostic "%s: More than one -d option specified\n",
44539991Sbostic progname);
44639991Sbostic (void) exit(EXIT_FAILURE);
44739991Sbostic }
44839991Sbostic break;
44939991Sbostic case 'l':
45039991Sbostic if (lcltime == NULL)
45139991Sbostic lcltime = optarg;
45239991Sbostic else {
45339991Sbostic (void) fprintf(stderr,
45439991Sbostic "%s: More than one -l option specified\n",
45539991Sbostic progname);
45639991Sbostic (void) exit(EXIT_FAILURE);
45739991Sbostic }
45839991Sbostic break;
45939991Sbostic case 'p':
46039991Sbostic if (psxrules == NULL)
46139991Sbostic psxrules = optarg;
46239991Sbostic else {
46339991Sbostic (void) fprintf(stderr,
46439991Sbostic "%s: More than one -p option specified\n",
46539991Sbostic progname);
46639991Sbostic (void) exit(EXIT_FAILURE);
46739991Sbostic }
46839991Sbostic break;
46939991Sbostic case 'L':
47039991Sbostic if (leapsec == NULL)
47139991Sbostic leapsec = optarg;
47239991Sbostic else {
47339991Sbostic (void) fprintf(stderr,
47439991Sbostic "%s: More than one -L option specified\n",
47539991Sbostic progname);
47639991Sbostic (void) exit(EXIT_FAILURE);
47739991Sbostic }
47839991Sbostic break;
47939991Sbostic case 'v':
48039991Sbostic noise = TRUE;
48139991Sbostic break;
48239991Sbostic case 's':
48339991Sbostic sflag = TRUE;
48439991Sbostic break;
48539991Sbostic }
48639991Sbostic if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
48739991Sbostic usage(); /* usage message by request */
48839991Sbostic if (directory == NULL)
48939991Sbostic directory = TZDIR;
49039991Sbostic
49139991Sbostic setboundaries();
49239991Sbostic
49339991Sbostic if (optind < argc && leapsec != NULL) {
49439991Sbostic infile(leapsec);
49539991Sbostic adjleap();
49639991Sbostic }
49739991Sbostic
49839991Sbostic zones = (struct zone *) emalloc(0);
49939991Sbostic rules = (struct rule *) emalloc(0);
50039991Sbostic links = (struct link *) emalloc(0);
50139991Sbostic for (i = optind; i < argc; ++i)
50239991Sbostic infile(argv[i]);
50339991Sbostic if (errors)
50439991Sbostic (void) exit(EXIT_FAILURE);
50539991Sbostic associate();
50639991Sbostic for (i = 0; i < nzones; i = j) {
50739991Sbostic /*
50839991Sbostic ** Find the next non-continuation zone entry.
50939991Sbostic */
51039991Sbostic for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
51139991Sbostic ;
51239991Sbostic outzone(&zones[i], j - i);
51339991Sbostic }
51439991Sbostic /*
51539991Sbostic ** Make links.
51639991Sbostic */
51739991Sbostic for (i = 0; i < nlinks; ++i)
51839991Sbostic dolink(links[i].l_from, links[i].l_to);
51939991Sbostic if (lcltime != NULL)
52039991Sbostic dolink(lcltime, TZDEFAULT);
52139991Sbostic if (psxrules != NULL)
52239991Sbostic dolink(psxrules, TZDEFRULES);
52339991Sbostic return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
52439991Sbostic }
52539991Sbostic
52639991Sbostic static void
dolink(fromfile,tofile)52739991Sbostic dolink(fromfile, tofile)
52839991Sbostic const char * const fromfile;
52939991Sbostic const char * const tofile;
53039991Sbostic {
53139991Sbostic register char * fromname;
53239991Sbostic register char * toname;
53339991Sbostic
53439991Sbostic fromname = ecpyalloc(directory);
53539991Sbostic fromname = ecatalloc(fromname, "/");
53639991Sbostic fromname = ecatalloc(fromname, fromfile);
53739991Sbostic toname = ecpyalloc(directory);
53839991Sbostic toname = ecatalloc(toname, "/");
53939991Sbostic toname = ecatalloc(toname, tofile);
54039991Sbostic /*
54139991Sbostic ** We get to be careful here since
54239991Sbostic ** there's a fair chance of root running us.
54339991Sbostic */
54439991Sbostic if (!itsdir(toname))
54539991Sbostic (void) remove(toname);
54639991Sbostic if (link(fromname, toname) != 0) {
54739991Sbostic (void) fprintf(stderr, "%s: Can't link from %s to ",
54839991Sbostic progname, fromname);
54939991Sbostic (void) perror(toname);
55039991Sbostic (void) exit(EXIT_FAILURE);
55139991Sbostic }
55239991Sbostic ifree(fromname);
55339991Sbostic ifree(toname);
55439991Sbostic }
55539991Sbostic
55639991Sbostic static void
setboundaries()55739991Sbostic setboundaries()
55839991Sbostic {
55939991Sbostic register time_t bit;
56039991Sbostic
56139991Sbostic for (bit = 1; bit > 0; bit <<= 1)
56239991Sbostic ;
56339991Sbostic if (bit == 0) { /* time_t is an unsigned type */
56439991Sbostic tt_signed = FALSE;
56539991Sbostic min_time = 0;
56639991Sbostic max_time = ~(time_t) 0;
56739991Sbostic if (sflag)
56839991Sbostic max_time >>= 1;
56939991Sbostic } else {
57039991Sbostic tt_signed = TRUE;
57139991Sbostic min_time = bit;
57239991Sbostic max_time = bit;
57339991Sbostic ++max_time;
57439991Sbostic max_time = -max_time;
57539991Sbostic if (sflag)
57639991Sbostic min_time = 0;
57739991Sbostic }
57839991Sbostic min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
57939991Sbostic max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
58039991Sbostic }
58139991Sbostic
58239991Sbostic static int
itsdir(name)58339991Sbostic itsdir(name)
58439991Sbostic const char * const name;
58539991Sbostic {
58639991Sbostic struct stat s;
58739991Sbostic
58846793Sbostic return (stat(name, &s) == 0 && S_ISDIR(s.st_mode));
58939991Sbostic }
59039991Sbostic
59139991Sbostic /*
59239991Sbostic ** Associate sets of rules with zones.
59339991Sbostic */
59439991Sbostic
59539991Sbostic /*
59639991Sbostic ** Sort by rule name.
59739991Sbostic */
59839991Sbostic
59939991Sbostic static int
rcomp(cp1,cp2)60039991Sbostic rcomp(cp1, cp2)
60146793Sbostic const void * cp1;
60246793Sbostic const void * cp2;
60339991Sbostic {
60439991Sbostic return strcmp(((struct rule *) cp1)->r_name,
60539991Sbostic ((struct rule *) cp2)->r_name);
60639991Sbostic }
60739991Sbostic
60839991Sbostic static void
associate()60939991Sbostic associate()
61039991Sbostic {
61139991Sbostic register struct zone * zp;
61239991Sbostic register struct rule * rp;
61339991Sbostic register int base, out;
61439991Sbostic register int i;
61539991Sbostic
61639991Sbostic if (nrules != 0)
61746793Sbostic (void) qsort((void *) rules, (size_t) nrules,
61846793Sbostic (size_t) sizeof *rules, rcomp);
61939991Sbostic for (i = 0; i < nzones; ++i) {
62039991Sbostic zp = &zones[i];
62139991Sbostic zp->z_rules = NULL;
62239991Sbostic zp->z_nrules = 0;
62339991Sbostic }
62439991Sbostic for (base = 0; base < nrules; base = out) {
62539991Sbostic rp = &rules[base];
62639991Sbostic for (out = base + 1; out < nrules; ++out)
62739991Sbostic if (strcmp(rp->r_name, rules[out].r_name) != 0)
62839991Sbostic break;
62939991Sbostic for (i = 0; i < nzones; ++i) {
63039991Sbostic zp = &zones[i];
63139991Sbostic if (strcmp(zp->z_rule, rp->r_name) != 0)
63239991Sbostic continue;
63339991Sbostic zp->z_rules = rp;
63439991Sbostic zp->z_nrules = out - base;
63539991Sbostic }
63639991Sbostic }
63739991Sbostic for (i = 0; i < nzones; ++i) {
63839991Sbostic zp = &zones[i];
63939991Sbostic if (zp->z_nrules == 0) {
64039991Sbostic /*
64139991Sbostic ** Maybe we have a local standard time offset.
64239991Sbostic */
64339991Sbostic eat(zp->z_filename, zp->z_linenum);
64446793Sbostic zp->z_stdoff =
64546793Sbostic gethms((char *)zp->z_rule, "unruly zone", TRUE);
64639991Sbostic /*
64739991Sbostic ** Note, though, that if there's no rule,
64839991Sbostic ** a '%s' in the format is a bad thing.
64939991Sbostic */
65039991Sbostic if (strchr(zp->z_format, '%') != 0)
65139991Sbostic error("%s in ruleless zone");
65239991Sbostic }
65339991Sbostic }
65439991Sbostic if (errors)
65539991Sbostic (void) exit(EXIT_FAILURE);
65639991Sbostic }
65739991Sbostic
65839991Sbostic static void
infile(name)65939991Sbostic infile(name)
66039991Sbostic const char * name;
66139991Sbostic {
66239991Sbostic register FILE * fp;
66339991Sbostic register char ** fields;
66439991Sbostic register char * cp;
66539991Sbostic register const struct lookup * lp;
66639991Sbostic register int nfields;
66739991Sbostic register int wantcont;
66839991Sbostic register int num;
66939991Sbostic char buf[BUFSIZ];
67039991Sbostic
67139991Sbostic if (strcmp(name, "-") == 0) {
67239991Sbostic name = "standard input";
67339991Sbostic fp = stdin;
67439991Sbostic } else if ((fp = fopen(name, "r")) == NULL) {
67539991Sbostic (void) fprintf(stderr, "%s: Can't open ", progname);
67639991Sbostic (void) perror(name);
67739991Sbostic (void) exit(EXIT_FAILURE);
67839991Sbostic }
67939991Sbostic wantcont = FALSE;
68039991Sbostic for (num = 1; ; ++num) {
68139991Sbostic eat(name, num);
68239991Sbostic if (fgets(buf, (int) sizeof buf, fp) != buf)
68339991Sbostic break;
68439991Sbostic cp = strchr(buf, '\n');
68539991Sbostic if (cp == NULL) {
68639991Sbostic error("line too long");
68739991Sbostic (void) exit(EXIT_FAILURE);
68839991Sbostic }
68939991Sbostic *cp = '\0';
69039991Sbostic fields = getfields(buf);
69139991Sbostic nfields = 0;
69239991Sbostic while (fields[nfields] != NULL) {
69339991Sbostic if (ciequal(fields[nfields], "-"))
69439991Sbostic fields[nfields] = "";
69539991Sbostic ++nfields;
69639991Sbostic }
69739991Sbostic if (nfields == 0) {
69839991Sbostic /* nothing to do */
69939991Sbostic } else if (wantcont) {
70039991Sbostic wantcont = inzcont(fields, nfields);
70139991Sbostic } else {
70239991Sbostic lp = byword(fields[0], line_codes);
70339991Sbostic if (lp == NULL)
70439991Sbostic error("input line of unknown type");
70539991Sbostic else switch ((int) (lp->l_value)) {
70639991Sbostic case LC_RULE:
70739991Sbostic inrule(fields, nfields);
70839991Sbostic wantcont = FALSE;
70939991Sbostic break;
71039991Sbostic case LC_ZONE:
71139991Sbostic wantcont = inzone(fields, nfields);
71239991Sbostic break;
71339991Sbostic case LC_LINK:
71439991Sbostic inlink(fields, nfields);
71539991Sbostic wantcont = FALSE;
71639991Sbostic break;
71739991Sbostic case LC_LEAP:
71839991Sbostic if (name != leapsec)
71939991Sbostic (void) fprintf(stderr,
72039991Sbostic "%s: Leap line in non leap seconds file %s\n",
72139991Sbostic progname, name);
72239991Sbostic else inleap(fields, nfields);
72339991Sbostic wantcont = FALSE;
72439991Sbostic break;
72539991Sbostic default: /* "cannot happen" */
72639991Sbostic (void) fprintf(stderr,
72739991Sbostic "%s: panic: Invalid l_value %d\n",
72839991Sbostic progname, lp->l_value);
72939991Sbostic (void) exit(EXIT_FAILURE);
73039991Sbostic }
73139991Sbostic }
73239991Sbostic ifree((char *) fields);
73339991Sbostic }
73439991Sbostic if (ferror(fp)) {
73539991Sbostic (void) fprintf(stderr, "%s: Error reading ", progname);
73639991Sbostic (void) perror(filename);
73739991Sbostic (void) exit(EXIT_FAILURE);
73839991Sbostic }
73939991Sbostic if (fp != stdin && fclose(fp)) {
74039991Sbostic (void) fprintf(stderr, "%s: Error closing ", progname);
74139991Sbostic (void) perror(filename);
74239991Sbostic (void) exit(EXIT_FAILURE);
74339991Sbostic }
74439991Sbostic if (wantcont)
74539991Sbostic error("expected continuation line not found");
74639991Sbostic }
74739991Sbostic
74839991Sbostic /*
74939991Sbostic ** Convert a string of one of the forms
75039991Sbostic ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
75139991Sbostic ** into a number of seconds.
75239991Sbostic ** A null string maps to zero.
75339991Sbostic ** Call error with errstring and return zero on errors.
75439991Sbostic */
75539991Sbostic
75639991Sbostic static long
gethms(string,errstring,signable)75739991Sbostic gethms(string, errstring, signable)
75846793Sbostic char * string;
75939991Sbostic const char * const errstring;
76039991Sbostic const int signable;
76139991Sbostic {
76239991Sbostic int hh, mm, ss, sign;
76339991Sbostic
76439991Sbostic if (string == NULL || *string == '\0')
76539991Sbostic return 0;
76639991Sbostic if (!signable)
76739991Sbostic sign = 1;
76839991Sbostic else if (*string == '-') {
76939991Sbostic sign = -1;
77039991Sbostic ++string;
77139991Sbostic } else sign = 1;
77239991Sbostic if (sscanf(string, scheck(string, "%d"), &hh) == 1)
77339991Sbostic mm = ss = 0;
77439991Sbostic else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
77539991Sbostic ss = 0;
77639991Sbostic else if (sscanf(string, scheck(string, "%d:%d:%d"),
77739991Sbostic &hh, &mm, &ss) != 3) {
77839991Sbostic error(errstring);
77939991Sbostic return 0;
78039991Sbostic }
78139991Sbostic if (hh < 0 || hh >= HOURSPERDAY ||
78239991Sbostic mm < 0 || mm >= MINSPERHOUR ||
78339991Sbostic ss < 0 || ss > SECSPERMIN) {
78439991Sbostic error(errstring);
78539991Sbostic return 0;
78639991Sbostic }
78739991Sbostic return eitol(sign) *
78839991Sbostic (eitol(hh * MINSPERHOUR + mm) *
78939991Sbostic eitol(SECSPERMIN) + eitol(ss));
79039991Sbostic }
79139991Sbostic
79239991Sbostic static void
inrule(fields,nfields)79339991Sbostic inrule(fields, nfields)
79439991Sbostic register char ** const fields;
79539991Sbostic const int nfields;
79639991Sbostic {
79739991Sbostic static struct rule r;
79839991Sbostic
79939991Sbostic if (nfields != RULE_FIELDS) {
80039991Sbostic error("wrong number of fields on Rule line");
80139991Sbostic return;
80239991Sbostic }
80339991Sbostic if (*fields[RF_NAME] == '\0') {
80439991Sbostic error("nameless rule");
80539991Sbostic return;
80639991Sbostic }
80739991Sbostic r.r_filename = filename;
80839991Sbostic r.r_linenum = linenum;
80939991Sbostic r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE);
81039991Sbostic rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
81139991Sbostic fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
81239991Sbostic r.r_name = ecpyalloc(fields[RF_NAME]);
81339991Sbostic r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
81439991Sbostic rules = (struct rule *) erealloc((char *) rules,
81539991Sbostic (int) ((nrules + 1) * sizeof *rules));
81639991Sbostic rules[nrules++] = r;
81739991Sbostic }
81839991Sbostic
81939991Sbostic static int
inzone(fields,nfields)82039991Sbostic inzone(fields, nfields)
82139991Sbostic register char ** const fields;
82239991Sbostic const int nfields;
82339991Sbostic {
82439991Sbostic register int i;
82539991Sbostic char buf[132];
82639991Sbostic
82739991Sbostic if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
82839991Sbostic error("wrong number of fields on Zone line");
82939991Sbostic return FALSE;
83039991Sbostic }
83139991Sbostic if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
83239991Sbostic (void) sprintf(buf,
83339991Sbostic "\"Zone %s\" line and -l option are mutually exclusive",
83439991Sbostic TZDEFAULT);
83539991Sbostic error(buf);
83639991Sbostic return FALSE;
83739991Sbostic }
83839991Sbostic if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
83939991Sbostic (void) sprintf(buf,
84039991Sbostic "\"Zone %s\" line and -p option are mutually exclusive",
84139991Sbostic TZDEFRULES);
84239991Sbostic error(buf);
84339991Sbostic return FALSE;
84439991Sbostic }
84539991Sbostic for (i = 0; i < nzones; ++i)
84639991Sbostic if (zones[i].z_name != NULL &&
84739991Sbostic strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
84839991Sbostic (void) sprintf(buf,
84939991Sbostic "duplicate zone name %s (file \"%s\", line %d)",
85039991Sbostic fields[ZF_NAME],
85139991Sbostic zones[i].z_filename,
85239991Sbostic zones[i].z_linenum);
85339991Sbostic error(buf);
85439991Sbostic return FALSE;
85539991Sbostic }
85639991Sbostic return inzsub(fields, nfields, FALSE);
85739991Sbostic }
85839991Sbostic
85939991Sbostic static int
inzcont(fields,nfields)86039991Sbostic inzcont(fields, nfields)
86139991Sbostic register char ** const fields;
86239991Sbostic const int nfields;
86339991Sbostic {
86439991Sbostic if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
86539991Sbostic error("wrong number of fields on Zone continuation line");
86639991Sbostic return FALSE;
86739991Sbostic }
86839991Sbostic return inzsub(fields, nfields, TRUE);
86939991Sbostic }
87039991Sbostic
87139991Sbostic static int
inzsub(fields,nfields,iscont)87239991Sbostic inzsub(fields, nfields, iscont)
87339991Sbostic register char ** const fields;
87439991Sbostic const int nfields;
87539991Sbostic const int iscont;
87639991Sbostic {
87739991Sbostic register char * cp;
87839991Sbostic static struct zone z;
87939991Sbostic register int i_gmtoff, i_rule, i_format;
88039991Sbostic register int i_untilyear, i_untilmonth;
88139991Sbostic register int i_untilday, i_untiltime;
88239991Sbostic register int hasuntil;
88339991Sbostic
88439991Sbostic if (iscont) {
88539991Sbostic i_gmtoff = ZFC_GMTOFF;
88639991Sbostic i_rule = ZFC_RULE;
88739991Sbostic i_format = ZFC_FORMAT;
88839991Sbostic i_untilyear = ZFC_TILYEAR;
88939991Sbostic i_untilmonth = ZFC_TILMONTH;
89039991Sbostic i_untilday = ZFC_TILDAY;
89139991Sbostic i_untiltime = ZFC_TILTIME;
89239991Sbostic z.z_name = NULL;
89339991Sbostic } else {
89439991Sbostic i_gmtoff = ZF_GMTOFF;
89539991Sbostic i_rule = ZF_RULE;
89639991Sbostic i_format = ZF_FORMAT;
89739991Sbostic i_untilyear = ZF_TILYEAR;
89839991Sbostic i_untilmonth = ZF_TILMONTH;
89939991Sbostic i_untilday = ZF_TILDAY;
90039991Sbostic i_untiltime = ZF_TILTIME;
90139991Sbostic z.z_name = ecpyalloc(fields[ZF_NAME]);
90239991Sbostic }
90339991Sbostic z.z_filename = filename;
90439991Sbostic z.z_linenum = linenum;
90539991Sbostic z.z_gmtoff = gethms(fields[i_gmtoff], "invalid GMT offset", TRUE);
90639991Sbostic if ((cp = strchr(fields[i_format], '%')) != 0) {
90739991Sbostic if (*++cp != 's' || strchr(cp, '%') != 0) {
90839991Sbostic error("invalid abbreviation format");
90939991Sbostic return FALSE;
91039991Sbostic }
91139991Sbostic }
91239991Sbostic z.z_rule = ecpyalloc(fields[i_rule]);
91339991Sbostic z.z_format = ecpyalloc(fields[i_format]);
91439991Sbostic hasuntil = nfields > i_untilyear;
91539991Sbostic if (hasuntil) {
91639991Sbostic z.z_untilrule.r_filename = filename;
91739991Sbostic z.z_untilrule.r_linenum = linenum;
91839991Sbostic rulesub(&z.z_untilrule,
91939991Sbostic fields[i_untilyear],
92039991Sbostic "only",
92139991Sbostic "",
92239991Sbostic (nfields > i_untilmonth) ? fields[i_untilmonth] : "Jan",
92339991Sbostic (nfields > i_untilday) ? fields[i_untilday] : "1",
92439991Sbostic (nfields > i_untiltime) ? fields[i_untiltime] : "0");
92539991Sbostic z.z_untiltime = rpytime(&z.z_untilrule, z.z_untilrule.r_loyear);
92639991Sbostic if (iscont && nzones > 0 && z.z_untiltime < max_time &&
92739991Sbostic z.z_untiltime > min_time &&
92839991Sbostic zones[nzones - 1].z_untiltime >= z.z_untiltime) {
92939991Sbostic error("Zone continuation line end time is not after end time of previous line");
93039991Sbostic return FALSE;
93139991Sbostic }
93239991Sbostic }
93339991Sbostic zones = (struct zone *) erealloc((char *) zones,
93439991Sbostic (int) ((nzones + 1) * sizeof *zones));
93539991Sbostic zones[nzones++] = z;
93639991Sbostic /*
93739991Sbostic ** If there was an UNTIL field on this line,
93839991Sbostic ** there's more information about the zone on the next line.
93939991Sbostic */
94039991Sbostic return hasuntil;
94139991Sbostic }
94239991Sbostic
94339991Sbostic static void
inleap(fields,nfields)94439991Sbostic inleap(fields, nfields)
94539991Sbostic register char ** const fields;
94639991Sbostic const int nfields;
94739991Sbostic {
94839991Sbostic register const char * cp;
94939991Sbostic register const struct lookup * lp;
95039991Sbostic register int i, j;
95139991Sbostic int year, month, day;
95239991Sbostic long dayoff, tod;
95339991Sbostic time_t t;
95439991Sbostic
95539991Sbostic if (nfields != LEAP_FIELDS) {
95639991Sbostic error("wrong number of fields on Leap line");
95739991Sbostic return;
95839991Sbostic }
95939991Sbostic dayoff = 0;
96039991Sbostic cp = fields[LP_YEAR];
96146793Sbostic if (sscanf((char *)cp, scheck(cp, "%d"), &year) != 1 ||
96239991Sbostic year < min_year || year > max_year) {
96339991Sbostic /*
96439991Sbostic * Leapin' Lizards!
96539991Sbostic */
96639991Sbostic error("invalid leaping year");
96739991Sbostic return;
96839991Sbostic }
96939991Sbostic j = EPOCH_YEAR;
97039991Sbostic while (j != year) {
97139991Sbostic if (year > j) {
97239991Sbostic i = len_years[isleap(j)];
97339991Sbostic ++j;
97439991Sbostic } else {
97539991Sbostic --j;
97639991Sbostic i = -len_years[isleap(j)];
97739991Sbostic }
97839991Sbostic dayoff = oadd(dayoff, eitol(i));
97939991Sbostic }
98039991Sbostic if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
98139991Sbostic error("invalid month name");
98239991Sbostic return;
98339991Sbostic }
98439991Sbostic month = lp->l_value;
98539991Sbostic j = TM_JANUARY;
98639991Sbostic while (j != month) {
98739991Sbostic i = len_months[isleap(year)][j];
98839991Sbostic dayoff = oadd(dayoff, eitol(i));
98939991Sbostic ++j;
99039991Sbostic }
99139991Sbostic cp = fields[LP_DAY];
99246793Sbostic if (sscanf((char *)cp, scheck(cp, "%d"), &day) != 1 ||
99339991Sbostic day <= 0 || day > len_months[isleap(year)][month]) {
99439991Sbostic error("invalid day of month");
99539991Sbostic return;
99639991Sbostic }
99739991Sbostic dayoff = oadd(dayoff, eitol(day - 1));
99839991Sbostic if (dayoff < 0 && !tt_signed) {
99939991Sbostic error("time before zero");
100039991Sbostic return;
100139991Sbostic }
100239991Sbostic t = (time_t) dayoff * SECSPERDAY;
100339991Sbostic /*
100439991Sbostic ** Cheap overflow check.
100539991Sbostic */
100639991Sbostic if (t / SECSPERDAY != dayoff) {
100739991Sbostic error("time overflow");
100839991Sbostic return;
100939991Sbostic }
101039991Sbostic tod = gethms(fields[LP_TIME], "invalid time of day", FALSE);
101139991Sbostic cp = fields[LP_CORR];
101239991Sbostic if (strcmp(cp, "+") != 0 && strcmp(cp, "") != 0) {
101339991Sbostic /* infile() turned "-" into "" */
101439991Sbostic error("illegal CORRECTION field on Leap line");
101539991Sbostic return;
101639991Sbostic }
101739991Sbostic if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
101839991Sbostic error("illegal Rolling/Stationary field on Leap line");
101939991Sbostic return;
102039991Sbostic }
102139991Sbostic addleap(tadd(t, tod), *cp == '+', lp->l_value);
102239991Sbostic }
102339991Sbostic
102439991Sbostic static void
inlink(fields,nfields)102539991Sbostic inlink(fields, nfields)
102639991Sbostic register char ** const fields;
102739991Sbostic const int nfields;
102839991Sbostic {
102939991Sbostic struct link l;
103039991Sbostic
103139991Sbostic if (nfields != LINK_FIELDS) {
103239991Sbostic error("wrong number of fields on Link line");
103339991Sbostic return;
103439991Sbostic }
103539991Sbostic if (*fields[LF_FROM] == '\0') {
103639991Sbostic error("blank FROM field on Link line");
103739991Sbostic return;
103839991Sbostic }
103939991Sbostic if (*fields[LF_TO] == '\0') {
104039991Sbostic error("blank TO field on Link line");
104139991Sbostic return;
104239991Sbostic }
104339991Sbostic l.l_filename = filename;
104439991Sbostic l.l_linenum = linenum;
104539991Sbostic l.l_from = ecpyalloc(fields[LF_FROM]);
104639991Sbostic l.l_to = ecpyalloc(fields[LF_TO]);
104739991Sbostic links = (struct link *) erealloc((char *) links,
104839991Sbostic (int) ((nlinks + 1) * sizeof *links));
104939991Sbostic links[nlinks++] = l;
105039991Sbostic }
105139991Sbostic
105239991Sbostic static void
rulesub(rp,loyearp,hiyearp,typep,monthp,dayp,timep)105339991Sbostic rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
105439991Sbostic register struct rule * const rp;
105539991Sbostic char * const loyearp;
105639991Sbostic char * const hiyearp;
105739991Sbostic char * const typep;
105839991Sbostic char * const monthp;
105939991Sbostic char * const dayp;
106039991Sbostic char * const timep;
106139991Sbostic {
106239991Sbostic register struct lookup const * lp;
106339991Sbostic register char * cp;
106439991Sbostic
106539991Sbostic if ((lp = byword(monthp, mon_names)) == NULL) {
106639991Sbostic error("invalid month name");
106739991Sbostic return;
106839991Sbostic }
106939991Sbostic rp->r_month = lp->l_value;
107039991Sbostic rp->r_todisstd = FALSE;
107139991Sbostic cp = timep;
107239991Sbostic if (*cp != '\0') {
107339991Sbostic cp += strlen(cp) - 1;
107439991Sbostic switch (lowerit(*cp)) {
107539991Sbostic case 's':
107639991Sbostic rp->r_todisstd = TRUE;
107739991Sbostic *cp = '\0';
107839991Sbostic break;
107939991Sbostic case 'w':
108039991Sbostic rp->r_todisstd = FALSE;
108139991Sbostic *cp = '\0';
108239991Sbostic break;
108339991Sbostic }
108439991Sbostic }
108539991Sbostic rp->r_tod = gethms(timep, "invalid time of day", FALSE);
108639991Sbostic /*
108739991Sbostic ** Year work.
108839991Sbostic */
108939991Sbostic cp = loyearp;
109039991Sbostic if ((lp = byword(cp, begin_years)) != NULL) switch ((int) lp->l_value) {
109139991Sbostic case YR_MINIMUM:
109239991Sbostic rp->r_loyear = min_year;
109339991Sbostic break;
109439991Sbostic case YR_MAXIMUM:
109539991Sbostic rp->r_loyear = max_year;
109639991Sbostic break;
109739991Sbostic default: /* "cannot happen" */
109839991Sbostic (void) fprintf(stderr,
109939991Sbostic "%s: panic: Invalid l_value %d\n",
110039991Sbostic progname, lp->l_value);
110139991Sbostic (void) exit(EXIT_FAILURE);
110239991Sbostic } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1 ||
110339991Sbostic rp->r_loyear < min_year || rp->r_loyear > max_year) {
110439991Sbostic if (noise)
110539991Sbostic error("invalid starting year");
110639991Sbostic if (rp->r_loyear > max_year)
110739991Sbostic return;
110839991Sbostic }
110939991Sbostic cp = hiyearp;
111039991Sbostic if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
111139991Sbostic case YR_MINIMUM:
111239991Sbostic rp->r_hiyear = min_year;
111339991Sbostic break;
111439991Sbostic case YR_MAXIMUM:
111539991Sbostic rp->r_hiyear = max_year;
111639991Sbostic break;
111739991Sbostic case YR_ONLY:
111839991Sbostic rp->r_hiyear = rp->r_loyear;
111939991Sbostic break;
112039991Sbostic default: /* "cannot happen" */
112139991Sbostic (void) fprintf(stderr,
112239991Sbostic "%s: panic: Invalid l_value %d\n",
112339991Sbostic progname, lp->l_value);
112439991Sbostic (void) exit(EXIT_FAILURE);
112539991Sbostic } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1 ||
112639991Sbostic rp->r_hiyear < min_year || rp->r_hiyear > max_year) {
112739991Sbostic if (noise)
112839991Sbostic error("invalid ending year");
112939991Sbostic if (rp->r_hiyear < min_year)
113039991Sbostic return;
113139991Sbostic }
113239991Sbostic if (rp->r_hiyear < min_year)
113339991Sbostic return;
113439991Sbostic if (rp->r_loyear < min_year)
113539991Sbostic rp->r_loyear = min_year;
113639991Sbostic if (rp->r_hiyear > max_year)
113739991Sbostic rp->r_hiyear = max_year;
113839991Sbostic if (rp->r_loyear > rp->r_hiyear) {
113939991Sbostic error("starting year greater than ending year");
114039991Sbostic return;
114139991Sbostic }
114239991Sbostic if (*typep == '\0')
114339991Sbostic rp->r_yrtype = NULL;
114439991Sbostic else {
114539991Sbostic if (rp->r_loyear == rp->r_hiyear) {
114639991Sbostic error("typed single year");
114739991Sbostic return;
114839991Sbostic }
114939991Sbostic rp->r_yrtype = ecpyalloc(typep);
115039991Sbostic }
115139991Sbostic /*
115239991Sbostic ** Day work.
115339991Sbostic ** Accept things such as:
115439991Sbostic ** 1
115539991Sbostic ** last-Sunday
115639991Sbostic ** Sun<=20
115739991Sbostic ** Sun>=7
115839991Sbostic */
115939991Sbostic if ((lp = byword(dayp, lasts)) != NULL) {
116039991Sbostic rp->r_dycode = DC_DOWLEQ;
116139991Sbostic rp->r_wday = lp->l_value;
116239991Sbostic rp->r_dayofmonth = len_months[1][rp->r_month];
116339991Sbostic } else {
116439991Sbostic if ((cp = strchr(dayp, '<')) != 0)
116539991Sbostic rp->r_dycode = DC_DOWLEQ;
116639991Sbostic else if ((cp = strchr(dayp, '>')) != 0)
116739991Sbostic rp->r_dycode = DC_DOWGEQ;
116839991Sbostic else {
116939991Sbostic cp = dayp;
117039991Sbostic rp->r_dycode = DC_DOM;
117139991Sbostic }
117239991Sbostic if (rp->r_dycode != DC_DOM) {
117339991Sbostic *cp++ = 0;
117439991Sbostic if (*cp++ != '=') {
117539991Sbostic error("invalid day of month");
117639991Sbostic return;
117739991Sbostic }
117839991Sbostic if ((lp = byword(dayp, wday_names)) == NULL) {
117939991Sbostic error("invalid weekday name");
118039991Sbostic return;
118139991Sbostic }
118239991Sbostic rp->r_wday = lp->l_value;
118339991Sbostic }
118439991Sbostic if (sscanf(cp, scheck(cp, "%d"), &rp->r_dayofmonth) != 1 ||
118539991Sbostic rp->r_dayofmonth <= 0 ||
118639991Sbostic (rp->r_dayofmonth > len_months[1][rp->r_month])) {
118739991Sbostic error("invalid day of month");
118839991Sbostic return;
118939991Sbostic }
119039991Sbostic }
119139991Sbostic }
119239991Sbostic
119339991Sbostic static void
convert(val,buf)119439991Sbostic convert(val, buf)
119539991Sbostic const long val;
119639991Sbostic char * const buf;
119739991Sbostic {
119839991Sbostic register int i;
119939991Sbostic register long shift;
120039991Sbostic
120139991Sbostic for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
120239991Sbostic buf[i] = val >> shift;
120339991Sbostic }
120439991Sbostic
120539991Sbostic static void
puttzcode(val,fp)120639991Sbostic puttzcode(val, fp)
120739991Sbostic const long val;
120839991Sbostic FILE * const fp;
120939991Sbostic {
121039991Sbostic char buf[4];
121139991Sbostic
121239991Sbostic convert(val, buf);
121346793Sbostic (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
121439991Sbostic }
121539991Sbostic
121639991Sbostic static void
writezone(name)121739991Sbostic writezone(name)
121839991Sbostic const char * const name;
121939991Sbostic {
122039991Sbostic register FILE * fp;
122139991Sbostic register int i, j;
122239991Sbostic char fullname[BUFSIZ];
122339991Sbostic static struct tzhead tzh;
122439991Sbostic
122539991Sbostic if (strlen(directory) + 1 + strlen(name) >= sizeof fullname) {
122639991Sbostic (void) fprintf(stderr,
122739991Sbostic "%s: File name %s/%s too long\n", progname,
122839991Sbostic directory, name);
122939991Sbostic (void) exit(EXIT_FAILURE);
123039991Sbostic }
123139991Sbostic (void) sprintf(fullname, "%s/%s", directory, name);
123239991Sbostic if ((fp = fopen(fullname, "wb")) == NULL) {
123339991Sbostic if (mkdirs(fullname) != 0)
123439991Sbostic (void) exit(EXIT_FAILURE);
123539991Sbostic if ((fp = fopen(fullname, "wb")) == NULL) {
123639991Sbostic (void) fprintf(stderr, "%s: Can't create ", progname);
123739991Sbostic (void) perror(fullname);
123839991Sbostic (void) exit(EXIT_FAILURE);
123939991Sbostic }
124039991Sbostic }
124139991Sbostic convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
124239991Sbostic convert(eitol(leapcnt), tzh.tzh_leapcnt);
124339991Sbostic convert(eitol(timecnt), tzh.tzh_timecnt);
124439991Sbostic convert(eitol(typecnt), tzh.tzh_typecnt);
124539991Sbostic convert(eitol(charcnt), tzh.tzh_charcnt);
124646793Sbostic (void) fwrite((void *) &tzh, (size_t) sizeof tzh, (size_t) 1, fp);
124739991Sbostic for (i = 0; i < timecnt; ++i) {
124839991Sbostic j = leapcnt;
124939991Sbostic while (--j >= 0)
125039991Sbostic if (ats[i] >= trans[j]) {
125139991Sbostic ats[i] = tadd(ats[i], corr[j]);
125239991Sbostic break;
125339991Sbostic }
125439991Sbostic puttzcode((long) ats[i], fp);
125539991Sbostic }
125639991Sbostic if (timecnt > 0)
125746793Sbostic (void) fwrite((void *) types, (size_t) sizeof types[0],
125846793Sbostic (size_t) timecnt, fp);
125939991Sbostic for (i = 0; i < typecnt; ++i) {
126039991Sbostic puttzcode((long) gmtoffs[i], fp);
126139991Sbostic (void) putc(isdsts[i], fp);
126239991Sbostic (void) putc(abbrinds[i], fp);
126339991Sbostic }
126439991Sbostic if (charcnt != 0)
126546793Sbostic (void) fwrite((void *) chars, (size_t) sizeof chars[0],
126646793Sbostic (size_t) charcnt, fp);
126739991Sbostic for (i = 0; i < leapcnt; ++i) {
126839991Sbostic if (roll[i]) {
126939991Sbostic if (timecnt == 0 || trans[i] < ats[0]) {
127039991Sbostic j = 0;
127139991Sbostic while (isdsts[j])
127239991Sbostic if (++j >= typecnt) {
127339991Sbostic j = 0;
127439991Sbostic break;
127539991Sbostic }
127639991Sbostic } else {
127739991Sbostic j = 1;
127839991Sbostic while (j < timecnt && trans[i] >= ats[j])
127939991Sbostic ++j;
128039991Sbostic j = types[j - 1];
128139991Sbostic }
128239991Sbostic puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
128339991Sbostic } else puttzcode((long) trans[i], fp);
128439991Sbostic puttzcode((long) corr[i], fp);
128539991Sbostic }
128639991Sbostic for (i = 0; i < typecnt; ++i)
128739991Sbostic (void) putc(ttisstds[i], fp);
128839991Sbostic if (ferror(fp) || fclose(fp)) {
128939991Sbostic (void) fprintf(stderr, "%s: Write error on ", progname);
129039991Sbostic (void) perror(fullname);
129139991Sbostic (void) exit(EXIT_FAILURE);
129239991Sbostic }
129339991Sbostic }
129439991Sbostic
129539991Sbostic static void
outzone(zpfirst,zonecount)129639991Sbostic outzone(zpfirst, zonecount)
129739991Sbostic const struct zone * const zpfirst;
129839991Sbostic const int zonecount;
129939991Sbostic {
130039991Sbostic register const struct zone * zp;
130139991Sbostic register struct rule * rp;
130239991Sbostic register int i, j;
130339991Sbostic register int usestart, useuntil;
130439991Sbostic register time_t starttime, untiltime;
130539991Sbostic register long gmtoff;
130639991Sbostic register long stdoff;
130739991Sbostic register int year;
130839991Sbostic register long startoff;
130939991Sbostic register int startisdst;
131039991Sbostic register int startttisstd;
131139991Sbostic register int type;
131239991Sbostic char startbuf[BUFSIZ];
131339991Sbostic
131439991Sbostic /*
131539991Sbostic ** Now. . .finally. . .generate some useful data!
131639991Sbostic */
131739991Sbostic timecnt = 0;
131839991Sbostic typecnt = 0;
131939991Sbostic charcnt = 0;
132039991Sbostic /*
132139991Sbostic ** Two guesses. . .the second may well be corrected later.
132239991Sbostic */
132339991Sbostic gmtoff = zpfirst->z_gmtoff;
132439991Sbostic stdoff = 0;
132539991Sbostic starttime = 0;
132639991Sbostic startttisstd = FALSE;
132739991Sbostic for (i = 0; i < zonecount; ++i) {
132839991Sbostic usestart = i > 0;
132939991Sbostic useuntil = i < (zonecount - 1);
133039991Sbostic zp = &zpfirst[i];
133139991Sbostic eat(zp->z_filename, zp->z_linenum);
133239991Sbostic startisdst = -1;
133339991Sbostic if (zp->z_nrules == 0) {
133439991Sbostic type = addtype(oadd(zp->z_gmtoff, zp->z_stdoff),
133539991Sbostic zp->z_format, zp->z_stdoff != 0,
133639991Sbostic startttisstd);
133739991Sbostic if (usestart)
133839991Sbostic addtt(starttime, type);
133939991Sbostic gmtoff = zp->z_gmtoff;
134039991Sbostic stdoff = zp->z_stdoff;
134139991Sbostic } else for (year = min_year; year <= max_year; ++year) {
134239991Sbostic if (useuntil && year > zp->z_untilrule.r_hiyear)
134339991Sbostic break;
134439991Sbostic /*
134539991Sbostic ** Mark which rules to do in the current year.
134639991Sbostic ** For those to do, calculate rpytime(rp, year);
134739991Sbostic */
134839991Sbostic for (j = 0; j < zp->z_nrules; ++j) {
134939991Sbostic rp = &zp->z_rules[j];
135039991Sbostic eats(zp->z_filename, zp->z_linenum,
135139991Sbostic rp->r_filename, rp->r_linenum);
135239991Sbostic rp->r_todo = year >= rp->r_loyear &&
135339991Sbostic year <= rp->r_hiyear &&
135439991Sbostic yearistype(year, rp->r_yrtype);
135539991Sbostic if (rp->r_todo)
135639991Sbostic rp->r_temp = rpytime(rp, year);
135739991Sbostic }
135839991Sbostic for ( ; ; ) {
135939991Sbostic register int k;
136039991Sbostic register time_t jtime, ktime;
136139991Sbostic register long offset;
136239991Sbostic char buf[BUFSIZ];
136339991Sbostic
136439991Sbostic if (useuntil) {
136539991Sbostic /*
136639991Sbostic ** Turn untiltime into GMT
136739991Sbostic ** assuming the current gmtoff and
136839991Sbostic ** stdoff values.
136939991Sbostic */
137039991Sbostic offset = gmtoff;
137139991Sbostic if (!zp->z_untilrule.r_todisstd)
137239991Sbostic offset = oadd(offset, stdoff);
137339991Sbostic untiltime = tadd(zp->z_untiltime,
137439991Sbostic -offset);
137539991Sbostic }
137639991Sbostic /*
137739991Sbostic ** Find the rule (of those to do, if any)
137839991Sbostic ** that takes effect earliest in the year.
137939991Sbostic */
138039991Sbostic k = -1;
138139991Sbostic #ifdef lint
138239991Sbostic ktime = 0;
138339991Sbostic #endif /* defined lint */
138439991Sbostic for (j = 0; j < zp->z_nrules; ++j) {
138539991Sbostic rp = &zp->z_rules[j];
138639991Sbostic if (!rp->r_todo)
138739991Sbostic continue;
138839991Sbostic eats(zp->z_filename, zp->z_linenum,
138939991Sbostic rp->r_filename, rp->r_linenum);
139039991Sbostic offset = gmtoff;
139139991Sbostic if (!rp->r_todisstd)
139239991Sbostic offset = oadd(offset, stdoff);
139339991Sbostic jtime = rp->r_temp;
139439991Sbostic if (jtime == min_time ||
139539991Sbostic jtime == max_time)
139639991Sbostic continue;
139739991Sbostic jtime = tadd(jtime, -offset);
139839991Sbostic if (k < 0 || jtime < ktime) {
139939991Sbostic k = j;
140039991Sbostic ktime = jtime;
140139991Sbostic }
140239991Sbostic }
140339991Sbostic if (k < 0)
140439991Sbostic break; /* go on to next year */
140539991Sbostic rp = &zp->z_rules[k];
140639991Sbostic rp->r_todo = FALSE;
140739991Sbostic if (useuntil && ktime >= untiltime)
140839991Sbostic break;
140939991Sbostic if (usestart) {
141039991Sbostic if (ktime < starttime) {
141139991Sbostic stdoff = rp->r_stdoff;
141239991Sbostic startoff = oadd(zp->z_gmtoff,
141339991Sbostic rp->r_stdoff);
141439991Sbostic (void) sprintf(startbuf,
141539991Sbostic zp->z_format,
141639991Sbostic rp->r_abbrvar);
141739991Sbostic startisdst =
141839991Sbostic rp->r_stdoff != 0;
141939991Sbostic continue;
142039991Sbostic }
142139991Sbostic if (ktime != starttime &&
142239991Sbostic startisdst >= 0)
142339991Sbostic addtt(starttime, addtype(startoff, startbuf, startisdst, startttisstd));
142439991Sbostic usestart = FALSE;
142539991Sbostic }
142639991Sbostic eats(zp->z_filename, zp->z_linenum,
142739991Sbostic rp->r_filename, rp->r_linenum);
142839991Sbostic (void) sprintf(buf, zp->z_format,
142939991Sbostic rp->r_abbrvar);
143039991Sbostic offset = oadd(zp->z_gmtoff, rp->r_stdoff);
143139991Sbostic type = addtype(offset, buf, rp->r_stdoff != 0,
143239991Sbostic rp->r_todisstd);
143339991Sbostic if (timecnt != 0 || rp->r_stdoff != 0)
143439991Sbostic addtt(ktime, type);
143539991Sbostic gmtoff = zp->z_gmtoff;
143639991Sbostic stdoff = rp->r_stdoff;
143739991Sbostic }
143839991Sbostic }
143939991Sbostic /*
144039991Sbostic ** Now we may get to set starttime for the next zone line.
144139991Sbostic */
144239991Sbostic if (useuntil) {
144339991Sbostic starttime = tadd(zp->z_untiltime,
144439991Sbostic -gmtoffs[types[timecnt - 1]]);
144539991Sbostic startttisstd = zp->z_untilrule.r_todisstd;
144639991Sbostic }
144739991Sbostic }
144839991Sbostic writezone(zpfirst->z_name);
144939991Sbostic }
145039991Sbostic
145139991Sbostic static void
addtt(starttime,type)145239991Sbostic addtt(starttime, type)
145339991Sbostic const time_t starttime;
145439991Sbostic const int type;
145539991Sbostic {
145639991Sbostic if (timecnt != 0 && type == types[timecnt - 1])
145739991Sbostic return; /* easy enough! */
145839991Sbostic if (timecnt >= TZ_MAX_TIMES) {
145939991Sbostic error("too many transitions?!");
146039991Sbostic (void) exit(EXIT_FAILURE);
146139991Sbostic }
146239991Sbostic ats[timecnt] = starttime;
146339991Sbostic types[timecnt] = type;
146439991Sbostic ++timecnt;
146539991Sbostic }
146639991Sbostic
146739991Sbostic static int
addtype(gmtoff,abbr,isdst,ttisstd)146839991Sbostic addtype(gmtoff, abbr, isdst, ttisstd)
146939991Sbostic const long gmtoff;
147039991Sbostic const char * const abbr;
147139991Sbostic const int isdst;
147239991Sbostic const int ttisstd;
147339991Sbostic {
147439991Sbostic register int i, j;
147539991Sbostic
147639991Sbostic /*
147739991Sbostic ** See if there's already an entry for this zone type.
147839991Sbostic ** If so, just return its index.
147939991Sbostic */
148039991Sbostic for (i = 0; i < typecnt; ++i) {
148139991Sbostic if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
148239991Sbostic strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
148339991Sbostic ttisstd == ttisstds[i])
148439991Sbostic return i;
148539991Sbostic }
148639991Sbostic /*
148739991Sbostic ** There isn't one; add a new one, unless there are already too
148839991Sbostic ** many.
148939991Sbostic */
149039991Sbostic if (typecnt >= TZ_MAX_TYPES) {
149139991Sbostic error("too many local time types");
149239991Sbostic (void) exit(EXIT_FAILURE);
149339991Sbostic }
149439991Sbostic gmtoffs[i] = gmtoff;
149539991Sbostic isdsts[i] = isdst;
149639991Sbostic ttisstds[i] = ttisstd;
149739991Sbostic
149839991Sbostic for (j = 0; j < charcnt; ++j)
149939991Sbostic if (strcmp(&chars[j], abbr) == 0)
150039991Sbostic break;
150139991Sbostic if (j == charcnt)
150239991Sbostic newabbr(abbr);
150339991Sbostic abbrinds[i] = j;
150439991Sbostic ++typecnt;
150539991Sbostic return i;
150639991Sbostic }
150739991Sbostic
150839991Sbostic static void
addleap(t,positive,rolling)150939991Sbostic addleap(t, positive, rolling)
151039991Sbostic const time_t t;
151139991Sbostic const int positive;
151239991Sbostic const int rolling;
151339991Sbostic {
151439991Sbostic register int i, j;
151539991Sbostic
151639991Sbostic if (leapcnt >= TZ_MAX_LEAPS) {
151739991Sbostic error("too many leap seconds");
151839991Sbostic (void) exit(EXIT_FAILURE);
151939991Sbostic }
152039991Sbostic for (i = 0; i < leapcnt; ++i)
152139991Sbostic if (t <= trans[i]) {
152239991Sbostic if (t == trans[i]) {
152339991Sbostic error("repeated leap second moment");
152439991Sbostic (void) exit(EXIT_FAILURE);
152539991Sbostic }
152639991Sbostic break;
152739991Sbostic }
152839991Sbostic for (j = leapcnt; j > i; --j) {
152939991Sbostic trans[j] = trans[j-1];
153039991Sbostic corr[j] = corr[j-1];
153139991Sbostic roll[j] = roll[j-1];
153239991Sbostic }
153339991Sbostic trans[i] = t;
153439991Sbostic corr[i] = (positive ? 1L : -1L);
153539991Sbostic roll[i] = rolling;
153639991Sbostic ++leapcnt;
153739991Sbostic }
153839991Sbostic
153939991Sbostic static void
adjleap()154039991Sbostic adjleap()
154139991Sbostic {
154239991Sbostic register int i;
154339991Sbostic register long last = 0;
154439991Sbostic
154539991Sbostic /*
154639991Sbostic ** propagate leap seconds forward
154739991Sbostic */
154839991Sbostic for (i = 0; i < leapcnt; ++i) {
154939991Sbostic trans[i] = tadd(trans[i], last);
155039991Sbostic last = corr[i] += last;
155139991Sbostic }
155239991Sbostic }
155339991Sbostic
155439991Sbostic static int
yearistype(year,type)155539991Sbostic yearistype(year, type)
155639991Sbostic const int year;
155739991Sbostic const char * const type;
155839991Sbostic {
155939991Sbostic char buf[BUFSIZ];
156039991Sbostic int result;
156139991Sbostic
156239991Sbostic if (type == NULL || *type == '\0')
156339991Sbostic return TRUE;
156439991Sbostic if (strcmp(type, "uspres") == 0)
156539991Sbostic return (year % 4) == 0;
156639991Sbostic if (strcmp(type, "nonpres") == 0)
156739991Sbostic return (year % 4) != 0;
156839991Sbostic (void) sprintf(buf, "yearistype %d %s", year, type);
156939991Sbostic result = system(buf);
157039991Sbostic if (result == 0)
157139991Sbostic return TRUE;
157239991Sbostic if (result == (1 << 8))
157339991Sbostic return FALSE;
157439991Sbostic error("Wild result from command execution");
157539991Sbostic (void) fprintf(stderr, "%s: command was '%s', result was %d\n",
157639991Sbostic progname, buf, result);
157739991Sbostic for ( ; ; )
157839991Sbostic (void) exit(EXIT_FAILURE);
157939991Sbostic }
158039991Sbostic
158139991Sbostic static int
lowerit(a)158239991Sbostic lowerit(a)
158339991Sbostic const int a;
158439991Sbostic {
158539991Sbostic return (isascii(a) && isupper(a)) ? tolower(a) : a;
158639991Sbostic }
158739991Sbostic
158839991Sbostic static int
ciequal(ap,bp)158939991Sbostic ciequal(ap, bp) /* case-insensitive equality */
159039991Sbostic register const char * ap;
159139991Sbostic register const char * bp;
159239991Sbostic {
159339991Sbostic while (lowerit(*ap) == lowerit(*bp++))
159439991Sbostic if (*ap++ == '\0')
159539991Sbostic return TRUE;
159639991Sbostic return FALSE;
159739991Sbostic }
159839991Sbostic
159939991Sbostic static int
itsabbr(abbr,word)160039991Sbostic itsabbr(abbr, word)
160139991Sbostic register const char * abbr;
160239991Sbostic register const char * word;
160339991Sbostic {
160439991Sbostic if (lowerit(*abbr) != lowerit(*word))
160539991Sbostic return FALSE;
160639991Sbostic ++word;
160739991Sbostic while (*++abbr != '\0')
160839991Sbostic do if (*word == '\0')
160939991Sbostic return FALSE;
161039991Sbostic while (lowerit(*word++) != lowerit(*abbr));
161139991Sbostic return TRUE;
161239991Sbostic }
161339991Sbostic
161439991Sbostic static const struct lookup *
byword(word,table)161539991Sbostic byword(word, table)
161639991Sbostic register const char * const word;
161739991Sbostic register const struct lookup * const table;
161839991Sbostic {
161939991Sbostic register const struct lookup * foundlp;
162039991Sbostic register const struct lookup * lp;
162139991Sbostic
162239991Sbostic if (word == NULL || table == NULL)
162339991Sbostic return NULL;
162439991Sbostic /*
162539991Sbostic ** Look for exact match.
162639991Sbostic */
162739991Sbostic for (lp = table; lp->l_word != NULL; ++lp)
162839991Sbostic if (ciequal(word, lp->l_word))
162939991Sbostic return lp;
163039991Sbostic /*
163139991Sbostic ** Look for inexact match.
163239991Sbostic */
163339991Sbostic foundlp = NULL;
163439991Sbostic for (lp = table; lp->l_word != NULL; ++lp)
163539991Sbostic if (itsabbr(word, lp->l_word))
163639991Sbostic if (foundlp == NULL)
163739991Sbostic foundlp = lp;
163839991Sbostic else return NULL; /* multiple inexact matches */
163939991Sbostic return foundlp;
164039991Sbostic }
164139991Sbostic
164239991Sbostic static char **
getfields(cp)164339991Sbostic getfields(cp)
164439991Sbostic register char * cp;
164539991Sbostic {
164639991Sbostic register char * dp;
164739991Sbostic register char ** array;
164839991Sbostic register int nsubs;
164939991Sbostic
165039991Sbostic if (cp == NULL)
165139991Sbostic return NULL;
165239991Sbostic array = (char **) emalloc((int) ((strlen(cp) + 1) * sizeof *array));
165339991Sbostic nsubs = 0;
165439991Sbostic for ( ; ; ) {
165539991Sbostic while (isascii(*cp) && isspace(*cp))
165639991Sbostic ++cp;
165739991Sbostic if (*cp == '\0' || *cp == '#')
165839991Sbostic break;
165939991Sbostic array[nsubs++] = dp = cp;
166039991Sbostic do {
166139991Sbostic if ((*dp = *cp++) != '"')
166239991Sbostic ++dp;
166339991Sbostic else while ((*dp = *cp++) != '"')
166439991Sbostic if (*dp != '\0')
166539991Sbostic ++dp;
166639991Sbostic else error("Odd number of quotation marks");
166739991Sbostic } while (*cp != '\0' && *cp != '#' &&
166839991Sbostic (!isascii(*cp) || !isspace(*cp)));
166939991Sbostic if (isascii(*cp) && isspace(*cp))
167039991Sbostic ++cp;
167139991Sbostic *dp = '\0';
167239991Sbostic }
167339991Sbostic array[nsubs] = NULL;
167439991Sbostic return array;
167539991Sbostic }
167639991Sbostic
167739991Sbostic static long
oadd(t1,t2)167839991Sbostic oadd(t1, t2)
167939991Sbostic const long t1;
168039991Sbostic const long t2;
168139991Sbostic {
168239991Sbostic register long t;
168339991Sbostic
168439991Sbostic t = t1 + t2;
168539991Sbostic if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) {
168639991Sbostic error("time overflow");
168739991Sbostic (void) exit(EXIT_FAILURE);
168839991Sbostic }
168939991Sbostic return t;
169039991Sbostic }
169139991Sbostic
169239991Sbostic static time_t
tadd(t1,t2)169339991Sbostic tadd(t1, t2)
169439991Sbostic const time_t t1;
169539991Sbostic const long t2;
169639991Sbostic {
169739991Sbostic register time_t t;
169839991Sbostic
169939991Sbostic if (t1 == max_time && t2 > 0)
170039991Sbostic return max_time;
170139991Sbostic if (t1 == min_time && t2 < 0)
170239991Sbostic return min_time;
170339991Sbostic t = t1 + t2;
170439991Sbostic if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) {
170539991Sbostic error("time overflow");
170639991Sbostic (void) exit(EXIT_FAILURE);
170739991Sbostic }
170839991Sbostic return t;
170939991Sbostic }
171039991Sbostic
171139991Sbostic /*
171239991Sbostic ** Given a rule, and a year, compute the date - in seconds since January 1,
171339991Sbostic ** 1970, 00:00 LOCAL time - in that year that the rule refers to.
171439991Sbostic */
171539991Sbostic
171639991Sbostic static time_t
rpytime(rp,wantedy)171739991Sbostic rpytime(rp, wantedy)
171839991Sbostic register const struct rule * const rp;
171939991Sbostic register const int wantedy;
172039991Sbostic {
172139991Sbostic register int y, m, i;
172239991Sbostic register long dayoff; /* with a nod to Margaret O. */
172339991Sbostic register time_t t;
172439991Sbostic
172539991Sbostic dayoff = 0;
172639991Sbostic m = TM_JANUARY;
172739991Sbostic y = EPOCH_YEAR;
172839991Sbostic while (wantedy != y) {
172939991Sbostic if (wantedy > y) {
173039991Sbostic i = len_years[isleap(y)];
173139991Sbostic ++y;
173239991Sbostic } else {
173339991Sbostic --y;
173439991Sbostic i = -len_years[isleap(y)];
173539991Sbostic }
173639991Sbostic dayoff = oadd(dayoff, eitol(i));
173739991Sbostic }
173839991Sbostic while (m != rp->r_month) {
173939991Sbostic i = len_months[isleap(y)][m];
174039991Sbostic dayoff = oadd(dayoff, eitol(i));
174139991Sbostic ++m;
174239991Sbostic }
174339991Sbostic i = rp->r_dayofmonth;
174439991Sbostic if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
174539991Sbostic if (rp->r_dycode == DC_DOWLEQ)
174639991Sbostic --i;
174739991Sbostic else {
174839991Sbostic error("use of 2/29 in non leap-year");
174939991Sbostic (void) exit(EXIT_FAILURE);
175039991Sbostic }
175139991Sbostic }
175239991Sbostic --i;
175339991Sbostic dayoff = oadd(dayoff, eitol(i));
175439991Sbostic if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
175539991Sbostic register long wday;
175639991Sbostic
175739991Sbostic #define LDAYSPERWEEK ((long) DAYSPERWEEK)
175839991Sbostic wday = eitol(EPOCH_WDAY);
175939991Sbostic /*
176039991Sbostic ** Don't trust mod of negative numbers.
176139991Sbostic */
176239991Sbostic if (dayoff >= 0)
176339991Sbostic wday = (wday + dayoff) % LDAYSPERWEEK;
176439991Sbostic else {
176539991Sbostic wday -= ((-dayoff) % LDAYSPERWEEK);
176639991Sbostic if (wday < 0)
176739991Sbostic wday += LDAYSPERWEEK;
176839991Sbostic }
176939991Sbostic while (wday != eitol(rp->r_wday))
177039991Sbostic if (rp->r_dycode == DC_DOWGEQ) {
177139991Sbostic dayoff = oadd(dayoff, (long) 1);
177239991Sbostic if (++wday >= LDAYSPERWEEK)
177339991Sbostic wday = 0;
177439991Sbostic ++i;
177539991Sbostic } else {
177639991Sbostic dayoff = oadd(dayoff, (long) -1);
177739991Sbostic if (--wday < 0)
177863908Sbostic wday = LDAYSPERWEEK - 1;
177939991Sbostic --i;
178039991Sbostic }
178139991Sbostic if (i < 0 || i >= len_months[isleap(y)][m]) {
178239991Sbostic error("no day in month matches rule");
178339991Sbostic (void) exit(EXIT_FAILURE);
178439991Sbostic }
178539991Sbostic }
178639991Sbostic if (dayoff < 0 && !tt_signed) {
178739991Sbostic if (wantedy == rp->r_loyear)
178839991Sbostic return min_time;
178939991Sbostic error("time before zero");
179039991Sbostic (void) exit(EXIT_FAILURE);
179139991Sbostic }
179239991Sbostic t = (time_t) dayoff * SECSPERDAY;
179339991Sbostic /*
179439991Sbostic ** Cheap overflow check.
179539991Sbostic */
179639991Sbostic if (t / SECSPERDAY != dayoff) {
179739991Sbostic if (wantedy == rp->r_hiyear)
179839991Sbostic return max_time;
179939991Sbostic if (wantedy == rp->r_loyear)
180039991Sbostic return min_time;
180139991Sbostic error("time overflow");
180239991Sbostic (void) exit(EXIT_FAILURE);
180339991Sbostic }
180439991Sbostic return tadd(t, rp->r_tod);
180539991Sbostic }
180639991Sbostic
180739991Sbostic static void
newabbr(string)180839991Sbostic newabbr(string)
180939991Sbostic const char * const string;
181039991Sbostic {
181139991Sbostic register int i;
181239991Sbostic
181339991Sbostic i = strlen(string) + 1;
181439991Sbostic if (charcnt + i >= TZ_MAX_CHARS) {
181539991Sbostic error("too many, or too long, time zone abbreviations");
181639991Sbostic (void) exit(EXIT_FAILURE);
181739991Sbostic }
181839991Sbostic (void) strcpy(&chars[charcnt], string);
181939991Sbostic charcnt += eitol(i);
182039991Sbostic }
182139991Sbostic
182239991Sbostic static int
mkdirs(name)182339991Sbostic mkdirs(name)
182439991Sbostic char * const name;
182539991Sbostic {
182639991Sbostic register char * cp;
182739991Sbostic
182839991Sbostic if ((cp = name) == NULL || *cp == '\0')
182939991Sbostic return 0;
183039991Sbostic while ((cp = strchr(cp + 1, '/')) != 0) {
183139991Sbostic *cp = '\0';
183239991Sbostic if (!itsdir(name)) {
183339991Sbostic /*
183439991Sbostic ** It doesn't seem to exist, so we try to create it.
183539991Sbostic */
183646793Sbostic if (mkdir(name, 0755) != 0) {
183739991Sbostic (void) fprintf(stderr,
183839991Sbostic "%s: Can't create directory ",
183939991Sbostic progname);
184039991Sbostic (void) perror(name);
184139991Sbostic return -1;
184239991Sbostic }
184339991Sbostic }
184439991Sbostic *cp = '/';
184539991Sbostic }
184639991Sbostic return 0;
184739991Sbostic }
184839991Sbostic
184939991Sbostic static long
eitol(i)185039991Sbostic eitol(i)
185139991Sbostic const int i;
185239991Sbostic {
185339991Sbostic long l;
185439991Sbostic
185539991Sbostic l = i;
185639991Sbostic if (i < 0 && l >= 0 || i == 0 && l != 0 || i > 0 && l <= 0) {
185739991Sbostic (void) fprintf(stderr, "%s: %d did not sign extend correctly\n",
185839991Sbostic progname, i);
185939991Sbostic (void) exit(EXIT_FAILURE);
186039991Sbostic }
186139991Sbostic return l;
186239991Sbostic }
186339991Sbostic
186439991Sbostic /*
186539991Sbostic ** UNIX is a registered trademark of AT&T.
186639991Sbostic */
1867