17b35baebSbostic /*-
2*8b296c7aSbostic * Copyright (c) 1991, 1993
3*8b296c7aSbostic * The Regents of the University of California. All rights reserved.
47b35baebSbostic *
57b35baebSbostic * This code is derived from software contributed to Berkeley by
67b35baebSbostic * Arthur David Olson of the National Cancer Institute.
77b35baebSbostic *
87b35baebSbostic * %sccs.include.redist.c%
97b35baebSbostic */
107b35baebSbostic
1151034c06Sbostic #ifndef lint
12*8b296c7aSbostic static char sccsid[] = "@(#)zic.c 8.1 (Berkeley) 07/19/93";
137b35baebSbostic #endif /* not lint */
147b35baebSbostic
157b35baebSbostic #ifdef notdef
1651034c06Sbostic static char elsieid[] = "@(#)zic.c 4.12";
177b35baebSbostic #endif
1851034c06Sbostic
196068c877Sbostic #include <sys/types.h>
206068c877Sbostic #include <sys/cdefs.h>
216068c877Sbostic #include <sys/stat.h>
226068c877Sbostic #include <time.h>
236068c877Sbostic #include <tzfile.h>
246068c877Sbostic #include <stdio.h>
256068c877Sbostic #include <ctype.h>
266068c877Sbostic #include <string.h>
276068c877Sbostic #include <stdlib.h>
2851034c06Sbostic
2951034c06Sbostic #ifndef TRUE
3051034c06Sbostic #define TRUE 1
3151034c06Sbostic #define FALSE 0
3251034c06Sbostic #endif /* !defined TRUE */
3351034c06Sbostic
3451034c06Sbostic struct rule {
3551034c06Sbostic const char * r_filename;
3651034c06Sbostic int r_linenum;
3751034c06Sbostic const char * r_name;
3851034c06Sbostic
3951034c06Sbostic int r_loyear; /* for example, 1986 */
4051034c06Sbostic int r_hiyear; /* for example, 1986 */
4151034c06Sbostic const char * r_yrtype;
4251034c06Sbostic
4351034c06Sbostic int r_month; /* 0..11 */
4451034c06Sbostic
4551034c06Sbostic int r_dycode; /* see below */
4651034c06Sbostic int r_dayofmonth;
4751034c06Sbostic int r_wday;
4851034c06Sbostic
4951034c06Sbostic long r_tod; /* time from midnight */
5051034c06Sbostic int r_todisstd; /* above is standard time if TRUE */
5151034c06Sbostic /* or wall clock time if FALSE */
5251034c06Sbostic long r_stdoff; /* offset from standard time */
5351034c06Sbostic const char * r_abbrvar; /* variable part of abbreviation */
5451034c06Sbostic
5551034c06Sbostic int r_todo; /* a rule to do (used in outzone) */
5651034c06Sbostic time_t r_temp; /* used in outzone */
5751034c06Sbostic };
5851034c06Sbostic
5951034c06Sbostic /*
6051034c06Sbostic ** r_dycode r_dayofmonth r_wday
6151034c06Sbostic */
6251034c06Sbostic
6351034c06Sbostic #define DC_DOM 0 /* 1..31 */ /* unused */
6451034c06Sbostic #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
6551034c06Sbostic #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
6651034c06Sbostic
6751034c06Sbostic struct zone {
6851034c06Sbostic const char * z_filename;
6951034c06Sbostic int z_linenum;
7051034c06Sbostic
7151034c06Sbostic const char * z_name;
7251034c06Sbostic long z_gmtoff;
7351034c06Sbostic const char * z_rule;
7451034c06Sbostic const char * z_format;
7551034c06Sbostic
7651034c06Sbostic long z_stdoff;
7751034c06Sbostic
7851034c06Sbostic struct rule * z_rules;
7951034c06Sbostic int z_nrules;
8051034c06Sbostic
8151034c06Sbostic struct rule z_untilrule;
8251034c06Sbostic time_t z_untiltime;
8351034c06Sbostic };
8451034c06Sbostic
856068c877Sbostic extern char * icatalloc __P((char * old, const char * new));
866068c877Sbostic extern char * icpyalloc __P((const char * string));
876068c877Sbostic extern void ifree __P((char * p));
886068c877Sbostic extern char * imalloc __P((int n));
896068c877Sbostic extern char * irealloc __P((char * old, int n));
906068c877Sbostic extern int link __P((const char * fromname, const char * toname));
9151034c06Sbostic extern char * optarg;
9251034c06Sbostic extern int optind;
936068c877Sbostic extern void perror __P((const char * string));
946068c877Sbostic extern char * scheck __P((const char * string, const char * format));
956068c877Sbostic static void addtt __P((time_t starttime, int type));
966068c877Sbostic static int addtype
976068c877Sbostic __P((long gmtoff, const char * abbr, int isdst,
9851034c06Sbostic int ttisstd));
996068c877Sbostic static void addleap __P((time_t t, int positive, int rolling));
1006068c877Sbostic static void adjleap __P((void));
1016068c877Sbostic static void associate __P((void));
1026068c877Sbostic static int ciequal __P((const char * ap, const char * bp));
1036068c877Sbostic static void convert __P((long val, char * buf));
1046068c877Sbostic static void dolink __P((const char * fromfile, const char * tofile));
1056068c877Sbostic static void eat __P((const char * name, int num));
1066068c877Sbostic static void eats __P((const char * name, int num,
10751034c06Sbostic const char * rname, int rnum));
1086068c877Sbostic static long eitol __P((int i));
1096068c877Sbostic static void error __P((const char * message));
1106068c877Sbostic static char ** getfields __P((char * buf));
1116068c877Sbostic static long gethms __P((char * string, const char * errstrng,
11251034c06Sbostic int signable));
1136068c877Sbostic static void infile __P((const char * filename));
1146068c877Sbostic static void inleap __P((char ** fields, int nfields));
1156068c877Sbostic static void inlink __P((char ** fields, int nfields));
1166068c877Sbostic static void inrule __P((char ** fields, int nfields));
1176068c877Sbostic static int inzcont __P((char ** fields, int nfields));
1186068c877Sbostic static int inzone __P((char ** fields, int nfields));
1196068c877Sbostic static int inzsub __P((char ** fields, int nfields, int iscont));
1206068c877Sbostic static int itsabbr __P((const char * abbr, const char * word));
1216068c877Sbostic static int itsdir __P((const char * name));
1226068c877Sbostic static int lowerit __P((int c));
1236068c877Sbostic static char * memcheck __P((char * tocheck));
1246068c877Sbostic static int mkdirs __P((char * filename));
1256068c877Sbostic static void newabbr __P((const char * abbr));
1266068c877Sbostic static long oadd __P((long t1, long t2));
1276068c877Sbostic static void outzone __P((const struct zone * zp, int ntzones));
1286068c877Sbostic static void puttzcode __P((long code, FILE * fp));
1296068c877Sbostic static int rcomp __P((const void *leftp, const void *rightp));
1306068c877Sbostic static time_t rpytime __P((const struct rule * rp, int wantedy));
1316068c877Sbostic static void rulesub __P((struct rule * rp, char * loyearp, char * hiyearp,
1326068c877Sbostic char * typep, char * monthp, char * dayp, char * timep));
1336068c877Sbostic static void setboundaries __P((void));
1346068c877Sbostic static time_t tadd __P((time_t t1, long t2));
1356068c877Sbostic static void usage __P((void));
1366068c877Sbostic static void writezone __P((const char * name));
1376068c877Sbostic static int yearistype __P((int year, const char * type));
13851034c06Sbostic
13951034c06Sbostic static int charcnt;
14051034c06Sbostic static int errors;
14151034c06Sbostic static const char * filename;
14251034c06Sbostic static int leapcnt;
14351034c06Sbostic static int linenum;
14451034c06Sbostic static time_t max_time;
14551034c06Sbostic static int max_year;
14651034c06Sbostic static time_t min_time;
14751034c06Sbostic static int min_year;
14851034c06Sbostic static int noise;
14951034c06Sbostic static const char * rfilename;
15051034c06Sbostic static int rlinenum;
15151034c06Sbostic static const char * progname;
15251034c06Sbostic static int timecnt;
15351034c06Sbostic static int typecnt;
15451034c06Sbostic static int tt_signed;
15551034c06Sbostic
15651034c06Sbostic /*
15751034c06Sbostic ** Line codes.
15851034c06Sbostic */
15951034c06Sbostic
16051034c06Sbostic #define LC_RULE 0
16151034c06Sbostic #define LC_ZONE 1
16251034c06Sbostic #define LC_LINK 2
16351034c06Sbostic #define LC_LEAP 3
16451034c06Sbostic
16551034c06Sbostic /*
16651034c06Sbostic ** Which fields are which on a Zone line.
16751034c06Sbostic */
16851034c06Sbostic
16951034c06Sbostic #define ZF_NAME 1
17051034c06Sbostic #define ZF_GMTOFF 2
17151034c06Sbostic #define ZF_RULE 3
17251034c06Sbostic #define ZF_FORMAT 4
17351034c06Sbostic #define ZF_TILYEAR 5
17451034c06Sbostic #define ZF_TILMONTH 6
17551034c06Sbostic #define ZF_TILDAY 7
17651034c06Sbostic #define ZF_TILTIME 8
17751034c06Sbostic #define ZONE_MINFIELDS 5
17851034c06Sbostic #define ZONE_MAXFIELDS 9
17951034c06Sbostic
18051034c06Sbostic /*
18151034c06Sbostic ** Which fields are which on a Zone continuation line.
18251034c06Sbostic */
18351034c06Sbostic
18451034c06Sbostic #define ZFC_GMTOFF 0
18551034c06Sbostic #define ZFC_RULE 1
18651034c06Sbostic #define ZFC_FORMAT 2
18751034c06Sbostic #define ZFC_TILYEAR 3
18851034c06Sbostic #define ZFC_TILMONTH 4
18951034c06Sbostic #define ZFC_TILDAY 5
19051034c06Sbostic #define ZFC_TILTIME 6
19151034c06Sbostic #define ZONEC_MINFIELDS 3
19251034c06Sbostic #define ZONEC_MAXFIELDS 7
19351034c06Sbostic
19451034c06Sbostic /*
19551034c06Sbostic ** Which files are which on a Rule line.
19651034c06Sbostic */
19751034c06Sbostic
19851034c06Sbostic #define RF_NAME 1
19951034c06Sbostic #define RF_LOYEAR 2
20051034c06Sbostic #define RF_HIYEAR 3
20151034c06Sbostic #define RF_COMMAND 4
20251034c06Sbostic #define RF_MONTH 5
20351034c06Sbostic #define RF_DAY 6
20451034c06Sbostic #define RF_TOD 7
20551034c06Sbostic #define RF_STDOFF 8
20651034c06Sbostic #define RF_ABBRVAR 9
20751034c06Sbostic #define RULE_FIELDS 10
20851034c06Sbostic
20951034c06Sbostic /*
21051034c06Sbostic ** Which fields are which on a Link line.
21151034c06Sbostic */
21251034c06Sbostic
21351034c06Sbostic #define LF_FROM 1
21451034c06Sbostic #define LF_TO 2
21551034c06Sbostic #define LINK_FIELDS 3
21651034c06Sbostic
21751034c06Sbostic /*
21851034c06Sbostic ** Which fields are which on a Leap line.
21951034c06Sbostic */
22051034c06Sbostic
22151034c06Sbostic #define LP_YEAR 1
22251034c06Sbostic #define LP_MONTH 2
22351034c06Sbostic #define LP_DAY 3
22451034c06Sbostic #define LP_TIME 4
22551034c06Sbostic #define LP_CORR 5
22651034c06Sbostic #define LP_ROLL 6
22751034c06Sbostic #define LEAP_FIELDS 7
22851034c06Sbostic
22951034c06Sbostic /*
23051034c06Sbostic ** Year synonyms.
23151034c06Sbostic */
23251034c06Sbostic
23351034c06Sbostic #define YR_MINIMUM 0
23451034c06Sbostic #define YR_MAXIMUM 1
23551034c06Sbostic #define YR_ONLY 2
23651034c06Sbostic
23751034c06Sbostic static struct rule * rules;
23851034c06Sbostic static int nrules; /* number of rules */
23951034c06Sbostic
24051034c06Sbostic static struct zone * zones;
24151034c06Sbostic static int nzones; /* number of zones */
24251034c06Sbostic
24351034c06Sbostic struct link {
24451034c06Sbostic const char * l_filename;
24551034c06Sbostic int l_linenum;
24651034c06Sbostic const char * l_from;
24751034c06Sbostic const char * l_to;
24851034c06Sbostic };
24951034c06Sbostic
25051034c06Sbostic static struct link * links;
25151034c06Sbostic static int nlinks;
25251034c06Sbostic
25351034c06Sbostic struct lookup {
25451034c06Sbostic const char * l_word;
25551034c06Sbostic const int l_value;
25651034c06Sbostic };
25751034c06Sbostic
2586068c877Sbostic static struct lookup const * byword __P((const char * string,
25951034c06Sbostic const struct lookup * lp));
26051034c06Sbostic
26151034c06Sbostic static struct lookup const line_codes[] = {
26251034c06Sbostic "Rule", LC_RULE,
26351034c06Sbostic "Zone", LC_ZONE,
26451034c06Sbostic "Link", LC_LINK,
26551034c06Sbostic "Leap", LC_LEAP,
26651034c06Sbostic NULL, 0
26751034c06Sbostic };
26851034c06Sbostic
26951034c06Sbostic static struct lookup const mon_names[] = {
27051034c06Sbostic "January", TM_JANUARY,
27151034c06Sbostic "February", TM_FEBRUARY,
27251034c06Sbostic "March", TM_MARCH,
27351034c06Sbostic "April", TM_APRIL,
27451034c06Sbostic "May", TM_MAY,
27551034c06Sbostic "June", TM_JUNE,
27651034c06Sbostic "July", TM_JULY,
27751034c06Sbostic "August", TM_AUGUST,
27851034c06Sbostic "September", TM_SEPTEMBER,
27951034c06Sbostic "October", TM_OCTOBER,
28051034c06Sbostic "November", TM_NOVEMBER,
28151034c06Sbostic "December", TM_DECEMBER,
28251034c06Sbostic NULL, 0
28351034c06Sbostic };
28451034c06Sbostic
28551034c06Sbostic static struct lookup const wday_names[] = {
28651034c06Sbostic "Sunday", TM_SUNDAY,
28751034c06Sbostic "Monday", TM_MONDAY,
28851034c06Sbostic "Tuesday", TM_TUESDAY,
28951034c06Sbostic "Wednesday", TM_WEDNESDAY,
29051034c06Sbostic "Thursday", TM_THURSDAY,
29151034c06Sbostic "Friday", TM_FRIDAY,
29251034c06Sbostic "Saturday", TM_SATURDAY,
29351034c06Sbostic NULL, 0
29451034c06Sbostic };
29551034c06Sbostic
29651034c06Sbostic static struct lookup const lasts[] = {
29751034c06Sbostic "last-Sunday", TM_SUNDAY,
29851034c06Sbostic "last-Monday", TM_MONDAY,
29951034c06Sbostic "last-Tuesday", TM_TUESDAY,
30051034c06Sbostic "last-Wednesday", TM_WEDNESDAY,
30151034c06Sbostic "last-Thursday", TM_THURSDAY,
30251034c06Sbostic "last-Friday", TM_FRIDAY,
30351034c06Sbostic "last-Saturday", TM_SATURDAY,
30451034c06Sbostic NULL, 0
30551034c06Sbostic };
30651034c06Sbostic
30751034c06Sbostic static struct lookup const begin_years[] = {
30851034c06Sbostic "minimum", YR_MINIMUM,
30951034c06Sbostic "maximum", YR_MAXIMUM,
31051034c06Sbostic NULL, 0
31151034c06Sbostic };
31251034c06Sbostic
31351034c06Sbostic static struct lookup const end_years[] = {
31451034c06Sbostic "minimum", YR_MINIMUM,
31551034c06Sbostic "maximum", YR_MAXIMUM,
31651034c06Sbostic "only", YR_ONLY,
31751034c06Sbostic NULL, 0
31851034c06Sbostic };
31951034c06Sbostic
32051034c06Sbostic static struct lookup const leap_types[] = {
32151034c06Sbostic "Rolling", TRUE,
32251034c06Sbostic "Stationary", FALSE,
32351034c06Sbostic NULL, 0
32451034c06Sbostic };
32551034c06Sbostic
32651034c06Sbostic static const int len_months[2][MONSPERYEAR] = {
32751034c06Sbostic 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
32851034c06Sbostic 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
32951034c06Sbostic };
33051034c06Sbostic
33151034c06Sbostic static const int len_years[2] = {
33251034c06Sbostic DAYSPERNYEAR, DAYSPERLYEAR
33351034c06Sbostic };
33451034c06Sbostic
33551034c06Sbostic static time_t ats[TZ_MAX_TIMES];
33651034c06Sbostic static unsigned char types[TZ_MAX_TIMES];
33751034c06Sbostic static long gmtoffs[TZ_MAX_TYPES];
33851034c06Sbostic static char isdsts[TZ_MAX_TYPES];
33951034c06Sbostic static char abbrinds[TZ_MAX_TYPES];
34051034c06Sbostic static char ttisstds[TZ_MAX_TYPES];
34151034c06Sbostic static char chars[TZ_MAX_CHARS];
34251034c06Sbostic static time_t trans[TZ_MAX_LEAPS];
34351034c06Sbostic static long corr[TZ_MAX_LEAPS];
34451034c06Sbostic static char roll[TZ_MAX_LEAPS];
34551034c06Sbostic
34651034c06Sbostic /*
34751034c06Sbostic ** Memory allocation.
34851034c06Sbostic */
34951034c06Sbostic
35051034c06Sbostic static char *
memcheck(ptr)35151034c06Sbostic memcheck(ptr)
35251034c06Sbostic char * const ptr;
35351034c06Sbostic {
35451034c06Sbostic if (ptr == NULL) {
35551034c06Sbostic (void) perror(progname);
35651034c06Sbostic (void) exit(EXIT_FAILURE);
35751034c06Sbostic }
35851034c06Sbostic return ptr;
35951034c06Sbostic }
36051034c06Sbostic
36151034c06Sbostic #define emalloc(size) memcheck(imalloc(size))
36251034c06Sbostic #define erealloc(ptr, size) memcheck(irealloc(ptr, size))
36351034c06Sbostic #define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
36451034c06Sbostic #define ecatalloc(oldp, newp) memcheck(icatalloc(oldp, newp))
36551034c06Sbostic
36651034c06Sbostic /*
36751034c06Sbostic ** Error handling.
36851034c06Sbostic */
36951034c06Sbostic
37051034c06Sbostic static void
eats(name,num,rname,rnum)37151034c06Sbostic eats(name, num, rname, rnum)
37251034c06Sbostic const char * const name;
37351034c06Sbostic const int num;
37451034c06Sbostic const char * const rname;
37551034c06Sbostic const int rnum;
37651034c06Sbostic {
37751034c06Sbostic filename = name;
37851034c06Sbostic linenum = num;
37951034c06Sbostic rfilename = rname;
38051034c06Sbostic rlinenum = rnum;
38151034c06Sbostic }
38251034c06Sbostic
38351034c06Sbostic static void
eat(name,num)38451034c06Sbostic eat(name, num)
38551034c06Sbostic const char * const name;
38651034c06Sbostic const int num;
38751034c06Sbostic {
38851034c06Sbostic eats(name, num, (char *) NULL, -1);
38951034c06Sbostic }
39051034c06Sbostic
39151034c06Sbostic static void
error(string)39251034c06Sbostic error(string)
39351034c06Sbostic const char * const string;
39451034c06Sbostic {
39551034c06Sbostic /*
39651034c06Sbostic ** Match the format of "cc" to allow sh users to
39751034c06Sbostic ** zic ... 2>&1 | error -t "*" -v
39851034c06Sbostic ** on BSD systems.
39951034c06Sbostic */
40051034c06Sbostic (void) fprintf(stderr, "\"%s\", line %d: %s",
40151034c06Sbostic filename, linenum, string);
40251034c06Sbostic if (rfilename != NULL)
40351034c06Sbostic (void) fprintf(stderr, " (rule from \"%s\", line %d)",
40451034c06Sbostic rfilename, rlinenum);
40551034c06Sbostic (void) fprintf(stderr, "\n");
40651034c06Sbostic ++errors;
40751034c06Sbostic }
40851034c06Sbostic
40951034c06Sbostic static void
usage()41051034c06Sbostic usage()
41151034c06Sbostic {
41251034c06Sbostic (void) fprintf(stderr,
41351034c06Sbostic "%s: usage is %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n\
41451034c06Sbostic \t[ -L leapseconds ] [ filename ... ]\n",
41551034c06Sbostic progname, progname);
41651034c06Sbostic (void) exit(EXIT_FAILURE);
41751034c06Sbostic }
41851034c06Sbostic
41951034c06Sbostic static const char * psxrules = NULL;
42051034c06Sbostic static const char * lcltime = NULL;
42151034c06Sbostic static const char * directory = NULL;
42251034c06Sbostic static const char * leapsec = NULL;
42351034c06Sbostic static int sflag = FALSE;
42451034c06Sbostic
42551034c06Sbostic int
main(argc,argv)42651034c06Sbostic main(argc, argv)
42751034c06Sbostic int argc;
42851034c06Sbostic char * argv[];
42951034c06Sbostic {
43051034c06Sbostic register int i, j;
43151034c06Sbostic register int c;
43251034c06Sbostic
43351034c06Sbostic (void) umask(umask(022) | 022);
43451034c06Sbostic progname = argv[0];
43551034c06Sbostic while ((c = getopt(argc, argv, "d:l:p:L:vs")) != EOF)
43651034c06Sbostic switch (c) {
43751034c06Sbostic default:
43851034c06Sbostic usage();
43951034c06Sbostic case 'd':
44051034c06Sbostic if (directory == NULL)
44151034c06Sbostic directory = optarg;
44251034c06Sbostic else {
44351034c06Sbostic (void) fprintf(stderr,
44451034c06Sbostic "%s: More than one -d option specified\n",
44551034c06Sbostic progname);
44651034c06Sbostic (void) exit(EXIT_FAILURE);
44751034c06Sbostic }
44851034c06Sbostic break;
44951034c06Sbostic case 'l':
45051034c06Sbostic if (lcltime == NULL)
45151034c06Sbostic lcltime = optarg;
45251034c06Sbostic else {
45351034c06Sbostic (void) fprintf(stderr,
45451034c06Sbostic "%s: More than one -l option specified\n",
45551034c06Sbostic progname);
45651034c06Sbostic (void) exit(EXIT_FAILURE);
45751034c06Sbostic }
45851034c06Sbostic break;
45951034c06Sbostic case 'p':
46051034c06Sbostic if (psxrules == NULL)
46151034c06Sbostic psxrules = optarg;
46251034c06Sbostic else {
46351034c06Sbostic (void) fprintf(stderr,
46451034c06Sbostic "%s: More than one -p option specified\n",
46551034c06Sbostic progname);
46651034c06Sbostic (void) exit(EXIT_FAILURE);
46751034c06Sbostic }
46851034c06Sbostic break;
46951034c06Sbostic case 'L':
47051034c06Sbostic if (leapsec == NULL)
47151034c06Sbostic leapsec = optarg;
47251034c06Sbostic else {
47351034c06Sbostic (void) fprintf(stderr,
47451034c06Sbostic "%s: More than one -L option specified\n",
47551034c06Sbostic progname);
47651034c06Sbostic (void) exit(EXIT_FAILURE);
47751034c06Sbostic }
47851034c06Sbostic break;
47951034c06Sbostic case 'v':
48051034c06Sbostic noise = TRUE;
48151034c06Sbostic break;
48251034c06Sbostic case 's':
48351034c06Sbostic sflag = TRUE;
48451034c06Sbostic break;
48551034c06Sbostic }
48651034c06Sbostic if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
48751034c06Sbostic usage(); /* usage message by request */
48851034c06Sbostic if (directory == NULL)
48951034c06Sbostic directory = TZDIR;
49051034c06Sbostic
49151034c06Sbostic setboundaries();
49251034c06Sbostic
49351034c06Sbostic if (optind < argc && leapsec != NULL) {
49451034c06Sbostic infile(leapsec);
49551034c06Sbostic adjleap();
49651034c06Sbostic }
49751034c06Sbostic
49851034c06Sbostic zones = (struct zone *) emalloc(0);
49951034c06Sbostic rules = (struct rule *) emalloc(0);
50051034c06Sbostic links = (struct link *) emalloc(0);
50151034c06Sbostic for (i = optind; i < argc; ++i)
50251034c06Sbostic infile(argv[i]);
50351034c06Sbostic if (errors)
50451034c06Sbostic (void) exit(EXIT_FAILURE);
50551034c06Sbostic associate();
50651034c06Sbostic for (i = 0; i < nzones; i = j) {
50751034c06Sbostic /*
50851034c06Sbostic ** Find the next non-continuation zone entry.
50951034c06Sbostic */
51051034c06Sbostic for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
51151034c06Sbostic ;
51251034c06Sbostic outzone(&zones[i], j - i);
51351034c06Sbostic }
51451034c06Sbostic /*
51551034c06Sbostic ** Make links.
51651034c06Sbostic */
51751034c06Sbostic for (i = 0; i < nlinks; ++i)
51851034c06Sbostic dolink(links[i].l_from, links[i].l_to);
51951034c06Sbostic if (lcltime != NULL)
52051034c06Sbostic dolink(lcltime, TZDEFAULT);
52151034c06Sbostic if (psxrules != NULL)
52251034c06Sbostic dolink(psxrules, TZDEFRULES);
52351034c06Sbostic return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
52451034c06Sbostic }
52551034c06Sbostic
52651034c06Sbostic static void
dolink(fromfile,tofile)52751034c06Sbostic dolink(fromfile, tofile)
52851034c06Sbostic const char * const fromfile;
52951034c06Sbostic const char * const tofile;
53051034c06Sbostic {
53151034c06Sbostic register char * fromname;
53251034c06Sbostic register char * toname;
53351034c06Sbostic
53451034c06Sbostic fromname = ecpyalloc(directory);
53551034c06Sbostic fromname = ecatalloc(fromname, "/");
53651034c06Sbostic fromname = ecatalloc(fromname, fromfile);
53751034c06Sbostic toname = ecpyalloc(directory);
53851034c06Sbostic toname = ecatalloc(toname, "/");
53951034c06Sbostic toname = ecatalloc(toname, tofile);
54051034c06Sbostic /*
54151034c06Sbostic ** We get to be careful here since
54251034c06Sbostic ** there's a fair chance of root running us.
54351034c06Sbostic */
54451034c06Sbostic if (!itsdir(toname))
54551034c06Sbostic (void) remove(toname);
54651034c06Sbostic if (link(fromname, toname) != 0) {
54751034c06Sbostic (void) fprintf(stderr, "%s: Can't link from %s to ",
54851034c06Sbostic progname, fromname);
54951034c06Sbostic (void) perror(toname);
55051034c06Sbostic (void) exit(EXIT_FAILURE);
55151034c06Sbostic }
55251034c06Sbostic ifree(fromname);
55351034c06Sbostic ifree(toname);
55451034c06Sbostic }
55551034c06Sbostic
55651034c06Sbostic static void
setboundaries()55751034c06Sbostic setboundaries()
55851034c06Sbostic {
55951034c06Sbostic register time_t bit;
56051034c06Sbostic
56151034c06Sbostic for (bit = 1; bit > 0; bit <<= 1)
56251034c06Sbostic ;
56351034c06Sbostic if (bit == 0) { /* time_t is an unsigned type */
56451034c06Sbostic tt_signed = FALSE;
56551034c06Sbostic min_time = 0;
56651034c06Sbostic max_time = ~(time_t) 0;
56751034c06Sbostic if (sflag)
56851034c06Sbostic max_time >>= 1;
56951034c06Sbostic } else {
57051034c06Sbostic tt_signed = TRUE;
57151034c06Sbostic min_time = bit;
57251034c06Sbostic max_time = bit;
57351034c06Sbostic ++max_time;
57451034c06Sbostic max_time = -max_time;
57551034c06Sbostic if (sflag)
57651034c06Sbostic min_time = 0;
57751034c06Sbostic }
57851034c06Sbostic min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
57951034c06Sbostic max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
58051034c06Sbostic }
58151034c06Sbostic
58251034c06Sbostic static int
itsdir(name)58351034c06Sbostic itsdir(name)
58451034c06Sbostic const char * const name;
58551034c06Sbostic {
58651034c06Sbostic struct stat s;
58751034c06Sbostic
5886068c877Sbostic return (stat(name, &s) == 0 && S_ISDIR(s.st_mode));
58951034c06Sbostic }
59051034c06Sbostic
59151034c06Sbostic /*
59251034c06Sbostic ** Associate sets of rules with zones.
59351034c06Sbostic */
59451034c06Sbostic
59551034c06Sbostic /*
59651034c06Sbostic ** Sort by rule name.
59751034c06Sbostic */
59851034c06Sbostic
59951034c06Sbostic static int
rcomp(cp1,cp2)60051034c06Sbostic rcomp(cp1, cp2)
6016068c877Sbostic const void * cp1;
6026068c877Sbostic const void * cp2;
60351034c06Sbostic {
60451034c06Sbostic return strcmp(((struct rule *) cp1)->r_name,
60551034c06Sbostic ((struct rule *) cp2)->r_name);
60651034c06Sbostic }
60751034c06Sbostic
60851034c06Sbostic static void
associate()60951034c06Sbostic associate()
61051034c06Sbostic {
61151034c06Sbostic register struct zone * zp;
61251034c06Sbostic register struct rule * rp;
61351034c06Sbostic register int base, out;
61451034c06Sbostic register int i;
61551034c06Sbostic
61651034c06Sbostic if (nrules != 0)
6176068c877Sbostic (void) qsort((void *) rules, (size_t) nrules,
6186068c877Sbostic (size_t) sizeof *rules, rcomp);
61951034c06Sbostic for (i = 0; i < nzones; ++i) {
62051034c06Sbostic zp = &zones[i];
62151034c06Sbostic zp->z_rules = NULL;
62251034c06Sbostic zp->z_nrules = 0;
62351034c06Sbostic }
62451034c06Sbostic for (base = 0; base < nrules; base = out) {
62551034c06Sbostic rp = &rules[base];
62651034c06Sbostic for (out = base + 1; out < nrules; ++out)
62751034c06Sbostic if (strcmp(rp->r_name, rules[out].r_name) != 0)
62851034c06Sbostic break;
62951034c06Sbostic for (i = 0; i < nzones; ++i) {
63051034c06Sbostic zp = &zones[i];
63151034c06Sbostic if (strcmp(zp->z_rule, rp->r_name) != 0)
63251034c06Sbostic continue;
63351034c06Sbostic zp->z_rules = rp;
63451034c06Sbostic zp->z_nrules = out - base;
63551034c06Sbostic }
63651034c06Sbostic }
63751034c06Sbostic for (i = 0; i < nzones; ++i) {
63851034c06Sbostic zp = &zones[i];
63951034c06Sbostic if (zp->z_nrules == 0) {
64051034c06Sbostic /*
64151034c06Sbostic ** Maybe we have a local standard time offset.
64251034c06Sbostic */
64351034c06Sbostic eat(zp->z_filename, zp->z_linenum);
6446068c877Sbostic zp->z_stdoff =
6456068c877Sbostic gethms((char *)zp->z_rule, "unruly zone", TRUE);
64651034c06Sbostic /*
64751034c06Sbostic ** Note, though, that if there's no rule,
64851034c06Sbostic ** a '%s' in the format is a bad thing.
64951034c06Sbostic */
65051034c06Sbostic if (strchr(zp->z_format, '%') != 0)
65151034c06Sbostic error("%s in ruleless zone");
65251034c06Sbostic }
65351034c06Sbostic }
65451034c06Sbostic if (errors)
65551034c06Sbostic (void) exit(EXIT_FAILURE);
65651034c06Sbostic }
65751034c06Sbostic
65851034c06Sbostic static void
infile(name)65951034c06Sbostic infile(name)
66051034c06Sbostic const char * name;
66151034c06Sbostic {
66251034c06Sbostic register FILE * fp;
66351034c06Sbostic register char ** fields;
66451034c06Sbostic register char * cp;
66551034c06Sbostic register const struct lookup * lp;
66651034c06Sbostic register int nfields;
66751034c06Sbostic register int wantcont;
66851034c06Sbostic register int num;
66951034c06Sbostic char buf[BUFSIZ];
67051034c06Sbostic
67151034c06Sbostic if (strcmp(name, "-") == 0) {
67251034c06Sbostic name = "standard input";
67351034c06Sbostic fp = stdin;
67451034c06Sbostic } else if ((fp = fopen(name, "r")) == NULL) {
67551034c06Sbostic (void) fprintf(stderr, "%s: Can't open ", progname);
67651034c06Sbostic (void) perror(name);
67751034c06Sbostic (void) exit(EXIT_FAILURE);
67851034c06Sbostic }
67951034c06Sbostic wantcont = FALSE;
68051034c06Sbostic for (num = 1; ; ++num) {
68151034c06Sbostic eat(name, num);
68251034c06Sbostic if (fgets(buf, (int) sizeof buf, fp) != buf)
68351034c06Sbostic break;
68451034c06Sbostic cp = strchr(buf, '\n');
68551034c06Sbostic if (cp == NULL) {
68651034c06Sbostic error("line too long");
68751034c06Sbostic (void) exit(EXIT_FAILURE);
68851034c06Sbostic }
68951034c06Sbostic *cp = '\0';
69051034c06Sbostic fields = getfields(buf);
69151034c06Sbostic nfields = 0;
69251034c06Sbostic while (fields[nfields] != NULL) {
69351034c06Sbostic if (ciequal(fields[nfields], "-"))
69451034c06Sbostic fields[nfields] = "";
69551034c06Sbostic ++nfields;
69651034c06Sbostic }
69751034c06Sbostic if (nfields == 0) {
69851034c06Sbostic /* nothing to do */
69951034c06Sbostic } else if (wantcont) {
70051034c06Sbostic wantcont = inzcont(fields, nfields);
70151034c06Sbostic } else {
70251034c06Sbostic lp = byword(fields[0], line_codes);
70351034c06Sbostic if (lp == NULL)
70451034c06Sbostic error("input line of unknown type");
70551034c06Sbostic else switch ((int) (lp->l_value)) {
70651034c06Sbostic case LC_RULE:
70751034c06Sbostic inrule(fields, nfields);
70851034c06Sbostic wantcont = FALSE;
70951034c06Sbostic break;
71051034c06Sbostic case LC_ZONE:
71151034c06Sbostic wantcont = inzone(fields, nfields);
71251034c06Sbostic break;
71351034c06Sbostic case LC_LINK:
71451034c06Sbostic inlink(fields, nfields);
71551034c06Sbostic wantcont = FALSE;
71651034c06Sbostic break;
71751034c06Sbostic case LC_LEAP:
71851034c06Sbostic if (name != leapsec)
71951034c06Sbostic (void) fprintf(stderr,
72051034c06Sbostic "%s: Leap line in non leap seconds file %s\n",
72151034c06Sbostic progname, name);
72251034c06Sbostic else inleap(fields, nfields);
72351034c06Sbostic wantcont = FALSE;
72451034c06Sbostic break;
72551034c06Sbostic default: /* "cannot happen" */
72651034c06Sbostic (void) fprintf(stderr,
72751034c06Sbostic "%s: panic: Invalid l_value %d\n",
72851034c06Sbostic progname, lp->l_value);
72951034c06Sbostic (void) exit(EXIT_FAILURE);
73051034c06Sbostic }
73151034c06Sbostic }
73251034c06Sbostic ifree((char *) fields);
73351034c06Sbostic }
73451034c06Sbostic if (ferror(fp)) {
73551034c06Sbostic (void) fprintf(stderr, "%s: Error reading ", progname);
73651034c06Sbostic (void) perror(filename);
73751034c06Sbostic (void) exit(EXIT_FAILURE);
73851034c06Sbostic }
73951034c06Sbostic if (fp != stdin && fclose(fp)) {
74051034c06Sbostic (void) fprintf(stderr, "%s: Error closing ", progname);
74151034c06Sbostic (void) perror(filename);
74251034c06Sbostic (void) exit(EXIT_FAILURE);
74351034c06Sbostic }
74451034c06Sbostic if (wantcont)
74551034c06Sbostic error("expected continuation line not found");
74651034c06Sbostic }
74751034c06Sbostic
74851034c06Sbostic /*
74951034c06Sbostic ** Convert a string of one of the forms
75051034c06Sbostic ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
75151034c06Sbostic ** into a number of seconds.
75251034c06Sbostic ** A null string maps to zero.
75351034c06Sbostic ** Call error with errstring and return zero on errors.
75451034c06Sbostic */
75551034c06Sbostic
75651034c06Sbostic static long
gethms(string,errstring,signable)75751034c06Sbostic gethms(string, errstring, signable)
7586068c877Sbostic char * string;
75951034c06Sbostic const char * const errstring;
76051034c06Sbostic const int signable;
76151034c06Sbostic {
76251034c06Sbostic int hh, mm, ss, sign;
76351034c06Sbostic
76451034c06Sbostic if (string == NULL || *string == '\0')
76551034c06Sbostic return 0;
76651034c06Sbostic if (!signable)
76751034c06Sbostic sign = 1;
76851034c06Sbostic else if (*string == '-') {
76951034c06Sbostic sign = -1;
77051034c06Sbostic ++string;
77151034c06Sbostic } else sign = 1;
77251034c06Sbostic if (sscanf(string, scheck(string, "%d"), &hh) == 1)
77351034c06Sbostic mm = ss = 0;
77451034c06Sbostic else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
77551034c06Sbostic ss = 0;
77651034c06Sbostic else if (sscanf(string, scheck(string, "%d:%d:%d"),
77751034c06Sbostic &hh, &mm, &ss) != 3) {
77851034c06Sbostic error(errstring);
77951034c06Sbostic return 0;
78051034c06Sbostic }
78151034c06Sbostic if (hh < 0 || hh >= HOURSPERDAY ||
78251034c06Sbostic mm < 0 || mm >= MINSPERHOUR ||
78351034c06Sbostic ss < 0 || ss > SECSPERMIN) {
78451034c06Sbostic error(errstring);
78551034c06Sbostic return 0;
78651034c06Sbostic }
78751034c06Sbostic return eitol(sign) *
78851034c06Sbostic (eitol(hh * MINSPERHOUR + mm) *
78951034c06Sbostic eitol(SECSPERMIN) + eitol(ss));
79051034c06Sbostic }
79151034c06Sbostic
79251034c06Sbostic static void
inrule(fields,nfields)79351034c06Sbostic inrule(fields, nfields)
79451034c06Sbostic register char ** const fields;
79551034c06Sbostic const int nfields;
79651034c06Sbostic {
79751034c06Sbostic static struct rule r;
79851034c06Sbostic
79951034c06Sbostic if (nfields != RULE_FIELDS) {
80051034c06Sbostic error("wrong number of fields on Rule line");
80151034c06Sbostic return;
80251034c06Sbostic }
80351034c06Sbostic if (*fields[RF_NAME] == '\0') {
80451034c06Sbostic error("nameless rule");
80551034c06Sbostic return;
80651034c06Sbostic }
80751034c06Sbostic r.r_filename = filename;
80851034c06Sbostic r.r_linenum = linenum;
80951034c06Sbostic r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE);
81051034c06Sbostic rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
81151034c06Sbostic fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
81251034c06Sbostic r.r_name = ecpyalloc(fields[RF_NAME]);
81351034c06Sbostic r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
81451034c06Sbostic rules = (struct rule *) erealloc((char *) rules,
81551034c06Sbostic (int) ((nrules + 1) * sizeof *rules));
81651034c06Sbostic rules[nrules++] = r;
81751034c06Sbostic }
81851034c06Sbostic
81951034c06Sbostic static int
inzone(fields,nfields)82051034c06Sbostic inzone(fields, nfields)
82151034c06Sbostic register char ** const fields;
82251034c06Sbostic const int nfields;
82351034c06Sbostic {
82451034c06Sbostic register int i;
82551034c06Sbostic char buf[132];
82651034c06Sbostic
82751034c06Sbostic if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
82851034c06Sbostic error("wrong number of fields on Zone line");
82951034c06Sbostic return FALSE;
83051034c06Sbostic }
83151034c06Sbostic if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
83251034c06Sbostic (void) sprintf(buf,
83351034c06Sbostic "\"Zone %s\" line and -l option are mutually exclusive",
83451034c06Sbostic TZDEFAULT);
83551034c06Sbostic error(buf);
83651034c06Sbostic return FALSE;
83751034c06Sbostic }
83851034c06Sbostic if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
83951034c06Sbostic (void) sprintf(buf,
84051034c06Sbostic "\"Zone %s\" line and -p option are mutually exclusive",
84151034c06Sbostic TZDEFRULES);
84251034c06Sbostic error(buf);
84351034c06Sbostic return FALSE;
84451034c06Sbostic }
84551034c06Sbostic for (i = 0; i < nzones; ++i)
84651034c06Sbostic if (zones[i].z_name != NULL &&
84751034c06Sbostic strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
84851034c06Sbostic (void) sprintf(buf,
84951034c06Sbostic "duplicate zone name %s (file \"%s\", line %d)",
85051034c06Sbostic fields[ZF_NAME],
85151034c06Sbostic zones[i].z_filename,
85251034c06Sbostic zones[i].z_linenum);
85351034c06Sbostic error(buf);
85451034c06Sbostic return FALSE;
85551034c06Sbostic }
85651034c06Sbostic return inzsub(fields, nfields, FALSE);
85751034c06Sbostic }
85851034c06Sbostic
85951034c06Sbostic static int
inzcont(fields,nfields)86051034c06Sbostic inzcont(fields, nfields)
86151034c06Sbostic register char ** const fields;
86251034c06Sbostic const int nfields;
86351034c06Sbostic {
86451034c06Sbostic if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
86551034c06Sbostic error("wrong number of fields on Zone continuation line");
86651034c06Sbostic return FALSE;
86751034c06Sbostic }
86851034c06Sbostic return inzsub(fields, nfields, TRUE);
86951034c06Sbostic }
87051034c06Sbostic
87151034c06Sbostic static int
inzsub(fields,nfields,iscont)87251034c06Sbostic inzsub(fields, nfields, iscont)
87351034c06Sbostic register char ** const fields;
87451034c06Sbostic const int nfields;
87551034c06Sbostic const int iscont;
87651034c06Sbostic {
87751034c06Sbostic register char * cp;
87851034c06Sbostic static struct zone z;
87951034c06Sbostic register int i_gmtoff, i_rule, i_format;
88051034c06Sbostic register int i_untilyear, i_untilmonth;
88151034c06Sbostic register int i_untilday, i_untiltime;
88251034c06Sbostic register int hasuntil;
88351034c06Sbostic
88451034c06Sbostic if (iscont) {
88551034c06Sbostic i_gmtoff = ZFC_GMTOFF;
88651034c06Sbostic i_rule = ZFC_RULE;
88751034c06Sbostic i_format = ZFC_FORMAT;
88851034c06Sbostic i_untilyear = ZFC_TILYEAR;
88951034c06Sbostic i_untilmonth = ZFC_TILMONTH;
89051034c06Sbostic i_untilday = ZFC_TILDAY;
89151034c06Sbostic i_untiltime = ZFC_TILTIME;
89251034c06Sbostic z.z_name = NULL;
89351034c06Sbostic } else {
89451034c06Sbostic i_gmtoff = ZF_GMTOFF;
89551034c06Sbostic i_rule = ZF_RULE;
89651034c06Sbostic i_format = ZF_FORMAT;
89751034c06Sbostic i_untilyear = ZF_TILYEAR;
89851034c06Sbostic i_untilmonth = ZF_TILMONTH;
89951034c06Sbostic i_untilday = ZF_TILDAY;
90051034c06Sbostic i_untiltime = ZF_TILTIME;
90151034c06Sbostic z.z_name = ecpyalloc(fields[ZF_NAME]);
90251034c06Sbostic }
90351034c06Sbostic z.z_filename = filename;
90451034c06Sbostic z.z_linenum = linenum;
90551034c06Sbostic z.z_gmtoff = gethms(fields[i_gmtoff], "invalid GMT offset", TRUE);
90651034c06Sbostic if ((cp = strchr(fields[i_format], '%')) != 0) {
90751034c06Sbostic if (*++cp != 's' || strchr(cp, '%') != 0) {
90851034c06Sbostic error("invalid abbreviation format");
90951034c06Sbostic return FALSE;
91051034c06Sbostic }
91151034c06Sbostic }
91251034c06Sbostic z.z_rule = ecpyalloc(fields[i_rule]);
91351034c06Sbostic z.z_format = ecpyalloc(fields[i_format]);
91451034c06Sbostic hasuntil = nfields > i_untilyear;
91551034c06Sbostic if (hasuntil) {
91651034c06Sbostic z.z_untilrule.r_filename = filename;
91751034c06Sbostic z.z_untilrule.r_linenum = linenum;
91851034c06Sbostic rulesub(&z.z_untilrule,
91951034c06Sbostic fields[i_untilyear],
92051034c06Sbostic "only",
92151034c06Sbostic "",
92251034c06Sbostic (nfields > i_untilmonth) ? fields[i_untilmonth] : "Jan",
92351034c06Sbostic (nfields > i_untilday) ? fields[i_untilday] : "1",
92451034c06Sbostic (nfields > i_untiltime) ? fields[i_untiltime] : "0");
92551034c06Sbostic z.z_untiltime = rpytime(&z.z_untilrule, z.z_untilrule.r_loyear);
92651034c06Sbostic if (iscont && nzones > 0 && z.z_untiltime < max_time &&
92751034c06Sbostic z.z_untiltime > min_time &&
92851034c06Sbostic zones[nzones - 1].z_untiltime >= z.z_untiltime) {
92951034c06Sbostic error("Zone continuation line end time is not after end time of previous line");
93051034c06Sbostic return FALSE;
93151034c06Sbostic }
93251034c06Sbostic }
93351034c06Sbostic zones = (struct zone *) erealloc((char *) zones,
93451034c06Sbostic (int) ((nzones + 1) * sizeof *zones));
93551034c06Sbostic zones[nzones++] = z;
93651034c06Sbostic /*
93751034c06Sbostic ** If there was an UNTIL field on this line,
93851034c06Sbostic ** there's more information about the zone on the next line.
93951034c06Sbostic */
94051034c06Sbostic return hasuntil;
94151034c06Sbostic }
94251034c06Sbostic
94351034c06Sbostic static void
inleap(fields,nfields)94451034c06Sbostic inleap(fields, nfields)
94551034c06Sbostic register char ** const fields;
94651034c06Sbostic const int nfields;
94751034c06Sbostic {
94851034c06Sbostic register const char * cp;
94951034c06Sbostic register const struct lookup * lp;
95051034c06Sbostic register int i, j;
95151034c06Sbostic int year, month, day;
95251034c06Sbostic long dayoff, tod;
95351034c06Sbostic time_t t;
95451034c06Sbostic
95551034c06Sbostic if (nfields != LEAP_FIELDS) {
95651034c06Sbostic error("wrong number of fields on Leap line");
95751034c06Sbostic return;
95851034c06Sbostic }
95951034c06Sbostic dayoff = 0;
96051034c06Sbostic cp = fields[LP_YEAR];
9616068c877Sbostic if (sscanf((char *)cp, scheck(cp, "%d"), &year) != 1 ||
96251034c06Sbostic year < min_year || year > max_year) {
96351034c06Sbostic /*
96451034c06Sbostic * Leapin' Lizards!
96551034c06Sbostic */
96651034c06Sbostic error("invalid leaping year");
96751034c06Sbostic return;
96851034c06Sbostic }
96951034c06Sbostic j = EPOCH_YEAR;
97051034c06Sbostic while (j != year) {
97151034c06Sbostic if (year > j) {
97251034c06Sbostic i = len_years[isleap(j)];
97351034c06Sbostic ++j;
97451034c06Sbostic } else {
97551034c06Sbostic --j;
97651034c06Sbostic i = -len_years[isleap(j)];
97751034c06Sbostic }
97851034c06Sbostic dayoff = oadd(dayoff, eitol(i));
97951034c06Sbostic }
98051034c06Sbostic if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
98151034c06Sbostic error("invalid month name");
98251034c06Sbostic return;
98351034c06Sbostic }
98451034c06Sbostic month = lp->l_value;
98551034c06Sbostic j = TM_JANUARY;
98651034c06Sbostic while (j != month) {
98751034c06Sbostic i = len_months[isleap(year)][j];
98851034c06Sbostic dayoff = oadd(dayoff, eitol(i));
98951034c06Sbostic ++j;
99051034c06Sbostic }
99151034c06Sbostic cp = fields[LP_DAY];
9926068c877Sbostic if (sscanf((char *)cp, scheck(cp, "%d"), &day) != 1 ||
99351034c06Sbostic day <= 0 || day > len_months[isleap(year)][month]) {
99451034c06Sbostic error("invalid day of month");
99551034c06Sbostic return;
99651034c06Sbostic }
99751034c06Sbostic dayoff = oadd(dayoff, eitol(day - 1));
99851034c06Sbostic if (dayoff < 0 && !tt_signed) {
99951034c06Sbostic error("time before zero");
100051034c06Sbostic return;
100151034c06Sbostic }
100251034c06Sbostic t = (time_t) dayoff * SECSPERDAY;
100351034c06Sbostic /*
100451034c06Sbostic ** Cheap overflow check.
100551034c06Sbostic */
100651034c06Sbostic if (t / SECSPERDAY != dayoff) {
100751034c06Sbostic error("time overflow");
100851034c06Sbostic return;
100951034c06Sbostic }
101051034c06Sbostic tod = gethms(fields[LP_TIME], "invalid time of day", FALSE);
101151034c06Sbostic cp = fields[LP_CORR];
101251034c06Sbostic if (strcmp(cp, "+") != 0 && strcmp(cp, "") != 0) {
101351034c06Sbostic /* infile() turned "-" into "" */
101451034c06Sbostic error("illegal CORRECTION field on Leap line");
101551034c06Sbostic return;
101651034c06Sbostic }
101751034c06Sbostic if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
101851034c06Sbostic error("illegal Rolling/Stationary field on Leap line");
101951034c06Sbostic return;
102051034c06Sbostic }
102151034c06Sbostic addleap(tadd(t, tod), *cp == '+', lp->l_value);
102251034c06Sbostic }
102351034c06Sbostic
102451034c06Sbostic static void
inlink(fields,nfields)102551034c06Sbostic inlink(fields, nfields)
102651034c06Sbostic register char ** const fields;
102751034c06Sbostic const int nfields;
102851034c06Sbostic {
102951034c06Sbostic struct link l;
103051034c06Sbostic
103151034c06Sbostic if (nfields != LINK_FIELDS) {
103251034c06Sbostic error("wrong number of fields on Link line");
103351034c06Sbostic return;
103451034c06Sbostic }
103551034c06Sbostic if (*fields[LF_FROM] == '\0') {
103651034c06Sbostic error("blank FROM field on Link line");
103751034c06Sbostic return;
103851034c06Sbostic }
103951034c06Sbostic if (*fields[LF_TO] == '\0') {
104051034c06Sbostic error("blank TO field on Link line");
104151034c06Sbostic return;
104251034c06Sbostic }
104351034c06Sbostic l.l_filename = filename;
104451034c06Sbostic l.l_linenum = linenum;
104551034c06Sbostic l.l_from = ecpyalloc(fields[LF_FROM]);
104651034c06Sbostic l.l_to = ecpyalloc(fields[LF_TO]);
104751034c06Sbostic links = (struct link *) erealloc((char *) links,
104851034c06Sbostic (int) ((nlinks + 1) * sizeof *links));
104951034c06Sbostic links[nlinks++] = l;
105051034c06Sbostic }
105151034c06Sbostic
105251034c06Sbostic static void
rulesub(rp,loyearp,hiyearp,typep,monthp,dayp,timep)105351034c06Sbostic rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
105451034c06Sbostic register struct rule * const rp;
105551034c06Sbostic char * const loyearp;
105651034c06Sbostic char * const hiyearp;
105751034c06Sbostic char * const typep;
105851034c06Sbostic char * const monthp;
105951034c06Sbostic char * const dayp;
106051034c06Sbostic char * const timep;
106151034c06Sbostic {
106251034c06Sbostic register struct lookup const * lp;
106351034c06Sbostic register char * cp;
106451034c06Sbostic
106551034c06Sbostic if ((lp = byword(monthp, mon_names)) == NULL) {
106651034c06Sbostic error("invalid month name");
106751034c06Sbostic return;
106851034c06Sbostic }
106951034c06Sbostic rp->r_month = lp->l_value;
107051034c06Sbostic rp->r_todisstd = FALSE;
107151034c06Sbostic cp = timep;
107251034c06Sbostic if (*cp != '\0') {
107351034c06Sbostic cp += strlen(cp) - 1;
107451034c06Sbostic switch (lowerit(*cp)) {
107551034c06Sbostic case 's':
107651034c06Sbostic rp->r_todisstd = TRUE;
107751034c06Sbostic *cp = '\0';
107851034c06Sbostic break;
107951034c06Sbostic case 'w':
108051034c06Sbostic rp->r_todisstd = FALSE;
108151034c06Sbostic *cp = '\0';
108251034c06Sbostic break;
108351034c06Sbostic }
108451034c06Sbostic }
108551034c06Sbostic rp->r_tod = gethms(timep, "invalid time of day", FALSE);
108651034c06Sbostic /*
108751034c06Sbostic ** Year work.
108851034c06Sbostic */
108951034c06Sbostic cp = loyearp;
109051034c06Sbostic if ((lp = byword(cp, begin_years)) != NULL) switch ((int) lp->l_value) {
109151034c06Sbostic case YR_MINIMUM:
109251034c06Sbostic rp->r_loyear = min_year;
109351034c06Sbostic break;
109451034c06Sbostic case YR_MAXIMUM:
109551034c06Sbostic rp->r_loyear = max_year;
109651034c06Sbostic break;
109751034c06Sbostic default: /* "cannot happen" */
109851034c06Sbostic (void) fprintf(stderr,
109951034c06Sbostic "%s: panic: Invalid l_value %d\n",
110051034c06Sbostic progname, lp->l_value);
110151034c06Sbostic (void) exit(EXIT_FAILURE);
110251034c06Sbostic } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1 ||
110351034c06Sbostic rp->r_loyear < min_year || rp->r_loyear > max_year) {
110451034c06Sbostic if (noise)
110551034c06Sbostic error("invalid starting year");
110651034c06Sbostic if (rp->r_loyear > max_year)
110751034c06Sbostic return;
110851034c06Sbostic }
110951034c06Sbostic cp = hiyearp;
111051034c06Sbostic if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
111151034c06Sbostic case YR_MINIMUM:
111251034c06Sbostic rp->r_hiyear = min_year;
111351034c06Sbostic break;
111451034c06Sbostic case YR_MAXIMUM:
111551034c06Sbostic rp->r_hiyear = max_year;
111651034c06Sbostic break;
111751034c06Sbostic case YR_ONLY:
111851034c06Sbostic rp->r_hiyear = rp->r_loyear;
111951034c06Sbostic break;
112051034c06Sbostic default: /* "cannot happen" */
112151034c06Sbostic (void) fprintf(stderr,
112251034c06Sbostic "%s: panic: Invalid l_value %d\n",
112351034c06Sbostic progname, lp->l_value);
112451034c06Sbostic (void) exit(EXIT_FAILURE);
112551034c06Sbostic } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1 ||
112651034c06Sbostic rp->r_hiyear < min_year || rp->r_hiyear > max_year) {
112751034c06Sbostic if (noise)
112851034c06Sbostic error("invalid ending year");
112951034c06Sbostic if (rp->r_hiyear < min_year)
113051034c06Sbostic return;
113151034c06Sbostic }
113251034c06Sbostic if (rp->r_hiyear < min_year)
113351034c06Sbostic return;
113451034c06Sbostic if (rp->r_loyear < min_year)
113551034c06Sbostic rp->r_loyear = min_year;
113651034c06Sbostic if (rp->r_hiyear > max_year)
113751034c06Sbostic rp->r_hiyear = max_year;
113851034c06Sbostic if (rp->r_loyear > rp->r_hiyear) {
113951034c06Sbostic error("starting year greater than ending year");
114051034c06Sbostic return;
114151034c06Sbostic }
114251034c06Sbostic if (*typep == '\0')
114351034c06Sbostic rp->r_yrtype = NULL;
114451034c06Sbostic else {
114551034c06Sbostic if (rp->r_loyear == rp->r_hiyear) {
114651034c06Sbostic error("typed single year");
114751034c06Sbostic return;
114851034c06Sbostic }
114951034c06Sbostic rp->r_yrtype = ecpyalloc(typep);
115051034c06Sbostic }
115151034c06Sbostic /*
115251034c06Sbostic ** Day work.
115351034c06Sbostic ** Accept things such as:
115451034c06Sbostic ** 1
115551034c06Sbostic ** last-Sunday
115651034c06Sbostic ** Sun<=20
115751034c06Sbostic ** Sun>=7
115851034c06Sbostic */
115951034c06Sbostic if ((lp = byword(dayp, lasts)) != NULL) {
116051034c06Sbostic rp->r_dycode = DC_DOWLEQ;
116151034c06Sbostic rp->r_wday = lp->l_value;
116251034c06Sbostic rp->r_dayofmonth = len_months[1][rp->r_month];
116351034c06Sbostic } else {
116451034c06Sbostic if ((cp = strchr(dayp, '<')) != 0)
116551034c06Sbostic rp->r_dycode = DC_DOWLEQ;
116651034c06Sbostic else if ((cp = strchr(dayp, '>')) != 0)
116751034c06Sbostic rp->r_dycode = DC_DOWGEQ;
116851034c06Sbostic else {
116951034c06Sbostic cp = dayp;
117051034c06Sbostic rp->r_dycode = DC_DOM;
117151034c06Sbostic }
117251034c06Sbostic if (rp->r_dycode != DC_DOM) {
117351034c06Sbostic *cp++ = 0;
117451034c06Sbostic if (*cp++ != '=') {
117551034c06Sbostic error("invalid day of month");
117651034c06Sbostic return;
117751034c06Sbostic }
117851034c06Sbostic if ((lp = byword(dayp, wday_names)) == NULL) {
117951034c06Sbostic error("invalid weekday name");
118051034c06Sbostic return;
118151034c06Sbostic }
118251034c06Sbostic rp->r_wday = lp->l_value;
118351034c06Sbostic }
118451034c06Sbostic if (sscanf(cp, scheck(cp, "%d"), &rp->r_dayofmonth) != 1 ||
118551034c06Sbostic rp->r_dayofmonth <= 0 ||
118651034c06Sbostic (rp->r_dayofmonth > len_months[1][rp->r_month])) {
118751034c06Sbostic error("invalid day of month");
118851034c06Sbostic return;
118951034c06Sbostic }
119051034c06Sbostic }
119151034c06Sbostic }
119251034c06Sbostic
119351034c06Sbostic static void
convert(val,buf)119451034c06Sbostic convert(val, buf)
119551034c06Sbostic const long val;
119651034c06Sbostic char * const buf;
119751034c06Sbostic {
119851034c06Sbostic register int i;
119951034c06Sbostic register long shift;
120051034c06Sbostic
120151034c06Sbostic for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
120251034c06Sbostic buf[i] = val >> shift;
120351034c06Sbostic }
120451034c06Sbostic
120551034c06Sbostic static void
puttzcode(val,fp)120651034c06Sbostic puttzcode(val, fp)
120751034c06Sbostic const long val;
120851034c06Sbostic FILE * const fp;
120951034c06Sbostic {
121051034c06Sbostic char buf[4];
121151034c06Sbostic
121251034c06Sbostic convert(val, buf);
12136068c877Sbostic (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
121451034c06Sbostic }
121551034c06Sbostic
121651034c06Sbostic static void
writezone(name)121751034c06Sbostic writezone(name)
121851034c06Sbostic const char * const name;
121951034c06Sbostic {
122051034c06Sbostic register FILE * fp;
122151034c06Sbostic register int i, j;
122251034c06Sbostic char fullname[BUFSIZ];
122351034c06Sbostic static struct tzhead tzh;
122451034c06Sbostic
122551034c06Sbostic if (strlen(directory) + 1 + strlen(name) >= sizeof fullname) {
122651034c06Sbostic (void) fprintf(stderr,
122751034c06Sbostic "%s: File name %s/%s too long\n", progname,
122851034c06Sbostic directory, name);
122951034c06Sbostic (void) exit(EXIT_FAILURE);
123051034c06Sbostic }
123151034c06Sbostic (void) sprintf(fullname, "%s/%s", directory, name);
123251034c06Sbostic if ((fp = fopen(fullname, "wb")) == NULL) {
123351034c06Sbostic if (mkdirs(fullname) != 0)
123451034c06Sbostic (void) exit(EXIT_FAILURE);
123551034c06Sbostic if ((fp = fopen(fullname, "wb")) == NULL) {
123651034c06Sbostic (void) fprintf(stderr, "%s: Can't create ", progname);
123751034c06Sbostic (void) perror(fullname);
123851034c06Sbostic (void) exit(EXIT_FAILURE);
123951034c06Sbostic }
124051034c06Sbostic }
124151034c06Sbostic convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
124251034c06Sbostic convert(eitol(leapcnt), tzh.tzh_leapcnt);
124351034c06Sbostic convert(eitol(timecnt), tzh.tzh_timecnt);
124451034c06Sbostic convert(eitol(typecnt), tzh.tzh_typecnt);
124551034c06Sbostic convert(eitol(charcnt), tzh.tzh_charcnt);
12466068c877Sbostic (void) fwrite((void *) &tzh, (size_t) sizeof tzh, (size_t) 1, fp);
124751034c06Sbostic for (i = 0; i < timecnt; ++i) {
124851034c06Sbostic j = leapcnt;
124951034c06Sbostic while (--j >= 0)
125051034c06Sbostic if (ats[i] >= trans[j]) {
125151034c06Sbostic ats[i] = tadd(ats[i], corr[j]);
125251034c06Sbostic break;
125351034c06Sbostic }
125451034c06Sbostic puttzcode((long) ats[i], fp);
125551034c06Sbostic }
125651034c06Sbostic if (timecnt > 0)
12576068c877Sbostic (void) fwrite((void *) types, (size_t) sizeof types[0],
12586068c877Sbostic (size_t) timecnt, fp);
125951034c06Sbostic for (i = 0; i < typecnt; ++i) {
126051034c06Sbostic puttzcode((long) gmtoffs[i], fp);
126151034c06Sbostic (void) putc(isdsts[i], fp);
126251034c06Sbostic (void) putc(abbrinds[i], fp);
126351034c06Sbostic }
126451034c06Sbostic if (charcnt != 0)
12656068c877Sbostic (void) fwrite((void *) chars, (size_t) sizeof chars[0],
12666068c877Sbostic (size_t) charcnt, fp);
126751034c06Sbostic for (i = 0; i < leapcnt; ++i) {
126851034c06Sbostic if (roll[i]) {
126951034c06Sbostic if (timecnt == 0 || trans[i] < ats[0]) {
127051034c06Sbostic j = 0;
127151034c06Sbostic while (isdsts[j])
127251034c06Sbostic if (++j >= typecnt) {
127351034c06Sbostic j = 0;
127451034c06Sbostic break;
127551034c06Sbostic }
127651034c06Sbostic } else {
127751034c06Sbostic j = 1;
127851034c06Sbostic while (j < timecnt && trans[i] >= ats[j])
127951034c06Sbostic ++j;
128051034c06Sbostic j = types[j - 1];
128151034c06Sbostic }
128251034c06Sbostic puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
128351034c06Sbostic } else puttzcode((long) trans[i], fp);
128451034c06Sbostic puttzcode((long) corr[i], fp);
128551034c06Sbostic }
128651034c06Sbostic for (i = 0; i < typecnt; ++i)
128751034c06Sbostic (void) putc(ttisstds[i], fp);
128851034c06Sbostic if (ferror(fp) || fclose(fp)) {
128951034c06Sbostic (void) fprintf(stderr, "%s: Write error on ", progname);
129051034c06Sbostic (void) perror(fullname);
129151034c06Sbostic (void) exit(EXIT_FAILURE);
129251034c06Sbostic }
129351034c06Sbostic }
129451034c06Sbostic
129551034c06Sbostic static void
outzone(zpfirst,zonecount)129651034c06Sbostic outzone(zpfirst, zonecount)
129751034c06Sbostic const struct zone * const zpfirst;
129851034c06Sbostic const int zonecount;
129951034c06Sbostic {
130051034c06Sbostic register const struct zone * zp;
130151034c06Sbostic register struct rule * rp;
130251034c06Sbostic register int i, j;
130351034c06Sbostic register int usestart, useuntil;
130451034c06Sbostic register time_t starttime, untiltime;
130551034c06Sbostic register long gmtoff;
130651034c06Sbostic register long stdoff;
130751034c06Sbostic register int year;
130851034c06Sbostic register long startoff;
130951034c06Sbostic register int startisdst;
131051034c06Sbostic register int startttisstd;
131151034c06Sbostic register int type;
131251034c06Sbostic char startbuf[BUFSIZ];
131351034c06Sbostic
131451034c06Sbostic /*
131551034c06Sbostic ** Now. . .finally. . .generate some useful data!
131651034c06Sbostic */
131751034c06Sbostic timecnt = 0;
131851034c06Sbostic typecnt = 0;
131951034c06Sbostic charcnt = 0;
132051034c06Sbostic /*
132151034c06Sbostic ** Two guesses. . .the second may well be corrected later.
132251034c06Sbostic */
132351034c06Sbostic gmtoff = zpfirst->z_gmtoff;
132451034c06Sbostic stdoff = 0;
132551034c06Sbostic starttime = 0;
132651034c06Sbostic startttisstd = FALSE;
132751034c06Sbostic for (i = 0; i < zonecount; ++i) {
132851034c06Sbostic usestart = i > 0;
132951034c06Sbostic useuntil = i < (zonecount - 1);
133051034c06Sbostic zp = &zpfirst[i];
133151034c06Sbostic eat(zp->z_filename, zp->z_linenum);
133251034c06Sbostic startisdst = -1;
133351034c06Sbostic if (zp->z_nrules == 0) {
133451034c06Sbostic type = addtype(oadd(zp->z_gmtoff, zp->z_stdoff),
133551034c06Sbostic zp->z_format, zp->z_stdoff != 0,
133651034c06Sbostic startttisstd);
133751034c06Sbostic if (usestart)
133851034c06Sbostic addtt(starttime, type);
133951034c06Sbostic gmtoff = zp->z_gmtoff;
134051034c06Sbostic stdoff = zp->z_stdoff;
134151034c06Sbostic } else for (year = min_year; year <= max_year; ++year) {
134251034c06Sbostic if (useuntil && year > zp->z_untilrule.r_hiyear)
134351034c06Sbostic break;
134451034c06Sbostic /*
134551034c06Sbostic ** Mark which rules to do in the current year.
134651034c06Sbostic ** For those to do, calculate rpytime(rp, year);
134751034c06Sbostic */
134851034c06Sbostic for (j = 0; j < zp->z_nrules; ++j) {
134951034c06Sbostic rp = &zp->z_rules[j];
135051034c06Sbostic eats(zp->z_filename, zp->z_linenum,
135151034c06Sbostic rp->r_filename, rp->r_linenum);
135251034c06Sbostic rp->r_todo = year >= rp->r_loyear &&
135351034c06Sbostic year <= rp->r_hiyear &&
135451034c06Sbostic yearistype(year, rp->r_yrtype);
135551034c06Sbostic if (rp->r_todo)
135651034c06Sbostic rp->r_temp = rpytime(rp, year);
135751034c06Sbostic }
135851034c06Sbostic for ( ; ; ) {
135951034c06Sbostic register int k;
136051034c06Sbostic register time_t jtime, ktime;
136151034c06Sbostic register long offset;
136251034c06Sbostic char buf[BUFSIZ];
136351034c06Sbostic
136451034c06Sbostic if (useuntil) {
136551034c06Sbostic /*
136651034c06Sbostic ** Turn untiltime into GMT
136751034c06Sbostic ** assuming the current gmtoff and
136851034c06Sbostic ** stdoff values.
136951034c06Sbostic */
137051034c06Sbostic offset = gmtoff;
137151034c06Sbostic if (!zp->z_untilrule.r_todisstd)
137251034c06Sbostic offset = oadd(offset, stdoff);
137351034c06Sbostic untiltime = tadd(zp->z_untiltime,
137451034c06Sbostic -offset);
137551034c06Sbostic }
137651034c06Sbostic /*
137751034c06Sbostic ** Find the rule (of those to do, if any)
137851034c06Sbostic ** that takes effect earliest in the year.
137951034c06Sbostic */
138051034c06Sbostic k = -1;
138151034c06Sbostic #ifdef lint
138251034c06Sbostic ktime = 0;
138351034c06Sbostic #endif /* defined lint */
138451034c06Sbostic for (j = 0; j < zp->z_nrules; ++j) {
138551034c06Sbostic rp = &zp->z_rules[j];
138651034c06Sbostic if (!rp->r_todo)
138751034c06Sbostic continue;
138851034c06Sbostic eats(zp->z_filename, zp->z_linenum,
138951034c06Sbostic rp->r_filename, rp->r_linenum);
139051034c06Sbostic offset = gmtoff;
139151034c06Sbostic if (!rp->r_todisstd)
139251034c06Sbostic offset = oadd(offset, stdoff);
139351034c06Sbostic jtime = rp->r_temp;
139451034c06Sbostic if (jtime == min_time ||
139551034c06Sbostic jtime == max_time)
139651034c06Sbostic continue;
139751034c06Sbostic jtime = tadd(jtime, -offset);
139851034c06Sbostic if (k < 0 || jtime < ktime) {
139951034c06Sbostic k = j;
140051034c06Sbostic ktime = jtime;
140151034c06Sbostic }
140251034c06Sbostic }
140351034c06Sbostic if (k < 0)
140451034c06Sbostic break; /* go on to next year */
140551034c06Sbostic rp = &zp->z_rules[k];
140651034c06Sbostic rp->r_todo = FALSE;
140751034c06Sbostic if (useuntil && ktime >= untiltime)
140851034c06Sbostic break;
140951034c06Sbostic if (usestart) {
141051034c06Sbostic if (ktime < starttime) {
141151034c06Sbostic stdoff = rp->r_stdoff;
141251034c06Sbostic startoff = oadd(zp->z_gmtoff,
141351034c06Sbostic rp->r_stdoff);
141451034c06Sbostic (void) sprintf(startbuf,
141551034c06Sbostic zp->z_format,
141651034c06Sbostic rp->r_abbrvar);
141751034c06Sbostic startisdst =
141851034c06Sbostic rp->r_stdoff != 0;
141951034c06Sbostic continue;
142051034c06Sbostic }
142151034c06Sbostic if (ktime != starttime &&
142251034c06Sbostic startisdst >= 0)
142351034c06Sbostic addtt(starttime, addtype(startoff, startbuf, startisdst, startttisstd));
142451034c06Sbostic usestart = FALSE;
142551034c06Sbostic }
142651034c06Sbostic eats(zp->z_filename, zp->z_linenum,
142751034c06Sbostic rp->r_filename, rp->r_linenum);
142851034c06Sbostic (void) sprintf(buf, zp->z_format,
142951034c06Sbostic rp->r_abbrvar);
143051034c06Sbostic offset = oadd(zp->z_gmtoff, rp->r_stdoff);
143151034c06Sbostic type = addtype(offset, buf, rp->r_stdoff != 0,
143251034c06Sbostic rp->r_todisstd);
143351034c06Sbostic if (timecnt != 0 || rp->r_stdoff != 0)
143451034c06Sbostic addtt(ktime, type);
143551034c06Sbostic gmtoff = zp->z_gmtoff;
143651034c06Sbostic stdoff = rp->r_stdoff;
143751034c06Sbostic }
143851034c06Sbostic }
143951034c06Sbostic /*
144051034c06Sbostic ** Now we may get to set starttime for the next zone line.
144151034c06Sbostic */
144251034c06Sbostic if (useuntil) {
144351034c06Sbostic starttime = tadd(zp->z_untiltime,
144451034c06Sbostic -gmtoffs[types[timecnt - 1]]);
144551034c06Sbostic startttisstd = zp->z_untilrule.r_todisstd;
144651034c06Sbostic }
144751034c06Sbostic }
144851034c06Sbostic writezone(zpfirst->z_name);
144951034c06Sbostic }
145051034c06Sbostic
145151034c06Sbostic static void
addtt(starttime,type)145251034c06Sbostic addtt(starttime, type)
145351034c06Sbostic const time_t starttime;
145451034c06Sbostic const int type;
145551034c06Sbostic {
145651034c06Sbostic if (timecnt != 0 && type == types[timecnt - 1])
145751034c06Sbostic return; /* easy enough! */
145851034c06Sbostic if (timecnt >= TZ_MAX_TIMES) {
145951034c06Sbostic error("too many transitions?!");
146051034c06Sbostic (void) exit(EXIT_FAILURE);
146151034c06Sbostic }
146251034c06Sbostic ats[timecnt] = starttime;
146351034c06Sbostic types[timecnt] = type;
146451034c06Sbostic ++timecnt;
146551034c06Sbostic }
146651034c06Sbostic
146751034c06Sbostic static int
addtype(gmtoff,abbr,isdst,ttisstd)146851034c06Sbostic addtype(gmtoff, abbr, isdst, ttisstd)
146951034c06Sbostic const long gmtoff;
147051034c06Sbostic const char * const abbr;
147151034c06Sbostic const int isdst;
147251034c06Sbostic const int ttisstd;
147351034c06Sbostic {
147451034c06Sbostic register int i, j;
147551034c06Sbostic
147651034c06Sbostic /*
147751034c06Sbostic ** See if there's already an entry for this zone type.
147851034c06Sbostic ** If so, just return its index.
147951034c06Sbostic */
148051034c06Sbostic for (i = 0; i < typecnt; ++i) {
148151034c06Sbostic if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
148251034c06Sbostic strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
148351034c06Sbostic ttisstd == ttisstds[i])
148451034c06Sbostic return i;
148551034c06Sbostic }
148651034c06Sbostic /*
148751034c06Sbostic ** There isn't one; add a new one, unless there are already too
148851034c06Sbostic ** many.
148951034c06Sbostic */
149051034c06Sbostic if (typecnt >= TZ_MAX_TYPES) {
149151034c06Sbostic error("too many local time types");
149251034c06Sbostic (void) exit(EXIT_FAILURE);
149351034c06Sbostic }
149451034c06Sbostic gmtoffs[i] = gmtoff;
149551034c06Sbostic isdsts[i] = isdst;
149651034c06Sbostic ttisstds[i] = ttisstd;
149751034c06Sbostic
149851034c06Sbostic for (j = 0; j < charcnt; ++j)
149951034c06Sbostic if (strcmp(&chars[j], abbr) == 0)
150051034c06Sbostic break;
150151034c06Sbostic if (j == charcnt)
150251034c06Sbostic newabbr(abbr);
150351034c06Sbostic abbrinds[i] = j;
150451034c06Sbostic ++typecnt;
150551034c06Sbostic return i;
150651034c06Sbostic }
150751034c06Sbostic
150851034c06Sbostic static void
addleap(t,positive,rolling)150951034c06Sbostic addleap(t, positive, rolling)
151051034c06Sbostic const time_t t;
151151034c06Sbostic const int positive;
151251034c06Sbostic const int rolling;
151351034c06Sbostic {
151451034c06Sbostic register int i, j;
151551034c06Sbostic
151651034c06Sbostic if (leapcnt >= TZ_MAX_LEAPS) {
151751034c06Sbostic error("too many leap seconds");
151851034c06Sbostic (void) exit(EXIT_FAILURE);
151951034c06Sbostic }
152051034c06Sbostic for (i = 0; i < leapcnt; ++i)
152151034c06Sbostic if (t <= trans[i]) {
152251034c06Sbostic if (t == trans[i]) {
152351034c06Sbostic error("repeated leap second moment");
152451034c06Sbostic (void) exit(EXIT_FAILURE);
152551034c06Sbostic }
152651034c06Sbostic break;
152751034c06Sbostic }
152851034c06Sbostic for (j = leapcnt; j > i; --j) {
152951034c06Sbostic trans[j] = trans[j-1];
153051034c06Sbostic corr[j] = corr[j-1];
153151034c06Sbostic roll[j] = roll[j-1];
153251034c06Sbostic }
153351034c06Sbostic trans[i] = t;
153451034c06Sbostic corr[i] = (positive ? 1L : -1L);
153551034c06Sbostic roll[i] = rolling;
153651034c06Sbostic ++leapcnt;
153751034c06Sbostic }
153851034c06Sbostic
153951034c06Sbostic static void
adjleap()154051034c06Sbostic adjleap()
154151034c06Sbostic {
154251034c06Sbostic register int i;
154351034c06Sbostic register long last = 0;
154451034c06Sbostic
154551034c06Sbostic /*
154651034c06Sbostic ** propagate leap seconds forward
154751034c06Sbostic */
154851034c06Sbostic for (i = 0; i < leapcnt; ++i) {
154951034c06Sbostic trans[i] = tadd(trans[i], last);
155051034c06Sbostic last = corr[i] += last;
155151034c06Sbostic }
155251034c06Sbostic }
155351034c06Sbostic
155451034c06Sbostic static int
yearistype(year,type)155551034c06Sbostic yearistype(year, type)
155651034c06Sbostic const int year;
155751034c06Sbostic const char * const type;
155851034c06Sbostic {
155951034c06Sbostic char buf[BUFSIZ];
156051034c06Sbostic int result;
156151034c06Sbostic
156251034c06Sbostic if (type == NULL || *type == '\0')
156351034c06Sbostic return TRUE;
156451034c06Sbostic if (strcmp(type, "uspres") == 0)
156551034c06Sbostic return (year % 4) == 0;
156651034c06Sbostic if (strcmp(type, "nonpres") == 0)
156751034c06Sbostic return (year % 4) != 0;
156851034c06Sbostic (void) sprintf(buf, "yearistype %d %s", year, type);
156951034c06Sbostic result = system(buf);
157051034c06Sbostic if (result == 0)
157151034c06Sbostic return TRUE;
157251034c06Sbostic if (result == (1 << 8))
157351034c06Sbostic return FALSE;
157451034c06Sbostic error("Wild result from command execution");
157551034c06Sbostic (void) fprintf(stderr, "%s: command was '%s', result was %d\n",
157651034c06Sbostic progname, buf, result);
157751034c06Sbostic for ( ; ; )
157851034c06Sbostic (void) exit(EXIT_FAILURE);
157951034c06Sbostic }
158051034c06Sbostic
158151034c06Sbostic static int
lowerit(a)158251034c06Sbostic lowerit(a)
158351034c06Sbostic const int a;
158451034c06Sbostic {
158551034c06Sbostic return (isascii(a) && isupper(a)) ? tolower(a) : a;
158651034c06Sbostic }
158751034c06Sbostic
158851034c06Sbostic static int
ciequal(ap,bp)158951034c06Sbostic ciequal(ap, bp) /* case-insensitive equality */
159051034c06Sbostic register const char * ap;
159151034c06Sbostic register const char * bp;
159251034c06Sbostic {
159351034c06Sbostic while (lowerit(*ap) == lowerit(*bp++))
159451034c06Sbostic if (*ap++ == '\0')
159551034c06Sbostic return TRUE;
159651034c06Sbostic return FALSE;
159751034c06Sbostic }
159851034c06Sbostic
159951034c06Sbostic static int
itsabbr(abbr,word)160051034c06Sbostic itsabbr(abbr, word)
160151034c06Sbostic register const char * abbr;
160251034c06Sbostic register const char * word;
160351034c06Sbostic {
160451034c06Sbostic if (lowerit(*abbr) != lowerit(*word))
160551034c06Sbostic return FALSE;
160651034c06Sbostic ++word;
160751034c06Sbostic while (*++abbr != '\0')
160851034c06Sbostic do if (*word == '\0')
160951034c06Sbostic return FALSE;
161051034c06Sbostic while (lowerit(*word++) != lowerit(*abbr));
161151034c06Sbostic return TRUE;
161251034c06Sbostic }
161351034c06Sbostic
161451034c06Sbostic static const struct lookup *
byword(word,table)161551034c06Sbostic byword(word, table)
161651034c06Sbostic register const char * const word;
161751034c06Sbostic register const struct lookup * const table;
161851034c06Sbostic {
161951034c06Sbostic register const struct lookup * foundlp;
162051034c06Sbostic register const struct lookup * lp;
162151034c06Sbostic
162251034c06Sbostic if (word == NULL || table == NULL)
162351034c06Sbostic return NULL;
162451034c06Sbostic /*
162551034c06Sbostic ** Look for exact match.
162651034c06Sbostic */
162751034c06Sbostic for (lp = table; lp->l_word != NULL; ++lp)
162851034c06Sbostic if (ciequal(word, lp->l_word))
162951034c06Sbostic return lp;
163051034c06Sbostic /*
163151034c06Sbostic ** Look for inexact match.
163251034c06Sbostic */
163351034c06Sbostic foundlp = NULL;
163451034c06Sbostic for (lp = table; lp->l_word != NULL; ++lp)
163551034c06Sbostic if (itsabbr(word, lp->l_word))
163651034c06Sbostic if (foundlp == NULL)
163751034c06Sbostic foundlp = lp;
163851034c06Sbostic else return NULL; /* multiple inexact matches */
163951034c06Sbostic return foundlp;
164051034c06Sbostic }
164151034c06Sbostic
164251034c06Sbostic static char **
getfields(cp)164351034c06Sbostic getfields(cp)
164451034c06Sbostic register char * cp;
164551034c06Sbostic {
164651034c06Sbostic register char * dp;
164751034c06Sbostic register char ** array;
164851034c06Sbostic register int nsubs;
164951034c06Sbostic
165051034c06Sbostic if (cp == NULL)
165151034c06Sbostic return NULL;
165251034c06Sbostic array = (char **) emalloc((int) ((strlen(cp) + 1) * sizeof *array));
165351034c06Sbostic nsubs = 0;
165451034c06Sbostic for ( ; ; ) {
165551034c06Sbostic while (isascii(*cp) && isspace(*cp))
165651034c06Sbostic ++cp;
165751034c06Sbostic if (*cp == '\0' || *cp == '#')
165851034c06Sbostic break;
165951034c06Sbostic array[nsubs++] = dp = cp;
166051034c06Sbostic do {
166151034c06Sbostic if ((*dp = *cp++) != '"')
166251034c06Sbostic ++dp;
166351034c06Sbostic else while ((*dp = *cp++) != '"')
166451034c06Sbostic if (*dp != '\0')
166551034c06Sbostic ++dp;
166651034c06Sbostic else error("Odd number of quotation marks");
166751034c06Sbostic } while (*cp != '\0' && *cp != '#' &&
166851034c06Sbostic (!isascii(*cp) || !isspace(*cp)));
166951034c06Sbostic if (isascii(*cp) && isspace(*cp))
167051034c06Sbostic ++cp;
167151034c06Sbostic *dp = '\0';
167251034c06Sbostic }
167351034c06Sbostic array[nsubs] = NULL;
167451034c06Sbostic return array;
167551034c06Sbostic }
167651034c06Sbostic
167751034c06Sbostic static long
oadd(t1,t2)167851034c06Sbostic oadd(t1, t2)
167951034c06Sbostic const long t1;
168051034c06Sbostic const long t2;
168151034c06Sbostic {
168251034c06Sbostic register long t;
168351034c06Sbostic
168451034c06Sbostic t = t1 + t2;
168551034c06Sbostic if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) {
168651034c06Sbostic error("time overflow");
168751034c06Sbostic (void) exit(EXIT_FAILURE);
168851034c06Sbostic }
168951034c06Sbostic return t;
169051034c06Sbostic }
169151034c06Sbostic
169251034c06Sbostic static time_t
tadd(t1,t2)169351034c06Sbostic tadd(t1, t2)
169451034c06Sbostic const time_t t1;
169551034c06Sbostic const long t2;
169651034c06Sbostic {
169751034c06Sbostic register time_t t;
169851034c06Sbostic
169951034c06Sbostic if (t1 == max_time && t2 > 0)
170051034c06Sbostic return max_time;
170151034c06Sbostic if (t1 == min_time && t2 < 0)
170251034c06Sbostic return min_time;
170351034c06Sbostic t = t1 + t2;
170451034c06Sbostic if (t2 > 0 && t <= t1 || t2 < 0 && t >= t1) {
170551034c06Sbostic error("time overflow");
170651034c06Sbostic (void) exit(EXIT_FAILURE);
170751034c06Sbostic }
170851034c06Sbostic return t;
170951034c06Sbostic }
171051034c06Sbostic
171151034c06Sbostic /*
171251034c06Sbostic ** Given a rule, and a year, compute the date - in seconds since January 1,
171351034c06Sbostic ** 1970, 00:00 LOCAL time - in that year that the rule refers to.
171451034c06Sbostic */
171551034c06Sbostic
171651034c06Sbostic static time_t
rpytime(rp,wantedy)171751034c06Sbostic rpytime(rp, wantedy)
171851034c06Sbostic register const struct rule * const rp;
171951034c06Sbostic register const int wantedy;
172051034c06Sbostic {
172151034c06Sbostic register int y, m, i;
172251034c06Sbostic register long dayoff; /* with a nod to Margaret O. */
172351034c06Sbostic register time_t t;
172451034c06Sbostic
172551034c06Sbostic dayoff = 0;
172651034c06Sbostic m = TM_JANUARY;
172751034c06Sbostic y = EPOCH_YEAR;
172851034c06Sbostic while (wantedy != y) {
172951034c06Sbostic if (wantedy > y) {
173051034c06Sbostic i = len_years[isleap(y)];
173151034c06Sbostic ++y;
173251034c06Sbostic } else {
173351034c06Sbostic --y;
173451034c06Sbostic i = -len_years[isleap(y)];
173551034c06Sbostic }
173651034c06Sbostic dayoff = oadd(dayoff, eitol(i));
173751034c06Sbostic }
173851034c06Sbostic while (m != rp->r_month) {
173951034c06Sbostic i = len_months[isleap(y)][m];
174051034c06Sbostic dayoff = oadd(dayoff, eitol(i));
174151034c06Sbostic ++m;
174251034c06Sbostic }
174351034c06Sbostic i = rp->r_dayofmonth;
174451034c06Sbostic if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
174551034c06Sbostic if (rp->r_dycode == DC_DOWLEQ)
174651034c06Sbostic --i;
174751034c06Sbostic else {
174851034c06Sbostic error("use of 2/29 in non leap-year");
174951034c06Sbostic (void) exit(EXIT_FAILURE);
175051034c06Sbostic }
175151034c06Sbostic }
175251034c06Sbostic --i;
175351034c06Sbostic dayoff = oadd(dayoff, eitol(i));
175451034c06Sbostic if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
175551034c06Sbostic register long wday;
175651034c06Sbostic
175751034c06Sbostic #define LDAYSPERWEEK ((long) DAYSPERWEEK)
175851034c06Sbostic wday = eitol(EPOCH_WDAY);
175951034c06Sbostic /*
176051034c06Sbostic ** Don't trust mod of negative numbers.
176151034c06Sbostic */
176251034c06Sbostic if (dayoff >= 0)
176351034c06Sbostic wday = (wday + dayoff) % LDAYSPERWEEK;
176451034c06Sbostic else {
176551034c06Sbostic wday -= ((-dayoff) % LDAYSPERWEEK);
176651034c06Sbostic if (wday < 0)
176751034c06Sbostic wday += LDAYSPERWEEK;
176851034c06Sbostic }
176951034c06Sbostic while (wday != eitol(rp->r_wday))
177051034c06Sbostic if (rp->r_dycode == DC_DOWGEQ) {
177151034c06Sbostic dayoff = oadd(dayoff, (long) 1);
177251034c06Sbostic if (++wday >= LDAYSPERWEEK)
177351034c06Sbostic wday = 0;
177451034c06Sbostic ++i;
177551034c06Sbostic } else {
177651034c06Sbostic dayoff = oadd(dayoff, (long) -1);
177751034c06Sbostic if (--wday < 0)
17783044ec37Sbostic wday = LDAYSPERWEEK - 1;
177951034c06Sbostic --i;
178051034c06Sbostic }
178151034c06Sbostic if (i < 0 || i >= len_months[isleap(y)][m]) {
178251034c06Sbostic error("no day in month matches rule");
178351034c06Sbostic (void) exit(EXIT_FAILURE);
178451034c06Sbostic }
178551034c06Sbostic }
178651034c06Sbostic if (dayoff < 0 && !tt_signed) {
178751034c06Sbostic if (wantedy == rp->r_loyear)
178851034c06Sbostic return min_time;
178951034c06Sbostic error("time before zero");
179051034c06Sbostic (void) exit(EXIT_FAILURE);
179151034c06Sbostic }
179251034c06Sbostic t = (time_t) dayoff * SECSPERDAY;
179351034c06Sbostic /*
179451034c06Sbostic ** Cheap overflow check.
179551034c06Sbostic */
179651034c06Sbostic if (t / SECSPERDAY != dayoff) {
179751034c06Sbostic if (wantedy == rp->r_hiyear)
179851034c06Sbostic return max_time;
179951034c06Sbostic if (wantedy == rp->r_loyear)
180051034c06Sbostic return min_time;
180151034c06Sbostic error("time overflow");
180251034c06Sbostic (void) exit(EXIT_FAILURE);
180351034c06Sbostic }
180451034c06Sbostic return tadd(t, rp->r_tod);
180551034c06Sbostic }
180651034c06Sbostic
180751034c06Sbostic static void
newabbr(string)180851034c06Sbostic newabbr(string)
180951034c06Sbostic const char * const string;
181051034c06Sbostic {
181151034c06Sbostic register int i;
181251034c06Sbostic
181351034c06Sbostic i = strlen(string) + 1;
181451034c06Sbostic if (charcnt + i >= TZ_MAX_CHARS) {
181551034c06Sbostic error("too many, or too long, time zone abbreviations");
181651034c06Sbostic (void) exit(EXIT_FAILURE);
181751034c06Sbostic }
181851034c06Sbostic (void) strcpy(&chars[charcnt], string);
181951034c06Sbostic charcnt += eitol(i);
182051034c06Sbostic }
182151034c06Sbostic
182251034c06Sbostic static int
mkdirs(name)182351034c06Sbostic mkdirs(name)
182451034c06Sbostic char * const name;
182551034c06Sbostic {
182651034c06Sbostic register char * cp;
182751034c06Sbostic
182851034c06Sbostic if ((cp = name) == NULL || *cp == '\0')
182951034c06Sbostic return 0;
183051034c06Sbostic while ((cp = strchr(cp + 1, '/')) != 0) {
183151034c06Sbostic *cp = '\0';
183251034c06Sbostic if (!itsdir(name)) {
183351034c06Sbostic /*
183451034c06Sbostic ** It doesn't seem to exist, so we try to create it.
183551034c06Sbostic */
18366068c877Sbostic if (mkdir(name, 0755) != 0) {
183751034c06Sbostic (void) fprintf(stderr,
183851034c06Sbostic "%s: Can't create directory ",
183951034c06Sbostic progname);
184051034c06Sbostic (void) perror(name);
184151034c06Sbostic return -1;
184251034c06Sbostic }
184351034c06Sbostic }
184451034c06Sbostic *cp = '/';
184551034c06Sbostic }
184651034c06Sbostic return 0;
184751034c06Sbostic }
184851034c06Sbostic
184951034c06Sbostic static long
eitol(i)185051034c06Sbostic eitol(i)
185151034c06Sbostic const int i;
185251034c06Sbostic {
185351034c06Sbostic long l;
185451034c06Sbostic
185551034c06Sbostic l = i;
185651034c06Sbostic if (i < 0 && l >= 0 || i == 0 && l != 0 || i > 0 && l <= 0) {
185751034c06Sbostic (void) fprintf(stderr, "%s: %d did not sign extend correctly\n",
185851034c06Sbostic progname, i);
185951034c06Sbostic (void) exit(EXIT_FAILURE);
186051034c06Sbostic }
186151034c06Sbostic return l;
186251034c06Sbostic }
186351034c06Sbostic
186451034c06Sbostic /*
186551034c06Sbostic ** UNIX is a registered trademark of AT&T.
186651034c06Sbostic */
1867