10Sstevel@tonic-gate /*
24218Srobbin * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
31138Srobbin * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
60Sstevel@tonic-gate
74218Srobbin static char elsieid[] = "@(#)zic.c 7.128.1";
80Sstevel@tonic-gate
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate * #define LEAPSECOND_SUPPORT
110Sstevel@tonic-gate */
120Sstevel@tonic-gate
131138Srobbin /*
141138Srobbin * Regardless of the type of time_t, we do our work using this type.
151138Srobbin */
161138Srobbin
171138Srobbin typedef int zic_t;
181138Srobbin
190Sstevel@tonic-gate #include "private.h"
200Sstevel@tonic-gate #include <tzfile.h> /* this is in system headers at Sun */
210Sstevel@tonic-gate
220Sstevel@tonic-gate #include <sys/stat.h> /* for umask manifest constants */
230Sstevel@tonic-gate #include <ctype.h>
240Sstevel@tonic-gate #include <locale.h>
250Sstevel@tonic-gate #include <stdlib.h> /* for getopt */
260Sstevel@tonic-gate
271138Srobbin #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
281138Srobbin #define ZIC_MAX_ABBR_LEN_WO_WARN 6
291138Srobbin #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
301138Srobbin
311138Srobbin #ifdef S_IRUSR
321138Srobbin #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
331138Srobbin #else
341138Srobbin #define MKDIR_UMASK 0755
351138Srobbin #endif
361138Srobbin
370Sstevel@tonic-gate struct rule {
380Sstevel@tonic-gate const char *r_filename;
390Sstevel@tonic-gate int r_linenum;
400Sstevel@tonic-gate const char *r_name;
410Sstevel@tonic-gate
420Sstevel@tonic-gate int r_loyear; /* for example, 1986 */
430Sstevel@tonic-gate int r_hiyear; /* for example, 1986 */
440Sstevel@tonic-gate const char *r_yrtype;
450Sstevel@tonic-gate
460Sstevel@tonic-gate int r_month; /* 0..11 */
470Sstevel@tonic-gate
480Sstevel@tonic-gate int r_dycode; /* see below */
490Sstevel@tonic-gate int r_dayofmonth;
500Sstevel@tonic-gate int r_wday;
510Sstevel@tonic-gate
520Sstevel@tonic-gate long r_tod; /* time from midnight */
530Sstevel@tonic-gate int r_todisstd; /* above is standard time if TRUE */
540Sstevel@tonic-gate /* or wall clock time if FALSE */
550Sstevel@tonic-gate int r_todisgmt; /* above is GMT if TRUE */
560Sstevel@tonic-gate /* or local time if FALSE */
570Sstevel@tonic-gate long r_stdoff; /* offset from standard time */
580Sstevel@tonic-gate const char *r_abbrvar; /* variable part of abbreviation */
590Sstevel@tonic-gate
600Sstevel@tonic-gate int r_todo; /* a rule to do (used in outzone) */
611138Srobbin zic_t r_temp; /* used in outzone */
620Sstevel@tonic-gate };
630Sstevel@tonic-gate
640Sstevel@tonic-gate /*
650Sstevel@tonic-gate * r_dycode r_dayofmonth r_wday
660Sstevel@tonic-gate */
670Sstevel@tonic-gate
680Sstevel@tonic-gate #define DC_DOM 0 /* 1..31 */ /* unused */
690Sstevel@tonic-gate #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
700Sstevel@tonic-gate #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
710Sstevel@tonic-gate
720Sstevel@tonic-gate struct zone {
730Sstevel@tonic-gate const char *z_filename;
740Sstevel@tonic-gate int z_linenum;
750Sstevel@tonic-gate
760Sstevel@tonic-gate const char *z_name;
770Sstevel@tonic-gate long z_gmtoff;
780Sstevel@tonic-gate const char *z_rule;
790Sstevel@tonic-gate const char *z_format;
800Sstevel@tonic-gate
810Sstevel@tonic-gate long z_stdoff;
820Sstevel@tonic-gate
830Sstevel@tonic-gate struct rule *z_rules;
840Sstevel@tonic-gate int z_nrules;
850Sstevel@tonic-gate
860Sstevel@tonic-gate struct rule z_untilrule;
871138Srobbin zic_t z_untiltime;
880Sstevel@tonic-gate };
890Sstevel@tonic-gate
901138Srobbin static void addtt(zic_t starttime, int type);
910Sstevel@tonic-gate static int addtype(long gmtoff, const char *abbr, int isdst,
920Sstevel@tonic-gate int ttisstd, int ttisgmt);
930Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
941138Srobbin static void leapadd(zic_t t, int positive, int rolling, int count);
950Sstevel@tonic-gate static void adjleap(void);
960Sstevel@tonic-gate #endif
970Sstevel@tonic-gate static void associate(void);
980Sstevel@tonic-gate static int ciequal(const char *ap, const char *bp);
990Sstevel@tonic-gate static void convert(long val, char *buf);
1004218Srobbin static void dolink(const char *fromfield, const char *tofield);
1010Sstevel@tonic-gate static void doabbr(char *abbr, const char *format,
1020Sstevel@tonic-gate const char *letters, int isdst);
1030Sstevel@tonic-gate static void eat(const char *name, int num);
1040Sstevel@tonic-gate static void eats(const char *name, int num,
1050Sstevel@tonic-gate const char *rname, int rnum);
1060Sstevel@tonic-gate static long eitol(int i);
1070Sstevel@tonic-gate static void error(const char *message);
1080Sstevel@tonic-gate static char **getfields(char *buf);
1090Sstevel@tonic-gate static long gethms(const char *string, const char *errstrng, int signable);
1100Sstevel@tonic-gate static void infile(const char *filename);
1110Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
1120Sstevel@tonic-gate static void inleap(char **fields, int nfields);
1130Sstevel@tonic-gate #endif
1140Sstevel@tonic-gate static void inlink(char **fields, int nfields);
1150Sstevel@tonic-gate static void inrule(char **fields, int nfields);
1160Sstevel@tonic-gate static int inzcont(char **fields, int nfields);
1170Sstevel@tonic-gate static int inzone(char **fields, int nfields);
1180Sstevel@tonic-gate static int inzsub(char **fields, int nfields, int iscont);
1190Sstevel@tonic-gate static int itsabbr(const char *abbr, const char *word);
1200Sstevel@tonic-gate static int itsdir(const char *name);
1210Sstevel@tonic-gate static int lowerit(int c);
1220Sstevel@tonic-gate static char *memcheck(char *tocheck);
1230Sstevel@tonic-gate static int mkdirs(char *filename);
1240Sstevel@tonic-gate static void newabbr(const char *abbr);
1250Sstevel@tonic-gate static long oadd(long t1, long t2);
1260Sstevel@tonic-gate static void outzone(const struct zone *zp, int ntzones);
1270Sstevel@tonic-gate static void puttzcode(long code, FILE *fp);
1280Sstevel@tonic-gate static int rcomp(const void *leftp, const void *rightp);
1291138Srobbin static zic_t rpytime(const struct rule *rp, int wantedy);
1300Sstevel@tonic-gate static void rulesub(struct rule *rp,
1310Sstevel@tonic-gate const char *loyearp, const char *hiyearp,
1320Sstevel@tonic-gate const char *typep, const char *monthp,
1330Sstevel@tonic-gate const char *dayp, const char *timep);
1340Sstevel@tonic-gate static void setboundaries(void);
1351138Srobbin static zic_t tadd(zic_t t1, long t2);
1360Sstevel@tonic-gate static void usage(void);
1370Sstevel@tonic-gate static void writezone(const char *name);
1380Sstevel@tonic-gate static int yearistype(int year, const char *type);
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate static int charcnt;
1410Sstevel@tonic-gate static int errors;
1420Sstevel@tonic-gate static const char *filename;
1430Sstevel@tonic-gate static int leapcnt;
1440Sstevel@tonic-gate static int linenum;
1451138Srobbin static zic_t max_time;
1460Sstevel@tonic-gate static int max_year;
1470Sstevel@tonic-gate static int max_year_representable;
1481138Srobbin static zic_t min_time;
1490Sstevel@tonic-gate static int min_year;
1500Sstevel@tonic-gate static int min_year_representable;
1510Sstevel@tonic-gate static int noise;
1520Sstevel@tonic-gate static const char *rfilename;
1530Sstevel@tonic-gate static int rlinenum;
1540Sstevel@tonic-gate static const char *progname;
1550Sstevel@tonic-gate static int timecnt;
1560Sstevel@tonic-gate static int typecnt;
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate /*
1590Sstevel@tonic-gate * Line codes.
1600Sstevel@tonic-gate */
1610Sstevel@tonic-gate
1620Sstevel@tonic-gate #define LC_RULE 0
1630Sstevel@tonic-gate #define LC_ZONE 1
1640Sstevel@tonic-gate #define LC_LINK 2
1650Sstevel@tonic-gate #define LC_LEAP 3
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate /*
1680Sstevel@tonic-gate * Which fields are which on a Zone line.
1690Sstevel@tonic-gate */
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate #define ZF_NAME 1
1720Sstevel@tonic-gate #define ZF_GMTOFF 2
1730Sstevel@tonic-gate #define ZF_RULE 3
1740Sstevel@tonic-gate #define ZF_FORMAT 4
1750Sstevel@tonic-gate #define ZF_TILYEAR 5
1760Sstevel@tonic-gate #define ZF_TILMONTH 6
1770Sstevel@tonic-gate #define ZF_TILDAY 7
1780Sstevel@tonic-gate #define ZF_TILTIME 8
1790Sstevel@tonic-gate #define ZONE_MINFIELDS 5
1800Sstevel@tonic-gate #define ZONE_MAXFIELDS 9
1810Sstevel@tonic-gate
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate * Which fields are which on a Zone continuation line.
1840Sstevel@tonic-gate */
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate #define ZFC_GMTOFF 0
1870Sstevel@tonic-gate #define ZFC_RULE 1
1880Sstevel@tonic-gate #define ZFC_FORMAT 2
1890Sstevel@tonic-gate #define ZFC_TILYEAR 3
1900Sstevel@tonic-gate #define ZFC_TILMONTH 4
1910Sstevel@tonic-gate #define ZFC_TILDAY 5
1920Sstevel@tonic-gate #define ZFC_TILTIME 6
1930Sstevel@tonic-gate #define ZONEC_MINFIELDS 3
1940Sstevel@tonic-gate #define ZONEC_MAXFIELDS 7
1950Sstevel@tonic-gate
1960Sstevel@tonic-gate /*
1970Sstevel@tonic-gate * Which files are which on a Rule line.
1980Sstevel@tonic-gate */
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate #define RF_NAME 1
2010Sstevel@tonic-gate #define RF_LOYEAR 2
2020Sstevel@tonic-gate #define RF_HIYEAR 3
2030Sstevel@tonic-gate #define RF_COMMAND 4
2040Sstevel@tonic-gate #define RF_MONTH 5
2050Sstevel@tonic-gate #define RF_DAY 6
2060Sstevel@tonic-gate #define RF_TOD 7
2070Sstevel@tonic-gate #define RF_STDOFF 8
2080Sstevel@tonic-gate #define RF_ABBRVAR 9
2090Sstevel@tonic-gate #define RULE_FIELDS 10
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate /*
2120Sstevel@tonic-gate * Which fields are which on a Link line.
2130Sstevel@tonic-gate */
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate #define LF_FROM 1
2160Sstevel@tonic-gate #define LF_TO 2
2170Sstevel@tonic-gate #define LINK_FIELDS 3
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate * Which fields are which on a Leap line.
2210Sstevel@tonic-gate */
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate #define LP_YEAR 1
2240Sstevel@tonic-gate #define LP_MONTH 2
2250Sstevel@tonic-gate #define LP_DAY 3
2260Sstevel@tonic-gate #define LP_TIME 4
2270Sstevel@tonic-gate #define LP_CORR 5
2280Sstevel@tonic-gate #define LP_ROLL 6
2290Sstevel@tonic-gate #define LEAP_FIELDS 7
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate /*
2320Sstevel@tonic-gate * Year synonyms.
2330Sstevel@tonic-gate */
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate #define YR_MINIMUM 0
2360Sstevel@tonic-gate #define YR_MAXIMUM 1
2370Sstevel@tonic-gate #define YR_ONLY 2
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate static struct rule *rules;
2400Sstevel@tonic-gate static int nrules; /* number of rules */
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate static struct zone *zones;
2430Sstevel@tonic-gate static int nzones; /* number of zones */
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate struct link {
2460Sstevel@tonic-gate const char *l_filename;
2470Sstevel@tonic-gate int l_linenum;
2480Sstevel@tonic-gate const char *l_from;
2490Sstevel@tonic-gate const char *l_to;
2500Sstevel@tonic-gate };
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate static struct link *links;
2530Sstevel@tonic-gate static int nlinks;
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate struct lookup {
2560Sstevel@tonic-gate const char *l_word;
2570Sstevel@tonic-gate const int l_value;
2580Sstevel@tonic-gate };
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate static struct lookup const *byword(const char *string,
2610Sstevel@tonic-gate const struct lookup *lp);
2620Sstevel@tonic-gate
2630Sstevel@tonic-gate static struct lookup const line_codes[] = {
2640Sstevel@tonic-gate { "Rule", LC_RULE },
2650Sstevel@tonic-gate { "Zone", LC_ZONE },
2660Sstevel@tonic-gate { "Link", LC_LINK },
2670Sstevel@tonic-gate { "Leap", LC_LEAP },
2680Sstevel@tonic-gate { NULL, 0}
2690Sstevel@tonic-gate };
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate static struct lookup const mon_names[] = {
2720Sstevel@tonic-gate { "January", TM_JANUARY },
2730Sstevel@tonic-gate { "February", TM_FEBRUARY },
2740Sstevel@tonic-gate { "March", TM_MARCH },
2750Sstevel@tonic-gate { "April", TM_APRIL },
2760Sstevel@tonic-gate { "May", TM_MAY },
2770Sstevel@tonic-gate { "June", TM_JUNE },
2780Sstevel@tonic-gate { "July", TM_JULY },
2790Sstevel@tonic-gate { "August", TM_AUGUST },
2800Sstevel@tonic-gate { "September", TM_SEPTEMBER },
2810Sstevel@tonic-gate { "October", TM_OCTOBER },
2820Sstevel@tonic-gate { "November", TM_NOVEMBER },
2830Sstevel@tonic-gate { "December", TM_DECEMBER },
2840Sstevel@tonic-gate { NULL, 0 }
2850Sstevel@tonic-gate };
2860Sstevel@tonic-gate
2870Sstevel@tonic-gate static struct lookup const wday_names[] = {
2880Sstevel@tonic-gate { "Sunday", TM_SUNDAY },
2890Sstevel@tonic-gate { "Monday", TM_MONDAY },
2900Sstevel@tonic-gate { "Tuesday", TM_TUESDAY },
2910Sstevel@tonic-gate { "Wednesday", TM_WEDNESDAY },
2920Sstevel@tonic-gate { "Thursday", TM_THURSDAY },
2930Sstevel@tonic-gate { "Friday", TM_FRIDAY },
2940Sstevel@tonic-gate { "Saturday", TM_SATURDAY },
2950Sstevel@tonic-gate { NULL, 0 }
2960Sstevel@tonic-gate };
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate static struct lookup const lasts[] = {
2990Sstevel@tonic-gate { "last-Sunday", TM_SUNDAY },
3000Sstevel@tonic-gate { "last-Monday", TM_MONDAY },
3010Sstevel@tonic-gate { "last-Tuesday", TM_TUESDAY },
3020Sstevel@tonic-gate { "last-Wednesday", TM_WEDNESDAY },
3030Sstevel@tonic-gate { "last-Thursday", TM_THURSDAY },
3040Sstevel@tonic-gate { "last-Friday", TM_FRIDAY },
3050Sstevel@tonic-gate { "last-Saturday", TM_SATURDAY },
3060Sstevel@tonic-gate { NULL, 0 }
3070Sstevel@tonic-gate };
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate static struct lookup const begin_years[] = {
3100Sstevel@tonic-gate { "minimum", YR_MINIMUM },
3110Sstevel@tonic-gate { "maximum", YR_MAXIMUM },
3120Sstevel@tonic-gate { NULL, 0 }
3130Sstevel@tonic-gate };
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate static struct lookup const end_years[] = {
3160Sstevel@tonic-gate { "minimum", YR_MINIMUM },
3170Sstevel@tonic-gate { "maximum", YR_MAXIMUM },
3180Sstevel@tonic-gate { "only", YR_ONLY },
3190Sstevel@tonic-gate { NULL, 0 }
3200Sstevel@tonic-gate };
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate static struct lookup const leap_types[] = {
3230Sstevel@tonic-gate { "Rolling", TRUE },
3240Sstevel@tonic-gate { "Stationary", FALSE },
3250Sstevel@tonic-gate { NULL, 0 }
3260Sstevel@tonic-gate };
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate static const int len_months[2][MONSPERYEAR] = {
3290Sstevel@tonic-gate { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
3300Sstevel@tonic-gate { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
3310Sstevel@tonic-gate };
3320Sstevel@tonic-gate
3330Sstevel@tonic-gate static const int len_years[2] = {
3340Sstevel@tonic-gate DAYSPERNYEAR, DAYSPERLYEAR
3350Sstevel@tonic-gate };
3360Sstevel@tonic-gate
3370Sstevel@tonic-gate static struct attype {
3381138Srobbin zic_t at;
3390Sstevel@tonic-gate unsigned char type;
3400Sstevel@tonic-gate } attypes[TZ_MAX_TIMES];
3410Sstevel@tonic-gate static long gmtoffs[TZ_MAX_TYPES];
3420Sstevel@tonic-gate static char isdsts[TZ_MAX_TYPES];
3430Sstevel@tonic-gate static unsigned char abbrinds[TZ_MAX_TYPES];
3440Sstevel@tonic-gate static char ttisstds[TZ_MAX_TYPES];
3450Sstevel@tonic-gate static char ttisgmts[TZ_MAX_TYPES];
3460Sstevel@tonic-gate static char chars[TZ_MAX_CHARS];
3471138Srobbin static zic_t trans[TZ_MAX_LEAPS];
3480Sstevel@tonic-gate static long corr[TZ_MAX_LEAPS];
3490Sstevel@tonic-gate static char roll[TZ_MAX_LEAPS];
3500Sstevel@tonic-gate
3510Sstevel@tonic-gate /*
3520Sstevel@tonic-gate * Memory allocation.
3530Sstevel@tonic-gate */
3540Sstevel@tonic-gate
3550Sstevel@tonic-gate static char *
memcheck(ptr)3560Sstevel@tonic-gate memcheck(ptr)
3570Sstevel@tonic-gate char * const ptr;
3580Sstevel@tonic-gate {
3590Sstevel@tonic-gate if (ptr == NULL) {
3600Sstevel@tonic-gate const char *e = strerror(errno);
3610Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Memory exhausted: %s\n"),
3620Sstevel@tonic-gate progname, e);
3631138Srobbin exit(EXIT_FAILURE);
3640Sstevel@tonic-gate }
3650Sstevel@tonic-gate return (ptr);
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate
3680Sstevel@tonic-gate #define emalloc(size) memcheck(imalloc(size))
3690Sstevel@tonic-gate #define erealloc(ptr, size) memcheck(irealloc((ptr), (size)))
3700Sstevel@tonic-gate #define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
3710Sstevel@tonic-gate #define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp)))
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate /*
3740Sstevel@tonic-gate * Error handling.
3750Sstevel@tonic-gate */
3760Sstevel@tonic-gate
3770Sstevel@tonic-gate static void
eats(name,num,rname,rnum)3780Sstevel@tonic-gate eats(name, num, rname, rnum)
3790Sstevel@tonic-gate const char * const name;
3800Sstevel@tonic-gate const int num;
3810Sstevel@tonic-gate const char * const rname;
3820Sstevel@tonic-gate const int rnum;
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate filename = name;
3850Sstevel@tonic-gate linenum = num;
3860Sstevel@tonic-gate rfilename = rname;
3870Sstevel@tonic-gate rlinenum = rnum;
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate static void
eat(name,num)3910Sstevel@tonic-gate eat(name, num)
3920Sstevel@tonic-gate const char * const name;
3930Sstevel@tonic-gate const int num;
3940Sstevel@tonic-gate {
3950Sstevel@tonic-gate eats(name, num, (char *)NULL, -1);
3960Sstevel@tonic-gate }
3970Sstevel@tonic-gate
3980Sstevel@tonic-gate static void
error(string)3990Sstevel@tonic-gate error(string)
4000Sstevel@tonic-gate const char * const string;
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate /*
4030Sstevel@tonic-gate * Match the format of "cc" to allow sh users to
4040Sstevel@tonic-gate * zic ... 2>&1 | error -t "*" -v
4050Sstevel@tonic-gate * on BSD systems.
4060Sstevel@tonic-gate */
4070Sstevel@tonic-gate (void) fprintf(stderr, gettext("\"%s\", line %d: %s"),
4080Sstevel@tonic-gate filename, linenum, string);
4090Sstevel@tonic-gate if (rfilename != NULL)
4100Sstevel@tonic-gate (void) fprintf(stderr, gettext(" (rule from \"%s\", line %d)"),
4110Sstevel@tonic-gate rfilename, rlinenum);
4120Sstevel@tonic-gate (void) fprintf(stderr, "\n");
4130Sstevel@tonic-gate ++errors;
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate static void
warning(string)4170Sstevel@tonic-gate warning(string)
4180Sstevel@tonic-gate const char * const string;
4190Sstevel@tonic-gate {
4200Sstevel@tonic-gate char *cp;
4210Sstevel@tonic-gate
4220Sstevel@tonic-gate cp = ecpyalloc(gettext("warning: "));
4230Sstevel@tonic-gate cp = ecatalloc(cp, string);
4240Sstevel@tonic-gate error(cp);
4250Sstevel@tonic-gate ifree(cp);
4260Sstevel@tonic-gate --errors;
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate static void
usage(void)4300Sstevel@tonic-gate usage(void)
4310Sstevel@tonic-gate {
4320Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
4331138Srobbin (void) fprintf(stderr, gettext("%s: usage is %s "
4341138Srobbin "[ --version ] [ -s ] [ -v ] [ -l localtime ] "
4351138Srobbin "\n\t[ -p posixrules ] [ -d directory ] [ -L leapseconds ] "
4361138Srobbin "[ -y yearistype ] [ filename ... ]\n"), progname, progname);
4370Sstevel@tonic-gate #else /* ! LEAPSECOND_SUPPORT */
4381138Srobbin (void) fprintf(stderr, gettext("%s: usage is %s "
4391138Srobbin "[ --version ] [ -s ] [ -v ] [ -l localtime ]"
4401138Srobbin "\n\t[ -p posixrules ] [ -d directory ] [ -y yearistype ] "
4411138Srobbin "[ filename ... ]\n"), progname, progname);
4420Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate static const char *psxrules;
4460Sstevel@tonic-gate static const char *lcltime;
4470Sstevel@tonic-gate static const char *directory;
4480Sstevel@tonic-gate static const char *leapsec;
4490Sstevel@tonic-gate static const char *yitcommand;
4500Sstevel@tonic-gate static int sflag = FALSE;
4510Sstevel@tonic-gate
4520Sstevel@tonic-gate int
main(argc,argv)4530Sstevel@tonic-gate main(argc, argv)
4540Sstevel@tonic-gate int argc;
4550Sstevel@tonic-gate char *argv[];
4560Sstevel@tonic-gate {
4570Sstevel@tonic-gate register int i;
4580Sstevel@tonic-gate register int j;
4590Sstevel@tonic-gate register int c;
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
4620Sstevel@tonic-gate
4630Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
4640Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
4650Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
4660Sstevel@tonic-gate #endif
4670Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate progname = argv[0];
4701138Srobbin for (i = 1; i < argc; ++i)
4711138Srobbin if (strcmp(argv[i], "--version") == 0) {
4721138Srobbin (void) printf("%s\n", elsieid);
4731138Srobbin exit(EXIT_SUCCESS);
4741138Srobbin }
4751138Srobbin
4760Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
4770Sstevel@tonic-gate while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF)
4780Sstevel@tonic-gate #else
4790Sstevel@tonic-gate while ((c = getopt(argc, argv, "d:l:p:vsy:")) != EOF)
4800Sstevel@tonic-gate #endif
4810Sstevel@tonic-gate switch (c) {
4820Sstevel@tonic-gate default:
4830Sstevel@tonic-gate usage();
4840Sstevel@tonic-gate case 'd':
4850Sstevel@tonic-gate if (directory == NULL)
4860Sstevel@tonic-gate directory = optarg;
4870Sstevel@tonic-gate else {
4880Sstevel@tonic-gate (void) fprintf(stderr, gettext(
4890Sstevel@tonic-gate "%s: More than one -d option specified\n"),
4900Sstevel@tonic-gate progname);
4911138Srobbin exit(EXIT_FAILURE);
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate break;
4940Sstevel@tonic-gate case 'l':
4950Sstevel@tonic-gate if (lcltime == NULL)
4960Sstevel@tonic-gate lcltime = optarg;
4970Sstevel@tonic-gate else {
4980Sstevel@tonic-gate (void) fprintf(stderr, gettext(
4990Sstevel@tonic-gate "%s: More than one -l option specified\n"),
5000Sstevel@tonic-gate progname);
5011138Srobbin exit(EXIT_FAILURE);
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate break;
5040Sstevel@tonic-gate case 'p':
5050Sstevel@tonic-gate if (psxrules == NULL)
5060Sstevel@tonic-gate psxrules = optarg;
5070Sstevel@tonic-gate else {
5080Sstevel@tonic-gate (void) fprintf(stderr, gettext(
5090Sstevel@tonic-gate "%s: More than one -p option specified\n"),
5100Sstevel@tonic-gate progname);
5111138Srobbin exit(EXIT_FAILURE);
5120Sstevel@tonic-gate }
5130Sstevel@tonic-gate break;
5140Sstevel@tonic-gate case 'y':
5150Sstevel@tonic-gate if (yitcommand == NULL)
5160Sstevel@tonic-gate yitcommand = optarg;
5170Sstevel@tonic-gate else {
5180Sstevel@tonic-gate (void) fprintf(stderr, gettext(
5190Sstevel@tonic-gate "%s: More than one -y option specified\n"),
5200Sstevel@tonic-gate progname);
5211138Srobbin exit(EXIT_FAILURE);
5220Sstevel@tonic-gate }
5230Sstevel@tonic-gate break;
5240Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
5250Sstevel@tonic-gate case 'L':
5260Sstevel@tonic-gate if (leapsec == NULL)
5270Sstevel@tonic-gate leapsec = optarg;
5280Sstevel@tonic-gate else {
5290Sstevel@tonic-gate (void) fprintf(stderr, gettext(
5300Sstevel@tonic-gate "%s: More than one -L option specified\n"),
5310Sstevel@tonic-gate progname);
5321138Srobbin exit(EXIT_FAILURE);
5330Sstevel@tonic-gate }
5340Sstevel@tonic-gate break;
5350Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
5360Sstevel@tonic-gate case 'v':
5370Sstevel@tonic-gate noise = TRUE;
5380Sstevel@tonic-gate break;
5390Sstevel@tonic-gate case 's':
5400Sstevel@tonic-gate sflag = TRUE;
5410Sstevel@tonic-gate break;
5420Sstevel@tonic-gate }
5430Sstevel@tonic-gate if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
5440Sstevel@tonic-gate usage(); /* usage message by request */
5450Sstevel@tonic-gate if (directory == NULL)
5460Sstevel@tonic-gate directory = TZDIR;
5470Sstevel@tonic-gate if (yitcommand == NULL)
5480Sstevel@tonic-gate yitcommand = "yearistype";
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate setboundaries();
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
5530Sstevel@tonic-gate if (optind < argc && leapsec != NULL) {
5540Sstevel@tonic-gate infile(leapsec);
5550Sstevel@tonic-gate adjleap();
5560Sstevel@tonic-gate }
5570Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate for (i = optind; i < argc; ++i)
5600Sstevel@tonic-gate infile(argv[i]);
5610Sstevel@tonic-gate if (errors)
5621138Srobbin exit(EXIT_FAILURE);
5630Sstevel@tonic-gate associate();
5640Sstevel@tonic-gate for (i = 0; i < nzones; i = j) {
5650Sstevel@tonic-gate /*
5660Sstevel@tonic-gate * Find the next non-continuation zone entry.
5670Sstevel@tonic-gate */
5680Sstevel@tonic-gate for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
5690Sstevel@tonic-gate continue;
5700Sstevel@tonic-gate outzone(&zones[i], j - i);
5710Sstevel@tonic-gate }
5720Sstevel@tonic-gate /*
5730Sstevel@tonic-gate * Make links.
5740Sstevel@tonic-gate */
5750Sstevel@tonic-gate for (i = 0; i < nlinks; ++i) {
5760Sstevel@tonic-gate eat(links[i].l_filename, links[i].l_linenum);
5770Sstevel@tonic-gate dolink(links[i].l_from, links[i].l_to);
5781138Srobbin if (noise)
5791138Srobbin for (j = 0; j < nlinks; ++j)
5801138Srobbin if (strcmp(links[i].l_to, links[j].l_from) == 0)
5811138Srobbin warning(gettext("link to link"));
5820Sstevel@tonic-gate }
5830Sstevel@tonic-gate if (lcltime != NULL) {
5840Sstevel@tonic-gate eat("command line", 1);
5850Sstevel@tonic-gate dolink(lcltime, TZDEFAULT);
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate if (psxrules != NULL) {
5880Sstevel@tonic-gate eat("command line", 1);
5890Sstevel@tonic-gate dolink(psxrules, TZDEFRULES);
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate return ((errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate static void
dolink(fromfield,tofield)5954218Srobbin dolink(fromfield, tofield)
5964218Srobbin const char * const fromfield;
5974218Srobbin const char * const tofield;
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate register char *fromname;
6000Sstevel@tonic-gate register char *toname;
6010Sstevel@tonic-gate
6024218Srobbin if (fromfield[0] == '/')
6034218Srobbin fromname = ecpyalloc(fromfield);
6040Sstevel@tonic-gate else {
6050Sstevel@tonic-gate fromname = ecpyalloc(directory);
6060Sstevel@tonic-gate fromname = ecatalloc(fromname, "/");
6074218Srobbin fromname = ecatalloc(fromname, fromfield);
6080Sstevel@tonic-gate }
6094218Srobbin if (tofield[0] == '/')
6104218Srobbin toname = ecpyalloc(tofield);
6110Sstevel@tonic-gate else {
6120Sstevel@tonic-gate toname = ecpyalloc(directory);
6130Sstevel@tonic-gate toname = ecatalloc(toname, "/");
6144218Srobbin toname = ecatalloc(toname, tofield);
6150Sstevel@tonic-gate }
6160Sstevel@tonic-gate /*
6170Sstevel@tonic-gate * We get to be careful here since
6180Sstevel@tonic-gate * there's a fair chance of root running us.
6190Sstevel@tonic-gate */
6200Sstevel@tonic-gate if (!itsdir(toname))
6210Sstevel@tonic-gate (void) remove(toname);
6220Sstevel@tonic-gate if (link(fromname, toname) != 0) {
6230Sstevel@tonic-gate int result;
6240Sstevel@tonic-gate
6250Sstevel@tonic-gate if (mkdirs(toname) != 0)
6261138Srobbin exit(EXIT_FAILURE);
6270Sstevel@tonic-gate
6280Sstevel@tonic-gate result = link(fromname, toname);
6290Sstevel@tonic-gate
6301138Srobbin if (result != 0 && access(fromname, F_OK) == 0 &&
6311138Srobbin !itsdir(fromname)) {
6324218Srobbin const char *s = tofield;
6330Sstevel@tonic-gate register char *symlinkcontents = NULL;
6341138Srobbin
6350Sstevel@tonic-gate while ((s = strchr(s+1, '/')) != NULL)
6360Sstevel@tonic-gate symlinkcontents = ecatalloc(symlinkcontents,
6371138Srobbin "../");
6384218Srobbin symlinkcontents = ecatalloc(symlinkcontents, fromname);
6390Sstevel@tonic-gate result = symlink(symlinkcontents, toname);
6400Sstevel@tonic-gate if (result == 0)
6410Sstevel@tonic-gate warning(gettext(
6420Sstevel@tonic-gate "hard link failed, symbolic link used"));
6430Sstevel@tonic-gate ifree(symlinkcontents);
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate if (result != 0) {
6470Sstevel@tonic-gate const char *e = strerror(errno);
6480Sstevel@tonic-gate
6490Sstevel@tonic-gate (void) fprintf(stderr, gettext(
6500Sstevel@tonic-gate "%s: Can't link from %s to %s: %s\n"),
6510Sstevel@tonic-gate progname, fromname, toname, e);
6521138Srobbin exit(EXIT_FAILURE);
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate ifree(fromname);
6560Sstevel@tonic-gate ifree(toname);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate
6590Sstevel@tonic-gate #ifndef INT_MAX
6600Sstevel@tonic-gate #define INT_MAX ((int)(((unsigned)~0)>>1))
6610Sstevel@tonic-gate #endif /* !defined INT_MAX */
6620Sstevel@tonic-gate
6630Sstevel@tonic-gate #ifndef INT_MIN
6640Sstevel@tonic-gate #define INT_MIN ((int)~(((unsigned)~0)>>1))
6650Sstevel@tonic-gate #endif /* !defined INT_MIN */
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate * The tz file format currently allows at most 32-bit quantities.
6690Sstevel@tonic-gate * This restriction should be removed before signed 32-bit values
6700Sstevel@tonic-gate * wrap around in 2038, but unfortunately this will require a
6710Sstevel@tonic-gate * change to the tz file format.
6720Sstevel@tonic-gate */
6730Sstevel@tonic-gate
6740Sstevel@tonic-gate #define MAX_BITS_IN_FILE 32
6750Sstevel@tonic-gate /* CSTYLED */
6761138Srobbin #define TIME_T_BITS_IN_FILE ((TYPE_BIT(zic_t) < MAX_BITS_IN_FILE) ? \
6771138Srobbin TYPE_BIT(zic_t): MAX_BITS_IN_FILE)
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate static void
setboundaries(void)6800Sstevel@tonic-gate setboundaries(void)
6810Sstevel@tonic-gate {
6821138Srobbin register int i;
6831138Srobbin
6841138Srobbin if (TYPE_SIGNED(zic_t)) {
6851138Srobbin min_time = -1;
6861138Srobbin for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
6871138Srobbin min_time *= 2;
6881138Srobbin max_time = -(min_time + 1);
6890Sstevel@tonic-gate if (sflag)
6900Sstevel@tonic-gate min_time = 0;
6910Sstevel@tonic-gate } else {
6920Sstevel@tonic-gate min_time = 0;
6930Sstevel@tonic-gate max_time = 2 - sflag;
6941138Srobbin for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
6951138Srobbin max_time *= 2;
6960Sstevel@tonic-gate --max_time;
6970Sstevel@tonic-gate }
6981138Srobbin {
6991138Srobbin time_t t;
7001138Srobbin
7011138Srobbin t = (time_t)min_time;
7021138Srobbin min_year = TM_YEAR_BASE + gmtime(&t)->tm_year;
7031138Srobbin t = (time_t)max_time;
7041138Srobbin max_year = TM_YEAR_BASE + gmtime(&t)->tm_year;
7051138Srobbin }
7060Sstevel@tonic-gate min_year_representable = min_year;
7070Sstevel@tonic-gate max_year_representable = max_year;
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate
7100Sstevel@tonic-gate static int
itsdir(name)7110Sstevel@tonic-gate itsdir(name)
7120Sstevel@tonic-gate const char * const name;
7130Sstevel@tonic-gate {
7140Sstevel@tonic-gate register char *myname;
7150Sstevel@tonic-gate register int accres;
7160Sstevel@tonic-gate
7170Sstevel@tonic-gate myname = ecpyalloc(name);
7180Sstevel@tonic-gate myname = ecatalloc(myname, "/.");
7190Sstevel@tonic-gate accres = access(myname, F_OK);
7200Sstevel@tonic-gate ifree(myname);
7210Sstevel@tonic-gate return (accres == 0);
7220Sstevel@tonic-gate }
7230Sstevel@tonic-gate
7240Sstevel@tonic-gate /*
7250Sstevel@tonic-gate * Associate sets of rules with zones.
7260Sstevel@tonic-gate */
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate /*
7290Sstevel@tonic-gate * Sort by rule name.
7300Sstevel@tonic-gate */
7310Sstevel@tonic-gate
7320Sstevel@tonic-gate static int
rcomp(cp1,cp2)7330Sstevel@tonic-gate rcomp(cp1, cp2)
7340Sstevel@tonic-gate const void * cp1;
7350Sstevel@tonic-gate const void * cp2;
7360Sstevel@tonic-gate {
7370Sstevel@tonic-gate return (strcmp(((const struct rule *) cp1)->r_name,
7380Sstevel@tonic-gate ((const struct rule *) cp2)->r_name));
7390Sstevel@tonic-gate }
7400Sstevel@tonic-gate
7410Sstevel@tonic-gate static void
associate(void)7420Sstevel@tonic-gate associate(void)
7430Sstevel@tonic-gate {
7440Sstevel@tonic-gate register struct zone *zp;
7450Sstevel@tonic-gate register struct rule *rp;
7460Sstevel@tonic-gate register int base, out;
7470Sstevel@tonic-gate register int i, j;
7480Sstevel@tonic-gate
7490Sstevel@tonic-gate if (nrules != 0) {
7500Sstevel@tonic-gate (void) qsort((void *)rules, (size_t)nrules,
7510Sstevel@tonic-gate (size_t)sizeof (*rules), rcomp);
7520Sstevel@tonic-gate for (i = 0; i < nrules - 1; ++i) {
7530Sstevel@tonic-gate if (strcmp(rules[i].r_name,
7540Sstevel@tonic-gate rules[i + 1].r_name) != 0)
7550Sstevel@tonic-gate continue;
7560Sstevel@tonic-gate if (strcmp(rules[i].r_filename,
7570Sstevel@tonic-gate rules[i + 1].r_filename) == 0)
7580Sstevel@tonic-gate continue;
7590Sstevel@tonic-gate eat(rules[i].r_filename, rules[i].r_linenum);
7600Sstevel@tonic-gate warning(gettext("same rule name in multiple files"));
7610Sstevel@tonic-gate eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
7620Sstevel@tonic-gate warning(gettext("same rule name in multiple files"));
7630Sstevel@tonic-gate for (j = i + 2; j < nrules; ++j) {
7640Sstevel@tonic-gate if (strcmp(rules[i].r_name,
7650Sstevel@tonic-gate rules[j].r_name) != 0)
7660Sstevel@tonic-gate break;
7670Sstevel@tonic-gate if (strcmp(rules[i].r_filename,
7680Sstevel@tonic-gate rules[j].r_filename) == 0)
7690Sstevel@tonic-gate continue;
7700Sstevel@tonic-gate if (strcmp(rules[i + 1].r_filename,
7710Sstevel@tonic-gate rules[j].r_filename) == 0)
7720Sstevel@tonic-gate continue;
7730Sstevel@tonic-gate break;
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate i = j - 1;
7760Sstevel@tonic-gate }
7770Sstevel@tonic-gate }
7780Sstevel@tonic-gate for (i = 0; i < nzones; ++i) {
7790Sstevel@tonic-gate zp = &zones[i];
7800Sstevel@tonic-gate zp->z_rules = NULL;
7810Sstevel@tonic-gate zp->z_nrules = 0;
7820Sstevel@tonic-gate }
7830Sstevel@tonic-gate for (base = 0; base < nrules; base = out) {
7840Sstevel@tonic-gate rp = &rules[base];
7850Sstevel@tonic-gate for (out = base + 1; out < nrules; ++out)
7860Sstevel@tonic-gate if (strcmp(rp->r_name, rules[out].r_name) != 0)
7870Sstevel@tonic-gate break;
7880Sstevel@tonic-gate for (i = 0; i < nzones; ++i) {
7890Sstevel@tonic-gate zp = &zones[i];
7900Sstevel@tonic-gate if (strcmp(zp->z_rule, rp->r_name) != 0)
7910Sstevel@tonic-gate continue;
7920Sstevel@tonic-gate zp->z_rules = rp;
7930Sstevel@tonic-gate zp->z_nrules = out - base;
7940Sstevel@tonic-gate }
7950Sstevel@tonic-gate }
7960Sstevel@tonic-gate for (i = 0; i < nzones; ++i) {
7970Sstevel@tonic-gate zp = &zones[i];
7980Sstevel@tonic-gate if (zp->z_nrules == 0) {
7990Sstevel@tonic-gate /*
8000Sstevel@tonic-gate * Maybe we have a local standard time offset.
8010Sstevel@tonic-gate */
8020Sstevel@tonic-gate eat(zp->z_filename, zp->z_linenum);
8030Sstevel@tonic-gate zp->z_stdoff = gethms(zp->z_rule,
8040Sstevel@tonic-gate gettext("unruly zone"), TRUE);
8050Sstevel@tonic-gate /*
8060Sstevel@tonic-gate * Note, though, that if there's no rule,
8070Sstevel@tonic-gate * a '%s' in the format is a bad thing.
8080Sstevel@tonic-gate */
8090Sstevel@tonic-gate if (strchr(zp->z_format, '%') != 0)
8100Sstevel@tonic-gate error(gettext("%s in ruleless zone"));
8110Sstevel@tonic-gate }
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate if (errors)
8141138Srobbin exit(EXIT_FAILURE);
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate
8170Sstevel@tonic-gate static void
infile(name)8180Sstevel@tonic-gate infile(name)
8190Sstevel@tonic-gate const char *name;
8200Sstevel@tonic-gate {
8210Sstevel@tonic-gate register FILE *fp;
8220Sstevel@tonic-gate register char **fields;
8230Sstevel@tonic-gate register char *cp;
8240Sstevel@tonic-gate register const struct lookup *lp;
8250Sstevel@tonic-gate register int nfields;
8260Sstevel@tonic-gate register int wantcont;
8270Sstevel@tonic-gate register int num;
8280Sstevel@tonic-gate char buf[BUFSIZ];
8290Sstevel@tonic-gate
8300Sstevel@tonic-gate if (strcmp(name, "-") == 0) {
8310Sstevel@tonic-gate name = gettext("standard input");
8320Sstevel@tonic-gate fp = stdin;
8330Sstevel@tonic-gate } else if ((fp = fopen(name, "r")) == NULL) {
8340Sstevel@tonic-gate const char *e = strerror(errno);
8350Sstevel@tonic-gate
8360Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Can't open %s: %s\n"),
8370Sstevel@tonic-gate progname, name, e);
8381138Srobbin exit(EXIT_FAILURE);
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate wantcont = FALSE;
8410Sstevel@tonic-gate for (num = 1; ; ++num) {
8420Sstevel@tonic-gate eat(name, num);
8430Sstevel@tonic-gate if (fgets(buf, (int)sizeof (buf), fp) != buf)
8440Sstevel@tonic-gate break;
8450Sstevel@tonic-gate cp = strchr(buf, '\n');
8460Sstevel@tonic-gate if (cp == NULL) {
8470Sstevel@tonic-gate error(gettext("line too long"));
8481138Srobbin exit(EXIT_FAILURE);
8490Sstevel@tonic-gate }
8500Sstevel@tonic-gate *cp = '\0';
8510Sstevel@tonic-gate fields = getfields(buf);
8520Sstevel@tonic-gate nfields = 0;
8530Sstevel@tonic-gate while (fields[nfields] != NULL) {
8540Sstevel@tonic-gate static char nada;
8550Sstevel@tonic-gate
8560Sstevel@tonic-gate if (strcmp(fields[nfields], "-") == 0)
8570Sstevel@tonic-gate fields[nfields] = &nada;
8580Sstevel@tonic-gate ++nfields;
8590Sstevel@tonic-gate }
8600Sstevel@tonic-gate if (nfields == 0) {
8610Sstevel@tonic-gate /* nothing to do */
8620Sstevel@tonic-gate } else if (wantcont) {
8630Sstevel@tonic-gate wantcont = inzcont(fields, nfields);
8640Sstevel@tonic-gate } else {
8650Sstevel@tonic-gate lp = byword(fields[0], line_codes);
8660Sstevel@tonic-gate if (lp == NULL)
8670Sstevel@tonic-gate error(gettext("input line of unknown type"));
8680Sstevel@tonic-gate else switch ((int)(lp->l_value)) {
8690Sstevel@tonic-gate case LC_RULE:
8700Sstevel@tonic-gate inrule(fields, nfields);
8710Sstevel@tonic-gate wantcont = FALSE;
8720Sstevel@tonic-gate break;
8730Sstevel@tonic-gate case LC_ZONE:
8740Sstevel@tonic-gate wantcont = inzone(fields, nfields);
8750Sstevel@tonic-gate break;
8760Sstevel@tonic-gate case LC_LINK:
8770Sstevel@tonic-gate inlink(fields, nfields);
8780Sstevel@tonic-gate wantcont = FALSE;
8790Sstevel@tonic-gate break;
8800Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
8810Sstevel@tonic-gate case LC_LEAP:
8820Sstevel@tonic-gate if (name != leapsec)
8830Sstevel@tonic-gate (void) fprintf(stderr, gettext(
8840Sstevel@tonic-gate "%s: Leap line in non leap seconds file %s\n"),
8850Sstevel@tonic-gate progname, name);
8860Sstevel@tonic-gate else inleap(fields, nfields);
8870Sstevel@tonic-gate wantcont = FALSE;
8880Sstevel@tonic-gate break;
8890Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
8900Sstevel@tonic-gate default: /* "cannot happen" */
8910Sstevel@tonic-gate (void) fprintf(stderr, gettext(
8920Sstevel@tonic-gate "%s: panic: Invalid l_value %d\n"),
8930Sstevel@tonic-gate progname, lp->l_value);
8941138Srobbin exit(EXIT_FAILURE);
8950Sstevel@tonic-gate }
8960Sstevel@tonic-gate }
8970Sstevel@tonic-gate ifree((char *)fields);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate if (ferror(fp)) {
9000Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Error reading %s\n"),
9010Sstevel@tonic-gate progname, filename);
9021138Srobbin exit(EXIT_FAILURE);
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate if (fp != stdin && fclose(fp)) {
9050Sstevel@tonic-gate const char *e = strerror(errno);
9060Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Error closing %s: %s\n"),
9070Sstevel@tonic-gate progname, filename, e);
9081138Srobbin exit(EXIT_FAILURE);
9090Sstevel@tonic-gate }
9100Sstevel@tonic-gate if (wantcont)
9110Sstevel@tonic-gate error(gettext("expected continuation line not found"));
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate
9140Sstevel@tonic-gate /*
9150Sstevel@tonic-gate * Convert a string of one of the forms
9160Sstevel@tonic-gate * h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
9170Sstevel@tonic-gate * into a number of seconds.
9180Sstevel@tonic-gate * A null string maps to zero.
9190Sstevel@tonic-gate * Call error with errstring and return zero on errors.
9200Sstevel@tonic-gate */
9210Sstevel@tonic-gate
9220Sstevel@tonic-gate static long
gethms(string,errstring,signable)9230Sstevel@tonic-gate gethms(string, errstring, signable)
9240Sstevel@tonic-gate const char *string;
9250Sstevel@tonic-gate const char * const errstring;
9260Sstevel@tonic-gate const int signable;
9270Sstevel@tonic-gate {
9284218Srobbin long hh;
9294218Srobbin int mm, ss, sign;
9300Sstevel@tonic-gate
9310Sstevel@tonic-gate if (string == NULL || *string == '\0')
9320Sstevel@tonic-gate return (0);
9330Sstevel@tonic-gate if (!signable)
9340Sstevel@tonic-gate sign = 1;
9350Sstevel@tonic-gate else if (*string == '-') {
9360Sstevel@tonic-gate sign = -1;
9370Sstevel@tonic-gate ++string;
9380Sstevel@tonic-gate } else sign = 1;
9394218Srobbin if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
9400Sstevel@tonic-gate mm = ss = 0;
9414218Srobbin else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
9420Sstevel@tonic-gate ss = 0;
9434218Srobbin else if (sscanf(string, scheck(string, "%ld:%d:%d"),
9440Sstevel@tonic-gate &hh, &mm, &ss) != 3) {
9450Sstevel@tonic-gate error(errstring);
9460Sstevel@tonic-gate return (0);
9470Sstevel@tonic-gate }
9484218Srobbin if (hh < 0 ||
9490Sstevel@tonic-gate mm < 0 || mm >= MINSPERHOUR ||
9504218Srobbin ss < 0 || ss > SECSPERMIN) {
9510Sstevel@tonic-gate error(errstring);
9520Sstevel@tonic-gate return (0);
9530Sstevel@tonic-gate }
9544218Srobbin if (LONG_MAX / SECSPERHOUR < hh) {
9554218Srobbin error(gettext("time overflow"));
9564218Srobbin return (0);
9574218Srobbin }
9584218Srobbin if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
9591138Srobbin warning(
9601138Srobbin gettext("24:00 not handled by pre-1998 versions of zic"));
9614218Srobbin if (noise && (hh > HOURSPERDAY ||
9624218Srobbin (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
9634218Srobbin warning(gettext("values over 24 hours not handled by "
9644218Srobbin "pre-2007 versions of zic"));
9654218Srobbin
9664218Srobbin return (oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
9674218Srobbin eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))));
9680Sstevel@tonic-gate }
9690Sstevel@tonic-gate
9700Sstevel@tonic-gate static void
inrule(fields,nfields)9710Sstevel@tonic-gate inrule(fields, nfields)
9720Sstevel@tonic-gate register char ** const fields;
9730Sstevel@tonic-gate const int nfields;
9740Sstevel@tonic-gate {
9750Sstevel@tonic-gate static struct rule r;
9760Sstevel@tonic-gate
9770Sstevel@tonic-gate if (nfields != RULE_FIELDS) {
9780Sstevel@tonic-gate error(gettext("wrong number of fields on Rule line"));
9790Sstevel@tonic-gate return;
9800Sstevel@tonic-gate }
9810Sstevel@tonic-gate if (*fields[RF_NAME] == '\0') {
9820Sstevel@tonic-gate error(gettext("nameless rule"));
9830Sstevel@tonic-gate return;
9840Sstevel@tonic-gate }
9850Sstevel@tonic-gate r.r_filename = filename;
9860Sstevel@tonic-gate r.r_linenum = linenum;
9870Sstevel@tonic-gate r.r_stdoff = gethms(fields[RF_STDOFF], gettext("invalid saved time"),
9880Sstevel@tonic-gate TRUE);
9890Sstevel@tonic-gate rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
9900Sstevel@tonic-gate fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
9910Sstevel@tonic-gate r.r_name = ecpyalloc(fields[RF_NAME]);
9920Sstevel@tonic-gate r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
9930Sstevel@tonic-gate rules = (struct rule *)(void *)erealloc((char *)rules,
9940Sstevel@tonic-gate (int)((nrules + 1) * sizeof (*rules)));
9950Sstevel@tonic-gate rules[nrules++] = r;
9960Sstevel@tonic-gate }
9970Sstevel@tonic-gate
9980Sstevel@tonic-gate static int
inzone(fields,nfields)9990Sstevel@tonic-gate inzone(fields, nfields)
10000Sstevel@tonic-gate register char ** const fields;
10010Sstevel@tonic-gate const int nfields;
10020Sstevel@tonic-gate {
10030Sstevel@tonic-gate register int i;
10040Sstevel@tonic-gate static char *buf;
10050Sstevel@tonic-gate
10060Sstevel@tonic-gate if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
10070Sstevel@tonic-gate error(gettext("wrong number of fields on Zone line"));
10080Sstevel@tonic-gate return (FALSE);
10090Sstevel@tonic-gate }
10100Sstevel@tonic-gate if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
10110Sstevel@tonic-gate buf = erealloc(buf, (int)(132 + strlen(TZDEFAULT)));
10120Sstevel@tonic-gate (void) sprintf(buf,
10130Sstevel@tonic-gate gettext("\"Zone %s\" line and -l option are mutually exclusive"),
10140Sstevel@tonic-gate TZDEFAULT);
10150Sstevel@tonic-gate error(buf);
10160Sstevel@tonic-gate return (FALSE);
10170Sstevel@tonic-gate }
10180Sstevel@tonic-gate if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
10190Sstevel@tonic-gate buf = erealloc(buf, (int)(132 + strlen(TZDEFRULES)));
10200Sstevel@tonic-gate (void) sprintf(buf,
10210Sstevel@tonic-gate gettext("\"Zone %s\" line and -p option are mutually exclusive"),
10220Sstevel@tonic-gate TZDEFRULES);
10230Sstevel@tonic-gate error(buf);
10240Sstevel@tonic-gate return (FALSE);
10250Sstevel@tonic-gate }
10260Sstevel@tonic-gate for (i = 0; i < nzones; ++i)
10270Sstevel@tonic-gate if (zones[i].z_name != NULL &&
10280Sstevel@tonic-gate strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
10290Sstevel@tonic-gate buf = erealloc(buf, (int)(132 +
10300Sstevel@tonic-gate strlen(fields[ZF_NAME]) +
10310Sstevel@tonic-gate strlen(zones[i].z_filename)));
10320Sstevel@tonic-gate (void) sprintf(buf,
10330Sstevel@tonic-gate gettext("duplicate zone name %s (file \"%s\", line %d)"),
10340Sstevel@tonic-gate fields[ZF_NAME],
10350Sstevel@tonic-gate zones[i].z_filename,
10360Sstevel@tonic-gate zones[i].z_linenum);
10370Sstevel@tonic-gate error(buf);
10380Sstevel@tonic-gate return (FALSE);
10390Sstevel@tonic-gate }
10400Sstevel@tonic-gate return (inzsub(fields, nfields, FALSE));
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate
10430Sstevel@tonic-gate static int
inzcont(fields,nfields)10440Sstevel@tonic-gate inzcont(fields, nfields)
10450Sstevel@tonic-gate register char ** const fields;
10460Sstevel@tonic-gate const int nfields;
10470Sstevel@tonic-gate {
10480Sstevel@tonic-gate if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
10490Sstevel@tonic-gate error(gettext(
10500Sstevel@tonic-gate "wrong number of fields on Zone continuation line"));
10510Sstevel@tonic-gate return (FALSE);
10520Sstevel@tonic-gate }
10530Sstevel@tonic-gate return (inzsub(fields, nfields, TRUE));
10540Sstevel@tonic-gate }
10550Sstevel@tonic-gate
10560Sstevel@tonic-gate static int
inzsub(fields,nfields,iscont)10570Sstevel@tonic-gate inzsub(fields, nfields, iscont)
10580Sstevel@tonic-gate register char ** const fields;
10590Sstevel@tonic-gate const int nfields;
10600Sstevel@tonic-gate const int iscont;
10610Sstevel@tonic-gate {
10620Sstevel@tonic-gate register char *cp;
10630Sstevel@tonic-gate static struct zone z;
10640Sstevel@tonic-gate register int i_gmtoff, i_rule, i_format;
10650Sstevel@tonic-gate register int i_untilyear, i_untilmonth;
10660Sstevel@tonic-gate register int i_untilday, i_untiltime;
10670Sstevel@tonic-gate register int hasuntil;
10680Sstevel@tonic-gate
10690Sstevel@tonic-gate if (iscont) {
10700Sstevel@tonic-gate i_gmtoff = ZFC_GMTOFF;
10710Sstevel@tonic-gate i_rule = ZFC_RULE;
10720Sstevel@tonic-gate i_format = ZFC_FORMAT;
10730Sstevel@tonic-gate i_untilyear = ZFC_TILYEAR;
10740Sstevel@tonic-gate i_untilmonth = ZFC_TILMONTH;
10750Sstevel@tonic-gate i_untilday = ZFC_TILDAY;
10760Sstevel@tonic-gate i_untiltime = ZFC_TILTIME;
10770Sstevel@tonic-gate z.z_name = NULL;
10780Sstevel@tonic-gate } else {
10790Sstevel@tonic-gate i_gmtoff = ZF_GMTOFF;
10800Sstevel@tonic-gate i_rule = ZF_RULE;
10810Sstevel@tonic-gate i_format = ZF_FORMAT;
10820Sstevel@tonic-gate i_untilyear = ZF_TILYEAR;
10830Sstevel@tonic-gate i_untilmonth = ZF_TILMONTH;
10840Sstevel@tonic-gate i_untilday = ZF_TILDAY;
10850Sstevel@tonic-gate i_untiltime = ZF_TILTIME;
10860Sstevel@tonic-gate z.z_name = ecpyalloc(fields[ZF_NAME]);
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate z.z_filename = filename;
10890Sstevel@tonic-gate z.z_linenum = linenum;
10900Sstevel@tonic-gate z.z_gmtoff = gethms(fields[i_gmtoff], gettext("invalid UTC offset"),
10910Sstevel@tonic-gate TRUE);
10920Sstevel@tonic-gate if ((cp = strchr(fields[i_format], '%')) != 0) {
10930Sstevel@tonic-gate if (*++cp != 's' || strchr(cp, '%') != 0) {
10940Sstevel@tonic-gate error(gettext("invalid abbreviation format"));
10950Sstevel@tonic-gate return (FALSE);
10960Sstevel@tonic-gate }
10970Sstevel@tonic-gate }
10980Sstevel@tonic-gate z.z_rule = ecpyalloc(fields[i_rule]);
10990Sstevel@tonic-gate z.z_format = ecpyalloc(fields[i_format]);
11000Sstevel@tonic-gate hasuntil = nfields > i_untilyear;
11010Sstevel@tonic-gate if (hasuntil) {
11020Sstevel@tonic-gate z.z_untilrule.r_filename = filename;
11030Sstevel@tonic-gate z.z_untilrule.r_linenum = linenum;
11040Sstevel@tonic-gate rulesub(&z.z_untilrule,
11050Sstevel@tonic-gate fields[i_untilyear],
11060Sstevel@tonic-gate "only",
11070Sstevel@tonic-gate "",
11080Sstevel@tonic-gate (nfields > i_untilmonth) ?
11090Sstevel@tonic-gate fields[i_untilmonth] : "Jan",
11100Sstevel@tonic-gate (nfields > i_untilday) ? fields[i_untilday] : "1",
11110Sstevel@tonic-gate (nfields > i_untiltime) ? fields[i_untiltime] : "0");
11120Sstevel@tonic-gate z.z_untiltime = rpytime(&z.z_untilrule,
11130Sstevel@tonic-gate z.z_untilrule.r_loyear);
11140Sstevel@tonic-gate if (iscont && nzones > 0 &&
11150Sstevel@tonic-gate z.z_untiltime > min_time &&
11160Sstevel@tonic-gate z.z_untiltime < max_time &&
11170Sstevel@tonic-gate zones[nzones - 1].z_untiltime > min_time &&
11180Sstevel@tonic-gate zones[nzones - 1].z_untiltime < max_time &&
11190Sstevel@tonic-gate zones[nzones - 1].z_untiltime >= z.z_untiltime) {
11200Sstevel@tonic-gate error(gettext(
11210Sstevel@tonic-gate "Zone continuation line end time is not after end time of previous line"));
11220Sstevel@tonic-gate return (FALSE);
11230Sstevel@tonic-gate }
11240Sstevel@tonic-gate }
11250Sstevel@tonic-gate zones = (struct zone *)(void *)erealloc((char *)zones,
11260Sstevel@tonic-gate (int)((nzones + 1) * sizeof (*zones)));
11270Sstevel@tonic-gate zones[nzones++] = z;
11280Sstevel@tonic-gate /*
11290Sstevel@tonic-gate * If there was an UNTIL field on this line,
11300Sstevel@tonic-gate * there's more information about the zone on the next line.
11310Sstevel@tonic-gate */
11320Sstevel@tonic-gate return (hasuntil);
11330Sstevel@tonic-gate }
11340Sstevel@tonic-gate
11350Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
11360Sstevel@tonic-gate static void
inleap(fields,nfields)11370Sstevel@tonic-gate inleap(fields, nfields)
11380Sstevel@tonic-gate register char ** const fields;
11390Sstevel@tonic-gate const int nfields;
11400Sstevel@tonic-gate {
11411138Srobbin register const char *cp;
11421138Srobbin register const struct lookup *lp;
11430Sstevel@tonic-gate register int i, j;
11440Sstevel@tonic-gate int year, month, day;
11450Sstevel@tonic-gate long dayoff, tod;
11461138Srobbin zic_t t;
11470Sstevel@tonic-gate
11480Sstevel@tonic-gate if (nfields != LEAP_FIELDS) {
11490Sstevel@tonic-gate error(gettext("wrong number of fields on Leap line"));
11500Sstevel@tonic-gate return;
11510Sstevel@tonic-gate }
11520Sstevel@tonic-gate dayoff = 0;
11530Sstevel@tonic-gate cp = fields[LP_YEAR];
11540Sstevel@tonic-gate if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
11551138Srobbin /*
11561138Srobbin * Leapin' Lizards!
11571138Srobbin */
11581138Srobbin error(gettext("invalid leaping year"));
11591138Srobbin return;
11600Sstevel@tonic-gate }
11610Sstevel@tonic-gate j = EPOCH_YEAR;
11620Sstevel@tonic-gate while (j != year) {
11630Sstevel@tonic-gate if (year > j) {
11640Sstevel@tonic-gate i = len_years[isleap(j)];
11650Sstevel@tonic-gate ++j;
11660Sstevel@tonic-gate } else {
11670Sstevel@tonic-gate --j;
11680Sstevel@tonic-gate i = -len_years[isleap(j)];
11690Sstevel@tonic-gate }
11700Sstevel@tonic-gate dayoff = oadd(dayoff, eitol(i));
11710Sstevel@tonic-gate }
11720Sstevel@tonic-gate if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
11730Sstevel@tonic-gate error(gettext("invalid month name"));
11740Sstevel@tonic-gate return;
11750Sstevel@tonic-gate }
11760Sstevel@tonic-gate month = lp->l_value;
11770Sstevel@tonic-gate j = TM_JANUARY;
11780Sstevel@tonic-gate while (j != month) {
11790Sstevel@tonic-gate i = len_months[isleap(year)][j];
11800Sstevel@tonic-gate dayoff = oadd(dayoff, eitol(i));
11810Sstevel@tonic-gate ++j;
11820Sstevel@tonic-gate }
11830Sstevel@tonic-gate cp = fields[LP_DAY];
11840Sstevel@tonic-gate if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
11850Sstevel@tonic-gate day <= 0 || day > len_months[isleap(year)][month]) {
11860Sstevel@tonic-gate error(gettext("invalid day of month"));
11870Sstevel@tonic-gate return;
11880Sstevel@tonic-gate }
11890Sstevel@tonic-gate dayoff = oadd(dayoff, eitol(day - 1));
11901138Srobbin if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
11910Sstevel@tonic-gate error(gettext("time before zero"));
11920Sstevel@tonic-gate return;
11930Sstevel@tonic-gate }
11941138Srobbin if (dayoff < min_time / SECSPERDAY) {
11951138Srobbin error(gettext("time too small"));
11960Sstevel@tonic-gate return;
11970Sstevel@tonic-gate }
11981138Srobbin if (dayoff > max_time / SECSPERDAY) {
11991138Srobbin error(gettext("time too large"));
12001138Srobbin return;
12011138Srobbin }
12021138Srobbin t = (zic_t)dayoff * SECSPERDAY;
12030Sstevel@tonic-gate tod = gethms(fields[LP_TIME], gettext("invalid time of day"), FALSE);
12040Sstevel@tonic-gate cp = fields[LP_CORR];
12050Sstevel@tonic-gate {
12060Sstevel@tonic-gate register int positive;
12070Sstevel@tonic-gate int count;
12080Sstevel@tonic-gate
12090Sstevel@tonic-gate if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
12100Sstevel@tonic-gate positive = FALSE;
12110Sstevel@tonic-gate count = 1;
12120Sstevel@tonic-gate } else if (strcmp(cp, "--") == 0) {
12130Sstevel@tonic-gate positive = FALSE;
12140Sstevel@tonic-gate count = 2;
12150Sstevel@tonic-gate } else if (strcmp(cp, "+") == 0) {
12160Sstevel@tonic-gate positive = TRUE;
12170Sstevel@tonic-gate count = 1;
12180Sstevel@tonic-gate } else if (strcmp(cp, "++") == 0) {
12190Sstevel@tonic-gate positive = TRUE;
12200Sstevel@tonic-gate count = 2;
12210Sstevel@tonic-gate } else {
12220Sstevel@tonic-gate error(gettext("illegal CORRECTION field on Leap line"));
12230Sstevel@tonic-gate return;
12240Sstevel@tonic-gate }
12250Sstevel@tonic-gate if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
12260Sstevel@tonic-gate error(gettext(
12271138Srobbin "illegal Rolling/Stationary field on Leap line"));
12280Sstevel@tonic-gate return;
12290Sstevel@tonic-gate }
12300Sstevel@tonic-gate leapadd(tadd(t, tod), positive, lp->l_value, count);
12310Sstevel@tonic-gate }
12320Sstevel@tonic-gate }
12330Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
12340Sstevel@tonic-gate
12350Sstevel@tonic-gate static void
inlink(fields,nfields)12360Sstevel@tonic-gate inlink(fields, nfields)
12370Sstevel@tonic-gate register char ** const fields;
12380Sstevel@tonic-gate const int nfields;
12390Sstevel@tonic-gate {
12400Sstevel@tonic-gate struct link l;
12410Sstevel@tonic-gate
12420Sstevel@tonic-gate if (nfields != LINK_FIELDS) {
12430Sstevel@tonic-gate error(gettext("wrong number of fields on Link line"));
12440Sstevel@tonic-gate return;
12450Sstevel@tonic-gate }
12460Sstevel@tonic-gate if (*fields[LF_FROM] == '\0') {
12470Sstevel@tonic-gate error(gettext("blank FROM field on Link line"));
12480Sstevel@tonic-gate return;
12490Sstevel@tonic-gate }
12500Sstevel@tonic-gate if (*fields[LF_TO] == '\0') {
12510Sstevel@tonic-gate error(gettext("blank TO field on Link line"));
12520Sstevel@tonic-gate return;
12530Sstevel@tonic-gate }
12540Sstevel@tonic-gate l.l_filename = filename;
12550Sstevel@tonic-gate l.l_linenum = linenum;
12560Sstevel@tonic-gate l.l_from = ecpyalloc(fields[LF_FROM]);
12570Sstevel@tonic-gate l.l_to = ecpyalloc(fields[LF_TO]);
12580Sstevel@tonic-gate links = (struct link *)(void *)erealloc((char *)links,
12590Sstevel@tonic-gate (int)((nlinks + 1) * sizeof (*links)));
12600Sstevel@tonic-gate links[nlinks++] = l;
12610Sstevel@tonic-gate }
12620Sstevel@tonic-gate
12630Sstevel@tonic-gate static void
rulesub(rp,loyearp,hiyearp,typep,monthp,dayp,timep)12640Sstevel@tonic-gate rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
12650Sstevel@tonic-gate register struct rule * const rp;
12660Sstevel@tonic-gate const char * const loyearp;
12670Sstevel@tonic-gate const char * const hiyearp;
12680Sstevel@tonic-gate const char * const typep;
12690Sstevel@tonic-gate const char * const monthp;
12700Sstevel@tonic-gate const char * const dayp;
12710Sstevel@tonic-gate const char * const timep;
12720Sstevel@tonic-gate {
12730Sstevel@tonic-gate register const struct lookup *lp;
12740Sstevel@tonic-gate register const char *cp;
12750Sstevel@tonic-gate register char *dp;
12760Sstevel@tonic-gate register char *ep;
12770Sstevel@tonic-gate
12780Sstevel@tonic-gate if ((lp = byword(monthp, mon_names)) == NULL) {
12790Sstevel@tonic-gate error(gettext("invalid month name"));
12800Sstevel@tonic-gate return;
12810Sstevel@tonic-gate }
12820Sstevel@tonic-gate rp->r_month = lp->l_value;
12830Sstevel@tonic-gate rp->r_todisstd = FALSE;
12840Sstevel@tonic-gate rp->r_todisgmt = FALSE;
12850Sstevel@tonic-gate dp = ecpyalloc(timep);
12860Sstevel@tonic-gate if (*dp != '\0') {
12870Sstevel@tonic-gate ep = dp + strlen(dp) - 1;
12880Sstevel@tonic-gate switch (lowerit(*ep)) {
12890Sstevel@tonic-gate case 's': /* Standard */
12900Sstevel@tonic-gate rp->r_todisstd = TRUE;
12910Sstevel@tonic-gate rp->r_todisgmt = FALSE;
12920Sstevel@tonic-gate *ep = '\0';
12930Sstevel@tonic-gate break;
12940Sstevel@tonic-gate case 'w': /* Wall */
12950Sstevel@tonic-gate rp->r_todisstd = FALSE;
12960Sstevel@tonic-gate rp->r_todisgmt = FALSE;
12970Sstevel@tonic-gate *ep = '\0';
12980Sstevel@tonic-gate break;
12990Sstevel@tonic-gate case 'g': /* Greenwich */
13000Sstevel@tonic-gate case 'u': /* Universal */
13010Sstevel@tonic-gate case 'z': /* Zulu */
13020Sstevel@tonic-gate rp->r_todisstd = TRUE;
13030Sstevel@tonic-gate rp->r_todisgmt = TRUE;
13040Sstevel@tonic-gate *ep = '\0';
13050Sstevel@tonic-gate break;
13060Sstevel@tonic-gate }
13070Sstevel@tonic-gate }
13080Sstevel@tonic-gate rp->r_tod = gethms(dp, gettext("invalid time of day"), FALSE);
13090Sstevel@tonic-gate ifree(dp);
13100Sstevel@tonic-gate /*
13110Sstevel@tonic-gate * Year work.
13120Sstevel@tonic-gate */
13130Sstevel@tonic-gate cp = loyearp;
13140Sstevel@tonic-gate lp = byword(cp, begin_years);
13150Sstevel@tonic-gate if (lp != NULL) {
13160Sstevel@tonic-gate switch ((int)lp->l_value) {
13170Sstevel@tonic-gate case YR_MINIMUM:
13180Sstevel@tonic-gate rp->r_loyear = INT_MIN;
13190Sstevel@tonic-gate break;
13200Sstevel@tonic-gate case YR_MAXIMUM:
13210Sstevel@tonic-gate rp->r_loyear = INT_MAX;
13220Sstevel@tonic-gate break;
13230Sstevel@tonic-gate default: /* "cannot happen" */
13240Sstevel@tonic-gate (void) fprintf(stderr,
13250Sstevel@tonic-gate gettext("%s: panic: Invalid l_value %d\n"),
13260Sstevel@tonic-gate progname, lp->l_value);
13271138Srobbin exit(EXIT_FAILURE);
13280Sstevel@tonic-gate }
13290Sstevel@tonic-gate } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
13300Sstevel@tonic-gate error(gettext("invalid starting year"));
13310Sstevel@tonic-gate return;
13320Sstevel@tonic-gate } else if (noise) {
13330Sstevel@tonic-gate if (rp->r_loyear < min_year_representable)
13340Sstevel@tonic-gate warning(gettext(
13350Sstevel@tonic-gate "starting year too low to be represented"));
13360Sstevel@tonic-gate else if (rp->r_loyear > max_year_representable)
13370Sstevel@tonic-gate warning(gettext(
13380Sstevel@tonic-gate "starting year too high to be represented"));
13390Sstevel@tonic-gate }
13400Sstevel@tonic-gate cp = hiyearp;
13410Sstevel@tonic-gate if ((lp = byword(cp, end_years)) != NULL) {
13420Sstevel@tonic-gate switch ((int)lp->l_value) {
13430Sstevel@tonic-gate case YR_MINIMUM:
13440Sstevel@tonic-gate rp->r_hiyear = INT_MIN;
13450Sstevel@tonic-gate break;
13460Sstevel@tonic-gate case YR_MAXIMUM:
13470Sstevel@tonic-gate rp->r_hiyear = INT_MAX;
13480Sstevel@tonic-gate break;
13490Sstevel@tonic-gate case YR_ONLY:
13500Sstevel@tonic-gate rp->r_hiyear = rp->r_loyear;
13510Sstevel@tonic-gate break;
13520Sstevel@tonic-gate default: /* "cannot happen" */
13530Sstevel@tonic-gate (void) fprintf(stderr,
13540Sstevel@tonic-gate gettext("%s: panic: Invalid l_value %d\n"),
13550Sstevel@tonic-gate progname, lp->l_value);
13561138Srobbin exit(EXIT_FAILURE);
13570Sstevel@tonic-gate }
13580Sstevel@tonic-gate } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
13590Sstevel@tonic-gate error(gettext("invalid ending year"));
13600Sstevel@tonic-gate return;
13610Sstevel@tonic-gate } else if (noise) {
13620Sstevel@tonic-gate if (rp->r_loyear < min_year_representable)
13630Sstevel@tonic-gate warning(gettext(
13641138Srobbin "ending year too low to be represented"));
13650Sstevel@tonic-gate else if (rp->r_loyear > max_year_representable)
13660Sstevel@tonic-gate warning(gettext(
13671138Srobbin "ending year too high to be represented"));
13680Sstevel@tonic-gate }
13690Sstevel@tonic-gate if (rp->r_loyear > rp->r_hiyear) {
13700Sstevel@tonic-gate error(gettext("starting year greater than ending year"));
13710Sstevel@tonic-gate return;
13720Sstevel@tonic-gate }
13730Sstevel@tonic-gate if (*typep == '\0')
13740Sstevel@tonic-gate rp->r_yrtype = NULL;
13750Sstevel@tonic-gate else {
13760Sstevel@tonic-gate if (rp->r_loyear == rp->r_hiyear) {
13770Sstevel@tonic-gate error(gettext("typed single year"));
13780Sstevel@tonic-gate return;
13790Sstevel@tonic-gate }
13800Sstevel@tonic-gate rp->r_yrtype = ecpyalloc(typep);
13810Sstevel@tonic-gate }
13820Sstevel@tonic-gate if (rp->r_loyear < min_year && rp->r_loyear > 0)
13830Sstevel@tonic-gate min_year = rp->r_loyear;
13840Sstevel@tonic-gate /*
13850Sstevel@tonic-gate * Day work.
13860Sstevel@tonic-gate * Accept things such as:
13870Sstevel@tonic-gate * 1
13880Sstevel@tonic-gate * last-Sunday
13890Sstevel@tonic-gate * Sun<=20
13900Sstevel@tonic-gate * Sun>=7
13910Sstevel@tonic-gate */
13920Sstevel@tonic-gate dp = ecpyalloc(dayp);
13930Sstevel@tonic-gate if ((lp = byword(dp, lasts)) != NULL) {
13940Sstevel@tonic-gate rp->r_dycode = DC_DOWLEQ;
13950Sstevel@tonic-gate rp->r_wday = lp->l_value;
13960Sstevel@tonic-gate rp->r_dayofmonth = len_months[1][rp->r_month];
13970Sstevel@tonic-gate } else {
13980Sstevel@tonic-gate if ((ep = strchr(dp, '<')) != 0)
13990Sstevel@tonic-gate rp->r_dycode = DC_DOWLEQ;
14000Sstevel@tonic-gate else if ((ep = strchr(dp, '>')) != 0)
14010Sstevel@tonic-gate rp->r_dycode = DC_DOWGEQ;
14020Sstevel@tonic-gate else {
14030Sstevel@tonic-gate ep = dp;
14040Sstevel@tonic-gate rp->r_dycode = DC_DOM;
14050Sstevel@tonic-gate }
14060Sstevel@tonic-gate if (rp->r_dycode != DC_DOM) {
14070Sstevel@tonic-gate *ep++ = 0;
14080Sstevel@tonic-gate if (*ep++ != '=') {
14090Sstevel@tonic-gate error(gettext("invalid day of month"));
14100Sstevel@tonic-gate ifree(dp);
14110Sstevel@tonic-gate return;
14120Sstevel@tonic-gate }
14130Sstevel@tonic-gate if ((lp = byword(dp, wday_names)) == NULL) {
14140Sstevel@tonic-gate error(gettext("invalid weekday name"));
14150Sstevel@tonic-gate ifree(dp);
14160Sstevel@tonic-gate return;
14170Sstevel@tonic-gate }
14180Sstevel@tonic-gate rp->r_wday = lp->l_value;
14190Sstevel@tonic-gate }
14200Sstevel@tonic-gate if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
14210Sstevel@tonic-gate rp->r_dayofmonth <= 0 ||
14220Sstevel@tonic-gate (rp->r_dayofmonth > len_months[1][rp->r_month])) {
14230Sstevel@tonic-gate error(gettext("invalid day of month"));
14240Sstevel@tonic-gate ifree(dp);
14250Sstevel@tonic-gate return;
14260Sstevel@tonic-gate }
14270Sstevel@tonic-gate }
14280Sstevel@tonic-gate ifree(dp);
14290Sstevel@tonic-gate }
14300Sstevel@tonic-gate
14310Sstevel@tonic-gate static void
convert(val,buf)14320Sstevel@tonic-gate convert(val, buf)
14330Sstevel@tonic-gate const long val;
14340Sstevel@tonic-gate char * const buf;
14350Sstevel@tonic-gate {
14360Sstevel@tonic-gate register int i;
14370Sstevel@tonic-gate register long shift;
14380Sstevel@tonic-gate
14390Sstevel@tonic-gate for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
14400Sstevel@tonic-gate buf[i] = val >> shift;
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate
14430Sstevel@tonic-gate static void
puttzcode(val,fp)14440Sstevel@tonic-gate puttzcode(val, fp)
14450Sstevel@tonic-gate const long val;
14460Sstevel@tonic-gate FILE * const fp;
14470Sstevel@tonic-gate {
14480Sstevel@tonic-gate char buf[4];
14490Sstevel@tonic-gate
14500Sstevel@tonic-gate convert(val, buf);
14510Sstevel@tonic-gate (void) fwrite((void *)buf, (size_t)sizeof (buf), (size_t)1, fp);
14520Sstevel@tonic-gate }
14530Sstevel@tonic-gate
14540Sstevel@tonic-gate static int
atcomp(avp,bvp)14550Sstevel@tonic-gate atcomp(avp, bvp)
14560Sstevel@tonic-gate const void *avp;
14570Sstevel@tonic-gate const void *bvp;
14580Sstevel@tonic-gate {
14590Sstevel@tonic-gate if (((struct attype *)avp)->at < ((struct attype *)bvp)->at)
14600Sstevel@tonic-gate return (-1);
14610Sstevel@tonic-gate else if (((struct attype *)avp)->at > ((struct attype *)bvp)->at)
14620Sstevel@tonic-gate return (1);
14630Sstevel@tonic-gate else return (0);
14640Sstevel@tonic-gate }
14650Sstevel@tonic-gate
14660Sstevel@tonic-gate static void
writezone(name)14670Sstevel@tonic-gate writezone(name)
14680Sstevel@tonic-gate const char * const name;
14690Sstevel@tonic-gate {
14700Sstevel@tonic-gate register FILE *fp;
14710Sstevel@tonic-gate register int i, j;
14720Sstevel@tonic-gate static char *fullname;
14730Sstevel@tonic-gate static struct tzhead tzh;
14741138Srobbin zic_t ats[TZ_MAX_TIMES];
14750Sstevel@tonic-gate unsigned char types[TZ_MAX_TIMES];
14760Sstevel@tonic-gate
14770Sstevel@tonic-gate /*
14780Sstevel@tonic-gate * Sort.
14790Sstevel@tonic-gate */
14800Sstevel@tonic-gate if (timecnt > 1)
14810Sstevel@tonic-gate (void) qsort((void *)attypes, (size_t)timecnt,
14820Sstevel@tonic-gate (size_t)sizeof (*attypes), atcomp);
14830Sstevel@tonic-gate /*
14840Sstevel@tonic-gate * Optimize.
14850Sstevel@tonic-gate */
14860Sstevel@tonic-gate {
14870Sstevel@tonic-gate int fromi;
14880Sstevel@tonic-gate int toi;
14890Sstevel@tonic-gate
14900Sstevel@tonic-gate toi = 0;
14910Sstevel@tonic-gate fromi = 0;
14920Sstevel@tonic-gate while (fromi < timecnt && attypes[fromi].at < min_time)
14930Sstevel@tonic-gate ++fromi;
14940Sstevel@tonic-gate if (isdsts[0] == 0)
14950Sstevel@tonic-gate while (fromi < timecnt && attypes[fromi].type == 0)
14960Sstevel@tonic-gate ++fromi; /* handled by default rule */
14970Sstevel@tonic-gate for (; fromi < timecnt; ++fromi) {
14981138Srobbin if (toi != 0 && ((attypes[fromi].at +
14990Sstevel@tonic-gate gmtoffs[attypes[toi - 1].type]) <=
15001138Srobbin (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
15010Sstevel@tonic-gate : attypes[toi - 2].type]))) {
15021138Srobbin attypes[toi - 1].type =
15031138Srobbin attypes[fromi].type;
15041138Srobbin continue;
15050Sstevel@tonic-gate }
15060Sstevel@tonic-gate if (toi == 0 ||
15070Sstevel@tonic-gate attypes[toi - 1].type != attypes[fromi].type)
15080Sstevel@tonic-gate attypes[toi++] = attypes[fromi];
15090Sstevel@tonic-gate }
15100Sstevel@tonic-gate timecnt = toi;
15110Sstevel@tonic-gate }
15120Sstevel@tonic-gate /*
15130Sstevel@tonic-gate * Transfer.
15140Sstevel@tonic-gate */
15150Sstevel@tonic-gate for (i = 0; i < timecnt; ++i) {
15160Sstevel@tonic-gate ats[i] = attypes[i].at;
15170Sstevel@tonic-gate types[i] = attypes[i].type;
15180Sstevel@tonic-gate }
15190Sstevel@tonic-gate fullname = erealloc(fullname,
15200Sstevel@tonic-gate (int)(strlen(directory) + 1 + strlen(name) + 1));
15210Sstevel@tonic-gate (void) sprintf(fullname, "%s/%s", directory, name);
15220Sstevel@tonic-gate /*
15230Sstevel@tonic-gate * Remove old file, if any, to snap links.
15240Sstevel@tonic-gate */
15250Sstevel@tonic-gate if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
15260Sstevel@tonic-gate const char *e = strerror(errno);
15270Sstevel@tonic-gate
15280Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Can't remove %s: %s\n"),
15290Sstevel@tonic-gate progname, fullname, e);
15301138Srobbin exit(EXIT_FAILURE);
15310Sstevel@tonic-gate }
15320Sstevel@tonic-gate if ((fp = fopen(fullname, "wb")) == NULL) {
15330Sstevel@tonic-gate if (mkdirs(fullname) != 0)
15341138Srobbin exit(EXIT_FAILURE);
15350Sstevel@tonic-gate if ((fp = fopen(fullname, "wb")) == NULL) {
15360Sstevel@tonic-gate const char *e = strerror(errno);
15370Sstevel@tonic-gate (void) fprintf(stderr, gettext(
15380Sstevel@tonic-gate "%s: Can't create %s: %s\n"),
15390Sstevel@tonic-gate progname, fullname, e);
15401138Srobbin exit(EXIT_FAILURE);
15410Sstevel@tonic-gate }
15420Sstevel@tonic-gate }
15430Sstevel@tonic-gate convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
15440Sstevel@tonic-gate convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
15450Sstevel@tonic-gate convert(eitol(leapcnt), tzh.tzh_leapcnt);
15460Sstevel@tonic-gate convert(eitol(timecnt), tzh.tzh_timecnt);
15470Sstevel@tonic-gate convert(eitol(typecnt), tzh.tzh_typecnt);
15480Sstevel@tonic-gate convert(eitol(charcnt), tzh.tzh_charcnt);
15490Sstevel@tonic-gate (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof (tzh.tzh_magic));
15500Sstevel@tonic-gate #define DO(field) (void) fwrite((void *) tzh.field, \
15510Sstevel@tonic-gate (size_t)sizeof (tzh.field), (size_t)1, fp)
15520Sstevel@tonic-gate DO(tzh_magic);
15530Sstevel@tonic-gate DO(tzh_reserved);
15540Sstevel@tonic-gate DO(tzh_ttisgmtcnt);
15550Sstevel@tonic-gate DO(tzh_ttisstdcnt);
15560Sstevel@tonic-gate DO(tzh_leapcnt);
15570Sstevel@tonic-gate DO(tzh_timecnt);
15580Sstevel@tonic-gate DO(tzh_typecnt);
15590Sstevel@tonic-gate DO(tzh_charcnt);
15600Sstevel@tonic-gate #undef DO
15610Sstevel@tonic-gate for (i = 0; i < timecnt; ++i) {
15620Sstevel@tonic-gate j = leapcnt;
15630Sstevel@tonic-gate while (--j >= 0)
15640Sstevel@tonic-gate if (ats[i] >= trans[j]) {
15650Sstevel@tonic-gate ats[i] = tadd(ats[i], corr[j]);
15660Sstevel@tonic-gate break;
15670Sstevel@tonic-gate }
15680Sstevel@tonic-gate puttzcode((long)ats[i], fp);
15690Sstevel@tonic-gate }
15700Sstevel@tonic-gate if (timecnt > 0)
15710Sstevel@tonic-gate (void) fwrite((void *)types, (size_t)sizeof (types[0]),
15720Sstevel@tonic-gate (size_t)timecnt, fp);
15730Sstevel@tonic-gate for (i = 0; i < typecnt; ++i) {
15740Sstevel@tonic-gate puttzcode((long)gmtoffs[i], fp);
15750Sstevel@tonic-gate (void) putc(isdsts[i], fp);
15760Sstevel@tonic-gate (void) putc(abbrinds[i], fp);
15770Sstevel@tonic-gate }
15780Sstevel@tonic-gate if (charcnt != 0)
15790Sstevel@tonic-gate (void) fwrite((void *)chars, (size_t)sizeof (chars[0]),
15800Sstevel@tonic-gate (size_t)charcnt, fp);
15810Sstevel@tonic-gate for (i = 0; i < leapcnt; ++i) {
15820Sstevel@tonic-gate if (roll[i]) {
15830Sstevel@tonic-gate if (timecnt == 0 || trans[i] < ats[0]) {
15840Sstevel@tonic-gate j = 0;
15850Sstevel@tonic-gate while (isdsts[j])
15860Sstevel@tonic-gate if (++j >= typecnt) {
15870Sstevel@tonic-gate j = 0;
15880Sstevel@tonic-gate break;
15890Sstevel@tonic-gate }
15900Sstevel@tonic-gate } else {
15910Sstevel@tonic-gate j = 1;
15920Sstevel@tonic-gate while (j < timecnt && trans[i] >= ats[j])
15930Sstevel@tonic-gate ++j;
15940Sstevel@tonic-gate j = types[j - 1];
15950Sstevel@tonic-gate }
15960Sstevel@tonic-gate puttzcode((long)tadd(trans[i], -gmtoffs[j]), fp);
15970Sstevel@tonic-gate } else puttzcode((long)trans[i], fp);
15980Sstevel@tonic-gate puttzcode((long)corr[i], fp);
15990Sstevel@tonic-gate }
16000Sstevel@tonic-gate for (i = 0; i < typecnt; ++i)
16010Sstevel@tonic-gate (void) putc(ttisstds[i], fp);
16020Sstevel@tonic-gate for (i = 0; i < typecnt; ++i)
16030Sstevel@tonic-gate (void) putc(ttisgmts[i], fp);
16040Sstevel@tonic-gate if (ferror(fp) || fclose(fp)) {
16050Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: Error writing %s\n"),
16060Sstevel@tonic-gate progname, fullname);
16071138Srobbin exit(EXIT_FAILURE);
16080Sstevel@tonic-gate }
16090Sstevel@tonic-gate }
16100Sstevel@tonic-gate
16110Sstevel@tonic-gate static void
doabbr(abbr,format,letters,isdst)16120Sstevel@tonic-gate doabbr(abbr, format, letters, isdst)
16130Sstevel@tonic-gate char * const abbr;
16140Sstevel@tonic-gate const char * const format;
16150Sstevel@tonic-gate const char * const letters;
16160Sstevel@tonic-gate const int isdst;
16170Sstevel@tonic-gate {
16180Sstevel@tonic-gate if (strchr(format, '/') == NULL) {
16190Sstevel@tonic-gate if (letters == NULL)
16200Sstevel@tonic-gate (void) strcpy(abbr, format);
16210Sstevel@tonic-gate else
16220Sstevel@tonic-gate (void) sprintf(abbr, format, letters);
16230Sstevel@tonic-gate } else if (isdst)
16240Sstevel@tonic-gate (void) strcpy(abbr, strchr(format, '/') + 1);
16250Sstevel@tonic-gate else {
16260Sstevel@tonic-gate (void) strcpy(abbr, format);
16270Sstevel@tonic-gate *strchr(abbr, '/') = '\0';
16280Sstevel@tonic-gate }
16290Sstevel@tonic-gate }
16300Sstevel@tonic-gate
16310Sstevel@tonic-gate static void
outzone(zpfirst,zonecount)16320Sstevel@tonic-gate outzone(zpfirst, zonecount)
16330Sstevel@tonic-gate const struct zone * const zpfirst;
16340Sstevel@tonic-gate const int zonecount;
16350Sstevel@tonic-gate {
16360Sstevel@tonic-gate register const struct zone *zp;
16370Sstevel@tonic-gate register struct rule *rp;
16380Sstevel@tonic-gate register int i, j;
16390Sstevel@tonic-gate register int usestart, useuntil;
16401138Srobbin register zic_t starttime, untiltime;
16410Sstevel@tonic-gate register long gmtoff;
16420Sstevel@tonic-gate register long stdoff;
16430Sstevel@tonic-gate register int year;
16440Sstevel@tonic-gate register long startoff;
16450Sstevel@tonic-gate register int startttisstd;
16460Sstevel@tonic-gate register int startttisgmt;
16470Sstevel@tonic-gate register int type;
16480Sstevel@tonic-gate char startbuf[BUFSIZ];
16490Sstevel@tonic-gate
16500Sstevel@tonic-gate INITIALIZE(untiltime);
16510Sstevel@tonic-gate INITIALIZE(starttime);
16520Sstevel@tonic-gate /*
16530Sstevel@tonic-gate * Now. . .finally. . .generate some useful data!
16540Sstevel@tonic-gate */
16550Sstevel@tonic-gate timecnt = 0;
16560Sstevel@tonic-gate typecnt = 0;
16570Sstevel@tonic-gate charcnt = 0;
16580Sstevel@tonic-gate /*
16591442Srobbin * Thanks to Earl Chew
16600Sstevel@tonic-gate * for noting the need to unconditionally initialize startttisstd.
16610Sstevel@tonic-gate */
16620Sstevel@tonic-gate startttisstd = FALSE;
16630Sstevel@tonic-gate startttisgmt = FALSE;
16640Sstevel@tonic-gate for (i = 0; i < zonecount; ++i) {
16650Sstevel@tonic-gate /*
16660Sstevel@tonic-gate * A guess that may well be corrected later.
16670Sstevel@tonic-gate */
16680Sstevel@tonic-gate stdoff = 0;
16690Sstevel@tonic-gate zp = &zpfirst[i];
16700Sstevel@tonic-gate usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
16710Sstevel@tonic-gate useuntil = i < (zonecount - 1);
16720Sstevel@tonic-gate if (useuntil && zp->z_untiltime <= min_time)
16730Sstevel@tonic-gate continue;
16740Sstevel@tonic-gate gmtoff = zp->z_gmtoff;
16750Sstevel@tonic-gate eat(zp->z_filename, zp->z_linenum);
16760Sstevel@tonic-gate *startbuf = '\0';
16770Sstevel@tonic-gate startoff = zp->z_gmtoff;
16780Sstevel@tonic-gate if (zp->z_nrules == 0) {
16790Sstevel@tonic-gate stdoff = zp->z_stdoff;
16800Sstevel@tonic-gate doabbr(startbuf, zp->z_format,
16810Sstevel@tonic-gate (char *)NULL, stdoff != 0);
16820Sstevel@tonic-gate type = addtype(oadd(zp->z_gmtoff, stdoff),
16830Sstevel@tonic-gate startbuf, stdoff != 0, startttisstd,
16840Sstevel@tonic-gate startttisgmt);
16850Sstevel@tonic-gate if (usestart) {
16860Sstevel@tonic-gate addtt(starttime, type);
16870Sstevel@tonic-gate usestart = FALSE;
16881138Srobbin } else if (stdoff != 0)
16891138Srobbin addtt(min_time, type);
16900Sstevel@tonic-gate } else
16910Sstevel@tonic-gate for (year = min_year; year <= max_year; ++year) {
16920Sstevel@tonic-gate if (useuntil && year > zp->z_untilrule.r_hiyear)
16930Sstevel@tonic-gate break;
16940Sstevel@tonic-gate /*
16950Sstevel@tonic-gate * Mark which rules to do in the current year.
16960Sstevel@tonic-gate * For those to do, calculate rpytime(rp, year);
16970Sstevel@tonic-gate */
16980Sstevel@tonic-gate for (j = 0; j < zp->z_nrules; ++j) {
16990Sstevel@tonic-gate rp = &zp->z_rules[j];
17000Sstevel@tonic-gate eats(zp->z_filename, zp->z_linenum,
17010Sstevel@tonic-gate rp->r_filename, rp->r_linenum);
17020Sstevel@tonic-gate rp->r_todo = year >= rp->r_loyear &&
17030Sstevel@tonic-gate year <= rp->r_hiyear &&
17040Sstevel@tonic-gate yearistype(year, rp->r_yrtype);
17050Sstevel@tonic-gate if (rp->r_todo)
17060Sstevel@tonic-gate rp->r_temp = rpytime(rp, year);
17070Sstevel@tonic-gate }
17080Sstevel@tonic-gate for (;;) {
17090Sstevel@tonic-gate register int k;
17101138Srobbin register zic_t jtime, ktime;
17110Sstevel@tonic-gate register long offset;
17120Sstevel@tonic-gate char buf[BUFSIZ];
17130Sstevel@tonic-gate
17140Sstevel@tonic-gate INITIALIZE(ktime);
17150Sstevel@tonic-gate if (useuntil) {
17160Sstevel@tonic-gate /*
17171138Srobbin * Turn untiltime into UTC * assuming
17181138Srobbin * the current gmtoff and stdoff values.
17190Sstevel@tonic-gate */
17200Sstevel@tonic-gate untiltime = zp->z_untiltime;
17210Sstevel@tonic-gate if (!zp->z_untilrule.r_todisgmt)
17220Sstevel@tonic-gate untiltime = tadd(untiltime,
17230Sstevel@tonic-gate -gmtoff);
17240Sstevel@tonic-gate if (!zp->z_untilrule.r_todisstd)
17250Sstevel@tonic-gate untiltime = tadd(untiltime,
17260Sstevel@tonic-gate -stdoff);
17270Sstevel@tonic-gate }
17280Sstevel@tonic-gate /*
17290Sstevel@tonic-gate * Find the rule (of those to do, if any)
17300Sstevel@tonic-gate * that takes effect earliest in the year.
17310Sstevel@tonic-gate */
17320Sstevel@tonic-gate k = -1;
17330Sstevel@tonic-gate for (j = 0; j < zp->z_nrules; ++j) {
17340Sstevel@tonic-gate rp = &zp->z_rules[j];
17350Sstevel@tonic-gate if (!rp->r_todo)
17360Sstevel@tonic-gate continue;
17370Sstevel@tonic-gate eats(zp->z_filename, zp->z_linenum,
17380Sstevel@tonic-gate rp->r_filename, rp->r_linenum);
17390Sstevel@tonic-gate offset = rp->r_todisgmt ? 0 : gmtoff;
17400Sstevel@tonic-gate if (!rp->r_todisstd)
17410Sstevel@tonic-gate offset = oadd(offset, stdoff);
17420Sstevel@tonic-gate jtime = rp->r_temp;
17430Sstevel@tonic-gate if (jtime == min_time ||
17440Sstevel@tonic-gate jtime == max_time)
17450Sstevel@tonic-gate continue;
17460Sstevel@tonic-gate jtime = tadd(jtime, -offset);
17470Sstevel@tonic-gate if (k < 0 || jtime < ktime) {
17480Sstevel@tonic-gate k = j;
17490Sstevel@tonic-gate ktime = jtime;
17500Sstevel@tonic-gate }
17510Sstevel@tonic-gate }
17520Sstevel@tonic-gate if (k < 0)
17530Sstevel@tonic-gate break; /* go on to next year */
17540Sstevel@tonic-gate rp = &zp->z_rules[k];
17550Sstevel@tonic-gate rp->r_todo = FALSE;
17560Sstevel@tonic-gate if (useuntil && ktime >= untiltime)
17570Sstevel@tonic-gate break;
17580Sstevel@tonic-gate stdoff = rp->r_stdoff;
17590Sstevel@tonic-gate if (usestart && ktime == starttime)
17600Sstevel@tonic-gate usestart = FALSE;
17610Sstevel@tonic-gate if (usestart) {
17620Sstevel@tonic-gate if (ktime < starttime) {
17630Sstevel@tonic-gate startoff = oadd(zp->z_gmtoff,
17640Sstevel@tonic-gate stdoff);
17650Sstevel@tonic-gate doabbr(startbuf, zp->z_format,
17660Sstevel@tonic-gate rp->r_abbrvar,
17670Sstevel@tonic-gate rp->r_stdoff != 0);
17680Sstevel@tonic-gate continue;
17690Sstevel@tonic-gate }
17700Sstevel@tonic-gate if (*startbuf == '\0' &&
17710Sstevel@tonic-gate startoff == oadd(zp->z_gmtoff,
17720Sstevel@tonic-gate stdoff)) {
17730Sstevel@tonic-gate doabbr(startbuf, zp->z_format,
17740Sstevel@tonic-gate rp->r_abbrvar,
17750Sstevel@tonic-gate rp->r_stdoff != 0);
17760Sstevel@tonic-gate }
17770Sstevel@tonic-gate }
17780Sstevel@tonic-gate eats(zp->z_filename, zp->z_linenum,
17790Sstevel@tonic-gate rp->r_filename, rp->r_linenum);
17800Sstevel@tonic-gate doabbr(buf, zp->z_format, rp->r_abbrvar,
17810Sstevel@tonic-gate rp->r_stdoff != 0);
17820Sstevel@tonic-gate offset = oadd(zp->z_gmtoff, rp->r_stdoff);
17830Sstevel@tonic-gate type = addtype(offset, buf, rp->r_stdoff != 0,
17840Sstevel@tonic-gate rp->r_todisstd, rp->r_todisgmt);
17850Sstevel@tonic-gate addtt(ktime, type);
17860Sstevel@tonic-gate }
17870Sstevel@tonic-gate }
17880Sstevel@tonic-gate if (usestart) {
17890Sstevel@tonic-gate if (*startbuf == '\0' &&
17900Sstevel@tonic-gate zp->z_format != NULL &&
17910Sstevel@tonic-gate strchr(zp->z_format, '%') == NULL &&
17920Sstevel@tonic-gate strchr(zp->z_format, '/') == NULL)
17930Sstevel@tonic-gate (void) strcpy(startbuf, zp->z_format);
17940Sstevel@tonic-gate eat(zp->z_filename, zp->z_linenum);
17950Sstevel@tonic-gate if (*startbuf == '\0')
17960Sstevel@tonic-gate error(gettext(
17970Sstevel@tonic-gate "can't determine time zone abbrevation to use just after until time"));
17980Sstevel@tonic-gate else addtt(starttime,
17990Sstevel@tonic-gate addtype(startoff, startbuf,
18000Sstevel@tonic-gate startoff != zp->z_gmtoff,
18010Sstevel@tonic-gate startttisstd,
18020Sstevel@tonic-gate startttisgmt));
18030Sstevel@tonic-gate }
18040Sstevel@tonic-gate /*
18050Sstevel@tonic-gate * Now we may get to set starttime for the next zone line.
18060Sstevel@tonic-gate */
18070Sstevel@tonic-gate if (useuntil) {
18080Sstevel@tonic-gate startttisstd = zp->z_untilrule.r_todisstd;
18090Sstevel@tonic-gate startttisgmt = zp->z_untilrule.r_todisgmt;
18100Sstevel@tonic-gate starttime = zp->z_untiltime;
18110Sstevel@tonic-gate if (!startttisstd)
18120Sstevel@tonic-gate starttime = tadd(starttime, -stdoff);
18130Sstevel@tonic-gate if (!startttisgmt)
18140Sstevel@tonic-gate starttime = tadd(starttime, -gmtoff);
18150Sstevel@tonic-gate }
18160Sstevel@tonic-gate }
18170Sstevel@tonic-gate writezone(zpfirst->z_name);
18180Sstevel@tonic-gate }
18190Sstevel@tonic-gate
18200Sstevel@tonic-gate static void
addtt(starttime,type)18210Sstevel@tonic-gate addtt(starttime, type)
18221138Srobbin const zic_t starttime;
18230Sstevel@tonic-gate int type;
18240Sstevel@tonic-gate {
18250Sstevel@tonic-gate if (starttime <= min_time ||
18260Sstevel@tonic-gate (timecnt == 1 && attypes[0].at < min_time)) {
18270Sstevel@tonic-gate gmtoffs[0] = gmtoffs[type];
18280Sstevel@tonic-gate isdsts[0] = isdsts[type];
18290Sstevel@tonic-gate ttisstds[0] = ttisstds[type];
18300Sstevel@tonic-gate ttisgmts[0] = ttisgmts[type];
18310Sstevel@tonic-gate if (abbrinds[type] != 0)
18320Sstevel@tonic-gate (void) strcpy(chars, &chars[abbrinds[type]]);
18330Sstevel@tonic-gate abbrinds[0] = 0;
18340Sstevel@tonic-gate charcnt = strlen(chars) + 1;
18350Sstevel@tonic-gate typecnt = 1;
18360Sstevel@tonic-gate timecnt = 0;
18370Sstevel@tonic-gate type = 0;
18380Sstevel@tonic-gate }
18390Sstevel@tonic-gate if (timecnt >= TZ_MAX_TIMES) {
18400Sstevel@tonic-gate error(gettext("too many transitions?!"));
18411138Srobbin exit(EXIT_FAILURE);
18420Sstevel@tonic-gate }
18430Sstevel@tonic-gate attypes[timecnt].at = starttime;
18440Sstevel@tonic-gate attypes[timecnt].type = type;
18450Sstevel@tonic-gate ++timecnt;
18460Sstevel@tonic-gate }
18470Sstevel@tonic-gate
18480Sstevel@tonic-gate static int
addtype(gmtoff,abbr,isdst,ttisstd,ttisgmt)18490Sstevel@tonic-gate addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
18500Sstevel@tonic-gate const long gmtoff;
18510Sstevel@tonic-gate const char * const abbr;
18520Sstevel@tonic-gate const int isdst;
18530Sstevel@tonic-gate const int ttisstd;
18540Sstevel@tonic-gate const int ttisgmt;
18550Sstevel@tonic-gate {
18560Sstevel@tonic-gate register int i, j;
18570Sstevel@tonic-gate
18580Sstevel@tonic-gate if (isdst != TRUE && isdst != FALSE) {
18590Sstevel@tonic-gate error(gettext(
18600Sstevel@tonic-gate "internal error - addtype called with bad isdst"));
18611138Srobbin exit(EXIT_FAILURE);
18620Sstevel@tonic-gate }
18630Sstevel@tonic-gate if (ttisstd != TRUE && ttisstd != FALSE) {
18640Sstevel@tonic-gate error(gettext(
18650Sstevel@tonic-gate "internal error - addtype called with bad ttisstd"));
18661138Srobbin exit(EXIT_FAILURE);
18670Sstevel@tonic-gate }
18680Sstevel@tonic-gate if (ttisgmt != TRUE && ttisgmt != FALSE) {
18690Sstevel@tonic-gate error(gettext(
18700Sstevel@tonic-gate "internal error - addtype called with bad ttisgmt"));
18711138Srobbin exit(EXIT_FAILURE);
18720Sstevel@tonic-gate }
18730Sstevel@tonic-gate /*
18740Sstevel@tonic-gate * See if there's already an entry for this zone type.
18750Sstevel@tonic-gate * If so, just return its index.
18760Sstevel@tonic-gate */
18770Sstevel@tonic-gate for (i = 0; i < typecnt; ++i) {
18780Sstevel@tonic-gate if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
18790Sstevel@tonic-gate strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
18800Sstevel@tonic-gate ttisstd == ttisstds[i] &&
18810Sstevel@tonic-gate ttisgmt == ttisgmts[i])
18820Sstevel@tonic-gate return (i);
18830Sstevel@tonic-gate }
18840Sstevel@tonic-gate /*
18850Sstevel@tonic-gate * There isn't one; add a new one, unless there are already too
18860Sstevel@tonic-gate * many.
18870Sstevel@tonic-gate */
18880Sstevel@tonic-gate if (typecnt >= TZ_MAX_TYPES) {
18890Sstevel@tonic-gate error(gettext("too many local time types"));
18901138Srobbin exit(EXIT_FAILURE);
18910Sstevel@tonic-gate }
18924218Srobbin if (!(-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
18934218Srobbin error(gettext("UTC offset out of range"));
18944218Srobbin exit(EXIT_FAILURE);
18954218Srobbin }
18960Sstevel@tonic-gate gmtoffs[i] = gmtoff;
18970Sstevel@tonic-gate isdsts[i] = isdst;
18980Sstevel@tonic-gate ttisstds[i] = ttisstd;
18990Sstevel@tonic-gate ttisgmts[i] = ttisgmt;
19000Sstevel@tonic-gate
19010Sstevel@tonic-gate for (j = 0; j < charcnt; ++j)
19020Sstevel@tonic-gate if (strcmp(&chars[j], abbr) == 0)
19030Sstevel@tonic-gate break;
19040Sstevel@tonic-gate if (j == charcnt)
19050Sstevel@tonic-gate newabbr(abbr);
19060Sstevel@tonic-gate abbrinds[i] = j;
19070Sstevel@tonic-gate ++typecnt;
19080Sstevel@tonic-gate return (i);
19090Sstevel@tonic-gate }
19100Sstevel@tonic-gate
19110Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
19120Sstevel@tonic-gate static void
leapadd(t,positive,rolling,count)19130Sstevel@tonic-gate leapadd(t, positive, rolling, count)
19141138Srobbin const zic_t t;
19150Sstevel@tonic-gate const int positive;
19160Sstevel@tonic-gate const int rolling;
19170Sstevel@tonic-gate int count;
19180Sstevel@tonic-gate {
19190Sstevel@tonic-gate register int i, j;
19200Sstevel@tonic-gate
19210Sstevel@tonic-gate if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
19220Sstevel@tonic-gate error(gettext("too many leap seconds"));
19231138Srobbin exit(EXIT_FAILURE);
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate for (i = 0; i < leapcnt; ++i)
19260Sstevel@tonic-gate if (t <= trans[i]) {
19270Sstevel@tonic-gate if (t == trans[i]) {
19280Sstevel@tonic-gate error(gettext("repeated leap second moment"));
19291138Srobbin exit(EXIT_FAILURE);
19300Sstevel@tonic-gate }
19310Sstevel@tonic-gate break;
19320Sstevel@tonic-gate }
19330Sstevel@tonic-gate do {
19340Sstevel@tonic-gate for (j = leapcnt; j > i; --j) {
19350Sstevel@tonic-gate trans[j] = trans[j - 1];
19360Sstevel@tonic-gate corr[j] = corr[j - 1];
19370Sstevel@tonic-gate roll[j] = roll[j - 1];
19380Sstevel@tonic-gate }
19390Sstevel@tonic-gate trans[i] = t;
19400Sstevel@tonic-gate corr[i] = positive ? 1L : eitol(-count);
19410Sstevel@tonic-gate roll[i] = rolling;
19420Sstevel@tonic-gate ++leapcnt;
19430Sstevel@tonic-gate } while (positive && --count != 0);
19440Sstevel@tonic-gate }
19450Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
19460Sstevel@tonic-gate
19470Sstevel@tonic-gate #ifdef LEAPSECOND_SUPPORT
19480Sstevel@tonic-gate static void
adjleap(void)19490Sstevel@tonic-gate adjleap(void)
19500Sstevel@tonic-gate {
19510Sstevel@tonic-gate register int i;
19520Sstevel@tonic-gate register long last = 0;
19530Sstevel@tonic-gate
19540Sstevel@tonic-gate /*
19550Sstevel@tonic-gate * propagate leap seconds forward
19560Sstevel@tonic-gate */
19570Sstevel@tonic-gate for (i = 0; i < leapcnt; ++i) {
19580Sstevel@tonic-gate trans[i] = tadd(trans[i], last);
19590Sstevel@tonic-gate last = corr[i] += last;
19600Sstevel@tonic-gate }
19610Sstevel@tonic-gate }
19620Sstevel@tonic-gate #endif /* LEAPSECOND_SUPPORT */
19630Sstevel@tonic-gate
19640Sstevel@tonic-gate static int
yearistype(year,type)19650Sstevel@tonic-gate yearistype(year, type)
19660Sstevel@tonic-gate const int year;
19670Sstevel@tonic-gate const char * const type;
19680Sstevel@tonic-gate {
19690Sstevel@tonic-gate static char *buf;
19700Sstevel@tonic-gate int result;
19710Sstevel@tonic-gate
19720Sstevel@tonic-gate if (type == NULL || *type == '\0')
19730Sstevel@tonic-gate return (TRUE);
19740Sstevel@tonic-gate #if defined(sun)
19750Sstevel@tonic-gate if (strcmp(type, "uspres") == 0)
19760Sstevel@tonic-gate return ((year % 4) == 0);
19770Sstevel@tonic-gate if (strcmp(type, "nonpres") == 0)
19780Sstevel@tonic-gate return ((year % 4) != 0);
19790Sstevel@tonic-gate if (strcmp(type, "even") == 0)
19800Sstevel@tonic-gate return ((year % 2) == 0);
19810Sstevel@tonic-gate if (strcmp(type, "odd") == 0)
19820Sstevel@tonic-gate return ((year % 2) != 0);
19830Sstevel@tonic-gate #endif /* defined(sun) */
19840Sstevel@tonic-gate
19850Sstevel@tonic-gate buf = erealloc(buf, (int)(132 + strlen(yitcommand) + strlen(type)));
19860Sstevel@tonic-gate (void) sprintf(buf, "%s %d %s", yitcommand, year, type);
19870Sstevel@tonic-gate result = system(buf);
19881138Srobbin if (WIFEXITED(result)) {
19891138Srobbin switch (WEXITSTATUS(result)) {
19901138Srobbin case 0:
19911138Srobbin return (TRUE);
19921138Srobbin case 1:
19931138Srobbin return (FALSE);
19941138Srobbin }
19951138Srobbin }
19960Sstevel@tonic-gate error(gettext("Wild result from command execution"));
19970Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s: command was '%s', result was %d\n"),
19980Sstevel@tonic-gate progname, buf, result);
19990Sstevel@tonic-gate for (;;)
20001138Srobbin exit(EXIT_FAILURE);
20010Sstevel@tonic-gate }
20020Sstevel@tonic-gate
20030Sstevel@tonic-gate static int
lowerit(a)20040Sstevel@tonic-gate lowerit(a)
20050Sstevel@tonic-gate int a;
20060Sstevel@tonic-gate {
20070Sstevel@tonic-gate a = (unsigned char) a;
20080Sstevel@tonic-gate return ((isascii(a) && isupper(a)) ? tolower(a) : a);
20090Sstevel@tonic-gate }
20100Sstevel@tonic-gate
20110Sstevel@tonic-gate static int
ciequal(ap,bp)20120Sstevel@tonic-gate ciequal(ap, bp) /* case-insensitive equality */
20130Sstevel@tonic-gate register const char *ap;
20140Sstevel@tonic-gate register const char *bp;
20150Sstevel@tonic-gate {
20160Sstevel@tonic-gate while (lowerit(*ap) == lowerit(*bp++))
20170Sstevel@tonic-gate if (*ap++ == '\0')
20180Sstevel@tonic-gate return (TRUE);
20190Sstevel@tonic-gate return (FALSE);
20200Sstevel@tonic-gate }
20210Sstevel@tonic-gate
20220Sstevel@tonic-gate static int
itsabbr(abbr,word)20230Sstevel@tonic-gate itsabbr(abbr, word)
20240Sstevel@tonic-gate register const char *abbr;
20250Sstevel@tonic-gate register const char *word;
20260Sstevel@tonic-gate {
20270Sstevel@tonic-gate if (lowerit(*abbr) != lowerit(*word))
20280Sstevel@tonic-gate return (FALSE);
20290Sstevel@tonic-gate ++word;
20300Sstevel@tonic-gate while (*++abbr != '\0')
20310Sstevel@tonic-gate do {
20320Sstevel@tonic-gate if (*word == '\0')
20330Sstevel@tonic-gate return (FALSE);
20340Sstevel@tonic-gate } while (lowerit(*word++) != lowerit(*abbr));
20350Sstevel@tonic-gate return (TRUE);
20360Sstevel@tonic-gate }
20370Sstevel@tonic-gate
20380Sstevel@tonic-gate static const struct lookup *
byword(word,table)20390Sstevel@tonic-gate byword(word, table)
20400Sstevel@tonic-gate register const char * const word;
20410Sstevel@tonic-gate register const struct lookup * const table;
20420Sstevel@tonic-gate {
20430Sstevel@tonic-gate register const struct lookup *foundlp;
20440Sstevel@tonic-gate register const struct lookup *lp;
20450Sstevel@tonic-gate
20460Sstevel@tonic-gate if (word == NULL || table == NULL)
20470Sstevel@tonic-gate return (NULL);
20480Sstevel@tonic-gate /*
20490Sstevel@tonic-gate * Look for exact match.
20500Sstevel@tonic-gate */
20510Sstevel@tonic-gate for (lp = table; lp->l_word != NULL; ++lp)
20520Sstevel@tonic-gate if (ciequal(word, lp->l_word))
20530Sstevel@tonic-gate return (lp);
20540Sstevel@tonic-gate /*
20550Sstevel@tonic-gate * Look for inexact match.
20560Sstevel@tonic-gate */
20570Sstevel@tonic-gate foundlp = NULL;
20580Sstevel@tonic-gate for (lp = table; lp->l_word != NULL; ++lp)
20590Sstevel@tonic-gate if (itsabbr(word, lp->l_word)) {
20600Sstevel@tonic-gate if (foundlp == NULL)
20610Sstevel@tonic-gate foundlp = lp;
20620Sstevel@tonic-gate else return (NULL); /* multiple inexact matches */
20630Sstevel@tonic-gate }
20640Sstevel@tonic-gate return (foundlp);
20650Sstevel@tonic-gate }
20660Sstevel@tonic-gate
20670Sstevel@tonic-gate static char **
getfields(cp)20680Sstevel@tonic-gate getfields(cp)
20690Sstevel@tonic-gate register char *cp;
20700Sstevel@tonic-gate {
20710Sstevel@tonic-gate register char *dp;
20720Sstevel@tonic-gate register char **array;
20730Sstevel@tonic-gate register int nsubs;
20740Sstevel@tonic-gate
20750Sstevel@tonic-gate if (cp == NULL)
20760Sstevel@tonic-gate return (NULL);
20770Sstevel@tonic-gate array = (char **)(void *)
20780Sstevel@tonic-gate emalloc((int)((strlen(cp) + 1) * sizeof (*array)));
20790Sstevel@tonic-gate nsubs = 0;
20800Sstevel@tonic-gate for (;;) {
20810Sstevel@tonic-gate while (isascii(*cp) && isspace((unsigned char) *cp))
20820Sstevel@tonic-gate ++cp;
20830Sstevel@tonic-gate if (*cp == '\0' || *cp == '#')
20840Sstevel@tonic-gate break;
20850Sstevel@tonic-gate array[nsubs++] = dp = cp;
20860Sstevel@tonic-gate do {
20870Sstevel@tonic-gate if ((*dp = *cp++) != '"')
20880Sstevel@tonic-gate ++dp;
20890Sstevel@tonic-gate else while ((*dp = *cp++) != '"')
20900Sstevel@tonic-gate if (*dp != '\0')
20910Sstevel@tonic-gate ++dp;
20924218Srobbin else {
20930Sstevel@tonic-gate error(gettext(
20940Sstevel@tonic-gate "Odd number of quotation marks"));
20954218Srobbin exit(1);
20964218Srobbin }
20970Sstevel@tonic-gate } while (*cp != '\0' && *cp != '#' &&
20980Sstevel@tonic-gate (!isascii(*cp) || !isspace((unsigned char) *cp)));
20990Sstevel@tonic-gate if (isascii(*cp) && isspace((unsigned char) *cp))
21000Sstevel@tonic-gate ++cp;
21010Sstevel@tonic-gate *dp = '\0';
21020Sstevel@tonic-gate }
21030Sstevel@tonic-gate array[nsubs] = NULL;
21040Sstevel@tonic-gate return (array);
21050Sstevel@tonic-gate }
21060Sstevel@tonic-gate
21070Sstevel@tonic-gate static long
oadd(t1,t2)21080Sstevel@tonic-gate oadd(t1, t2)
21090Sstevel@tonic-gate const long t1;
21100Sstevel@tonic-gate const long t2;
21110Sstevel@tonic-gate {
21120Sstevel@tonic-gate register long t;
21130Sstevel@tonic-gate
21140Sstevel@tonic-gate t = t1 + t2;
21150Sstevel@tonic-gate if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
21160Sstevel@tonic-gate error(gettext("time overflow"));
21171138Srobbin exit(EXIT_FAILURE);
21180Sstevel@tonic-gate }
21190Sstevel@tonic-gate return (t);
21200Sstevel@tonic-gate }
21210Sstevel@tonic-gate
21221138Srobbin static zic_t
tadd(t1,t2)21230Sstevel@tonic-gate tadd(t1, t2)
21241138Srobbin const zic_t t1;
21250Sstevel@tonic-gate const long t2;
21260Sstevel@tonic-gate {
21271138Srobbin register zic_t t;
21280Sstevel@tonic-gate
21290Sstevel@tonic-gate if (t1 == max_time && t2 > 0)
21300Sstevel@tonic-gate return (max_time);
21310Sstevel@tonic-gate if (t1 == min_time && t2 < 0)
21320Sstevel@tonic-gate return (min_time);
21330Sstevel@tonic-gate t = t1 + t2;
21340Sstevel@tonic-gate if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
21350Sstevel@tonic-gate error(gettext("time overflow"));
21361138Srobbin exit(EXIT_FAILURE);
21370Sstevel@tonic-gate }
21380Sstevel@tonic-gate return (t);
21390Sstevel@tonic-gate }
21400Sstevel@tonic-gate
21410Sstevel@tonic-gate /*
21420Sstevel@tonic-gate * Given a rule, and a year, compute the date - in seconds since January 1,
21430Sstevel@tonic-gate * 1970, 00:00 LOCAL time - in that year that the rule refers to.
21440Sstevel@tonic-gate */
21450Sstevel@tonic-gate
21461138Srobbin static zic_t
rpytime(rp,wantedy)21470Sstevel@tonic-gate rpytime(rp, wantedy)
21480Sstevel@tonic-gate register const struct rule * const rp;
21490Sstevel@tonic-gate register const int wantedy;
21500Sstevel@tonic-gate {
21510Sstevel@tonic-gate register int y, m, i;
21520Sstevel@tonic-gate register long dayoff; /* with a nod to Margaret O. */
21531138Srobbin register zic_t t;
21540Sstevel@tonic-gate
21550Sstevel@tonic-gate if (wantedy == INT_MIN)
21560Sstevel@tonic-gate return (min_time);
21570Sstevel@tonic-gate if (wantedy == INT_MAX)
21580Sstevel@tonic-gate return (max_time);
21590Sstevel@tonic-gate dayoff = 0;
21600Sstevel@tonic-gate m = TM_JANUARY;
21610Sstevel@tonic-gate y = EPOCH_YEAR;
21620Sstevel@tonic-gate while (wantedy != y) {
21630Sstevel@tonic-gate if (wantedy > y) {
21640Sstevel@tonic-gate i = len_years[isleap(y)];
21650Sstevel@tonic-gate ++y;
21660Sstevel@tonic-gate } else {
21670Sstevel@tonic-gate --y;
21680Sstevel@tonic-gate i = -len_years[isleap(y)];
21690Sstevel@tonic-gate }
21700Sstevel@tonic-gate dayoff = oadd(dayoff, eitol(i));
21710Sstevel@tonic-gate }
21720Sstevel@tonic-gate while (m != rp->r_month) {
21730Sstevel@tonic-gate i = len_months[isleap(y)][m];
21740Sstevel@tonic-gate dayoff = oadd(dayoff, eitol(i));
21750Sstevel@tonic-gate ++m;
21760Sstevel@tonic-gate }
21770Sstevel@tonic-gate i = rp->r_dayofmonth;
21780Sstevel@tonic-gate if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
21790Sstevel@tonic-gate if (rp->r_dycode == DC_DOWLEQ)
21800Sstevel@tonic-gate --i;
21810Sstevel@tonic-gate else {
21820Sstevel@tonic-gate error(gettext("use of 2/29 in non leap-year"));
21831138Srobbin exit(EXIT_FAILURE);
21840Sstevel@tonic-gate }
21850Sstevel@tonic-gate }
21860Sstevel@tonic-gate --i;
21870Sstevel@tonic-gate dayoff = oadd(dayoff, eitol(i));
21880Sstevel@tonic-gate if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
21890Sstevel@tonic-gate register long wday;
21900Sstevel@tonic-gate
21910Sstevel@tonic-gate #define LDAYSPERWEEK ((long)DAYSPERWEEK)
21920Sstevel@tonic-gate wday = eitol(EPOCH_WDAY);
21930Sstevel@tonic-gate /*
21940Sstevel@tonic-gate * Don't trust mod of negative numbers.
21950Sstevel@tonic-gate */
21960Sstevel@tonic-gate if (dayoff >= 0)
21970Sstevel@tonic-gate wday = (wday + dayoff) % LDAYSPERWEEK;
21980Sstevel@tonic-gate else {
21990Sstevel@tonic-gate wday -= ((-dayoff) % LDAYSPERWEEK);
22000Sstevel@tonic-gate if (wday < 0)
22010Sstevel@tonic-gate wday += LDAYSPERWEEK;
22020Sstevel@tonic-gate }
22030Sstevel@tonic-gate while (wday != eitol(rp->r_wday))
22040Sstevel@tonic-gate if (rp->r_dycode == DC_DOWGEQ) {
22050Sstevel@tonic-gate dayoff = oadd(dayoff, (long)1);
22060Sstevel@tonic-gate if (++wday >= LDAYSPERWEEK)
22070Sstevel@tonic-gate wday = 0;
22080Sstevel@tonic-gate ++i;
22090Sstevel@tonic-gate } else {
22100Sstevel@tonic-gate dayoff = oadd(dayoff, (long)-1);
22110Sstevel@tonic-gate if (--wday < 0)
22120Sstevel@tonic-gate wday = LDAYSPERWEEK - 1;
22130Sstevel@tonic-gate --i;
22140Sstevel@tonic-gate }
22150Sstevel@tonic-gate if (i < 0 || i >= len_months[isleap(y)][m]) {
22161138Srobbin if (noise)
22171138Srobbin warning(gettext("rule goes past start/end of "
22181138Srobbin "month--will not work with pre-2004 "
22191138Srobbin "versions of zic"));
22200Sstevel@tonic-gate }
22210Sstevel@tonic-gate }
22221138Srobbin if (dayoff < 0 && !TYPE_SIGNED(zic_t))
22231138Srobbin return (min_time);
22241138Srobbin if (dayoff < min_time / SECSPERDAY)
22250Sstevel@tonic-gate return (min_time);
22261138Srobbin if (dayoff > max_time / SECSPERDAY)
22271138Srobbin return (max_time);
22281138Srobbin t = (zic_t)dayoff * SECSPERDAY;
22290Sstevel@tonic-gate return (tadd(t, rp->r_tod));
22300Sstevel@tonic-gate }
22310Sstevel@tonic-gate
22320Sstevel@tonic-gate static void
newabbr(string)22330Sstevel@tonic-gate newabbr(string)
22340Sstevel@tonic-gate const char * const string;
22350Sstevel@tonic-gate {
22360Sstevel@tonic-gate register int i;
22370Sstevel@tonic-gate
22381138Srobbin if (strcmp(string, GRANDPARENTED) != 0) {
22391138Srobbin register const char *cp;
22401138Srobbin register char *wp;
22411138Srobbin
22421138Srobbin /*
22431138Srobbin * Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics
22441138Srobbin * optionally followed by a + or - and a number from 1 to 14.
22451138Srobbin */
22461138Srobbin cp = string;
22471138Srobbin wp = NULL;
22481138Srobbin while (isascii(*cp) && isalpha(*cp))
22491138Srobbin ++cp;
22501138Srobbin if (cp - string == 0)
22511138Srobbin wp = gettext(("time zone abbreviation lacks "
22521138Srobbin "alphabetic at start"));
22531138Srobbin if (noise && cp - string > 3)
22541138Srobbin wp = gettext(("time zone abbreviation has more than 3 "
22551138Srobbin "alphabetics"));
22561138Srobbin if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
22571138Srobbin wp = gettext(("time zone abbreviation has too many "
22581138Srobbin "alphabetics"));
22591138Srobbin if (wp == NULL && (*cp == '+' || *cp == '-')) {
22601138Srobbin ++cp;
22611138Srobbin if (isascii(*cp) && isdigit(*cp))
22621138Srobbin if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
22631138Srobbin ++cp;
22641138Srobbin }
22651138Srobbin if (*cp != '\0')
22661138Srobbin wp = gettext("time zone abbreviation differs from "
22671138Srobbin "POSIX standard");
22681138Srobbin if (wp != NULL) {
22691138Srobbin wp = ecpyalloc(wp);
22701138Srobbin wp = ecatalloc(wp, " (");
22711138Srobbin wp = ecatalloc(wp, string);
22721138Srobbin wp = ecatalloc(wp, ")");
22731138Srobbin warning(wp);
22741138Srobbin ifree(wp);
22751138Srobbin }
22761138Srobbin }
22770Sstevel@tonic-gate i = strlen(string) + 1;
22780Sstevel@tonic-gate if (charcnt + i > TZ_MAX_CHARS) {
22791138Srobbin error(gettext("too many, or too long, time zone "
22801138Srobbin "abbreviations"));
22811138Srobbin exit(EXIT_FAILURE);
22820Sstevel@tonic-gate }
22830Sstevel@tonic-gate (void) strcpy(&chars[charcnt], string);
22840Sstevel@tonic-gate charcnt += eitol(i);
22850Sstevel@tonic-gate }
22860Sstevel@tonic-gate
22870Sstevel@tonic-gate static int
mkdirs(argname)22880Sstevel@tonic-gate mkdirs(argname)
2289*4223Srobbin char *argname;
22900Sstevel@tonic-gate {
22910Sstevel@tonic-gate register char *name;
22920Sstevel@tonic-gate register char *cp;
22930Sstevel@tonic-gate
22940Sstevel@tonic-gate if (argname == NULL || *argname == '\0')
22950Sstevel@tonic-gate return (0);
22960Sstevel@tonic-gate cp = name = ecpyalloc(argname);
22970Sstevel@tonic-gate while ((cp = strchr(cp + 1, '/')) != 0) {
22980Sstevel@tonic-gate *cp = '\0';
22990Sstevel@tonic-gate if (!itsdir(name)) {
23000Sstevel@tonic-gate /*
23010Sstevel@tonic-gate * It doesn't seem to exist, so we try to create it.
23020Sstevel@tonic-gate * Creation may fail because of the directory being
23030Sstevel@tonic-gate * created by some other multiprocessor, so we get
23040Sstevel@tonic-gate * to do extra checking.
23050Sstevel@tonic-gate */
23061138Srobbin if (mkdir(name, MKDIR_UMASK) != 0) {
23070Sstevel@tonic-gate const char *e = strerror(errno);
23080Sstevel@tonic-gate
23090Sstevel@tonic-gate if (errno != EEXIST || !itsdir(name)) {
23100Sstevel@tonic-gate (void) fprintf(stderr, gettext(
23110Sstevel@tonic-gate "%s: Can't create directory %s: %s\n"),
23120Sstevel@tonic-gate progname, name, e);
23130Sstevel@tonic-gate ifree(name);
23140Sstevel@tonic-gate return (-1);
23150Sstevel@tonic-gate }
23160Sstevel@tonic-gate }
23170Sstevel@tonic-gate }
23180Sstevel@tonic-gate *cp = '/';
23190Sstevel@tonic-gate }
23200Sstevel@tonic-gate ifree(name);
23210Sstevel@tonic-gate return (0);
23220Sstevel@tonic-gate }
23230Sstevel@tonic-gate
23240Sstevel@tonic-gate static long
eitol(i)23250Sstevel@tonic-gate eitol(i)
23260Sstevel@tonic-gate const int i;
23270Sstevel@tonic-gate {
23280Sstevel@tonic-gate long l;
23290Sstevel@tonic-gate
23300Sstevel@tonic-gate l = i;
23310Sstevel@tonic-gate if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
23320Sstevel@tonic-gate (void) fprintf(stderr,
23330Sstevel@tonic-gate gettext("%s: %d did not sign extend correctly\n"),
23340Sstevel@tonic-gate progname, i);
23351138Srobbin exit(EXIT_FAILURE);
23360Sstevel@tonic-gate }
23370Sstevel@tonic-gate return (l);
23380Sstevel@tonic-gate }
23390Sstevel@tonic-gate
23400Sstevel@tonic-gate /*
23411138Srobbin * UNIX was a registered trademark of The Open Group in 2003.
23420Sstevel@tonic-gate */
2343