10Sstevel@tonic-gate /* 2*4218Srobbin * 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 7*4218Srobbin 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); 100*4218Srobbin 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 * 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 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 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 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 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 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 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 595*4218Srobbin dolink(fromfield, tofield) 596*4218Srobbin const char * const fromfield; 597*4218Srobbin const char * const tofield; 5980Sstevel@tonic-gate { 5990Sstevel@tonic-gate register char *fromname; 6000Sstevel@tonic-gate register char *toname; 6010Sstevel@tonic-gate 602*4218Srobbin if (fromfield[0] == '/') 603*4218Srobbin fromname = ecpyalloc(fromfield); 6040Sstevel@tonic-gate else { 6050Sstevel@tonic-gate fromname = ecpyalloc(directory); 6060Sstevel@tonic-gate fromname = ecatalloc(fromname, "/"); 607*4218Srobbin fromname = ecatalloc(fromname, fromfield); 6080Sstevel@tonic-gate } 609*4218Srobbin if (tofield[0] == '/') 610*4218Srobbin toname = ecpyalloc(tofield); 6110Sstevel@tonic-gate else { 6120Sstevel@tonic-gate toname = ecpyalloc(directory); 6130Sstevel@tonic-gate toname = ecatalloc(toname, "/"); 614*4218Srobbin 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)) { 632*4218Srobbin 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 "../"); 638*4218Srobbin 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 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 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 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 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 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 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 { 928*4218Srobbin long hh; 929*4218Srobbin 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; 939*4218Srobbin if (sscanf(string, scheck(string, "%ld"), &hh) == 1) 9400Sstevel@tonic-gate mm = ss = 0; 941*4218Srobbin else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2) 9420Sstevel@tonic-gate ss = 0; 943*4218Srobbin 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 } 948*4218Srobbin if (hh < 0 || 9490Sstevel@tonic-gate mm < 0 || mm >= MINSPERHOUR || 950*4218Srobbin ss < 0 || ss > SECSPERMIN) { 9510Sstevel@tonic-gate error(errstring); 9520Sstevel@tonic-gate return (0); 9530Sstevel@tonic-gate } 954*4218Srobbin if (LONG_MAX / SECSPERHOUR < hh) { 955*4218Srobbin error(gettext("time overflow")); 956*4218Srobbin return (0); 957*4218Srobbin } 958*4218Srobbin if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0) 9591138Srobbin warning( 9601138Srobbin gettext("24:00 not handled by pre-1998 versions of zic")); 961*4218Srobbin if (noise && (hh > HOURSPERDAY || 962*4218Srobbin (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) 963*4218Srobbin warning(gettext("values over 24 hours not handled by " 964*4218Srobbin "pre-2007 versions of zic")); 965*4218Srobbin 966*4218Srobbin return (oadd(eitol(sign) * hh * eitol(SECSPERHOUR), 967*4218Srobbin eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)))); 9680Sstevel@tonic-gate } 9690Sstevel@tonic-gate 9700Sstevel@tonic-gate static void 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 } 1892*4218Srobbin if (!(-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { 1893*4218Srobbin error(gettext("UTC offset out of range")); 1894*4218Srobbin exit(EXIT_FAILURE); 1895*4218Srobbin } 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 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 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 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 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 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 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 * 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 ** 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; 2092*4218Srobbin else { 20930Sstevel@tonic-gate error(gettext( 20940Sstevel@tonic-gate "Odd number of quotation marks")); 2095*4218Srobbin exit(1); 2096*4218Srobbin } 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 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 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 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 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 22880Sstevel@tonic-gate mkdirs(argname) 2289*4218Srobbin 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 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