1*6a4024dcSmillert /* $OpenBSD: zic.c,v 1.27 2024/09/18 17:05:50 millert Exp $ */ 22059035fStedu /* 32059035fStedu ** This file is in the public domain, so clarified as of 42059035fStedu ** 2006-07-17 by Arthur David Olson. 52059035fStedu */ 62059035fStedu 7f0dc9fbaSderaadt #include <sys/types.h> 8f0dc9fbaSderaadt #include <sys/wait.h> 9784122ecStedu #include <sys/stat.h> 10784122ecStedu #include <ctype.h> 114dc71ee2Stedu #include <err.h> 12f0dc9fbaSderaadt #include <errno.h> 13f0dc9fbaSderaadt #include <limits.h> 14f0dc9fbaSderaadt #include <stdio.h> 15f0dc9fbaSderaadt #include <stdlib.h> 16f0dc9fbaSderaadt #include <string.h> 17f0dc9fbaSderaadt #include <stdint.h> 18f0dc9fbaSderaadt #include <unistd.h> 19f0dc9fbaSderaadt #include <time.h> 20784122ecStedu 217341579fStedu #include "tzfile.h" 22f0dc9fbaSderaadt 23f0dc9fbaSderaadt #define TRUE 1 24f0dc9fbaSderaadt #define FALSE 0 25f0dc9fbaSderaadt 26f0dc9fbaSderaadt #define TYPE_SIGNED(type) (((type) -1) < 0) 27f0dc9fbaSderaadt 28f0dc9fbaSderaadt #define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ 29f0dc9fbaSderaadt 30f0dc9fbaSderaadt #define GRANDPARENTED "Local time zone must be set--see zic manual page" 312059035fStedu 322059035fStedu #define ZIC_VERSION '2' 332059035fStedu 342059035fStedu typedef int_fast64_t zic_t; 352059035fStedu 362059035fStedu #ifndef ZIC_MAX_ABBR_LEN_WO_WARN 372059035fStedu #define ZIC_MAX_ABBR_LEN_WO_WARN 6 382059035fStedu #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ 392059035fStedu 402059035fStedu #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 412059035fStedu 422059035fStedu #define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long)) 432059035fStedu #define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */ 442059035fStedu 452059035fStedu #define end(cp, n) (memchr((cp), '\0', (n))) 462059035fStedu 472059035fStedu struct rule { 482059035fStedu const char *r_filename; 492059035fStedu int r_linenum; 502059035fStedu const char *r_name; 512059035fStedu 522059035fStedu int r_loyear; /* for example, 1986 */ 532059035fStedu int r_hiyear; /* for example, 1986 */ 542059035fStedu int r_lowasnum; 552059035fStedu int r_hiwasnum; 562059035fStedu 572059035fStedu int r_month; /* 0..11 */ 582059035fStedu 592059035fStedu int r_dycode; /* see below */ 602059035fStedu int r_dayofmonth; 612059035fStedu int r_wday; 622059035fStedu 632059035fStedu long r_tod; /* time from midnight */ 642059035fStedu int r_todisstd; /* above is standard time if TRUE */ 652059035fStedu /* or wall clock time if FALSE */ 662059035fStedu int r_todisgmt; /* above is GMT if TRUE */ 672059035fStedu /* or local time if FALSE */ 682059035fStedu long r_stdoff; /* offset from standard time */ 692059035fStedu const char *r_abbrvar; /* variable part of abbreviation */ 702059035fStedu 712059035fStedu int r_todo; /* a rule to do (used in outzone) */ 722059035fStedu zic_t r_temp; /* used in outzone */ 732059035fStedu }; 742059035fStedu 752059035fStedu /* 762059035fStedu ** r_dycode r_dayofmonth r_wday 772059035fStedu */ 782059035fStedu 792059035fStedu #define DC_DOM 0 /* 1..31 */ /* unused */ 802059035fStedu #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ 812059035fStedu #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ 822059035fStedu 832059035fStedu struct zone { 842059035fStedu const char *z_filename; 852059035fStedu int z_linenum; 862059035fStedu 872059035fStedu const char *z_name; 882059035fStedu long z_gmtoff; 892059035fStedu const char *z_rule; 902059035fStedu const char *z_format; 91*6a4024dcSmillert char z_format_specifier; 922059035fStedu 932059035fStedu long z_stdoff; 942059035fStedu 952059035fStedu struct rule *z_rules; 962059035fStedu int z_nrules; 972059035fStedu 982059035fStedu struct rule z_untilrule; 992059035fStedu zic_t z_untiltime; 1002059035fStedu }; 1012059035fStedu 1022059035fStedu static void addtt(zic_t starttime, int type); 1032059035fStedu static int addtype(long gmtoff, const char *abbr, int isdst, 1042059035fStedu int ttisstd, int ttisgmt); 1052059035fStedu static void leapadd(zic_t t, int positive, int rolling, int count); 1062059035fStedu static void adjleap(void); 1072059035fStedu static void associate(void); 1082059035fStedu static void convert(long val, char *buf); 1092059035fStedu static void convert64(zic_t val, char *buf); 1102059035fStedu static void dolink(const char *fromfield, const char *tofield); 111*6a4024dcSmillert static void doabbr(char *abbr, size_t size, struct zone const *zp, 1122059035fStedu const char *letters, int isdst, int doquotes); 1132059035fStedu static void eat(const char *name, int num); 114f0dc9fbaSderaadt static void eats(const char *name, int num, const char *rname, int rnum); 1152059035fStedu static long eitol(int i); 1162059035fStedu static void error(const char *message); 1172059035fStedu static char **getfields(char *buf); 118f0dc9fbaSderaadt static long gethms(const char *string, const char *errstrng, int signable); 1192059035fStedu static void infile(const char *filename); 1202059035fStedu static void inleap(char **fields, int nfields); 1212059035fStedu static void inlink(char **fields, int nfields); 1222059035fStedu static void inrule(char **fields, int nfields); 1232059035fStedu static int inzcont(char **fields, int nfields); 1242059035fStedu static int inzone(char **fields, int nfields); 1252059035fStedu static int inzsub(char **fields, int nfields, int iscont); 1262059035fStedu static int is32(zic_t x); 12738cd2fb1Stedu static int itsabbr(const char *abbr, const char *word); 1282059035fStedu static int itsdir(const char *name); 1292059035fStedu static int mkdirs(char *filename); 1302059035fStedu static void newabbr(const char *abbr); 1312059035fStedu static long oadd(long t1, long t2); 1322059035fStedu static void outzone(const struct zone *zp, int ntzones); 1332059035fStedu static void puttzcode(long code, FILE *fp); 1342059035fStedu static void puttzcode64(zic_t code, FILE *fp); 1352059035fStedu static int rcomp(const void *leftp, const void *rightp); 1362059035fStedu static zic_t rpytime(const struct rule *rp, int wantedy); 137f0dc9fbaSderaadt static void rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, 1382059035fStedu const char *typep, const char *monthp, 1392059035fStedu const char *dayp, const char *timep); 1402059035fStedu static int stringoffset(char *result, size_t size, long offset); 1412059035fStedu static int stringrule(char *result, size_t size, const struct rule *rp, 1422059035fStedu long dstoff, long gmtoff); 1432059035fStedu static void stringzone(char *result, size_t size, 1442059035fStedu const struct zone *zp, int ntzones); 1452059035fStedu static void setboundaries(void); 1462059035fStedu static zic_t tadd(zic_t t1, long t2); 147b3cc38c2Stedu static void usage(void); 1482059035fStedu static void writezone(const char *name, const char *string); 1492059035fStedu 150f0dc9fbaSderaadt extern char *__progname; 151f0dc9fbaSderaadt 152*6a4024dcSmillert /* Bound on length of what %z can expand to. */ 153*6a4024dcSmillert enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 }; 154*6a4024dcSmillert 1552059035fStedu static int charcnt; 1562059035fStedu static int errors; 1572059035fStedu static const char *filename; 1582059035fStedu static int leapcnt; 1592059035fStedu static int leapseen; 1602059035fStedu static int leapminyear; 1612059035fStedu static int leapmaxyear; 1622059035fStedu static int linenum; 163*6a4024dcSmillert static int max_abbrvar_len = PERCENT_Z_LEN_BOUND; 1642059035fStedu static int max_format_len; 1652059035fStedu static zic_t max_time; 1662059035fStedu static int max_year; 1672059035fStedu static zic_t min_time; 1682059035fStedu static int min_year; 1692059035fStedu static int noise; 1702059035fStedu static const char *rfilename; 1712059035fStedu static int rlinenum; 1722059035fStedu static int timecnt; 1732059035fStedu static int typecnt; 1742059035fStedu 1752059035fStedu /* 1762059035fStedu ** Line codes. 1772059035fStedu */ 1782059035fStedu 1792059035fStedu #define LC_RULE 0 1802059035fStedu #define LC_ZONE 1 1812059035fStedu #define LC_LINK 2 1822059035fStedu #define LC_LEAP 3 1832059035fStedu 1842059035fStedu /* 1852059035fStedu ** Which fields are which on a Zone line. 1862059035fStedu */ 1872059035fStedu 1882059035fStedu #define ZF_NAME 1 1892059035fStedu #define ZF_GMTOFF 2 1902059035fStedu #define ZF_RULE 3 1912059035fStedu #define ZF_FORMAT 4 1922059035fStedu #define ZF_TILYEAR 5 1932059035fStedu #define ZF_TILMONTH 6 1942059035fStedu #define ZF_TILDAY 7 1952059035fStedu #define ZF_TILTIME 8 1962059035fStedu #define ZONE_MINFIELDS 5 1972059035fStedu #define ZONE_MAXFIELDS 9 1982059035fStedu 1992059035fStedu /* 2002059035fStedu ** Which fields are which on a Zone continuation line. 2012059035fStedu */ 2022059035fStedu 2032059035fStedu #define ZFC_GMTOFF 0 2042059035fStedu #define ZFC_RULE 1 2052059035fStedu #define ZFC_FORMAT 2 2062059035fStedu #define ZFC_TILYEAR 3 2072059035fStedu #define ZFC_TILMONTH 4 2082059035fStedu #define ZFC_TILDAY 5 2092059035fStedu #define ZFC_TILTIME 6 2102059035fStedu #define ZONEC_MINFIELDS 3 2112059035fStedu #define ZONEC_MAXFIELDS 7 2122059035fStedu 2132059035fStedu /* 2142059035fStedu ** Which files are which on a Rule line. 2152059035fStedu */ 2162059035fStedu 2172059035fStedu #define RF_NAME 1 2182059035fStedu #define RF_LOYEAR 2 2192059035fStedu #define RF_HIYEAR 3 2202059035fStedu #define RF_COMMAND 4 2212059035fStedu #define RF_MONTH 5 2222059035fStedu #define RF_DAY 6 2232059035fStedu #define RF_TOD 7 2242059035fStedu #define RF_STDOFF 8 2252059035fStedu #define RF_ABBRVAR 9 2262059035fStedu #define RULE_FIELDS 10 2272059035fStedu 2282059035fStedu /* 2292059035fStedu ** Which fields are which on a Link line. 2302059035fStedu */ 2312059035fStedu 2322059035fStedu #define LF_FROM 1 2332059035fStedu #define LF_TO 2 2342059035fStedu #define LINK_FIELDS 3 2352059035fStedu 2362059035fStedu /* 2372059035fStedu ** Which fields are which on a Leap line. 2382059035fStedu */ 2392059035fStedu 2402059035fStedu #define LP_YEAR 1 2412059035fStedu #define LP_MONTH 2 2422059035fStedu #define LP_DAY 3 2432059035fStedu #define LP_TIME 4 2442059035fStedu #define LP_CORR 5 2452059035fStedu #define LP_ROLL 6 2462059035fStedu #define LEAP_FIELDS 7 2472059035fStedu 2482059035fStedu /* 2492059035fStedu ** Year synonyms. 2502059035fStedu */ 2512059035fStedu 2522059035fStedu #define YR_MINIMUM 0 2532059035fStedu #define YR_MAXIMUM 1 2542059035fStedu #define YR_ONLY 2 2552059035fStedu 2562059035fStedu static struct rule *rules; 2572059035fStedu static int nrules; /* number of rules */ 2582059035fStedu 2592059035fStedu static struct zone *zones; 2602059035fStedu static int nzones; /* number of zones */ 2612059035fStedu 2622059035fStedu struct link { 2632059035fStedu const char *l_filename; 2642059035fStedu int l_linenum; 2652059035fStedu const char *l_from; 2662059035fStedu const char *l_to; 2672059035fStedu }; 2682059035fStedu 2692059035fStedu static struct link *links; 2702059035fStedu static int nlinks; 2712059035fStedu 2722059035fStedu struct lookup { 2732059035fStedu const char *l_word; 2742059035fStedu const int l_value; 2752059035fStedu }; 2762059035fStedu 277f0dc9fbaSderaadt static struct lookup const *byword(const char *string, const struct lookup *lp); 2782059035fStedu 2792059035fStedu static struct lookup const line_codes[] = { 2802059035fStedu { "Rule", LC_RULE }, 2812059035fStedu { "Zone", LC_ZONE }, 2822059035fStedu { "Link", LC_LINK }, 2832059035fStedu { "Leap", LC_LEAP }, 2842059035fStedu { NULL, 0} 2852059035fStedu }; 2862059035fStedu 2872059035fStedu static struct lookup const mon_names[] = { 2882059035fStedu { "January", TM_JANUARY }, 2892059035fStedu { "February", TM_FEBRUARY }, 2902059035fStedu { "March", TM_MARCH }, 2912059035fStedu { "April", TM_APRIL }, 2922059035fStedu { "May", TM_MAY }, 2932059035fStedu { "June", TM_JUNE }, 2942059035fStedu { "July", TM_JULY }, 2952059035fStedu { "August", TM_AUGUST }, 2962059035fStedu { "September", TM_SEPTEMBER }, 2972059035fStedu { "October", TM_OCTOBER }, 2982059035fStedu { "November", TM_NOVEMBER }, 2992059035fStedu { "December", TM_DECEMBER }, 3002059035fStedu { NULL, 0 } 3012059035fStedu }; 3022059035fStedu 3032059035fStedu static struct lookup const wday_names[] = { 3042059035fStedu { "Sunday", TM_SUNDAY }, 3052059035fStedu { "Monday", TM_MONDAY }, 3062059035fStedu { "Tuesday", TM_TUESDAY }, 3072059035fStedu { "Wednesday", TM_WEDNESDAY }, 3082059035fStedu { "Thursday", TM_THURSDAY }, 3092059035fStedu { "Friday", TM_FRIDAY }, 3102059035fStedu { "Saturday", TM_SATURDAY }, 3112059035fStedu { NULL, 0 } 3122059035fStedu }; 3132059035fStedu 3142059035fStedu static struct lookup const lasts[] = { 3152059035fStedu { "last-Sunday", TM_SUNDAY }, 3162059035fStedu { "last-Monday", TM_MONDAY }, 3172059035fStedu { "last-Tuesday", TM_TUESDAY }, 3182059035fStedu { "last-Wednesday", TM_WEDNESDAY }, 3192059035fStedu { "last-Thursday", TM_THURSDAY }, 3202059035fStedu { "last-Friday", TM_FRIDAY }, 3212059035fStedu { "last-Saturday", TM_SATURDAY }, 3222059035fStedu { NULL, 0 } 3232059035fStedu }; 3242059035fStedu 3252059035fStedu static struct lookup const begin_years[] = { 3262059035fStedu { "minimum", YR_MINIMUM }, 3272059035fStedu { "maximum", YR_MAXIMUM }, 3282059035fStedu { NULL, 0 } 3292059035fStedu }; 3302059035fStedu 3312059035fStedu static struct lookup const end_years[] = { 3322059035fStedu { "minimum", YR_MINIMUM }, 3332059035fStedu { "maximum", YR_MAXIMUM }, 3342059035fStedu { "only", YR_ONLY }, 3352059035fStedu { NULL, 0 } 3362059035fStedu }; 3372059035fStedu 3382059035fStedu static struct lookup const leap_types[] = { 3392059035fStedu { "Rolling", TRUE }, 3402059035fStedu { "Stationary", FALSE }, 3412059035fStedu { NULL, 0 } 3422059035fStedu }; 3432059035fStedu 3442059035fStedu static const int len_months[2][MONSPERYEAR] = { 3452059035fStedu { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 3462059035fStedu { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 3472059035fStedu }; 3482059035fStedu 3492059035fStedu static const int len_years[2] = { 3502059035fStedu DAYSPERNYEAR, DAYSPERLYEAR 3512059035fStedu }; 3522059035fStedu 3532059035fStedu static struct attype { 3542059035fStedu zic_t at; 3552059035fStedu unsigned char type; 3562059035fStedu } attypes[TZ_MAX_TIMES]; 357f0dc9fbaSderaadt 3582059035fStedu static long gmtoffs[TZ_MAX_TYPES]; 3592059035fStedu static char isdsts[TZ_MAX_TYPES]; 3602059035fStedu static unsigned char abbrinds[TZ_MAX_TYPES]; 3612059035fStedu static char ttisstds[TZ_MAX_TYPES]; 3622059035fStedu static char ttisgmts[TZ_MAX_TYPES]; 3632059035fStedu static char chars[TZ_MAX_CHARS]; 3642059035fStedu static zic_t trans[TZ_MAX_LEAPS]; 3652059035fStedu static long corr[TZ_MAX_LEAPS]; 3662059035fStedu static char roll[TZ_MAX_LEAPS]; 3672059035fStedu 3682059035fStedu /* 3692059035fStedu ** Memory allocation. 3702059035fStedu */ 3712059035fStedu 37275b1bb69Stedu static void * 373738fad2fStedu memcheck(void *ptr) 3742059035fStedu { 375f0dc9fbaSderaadt if (ptr == NULL) 3764dc71ee2Stedu err(1, "Memory exhausted"); 3772059035fStedu return ptr; 3782059035fStedu } 3792059035fStedu 3802059035fStedu static char * 3812059035fStedu ecatalloc(char *start, const char *tail) 3822059035fStedu { 3832059035fStedu size_t len; 3842059035fStedu char *str; 3852059035fStedu 3862059035fStedu len = strlen(start) + strlen(tail) + 1; 3872059035fStedu str = memcheck(realloc(start, len)); 3882059035fStedu strlcat(str, tail, len); 3892059035fStedu return str; 3902059035fStedu } 3912059035fStedu 3922059035fStedu #define emalloc(size) memcheck(malloc(size)) 393cc917873Sderaadt #define ereallocarray(ptr, nmemb, size) memcheck(reallocarray(ptr, nmemb, size)) 3942059035fStedu #define erealloc(ptr, size) memcheck(realloc((ptr), (size))) 3952059035fStedu #define ecpyalloc(ptr) memcheck(strdup(ptr)) 3962059035fStedu 3972059035fStedu /* 3982059035fStedu ** Error handling. 3992059035fStedu */ 4002059035fStedu 4012059035fStedu static void 402738fad2fStedu eats(const char *name, int num, const char *rname, int rnum) 4032059035fStedu { 4042059035fStedu filename = name; 4052059035fStedu linenum = num; 4062059035fStedu rfilename = rname; 4072059035fStedu rlinenum = rnum; 4082059035fStedu } 4092059035fStedu 4102059035fStedu static void 411738fad2fStedu eat(const char *name, int num) 4122059035fStedu { 41375b1bb69Stedu eats(name, num, NULL, -1); 4142059035fStedu } 4152059035fStedu 4162059035fStedu static void 417738fad2fStedu error(const char *string) 4182059035fStedu { 4192059035fStedu /* 4202059035fStedu ** Match the format of "cc" to allow sh users to 4212059035fStedu ** zic ... 2>&1 | error -t "*" -v 4222059035fStedu ** on BSD systems. 4232059035fStedu */ 424f0dc9fbaSderaadt fprintf(stderr, "\"%s\", line %d: %s", 4252059035fStedu filename, linenum, string); 4262059035fStedu if (rfilename != NULL) 427f0dc9fbaSderaadt fprintf(stderr, " (rule from \"%s\", line %d)", 4282059035fStedu rfilename, rlinenum); 42975b1bb69Stedu fprintf(stderr, "\n"); 4302059035fStedu ++errors; 4312059035fStedu } 4322059035fStedu 4332059035fStedu static void 434738fad2fStedu warning(const char *string) 4352059035fStedu { 4362059035fStedu char *cp; 4372059035fStedu 438f0dc9fbaSderaadt cp = ecpyalloc("warning: "); 4392059035fStedu cp = ecatalloc(cp, string); 4402059035fStedu error(cp); 4412059035fStedu free(cp); 4422059035fStedu --errors; 4432059035fStedu } 4442059035fStedu 4456d42e4cdStedu 4466d42e4cdStedu static const char * 4476d42e4cdStedu scheck(const char *string, const char *format) 4486d42e4cdStedu { 449f0dc9fbaSderaadt const char *fp, *result; 450f0dc9fbaSderaadt char *fbuf, *tp, dummy; 4516d42e4cdStedu int c; 4526d42e4cdStedu 4536d42e4cdStedu result = ""; 4546d42e4cdStedu if (string == NULL || format == NULL) 4556d42e4cdStedu return result; 456cc917873Sderaadt fbuf = reallocarray(NULL, strlen(format) + 2, 2); 4576d42e4cdStedu if (fbuf == NULL) 4586d42e4cdStedu return result; 4596d42e4cdStedu fp = format; 4606d42e4cdStedu tp = fbuf; 4616d42e4cdStedu while ((*tp++ = c = *fp++) != '\0') { 4626d42e4cdStedu if (c != '%') 4636d42e4cdStedu continue; 4646d42e4cdStedu if (*fp == '%') { 4656d42e4cdStedu *tp++ = *fp++; 4666d42e4cdStedu continue; 4676d42e4cdStedu } 4686d42e4cdStedu *tp++ = '*'; 4696d42e4cdStedu if (*fp == '*') 4706d42e4cdStedu ++fp; 4716d42e4cdStedu while (isdigit((unsigned char)*fp)) 4726d42e4cdStedu *tp++ = *fp++; 4736d42e4cdStedu if (*fp == 'l' || *fp == 'h') 4746d42e4cdStedu *tp++ = *fp++; 4756d42e4cdStedu else if (*fp == '[') 476f0dc9fbaSderaadt do { 477f0dc9fbaSderaadt *tp++ = *fp++; 478f0dc9fbaSderaadt } while (*fp != '\0' && *fp != ']'); 4796d42e4cdStedu if ((*tp++ = *fp++) == '\0') 4806d42e4cdStedu break; 4816d42e4cdStedu } 4826d42e4cdStedu *(tp - 1) = '%'; 4836d42e4cdStedu *tp++ = 'c'; 4846d42e4cdStedu *tp = '\0'; 4856d42e4cdStedu if (sscanf(string, fbuf, &dummy) != 1) 4866d42e4cdStedu result = format; 4876d42e4cdStedu free(fbuf); 4886d42e4cdStedu return result; 4896d42e4cdStedu } 4906d42e4cdStedu 4912059035fStedu static void 492b3cc38c2Stedu usage(void) 4932059035fStedu { 494f0dc9fbaSderaadt fprintf(stderr, 495f0dc9fbaSderaadt "usage: %s [-v] [-d directory] [-L leapsecondfilename] [-l timezone]\n" 496ce87e856Smillert "\t\t[-p timezone] [filename ...]\n", 497f0dc9fbaSderaadt __progname); 498b3cc38c2Stedu exit(EXIT_FAILURE); 4992059035fStedu } 5002059035fStedu 5012059035fStedu static const char *psxrules; 5022059035fStedu static const char *lcltime; 5032059035fStedu static const char *directory; 5042059035fStedu static const char *leapsec; 5052059035fStedu 5062059035fStedu int 507738fad2fStedu main(int argc, char **argv) 5082059035fStedu { 509f0dc9fbaSderaadt int i, j, c; 5102059035fStedu 5111e80dbeeSderaadt if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) 5121e80dbeeSderaadt err(1, "pledge"); 5131e80dbeeSderaadt 51475b1bb69Stedu umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); 5152059035fStedu while ((c = getopt(argc, argv, "d:l:p:L:vy:")) != -1) 5162059035fStedu switch (c) { 5172059035fStedu default: 518b3cc38c2Stedu usage(); 5192059035fStedu case 'd': 5202059035fStedu if (directory == NULL) 5212059035fStedu directory = optarg; 522f0dc9fbaSderaadt else 5234dc71ee2Stedu errx(1, "More than one -d option specified"); 5242059035fStedu break; 5252059035fStedu case 'l': 5262059035fStedu if (lcltime == NULL) 5272059035fStedu lcltime = optarg; 528f0dc9fbaSderaadt else 5294dc71ee2Stedu errx(1, "More than one -l option specified"); 5302059035fStedu break; 5312059035fStedu case 'p': 5322059035fStedu if (psxrules == NULL) 5332059035fStedu psxrules = optarg; 534f0dc9fbaSderaadt else 5354dc71ee2Stedu errx(1, "More than one -p option specified"); 5362059035fStedu break; 5372059035fStedu case 'y': 538a188d4d1Smillert warning("ignoring obsolescent option -y"); 5392059035fStedu break; 5402059035fStedu case 'L': 5412059035fStedu if (leapsec == NULL) 5422059035fStedu leapsec = optarg; 543f0dc9fbaSderaadt else 5444dc71ee2Stedu errx(1, "More than one -L option specified"); 5452059035fStedu break; 5462059035fStedu case 'v': 5472059035fStedu noise = TRUE; 5482059035fStedu break; 5492059035fStedu } 5502059035fStedu if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) 551b3cc38c2Stedu usage(); /* usage message by request */ 5522059035fStedu if (directory == NULL) 5532059035fStedu directory = TZDIR; 5542059035fStedu 5552059035fStedu setboundaries(); 5562059035fStedu 5572059035fStedu if (optind < argc && leapsec != NULL) { 5582059035fStedu infile(leapsec); 5592059035fStedu adjleap(); 5602059035fStedu } 5612059035fStedu 5622059035fStedu for (i = optind; i < argc; ++i) 5632059035fStedu infile(argv[i]); 5642059035fStedu if (errors) 5652059035fStedu exit(EXIT_FAILURE); 5662059035fStedu associate(); 5672059035fStedu for (i = 0; i < nzones; i = j) { 5682059035fStedu /* 5692059035fStedu ** Find the next non-continuation zone entry. 5702059035fStedu */ 5712059035fStedu for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) 5722059035fStedu continue; 5732059035fStedu outzone(&zones[i], j - i); 5742059035fStedu } 5752059035fStedu /* 5762059035fStedu ** Make links. 5772059035fStedu */ 5782059035fStedu for (i = 0; i < nlinks; ++i) { 5792059035fStedu eat(links[i].l_filename, links[i].l_linenum); 5802059035fStedu dolink(links[i].l_from, links[i].l_to); 5812059035fStedu if (noise) 5822059035fStedu for (j = 0; j < nlinks; ++j) 5832059035fStedu if (strcmp(links[i].l_to, 5842059035fStedu links[j].l_from) == 0) 585f0dc9fbaSderaadt warning("link to link"); 5862059035fStedu } 5872059035fStedu if (lcltime != NULL) { 5882059035fStedu eat("command line", 1); 5892059035fStedu dolink(lcltime, TZDEFAULT); 5902059035fStedu } 5912059035fStedu if (psxrules != NULL) { 5922059035fStedu eat("command line", 1); 5932059035fStedu dolink(psxrules, TZDEFRULES); 5942059035fStedu } 5952059035fStedu return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 5962059035fStedu } 5972059035fStedu 5982059035fStedu static void 599738fad2fStedu dolink(const char *fromfield, const char *tofield) 6002059035fStedu { 601f0dc9fbaSderaadt char *fromname, *toname; 6022059035fStedu 6032059035fStedu if (fromfield[0] == '/') 6042059035fStedu fromname = ecpyalloc(fromfield); 6052059035fStedu else { 6062059035fStedu fromname = ecpyalloc(directory); 6072059035fStedu fromname = ecatalloc(fromname, "/"); 6082059035fStedu fromname = ecatalloc(fromname, fromfield); 6092059035fStedu } 6102059035fStedu if (tofield[0] == '/') 6112059035fStedu toname = ecpyalloc(tofield); 6122059035fStedu else { 6132059035fStedu toname = ecpyalloc(directory); 6142059035fStedu toname = ecatalloc(toname, "/"); 6152059035fStedu toname = ecatalloc(toname, tofield); 6162059035fStedu } 6172059035fStedu /* 6182059035fStedu ** We get to be careful here since 6192059035fStedu ** there's a fair chance of root running us. 6202059035fStedu */ 6212059035fStedu if (!itsdir(toname)) 62275b1bb69Stedu remove(toname); 6232059035fStedu if (link(fromname, toname) != 0) { 6242059035fStedu int result; 6252059035fStedu 6262059035fStedu if (mkdirs(toname) != 0) 6272059035fStedu exit(EXIT_FAILURE); 6282059035fStedu 6292059035fStedu result = link(fromname, toname); 6302059035fStedu if (result != 0 && errno == EXDEV) 6312059035fStedu result = symlink(fromname, toname); 632f0dc9fbaSderaadt if (result != 0) 6334dc71ee2Stedu err(1, "Can't link from %s to %s", fromname, toname); 6342059035fStedu } 6352059035fStedu free(fromname); 6362059035fStedu free(toname); 6372059035fStedu } 6382059035fStedu 6392059035fStedu #define TIME_T_BITS_IN_FILE 64 6402059035fStedu 6412059035fStedu static void 6422059035fStedu setboundaries(void) 6432059035fStedu { 64466e58127Stedu int i; 6452059035fStedu 6462059035fStedu min_time = -1; 6472059035fStedu for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) 6482059035fStedu min_time *= 2; 6492059035fStedu max_time = -(min_time + 1); 6502059035fStedu } 6512059035fStedu 6522059035fStedu static int 653738fad2fStedu itsdir(const char *name) 6542059035fStedu { 65566e58127Stedu char *myname; 65666e58127Stedu int accres; 6572059035fStedu 6582059035fStedu myname = ecpyalloc(name); 6592059035fStedu myname = ecatalloc(myname, "/."); 6602059035fStedu accres = access(myname, F_OK); 6612059035fStedu free(myname); 6622059035fStedu return accres == 0; 6632059035fStedu } 6642059035fStedu 6652059035fStedu /* 6662059035fStedu ** Associate sets of rules with zones. 6672059035fStedu */ 6682059035fStedu 6692059035fStedu /* 6702059035fStedu ** Sort by rule name. 6712059035fStedu */ 6722059035fStedu 6732059035fStedu static int 674738fad2fStedu rcomp(const void *cp1, const void *cp2) 6752059035fStedu { 6762059035fStedu return strcmp(((const struct rule *) cp1)->r_name, 6772059035fStedu ((const struct rule *) cp2)->r_name); 6782059035fStedu } 6792059035fStedu 6802059035fStedu static void 6812059035fStedu associate(void) 6822059035fStedu { 68366e58127Stedu struct zone *zp; 68466e58127Stedu struct rule *rp; 685f0dc9fbaSderaadt int base, out, i, j; 6862059035fStedu 6872059035fStedu if (nrules != 0) { 68875b1bb69Stedu qsort(rules, nrules, sizeof *rules, rcomp); 6892059035fStedu for (i = 0; i < nrules - 1; ++i) { 6902059035fStedu if (strcmp(rules[i].r_name, 6912059035fStedu rules[i + 1].r_name) != 0) 6922059035fStedu continue; 6932059035fStedu if (strcmp(rules[i].r_filename, 6942059035fStedu rules[i + 1].r_filename) == 0) 6952059035fStedu continue; 6962059035fStedu eat(rules[i].r_filename, rules[i].r_linenum); 697f0dc9fbaSderaadt warning("same rule name in multiple files"); 6982059035fStedu eat(rules[i + 1].r_filename, rules[i + 1].r_linenum); 699f0dc9fbaSderaadt warning("same rule name in multiple files"); 7002059035fStedu for (j = i + 2; j < nrules; ++j) { 7012059035fStedu if (strcmp(rules[i].r_name, 7022059035fStedu rules[j].r_name) != 0) 7032059035fStedu break; 7042059035fStedu if (strcmp(rules[i].r_filename, 7052059035fStedu rules[j].r_filename) == 0) 7062059035fStedu continue; 7072059035fStedu if (strcmp(rules[i + 1].r_filename, 7082059035fStedu rules[j].r_filename) == 0) 7092059035fStedu continue; 7102059035fStedu break; 7112059035fStedu } 7122059035fStedu i = j - 1; 7132059035fStedu } 7142059035fStedu } 7152059035fStedu for (i = 0; i < nzones; ++i) { 7162059035fStedu zp = &zones[i]; 7172059035fStedu zp->z_rules = NULL; 7182059035fStedu zp->z_nrules = 0; 7192059035fStedu } 7202059035fStedu for (base = 0; base < nrules; base = out) { 7212059035fStedu rp = &rules[base]; 7222059035fStedu for (out = base + 1; out < nrules; ++out) 7232059035fStedu if (strcmp(rp->r_name, rules[out].r_name) != 0) 7242059035fStedu break; 7252059035fStedu for (i = 0; i < nzones; ++i) { 7262059035fStedu zp = &zones[i]; 7272059035fStedu if (strcmp(zp->z_rule, rp->r_name) != 0) 7282059035fStedu continue; 7292059035fStedu zp->z_rules = rp; 7302059035fStedu zp->z_nrules = out - base; 7312059035fStedu } 7322059035fStedu } 7332059035fStedu for (i = 0; i < nzones; ++i) { 7342059035fStedu zp = &zones[i]; 7352059035fStedu if (zp->z_nrules == 0) { 7362059035fStedu /* 7372059035fStedu ** Maybe we have a local standard time offset. 7382059035fStedu */ 7392059035fStedu eat(zp->z_filename, zp->z_linenum); 740f0dc9fbaSderaadt zp->z_stdoff = gethms(zp->z_rule, "unruly zone", 7412059035fStedu TRUE); 7422059035fStedu /* 7432059035fStedu ** Note, though, that if there's no rule, 7442059035fStedu ** a '%s' in the format is a bad thing. 7452059035fStedu */ 746*6a4024dcSmillert if (zp->z_format_specifier == 's') 747f0dc9fbaSderaadt error("%s in ruleless zone"); 7482059035fStedu } 7492059035fStedu } 7502059035fStedu if (errors) 7512059035fStedu exit(EXIT_FAILURE); 7522059035fStedu } 7532059035fStedu 7542059035fStedu static void 755738fad2fStedu infile(const char *name) 7562059035fStedu { 75766e58127Stedu FILE *fp; 758f0dc9fbaSderaadt char **fields, *cp; 75966e58127Stedu const struct lookup *lp; 760f0dc9fbaSderaadt int nfields, wantcont, num; 7612059035fStedu char buf[BUFSIZ]; 7622059035fStedu 7632059035fStedu if (strcmp(name, "-") == 0) { 764f0dc9fbaSderaadt name = "standard input"; 7652059035fStedu fp = stdin; 766f0dc9fbaSderaadt } else if ((fp = fopen(name, "r")) == NULL) 7674dc71ee2Stedu err(1, "Can't open %s", name); 7682059035fStedu wantcont = FALSE; 7692059035fStedu for (num = 1; ; ++num) { 7702059035fStedu eat(name, num); 77175b1bb69Stedu if (fgets(buf, sizeof buf, fp) != buf) 7722059035fStedu break; 7732059035fStedu cp = strchr(buf, '\n'); 7742059035fStedu if (cp == NULL) { 775f0dc9fbaSderaadt error("line too long"); 7762059035fStedu exit(EXIT_FAILURE); 7772059035fStedu } 7782059035fStedu *cp = '\0'; 7792059035fStedu fields = getfields(buf); 7802059035fStedu nfields = 0; 7812059035fStedu while (fields[nfields] != NULL) { 7822059035fStedu static char nada; 7832059035fStedu 7842059035fStedu if (strcmp(fields[nfields], "-") == 0) 7852059035fStedu fields[nfields] = &nada; 7862059035fStedu ++nfields; 7872059035fStedu } 7882059035fStedu if (nfields == 0) { 7892059035fStedu /* nothing to do */ 7902059035fStedu } else if (wantcont) { 7912059035fStedu wantcont = inzcont(fields, nfields); 7922059035fStedu } else { 7932059035fStedu lp = byword(fields[0], line_codes); 7942059035fStedu if (lp == NULL) 795f0dc9fbaSderaadt error("input line of unknown type"); 7962059035fStedu else switch ((int) (lp->l_value)) { 7972059035fStedu case LC_RULE: 7982059035fStedu inrule(fields, nfields); 7992059035fStedu wantcont = FALSE; 8002059035fStedu break; 8012059035fStedu case LC_ZONE: 8022059035fStedu wantcont = inzone(fields, nfields); 8032059035fStedu break; 8042059035fStedu case LC_LINK: 8052059035fStedu inlink(fields, nfields); 8062059035fStedu wantcont = FALSE; 8072059035fStedu break; 8082059035fStedu case LC_LEAP: 8092059035fStedu if (name != leapsec) 81075b1bb69Stedu fprintf(stderr, 811f0dc9fbaSderaadt "%s: Leap line in non leap seconds file %s\n", 812f0dc9fbaSderaadt __progname, name); 8134dc71ee2Stedu /* no exit? */ 8144dc71ee2Stedu else 8154dc71ee2Stedu inleap(fields, nfields); 8162059035fStedu wantcont = FALSE; 8172059035fStedu break; 8182059035fStedu default: /* "cannot happen" */ 8194dc71ee2Stedu errx(1, "panic: Invalid l_value %d", lp->l_value); 8202059035fStedu } 8212059035fStedu } 82275b1bb69Stedu free(fields); 8232059035fStedu } 824f0dc9fbaSderaadt if (ferror(fp)) 8254dc71ee2Stedu errx(1, "Error reading %s", filename); 826f0dc9fbaSderaadt if (fp != stdin && fclose(fp)) 8274dc71ee2Stedu err(1, "Error closing %s", filename); 8282059035fStedu if (wantcont) 829f0dc9fbaSderaadt error("expected continuation line not found"); 8302059035fStedu } 8312059035fStedu 8322059035fStedu /* 8332059035fStedu ** Convert a string of one of the forms 8342059035fStedu ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss 8352059035fStedu ** into a number of seconds. 8362059035fStedu ** A null string maps to zero. 8372059035fStedu ** Call error with errstring and return zero on errors. 8382059035fStedu */ 8392059035fStedu 8402059035fStedu static long 841738fad2fStedu gethms(const char *string, const char *errstring, int signable) 8422059035fStedu { 8432059035fStedu long hh; 8442059035fStedu int mm, ss, sign; 8452059035fStedu 8462059035fStedu if (string == NULL || *string == '\0') 8472059035fStedu return 0; 8482059035fStedu if (!signable) 8492059035fStedu sign = 1; 8502059035fStedu else if (*string == '-') { 8512059035fStedu sign = -1; 8522059035fStedu ++string; 853f0dc9fbaSderaadt } else 854f0dc9fbaSderaadt sign = 1; 8552059035fStedu if (sscanf(string, scheck(string, "%ld"), &hh) == 1) 8562059035fStedu mm = ss = 0; 8572059035fStedu else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2) 8582059035fStedu ss = 0; 8592059035fStedu else if (sscanf(string, scheck(string, "%ld:%d:%d"), 8602059035fStedu &hh, &mm, &ss) != 3) { 8612059035fStedu error(errstring); 8622059035fStedu return 0; 8632059035fStedu } 8642059035fStedu if (hh < 0 || 8652059035fStedu mm < 0 || mm >= MINSPERHOUR || 8662059035fStedu ss < 0 || ss > SECSPERMIN) { 8672059035fStedu error(errstring); 8682059035fStedu return 0; 8692059035fStedu } 8702059035fStedu if (LONG_MAX / SECSPERHOUR < hh) { 871f0dc9fbaSderaadt error("time overflow"); 8722059035fStedu return 0; 8732059035fStedu } 8742059035fStedu return oadd(eitol(sign) * hh * eitol(SECSPERHOUR), 8752059035fStedu eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))); 8762059035fStedu } 8772059035fStedu 8782059035fStedu static void 879738fad2fStedu inrule(char **fields, int nfields) 8802059035fStedu { 8812059035fStedu static struct rule r; 8822059035fStedu 8832059035fStedu if (nfields != RULE_FIELDS) { 884f0dc9fbaSderaadt error("wrong number of fields on Rule line"); 8852059035fStedu return; 8862059035fStedu } 8872059035fStedu if (*fields[RF_NAME] == '\0') { 888f0dc9fbaSderaadt error("nameless rule"); 8892059035fStedu return; 8902059035fStedu } 8912059035fStedu r.r_filename = filename; 8922059035fStedu r.r_linenum = linenum; 893f0dc9fbaSderaadt r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE); 8942059035fStedu rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], 8952059035fStedu fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); 8962059035fStedu r.r_name = ecpyalloc(fields[RF_NAME]); 8972059035fStedu r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); 8982059035fStedu if (max_abbrvar_len < strlen(r.r_abbrvar)) 8992059035fStedu max_abbrvar_len = strlen(r.r_abbrvar); 900cc917873Sderaadt rules = ereallocarray(rules, nrules + 1, sizeof *rules); 9012059035fStedu rules[nrules++] = r; 9022059035fStedu } 9032059035fStedu 9042059035fStedu static int 905738fad2fStedu inzone(char **fields, int nfields) 9062059035fStedu { 90766e58127Stedu int i; 9082059035fStedu static char *buf; 9092059035fStedu size_t len; 9102059035fStedu 9112059035fStedu if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { 912f0dc9fbaSderaadt error("wrong number of fields on Zone line"); 9132059035fStedu return FALSE; 9142059035fStedu } 9152059035fStedu if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { 9162059035fStedu len = 132 + strlen(TZDEFAULT); 9172059035fStedu buf = erealloc(buf, len); 918f0dc9fbaSderaadt snprintf(buf, len, 919f0dc9fbaSderaadt "\"Zone %s\" line and -l option are mutually exclusive", 9202059035fStedu TZDEFAULT); 9212059035fStedu error(buf); 9222059035fStedu return FALSE; 9232059035fStedu } 9242059035fStedu if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { 9252059035fStedu len = 132 + strlen(TZDEFRULES); 9262059035fStedu buf = erealloc(buf, len); 927f0dc9fbaSderaadt snprintf(buf, len, 928f0dc9fbaSderaadt "\"Zone %s\" line and -p option are mutually exclusive", 9292059035fStedu TZDEFRULES); 9302059035fStedu error(buf); 9312059035fStedu return FALSE; 9322059035fStedu } 9332059035fStedu for (i = 0; i < nzones; ++i) 9342059035fStedu if (zones[i].z_name != NULL && 9352059035fStedu strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { 9362059035fStedu len = 132 + strlen(fields[ZF_NAME]) + 9372059035fStedu strlen(zones[i].z_filename); 9382059035fStedu buf = erealloc(buf, len); 93975b1bb69Stedu snprintf(buf, len, 940f0dc9fbaSderaadt "duplicate zone name %s (file \"%s\", line %d)", 9412059035fStedu fields[ZF_NAME], 9422059035fStedu zones[i].z_filename, 9432059035fStedu zones[i].z_linenum); 9442059035fStedu error(buf); 9452059035fStedu return FALSE; 9462059035fStedu } 9472059035fStedu return inzsub(fields, nfields, FALSE); 9482059035fStedu } 9492059035fStedu 9502059035fStedu static int 951738fad2fStedu inzcont(char **fields, int nfields) 9522059035fStedu { 9532059035fStedu if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { 954f0dc9fbaSderaadt error("wrong number of fields on Zone continuation line"); 9552059035fStedu return FALSE; 9562059035fStedu } 9572059035fStedu return inzsub(fields, nfields, TRUE); 9582059035fStedu } 9592059035fStedu 9602059035fStedu static int 961738fad2fStedu inzsub(char **fields, int nfields, int iscont) 9622059035fStedu { 96366e58127Stedu char *cp; 964*6a4024dcSmillert char *cp1; 9652059035fStedu static struct zone z; 96666e58127Stedu int i_gmtoff, i_rule, i_format; 96766e58127Stedu int i_untilyear, i_untilmonth; 96866e58127Stedu int i_untilday, i_untiltime; 96966e58127Stedu int hasuntil; 9702059035fStedu 9712059035fStedu if (iscont) { 9722059035fStedu i_gmtoff = ZFC_GMTOFF; 9732059035fStedu i_rule = ZFC_RULE; 9742059035fStedu i_format = ZFC_FORMAT; 9752059035fStedu i_untilyear = ZFC_TILYEAR; 9762059035fStedu i_untilmonth = ZFC_TILMONTH; 9772059035fStedu i_untilday = ZFC_TILDAY; 9782059035fStedu i_untiltime = ZFC_TILTIME; 9792059035fStedu z.z_name = NULL; 9802059035fStedu } else { 9812059035fStedu i_gmtoff = ZF_GMTOFF; 9822059035fStedu i_rule = ZF_RULE; 9832059035fStedu i_format = ZF_FORMAT; 9842059035fStedu i_untilyear = ZF_TILYEAR; 9852059035fStedu i_untilmonth = ZF_TILMONTH; 9862059035fStedu i_untilday = ZF_TILDAY; 9872059035fStedu i_untiltime = ZF_TILTIME; 9882059035fStedu z.z_name = ecpyalloc(fields[ZF_NAME]); 9892059035fStedu } 9902059035fStedu z.z_filename = filename; 9912059035fStedu z.z_linenum = linenum; 992f0dc9fbaSderaadt z.z_gmtoff = gethms(fields[i_gmtoff], "invalid UTC offset", TRUE); 9932059035fStedu if ((cp = strchr(fields[i_format], '%')) != 0) { 994*6a4024dcSmillert if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') 995*6a4024dcSmillert || strchr(fields[i_format], '/')) { 996f0dc9fbaSderaadt error("invalid abbreviation format"); 9972059035fStedu return FALSE; 9982059035fStedu } 9992059035fStedu } 10002059035fStedu z.z_rule = ecpyalloc(fields[i_rule]); 1001*6a4024dcSmillert z.z_format = cp1 = ecpyalloc(fields[i_format]); 1002*6a4024dcSmillert z.z_format_specifier = cp ? *cp : '\0'; 1003*6a4024dcSmillert if (z.z_format_specifier == 'z') { 1004*6a4024dcSmillert if (noise) { 1005*6a4024dcSmillert warning("format '%%z' not handled by pre-2015 versions " 1006*6a4024dcSmillert "of zic"); 1007*6a4024dcSmillert } 1008*6a4024dcSmillert cp1[cp - fields[i_format]] = 's'; 1009*6a4024dcSmillert } 10102059035fStedu if (max_format_len < strlen(z.z_format)) 10112059035fStedu max_format_len = strlen(z.z_format); 10122059035fStedu hasuntil = nfields > i_untilyear; 10132059035fStedu if (hasuntil) { 10142059035fStedu z.z_untilrule.r_filename = filename; 10152059035fStedu z.z_untilrule.r_linenum = linenum; 10162059035fStedu rulesub(&z.z_untilrule, 10172059035fStedu fields[i_untilyear], 10182059035fStedu "only", 10192059035fStedu "", 10202059035fStedu (nfields > i_untilmonth) ? 10212059035fStedu fields[i_untilmonth] : "Jan", 10222059035fStedu (nfields > i_untilday) ? fields[i_untilday] : "1", 10232059035fStedu (nfields > i_untiltime) ? fields[i_untiltime] : "0"); 10242059035fStedu z.z_untiltime = rpytime(&z.z_untilrule, 10252059035fStedu z.z_untilrule.r_loyear); 10262059035fStedu if (iscont && nzones > 0 && 10272059035fStedu z.z_untiltime > min_time && 10282059035fStedu z.z_untiltime < max_time && 10292059035fStedu zones[nzones - 1].z_untiltime > min_time && 10302059035fStedu zones[nzones - 1].z_untiltime < max_time && 10312059035fStedu zones[nzones - 1].z_untiltime >= z.z_untiltime) { 1032f0dc9fbaSderaadt error("Zone continuation line end time is not after end time of previous line"); 10332059035fStedu return FALSE; 10342059035fStedu } 10352059035fStedu } 1036cc917873Sderaadt zones = ereallocarray(zones, nzones + 1, sizeof *zones); 10372059035fStedu zones[nzones++] = z; 10382059035fStedu /* 10392059035fStedu ** If there was an UNTIL field on this line, 10402059035fStedu ** there's more information about the zone on the next line. 10412059035fStedu */ 10422059035fStedu return hasuntil; 10432059035fStedu } 10442059035fStedu 10452059035fStedu static void 1046738fad2fStedu inleap(char **fields, int nfields) 10472059035fStedu { 104866e58127Stedu const char *cp; 104966e58127Stedu const struct lookup *lp; 105066e58127Stedu int i, j; 10512059035fStedu int year, month, day; 10522059035fStedu long dayoff, tod; 10532059035fStedu zic_t t; 10542059035fStedu 10552059035fStedu if (nfields != LEAP_FIELDS) { 1056f0dc9fbaSderaadt error("wrong number of fields on Leap line"); 10572059035fStedu return; 10582059035fStedu } 10592059035fStedu dayoff = 0; 10602059035fStedu cp = fields[LP_YEAR]; 10612059035fStedu if (sscanf(cp, scheck(cp, "%d"), &year) != 1) { 10622059035fStedu /* 10632059035fStedu ** Leapin' Lizards! 10642059035fStedu */ 1065f0dc9fbaSderaadt error("invalid leaping year"); 10662059035fStedu return; 10672059035fStedu } 10682059035fStedu if (!leapseen || leapmaxyear < year) 10692059035fStedu leapmaxyear = year; 10702059035fStedu if (!leapseen || leapminyear > year) 10712059035fStedu leapminyear = year; 10722059035fStedu leapseen = TRUE; 10732059035fStedu j = EPOCH_YEAR; 10742059035fStedu while (j != year) { 10752059035fStedu if (year > j) { 10762059035fStedu i = len_years[isleap(j)]; 10772059035fStedu ++j; 10782059035fStedu } else { 10792059035fStedu --j; 10802059035fStedu i = -len_years[isleap(j)]; 10812059035fStedu } 10822059035fStedu dayoff = oadd(dayoff, eitol(i)); 10832059035fStedu } 10842059035fStedu if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { 1085f0dc9fbaSderaadt error("invalid month name"); 10862059035fStedu return; 10872059035fStedu } 10882059035fStedu month = lp->l_value; 10892059035fStedu j = TM_JANUARY; 10902059035fStedu while (j != month) { 10912059035fStedu i = len_months[isleap(year)][j]; 10922059035fStedu dayoff = oadd(dayoff, eitol(i)); 10932059035fStedu ++j; 10942059035fStedu } 10952059035fStedu cp = fields[LP_DAY]; 10962059035fStedu if (sscanf(cp, scheck(cp, "%d"), &day) != 1 || 10972059035fStedu day <= 0 || day > len_months[isleap(year)][month]) { 1098f0dc9fbaSderaadt error("invalid day of month"); 10992059035fStedu return; 11002059035fStedu } 11012059035fStedu dayoff = oadd(dayoff, eitol(day - 1)); 11022059035fStedu if (dayoff < 0 && !TYPE_SIGNED(zic_t)) { 1103f0dc9fbaSderaadt error("time before zero"); 11042059035fStedu return; 11052059035fStedu } 11062059035fStedu if (dayoff < min_time / SECSPERDAY) { 1107f0dc9fbaSderaadt error("time too small"); 11082059035fStedu return; 11092059035fStedu } 11102059035fStedu if (dayoff > max_time / SECSPERDAY) { 1111f0dc9fbaSderaadt error("time too large"); 11122059035fStedu return; 11132059035fStedu } 11142059035fStedu t = (zic_t) dayoff * SECSPERDAY; 1115f0dc9fbaSderaadt tod = gethms(fields[LP_TIME], "invalid time of day", FALSE); 11162059035fStedu cp = fields[LP_CORR]; 11172059035fStedu { 111866e58127Stedu int positive; 11192059035fStedu int count; 11202059035fStedu 11212059035fStedu if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ 11222059035fStedu positive = FALSE; 11232059035fStedu count = 1; 11242059035fStedu } else if (strcmp(cp, "--") == 0) { 11252059035fStedu positive = FALSE; 11262059035fStedu count = 2; 11272059035fStedu } else if (strcmp(cp, "+") == 0) { 11282059035fStedu positive = TRUE; 11292059035fStedu count = 1; 11302059035fStedu } else if (strcmp(cp, "++") == 0) { 11312059035fStedu positive = TRUE; 11322059035fStedu count = 2; 11332059035fStedu } else { 1134f0dc9fbaSderaadt error("illegal CORRECTION field on Leap line"); 11352059035fStedu return; 11362059035fStedu } 11372059035fStedu if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { 1138f0dc9fbaSderaadt error("illegal Rolling/Stationary field on Leap line"); 11392059035fStedu return; 11402059035fStedu } 11412059035fStedu leapadd(tadd(t, tod), positive, lp->l_value, count); 11422059035fStedu } 11432059035fStedu } 11442059035fStedu 11452059035fStedu static void 1146738fad2fStedu inlink(char **fields, int nfields) 11472059035fStedu { 11482059035fStedu struct link l; 11492059035fStedu 11502059035fStedu if (nfields != LINK_FIELDS) { 1151f0dc9fbaSderaadt error("wrong number of fields on Link line"); 11522059035fStedu return; 11532059035fStedu } 11542059035fStedu if (*fields[LF_FROM] == '\0') { 1155f0dc9fbaSderaadt error("blank FROM field on Link line"); 11562059035fStedu return; 11572059035fStedu } 11582059035fStedu if (*fields[LF_TO] == '\0') { 1159f0dc9fbaSderaadt error("blank TO field on Link line"); 11602059035fStedu return; 11612059035fStedu } 11622059035fStedu l.l_filename = filename; 11632059035fStedu l.l_linenum = linenum; 11642059035fStedu l.l_from = ecpyalloc(fields[LF_FROM]); 11652059035fStedu l.l_to = ecpyalloc(fields[LF_TO]); 1166cc917873Sderaadt links = ereallocarray(links, nlinks + 1, sizeof *links); 11672059035fStedu links[nlinks++] = l; 11682059035fStedu } 11692059035fStedu 11702059035fStedu static void 1171f0dc9fbaSderaadt rulesub(struct rule * const rp, const char * const loyearp, 1172f0dc9fbaSderaadt const char * const hiyearp, const char * const typep, 1173f0dc9fbaSderaadt const char * const monthp, const char * const dayp, 1174f0dc9fbaSderaadt const char * const timep) 11752059035fStedu { 117666e58127Stedu const struct lookup *lp; 117766e58127Stedu const char *cp; 1178f0dc9fbaSderaadt char *dp, *ep; 11792059035fStedu 11802059035fStedu if ((lp = byword(monthp, mon_names)) == NULL) { 1181f0dc9fbaSderaadt error("invalid month name"); 11822059035fStedu return; 11832059035fStedu } 11842059035fStedu rp->r_month = lp->l_value; 11852059035fStedu rp->r_todisstd = FALSE; 11862059035fStedu rp->r_todisgmt = FALSE; 11872059035fStedu dp = ecpyalloc(timep); 11882059035fStedu if (*dp != '\0') { 11892059035fStedu ep = dp + strlen(dp) - 1; 1190fb356290Stedu switch (tolower((unsigned char)*ep)) { 11912059035fStedu case 's': /* Standard */ 11922059035fStedu rp->r_todisstd = TRUE; 11932059035fStedu rp->r_todisgmt = FALSE; 11942059035fStedu *ep = '\0'; 11952059035fStedu break; 11962059035fStedu case 'w': /* Wall */ 11972059035fStedu rp->r_todisstd = FALSE; 11982059035fStedu rp->r_todisgmt = FALSE; 11992059035fStedu *ep = '\0'; 12002059035fStedu break; 12012059035fStedu case 'g': /* Greenwich */ 12022059035fStedu case 'u': /* Universal */ 12032059035fStedu case 'z': /* Zulu */ 12042059035fStedu rp->r_todisstd = TRUE; 12052059035fStedu rp->r_todisgmt = TRUE; 12062059035fStedu *ep = '\0'; 12072059035fStedu break; 12082059035fStedu } 12092059035fStedu } 1210f0dc9fbaSderaadt rp->r_tod = gethms(dp, "invalid time of day", FALSE); 12112059035fStedu free(dp); 12122059035fStedu /* 12132059035fStedu ** Year work. 12142059035fStedu */ 12152059035fStedu cp = loyearp; 12162059035fStedu lp = byword(cp, begin_years); 12172059035fStedu rp->r_lowasnum = lp == NULL; 12182059035fStedu if (!rp->r_lowasnum) switch ((int) lp->l_value) { 12192059035fStedu case YR_MINIMUM: 12202059035fStedu rp->r_loyear = INT_MIN; 12212059035fStedu break; 12222059035fStedu case YR_MAXIMUM: 12232059035fStedu rp->r_loyear = INT_MAX; 12242059035fStedu break; 12252059035fStedu default: /* "cannot happen" */ 12264dc71ee2Stedu errx(1, "panic: Invalid l_value %d", lp->l_value); 12272059035fStedu } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { 1228f0dc9fbaSderaadt error("invalid starting year"); 12292059035fStedu return; 12302059035fStedu } 12312059035fStedu cp = hiyearp; 12322059035fStedu lp = byword(cp, end_years); 12332059035fStedu rp->r_hiwasnum = lp == NULL; 12342059035fStedu if (!rp->r_hiwasnum) switch ((int) lp->l_value) { 12352059035fStedu case YR_MINIMUM: 12362059035fStedu rp->r_hiyear = INT_MIN; 12372059035fStedu break; 12382059035fStedu case YR_MAXIMUM: 12392059035fStedu rp->r_hiyear = INT_MAX; 12402059035fStedu break; 12412059035fStedu case YR_ONLY: 12422059035fStedu rp->r_hiyear = rp->r_loyear; 12432059035fStedu break; 12442059035fStedu default: /* "cannot happen" */ 12454dc71ee2Stedu errx(1, "panic: Invalid l_value %d", lp->l_value); 12462059035fStedu } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) { 1247f0dc9fbaSderaadt error("invalid ending year"); 12482059035fStedu return; 12492059035fStedu } 12502059035fStedu if (rp->r_loyear > rp->r_hiyear) { 1251f0dc9fbaSderaadt error("starting year greater than ending year"); 12522059035fStedu return; 12532059035fStedu } 1254a188d4d1Smillert if (*typep != '\0') { 12552059035fStedu if (rp->r_loyear == rp->r_hiyear) { 1256f0dc9fbaSderaadt error("typed single year"); 12572059035fStedu return; 12582059035fStedu } 1259a188d4d1Smillert warning("year type is obsolete; use \"-\" instead"); 12602059035fStedu } 12612059035fStedu /* 12622059035fStedu ** Day work. 12632059035fStedu ** Accept things such as: 12642059035fStedu ** 1 12652059035fStedu ** last-Sunday 12662059035fStedu ** Sun<=20 12672059035fStedu ** Sun>=7 12682059035fStedu */ 12692059035fStedu dp = ecpyalloc(dayp); 12702059035fStedu if ((lp = byword(dp, lasts)) != NULL) { 12712059035fStedu rp->r_dycode = DC_DOWLEQ; 12722059035fStedu rp->r_wday = lp->l_value; 12732059035fStedu rp->r_dayofmonth = len_months[1][rp->r_month]; 12742059035fStedu } else { 12752059035fStedu if ((ep = strchr(dp, '<')) != 0) 12762059035fStedu rp->r_dycode = DC_DOWLEQ; 12772059035fStedu else if ((ep = strchr(dp, '>')) != 0) 12782059035fStedu rp->r_dycode = DC_DOWGEQ; 12792059035fStedu else { 12802059035fStedu ep = dp; 12812059035fStedu rp->r_dycode = DC_DOM; 12822059035fStedu } 12832059035fStedu if (rp->r_dycode != DC_DOM) { 12842059035fStedu *ep++ = 0; 12852059035fStedu if (*ep++ != '=') { 1286f0dc9fbaSderaadt error("invalid day of month"); 12872059035fStedu free(dp); 12882059035fStedu return; 12892059035fStedu } 12902059035fStedu if ((lp = byword(dp, wday_names)) == NULL) { 1291f0dc9fbaSderaadt error("invalid weekday name"); 12922059035fStedu free(dp); 12932059035fStedu return; 12942059035fStedu } 12952059035fStedu rp->r_wday = lp->l_value; 12962059035fStedu } 12972059035fStedu if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 || 12982059035fStedu rp->r_dayofmonth <= 0 || 12992059035fStedu (rp->r_dayofmonth > len_months[1][rp->r_month])) { 1300f0dc9fbaSderaadt error("invalid day of month"); 13012059035fStedu free(dp); 13022059035fStedu return; 13032059035fStedu } 13042059035fStedu } 13052059035fStedu free(dp); 13062059035fStedu } 13072059035fStedu 13082059035fStedu static void 1309738fad2fStedu convert(long val, char *buf) 13102059035fStedu { 131166e58127Stedu int i; 131266e58127Stedu int shift; 13132059035fStedu 13142059035fStedu for (i = 0, shift = 24; i < 4; ++i, shift -= 8) 13152059035fStedu buf[i] = val >> shift; 13162059035fStedu } 13172059035fStedu 13182059035fStedu static void 1319738fad2fStedu convert64(zic_t val, char *buf) 13202059035fStedu { 132166e58127Stedu int i; 132266e58127Stedu int shift; 13232059035fStedu 13242059035fStedu for (i = 0, shift = 56; i < 8; ++i, shift -= 8) 13252059035fStedu buf[i] = val >> shift; 13262059035fStedu } 13272059035fStedu 13282059035fStedu static void 1329738fad2fStedu puttzcode(long val, FILE *fp) 13302059035fStedu { 13312059035fStedu char buf[4]; 13322059035fStedu 13332059035fStedu convert(val, buf); 133475b1bb69Stedu fwrite(buf, sizeof buf, 1, fp); 13352059035fStedu } 13362059035fStedu 13372059035fStedu static void 1338738fad2fStedu puttzcode64(zic_t val, FILE *fp) 13392059035fStedu { 13402059035fStedu char buf[8]; 13412059035fStedu 13422059035fStedu convert64(val, buf); 134375b1bb69Stedu fwrite(buf, sizeof buf, 1, fp); 13442059035fStedu } 13452059035fStedu 13462059035fStedu static int 1347738fad2fStedu atcomp(const void *avp, const void *bvp) 13482059035fStedu { 13492059035fStedu const zic_t a = ((const struct attype *) avp)->at; 13502059035fStedu const zic_t b = ((const struct attype *) bvp)->at; 13512059035fStedu 13522059035fStedu return (a < b) ? -1 : (a > b); 13532059035fStedu } 13542059035fStedu 13552059035fStedu static int 1356738fad2fStedu is32(zic_t x) 13572059035fStedu { 13582059035fStedu return INT32_MIN <= x && x <= INT32_MAX; 13592059035fStedu } 13602059035fStedu 13612059035fStedu static void 1362738fad2fStedu writezone(const char *name, const char *string) 13632059035fStedu { 136466e58127Stedu FILE *fp; 136566e58127Stedu int i, j; 136666e58127Stedu int leapcnt32, leapi32; 136766e58127Stedu int timecnt32, timei32; 136866e58127Stedu int pass; 13692059035fStedu static char *fullname; 13702059035fStedu static const struct tzhead tzh0; 13712059035fStedu static struct tzhead tzh; 13722059035fStedu zic_t ats[TZ_MAX_TIMES]; 13732059035fStedu unsigned char types[TZ_MAX_TIMES]; 13742059035fStedu size_t len; 13752059035fStedu 13762059035fStedu /* 13772059035fStedu ** Sort. 13782059035fStedu */ 13792059035fStedu if (timecnt > 1) 138075b1bb69Stedu qsort(attypes, timecnt, sizeof *attypes, atcomp); 13812059035fStedu /* 13822059035fStedu ** Optimize. 13832059035fStedu */ 13842059035fStedu { 13852059035fStedu int fromi; 13862059035fStedu int toi; 13872059035fStedu 13882059035fStedu toi = 0; 13892059035fStedu fromi = 0; 13902059035fStedu while (fromi < timecnt && attypes[fromi].at < min_time) 13912059035fStedu ++fromi; 13922059035fStedu if (isdsts[0] == 0) 13932059035fStedu while (fromi < timecnt && attypes[fromi].type == 0) 13942059035fStedu ++fromi; /* handled by default rule */ 13952059035fStedu for ( ; fromi < timecnt; ++fromi) { 13962059035fStedu if (toi != 0 && ((attypes[fromi].at + 13972059035fStedu gmtoffs[attypes[toi - 1].type]) <= 1398f0dc9fbaSderaadt (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 : 1399f0dc9fbaSderaadt attypes[toi - 2].type]))) { 1400f0dc9fbaSderaadt attypes[toi - 1].type = attypes[fromi].type; 14012059035fStedu continue; 14022059035fStedu } 14032059035fStedu if (toi == 0 || 14042059035fStedu attypes[toi - 1].type != attypes[fromi].type) 14052059035fStedu attypes[toi++] = attypes[fromi]; 14062059035fStedu } 14072059035fStedu timecnt = toi; 14082059035fStedu } 14092059035fStedu /* 14102059035fStedu ** Transfer. 14112059035fStedu */ 14122059035fStedu for (i = 0; i < timecnt; ++i) { 14132059035fStedu ats[i] = attypes[i].at; 14142059035fStedu types[i] = attypes[i].type; 14152059035fStedu } 14162059035fStedu /* 14172059035fStedu ** Correct for leap seconds. 14182059035fStedu */ 14192059035fStedu for (i = 0; i < timecnt; ++i) { 14202059035fStedu j = leapcnt; 14212059035fStedu while (--j >= 0) 14222059035fStedu if (ats[i] > trans[j] - corr[j]) { 14232059035fStedu ats[i] = tadd(ats[i], corr[j]); 14242059035fStedu break; 14252059035fStedu } 14262059035fStedu } 14272059035fStedu /* 14282059035fStedu ** Figure out 32-bit-limited starts and counts. 14292059035fStedu */ 14302059035fStedu timecnt32 = timecnt; 14312059035fStedu timei32 = 0; 14322059035fStedu leapcnt32 = leapcnt; 14332059035fStedu leapi32 = 0; 14342059035fStedu while (timecnt32 > 0 && !is32(ats[timecnt32 - 1])) 14352059035fStedu --timecnt32; 14362059035fStedu while (timecnt32 > 0 && !is32(ats[timei32])) { 14372059035fStedu --timecnt32; 14382059035fStedu ++timei32; 14392059035fStedu } 14402059035fStedu while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) 14412059035fStedu --leapcnt32; 14422059035fStedu while (leapcnt32 > 0 && !is32(trans[leapi32])) { 14432059035fStedu --leapcnt32; 14442059035fStedu ++leapi32; 14452059035fStedu } 14462059035fStedu len = strlen(directory) + 1 + strlen(name) + 1; 14472059035fStedu fullname = erealloc(fullname, len); 144875b1bb69Stedu snprintf(fullname, len, "%s/%s", directory, name); 14492059035fStedu /* 14502059035fStedu ** Remove old file, if any, to snap links. 14512059035fStedu */ 1452f0dc9fbaSderaadt if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) 14534dc71ee2Stedu err(1, "Can't remove %s", fullname); 14542059035fStedu if ((fp = fopen(fullname, "wb")) == NULL) { 14552059035fStedu if (mkdirs(fullname) != 0) 14562059035fStedu exit(EXIT_FAILURE); 1457f0dc9fbaSderaadt if ((fp = fopen(fullname, "wb")) == NULL) 14584dc71ee2Stedu err(1, "Can't create %s", fullname); 14592059035fStedu } 14602059035fStedu for (pass = 1; pass <= 2; ++pass) { 146166e58127Stedu int thistimei, thistimecnt; 146266e58127Stedu int thisleapi, thisleapcnt; 146366e58127Stedu int thistimelim, thisleaplim; 14642059035fStedu int writetype[TZ_MAX_TIMES]; 14652059035fStedu int typemap[TZ_MAX_TYPES]; 146666e58127Stedu int thistypecnt; 14672059035fStedu char thischars[TZ_MAX_CHARS]; 14682059035fStedu char thischarcnt; 14692059035fStedu int indmap[TZ_MAX_CHARS]; 14702059035fStedu 14712059035fStedu if (pass == 1) { 14722059035fStedu thistimei = timei32; 14732059035fStedu thistimecnt = timecnt32; 14742059035fStedu thisleapi = leapi32; 14752059035fStedu thisleapcnt = leapcnt32; 14762059035fStedu } else { 14772059035fStedu thistimei = 0; 14782059035fStedu thistimecnt = timecnt; 14792059035fStedu thisleapi = 0; 14802059035fStedu thisleapcnt = leapcnt; 14812059035fStedu } 14822059035fStedu thistimelim = thistimei + thistimecnt; 14832059035fStedu thisleaplim = thisleapi + thisleapcnt; 14842059035fStedu for (i = 0; i < typecnt; ++i) 14852059035fStedu writetype[i] = thistimecnt == timecnt; 14862059035fStedu if (thistimecnt == 0) { 14872059035fStedu /* 14882059035fStedu ** No transition times fall in the current 14892059035fStedu ** (32- or 64-bit) window. 14902059035fStedu */ 14912059035fStedu if (typecnt != 0) 14922059035fStedu writetype[typecnt - 1] = TRUE; 14932059035fStedu } else { 14942059035fStedu for (i = thistimei - 1; i < thistimelim; ++i) 14952059035fStedu if (i >= 0) 14962059035fStedu writetype[types[i]] = TRUE; 14972059035fStedu /* 14982059035fStedu ** For America/Godthab and Antarctica/Palmer 14992059035fStedu */ 15002059035fStedu if (thistimei == 0) 15012059035fStedu writetype[0] = TRUE; 15022059035fStedu } 15032059035fStedu #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH 15042059035fStedu /* 15052059035fStedu ** For some pre-2011 systems: if the last-to-be-written 15062059035fStedu ** standard (or daylight) type has an offset different from the 15072059035fStedu ** most recently used offset, 15082059035fStedu ** append an (unused) copy of the most recently used type 15092059035fStedu ** (to help get global "altzone" and "timezone" variables 15102059035fStedu ** set correctly). 15112059035fStedu */ 15122059035fStedu { 151366e58127Stedu int mrudst, mrustd, hidst, histd, type; 15142059035fStedu 15152059035fStedu hidst = histd = mrudst = mrustd = -1; 15162059035fStedu for (i = thistimei; i < thistimelim; ++i) 15172059035fStedu if (isdsts[types[i]]) 15182059035fStedu mrudst = types[i]; 1519f0dc9fbaSderaadt else 1520f0dc9fbaSderaadt mrustd = types[i]; 15212059035fStedu for (i = 0; i < typecnt; ++i) 15222059035fStedu if (writetype[i]) { 15232059035fStedu if (isdsts[i]) 15242059035fStedu hidst = i; 1525f0dc9fbaSderaadt else 1526f0dc9fbaSderaadt histd = i; 15272059035fStedu } 15282059035fStedu if (hidst >= 0 && mrudst >= 0 && hidst != mrudst && 15292059035fStedu gmtoffs[hidst] != gmtoffs[mrudst]) { 15302059035fStedu isdsts[mrudst] = -1; 15312059035fStedu type = addtype(gmtoffs[mrudst], 15322059035fStedu &chars[abbrinds[mrudst]], 1533f0dc9fbaSderaadt TRUE, ttisstds[mrudst], 15342059035fStedu ttisgmts[mrudst]); 15352059035fStedu isdsts[mrudst] = TRUE; 15362059035fStedu writetype[type] = TRUE; 15372059035fStedu } 15382059035fStedu if (histd >= 0 && mrustd >= 0 && histd != mrustd && 15392059035fStedu gmtoffs[histd] != gmtoffs[mrustd]) { 15402059035fStedu isdsts[mrustd] = -1; 15412059035fStedu type = addtype(gmtoffs[mrustd], 15422059035fStedu &chars[abbrinds[mrustd]], 1543f0dc9fbaSderaadt FALSE, ttisstds[mrustd], 15442059035fStedu ttisgmts[mrustd]); 15452059035fStedu isdsts[mrustd] = FALSE; 15462059035fStedu writetype[type] = TRUE; 15472059035fStedu } 15482059035fStedu } 15492059035fStedu #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ 15502059035fStedu thistypecnt = 0; 15512059035fStedu for (i = 0; i < typecnt; ++i) 15522059035fStedu typemap[i] = writetype[i] ? thistypecnt++ : -1; 15532059035fStedu for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i) 15542059035fStedu indmap[i] = -1; 15552059035fStedu thischarcnt = 0; 15562059035fStedu for (i = 0; i < typecnt; ++i) { 155766e58127Stedu char *thisabbr; 15582059035fStedu 15592059035fStedu if (!writetype[i]) 15602059035fStedu continue; 15612059035fStedu if (indmap[abbrinds[i]] >= 0) 15622059035fStedu continue; 15632059035fStedu thisabbr = &chars[abbrinds[i]]; 15642059035fStedu for (j = 0; j < thischarcnt; ++j) 15652059035fStedu if (strcmp(&thischars[j], thisabbr) == 0) 15662059035fStedu break; 15672059035fStedu if (j == thischarcnt) { 156875b1bb69Stedu strlcpy(&thischars[(int) thischarcnt], 15692059035fStedu thisabbr, sizeof(thischars) - thischarcnt); 15702059035fStedu thischarcnt += strlen(thisabbr) + 1; 15712059035fStedu } 15722059035fStedu indmap[abbrinds[i]] = j; 15732059035fStedu } 157475b1bb69Stedu #define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp) 15752059035fStedu tzh = tzh0; 157675b1bb69Stedu strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); 15772059035fStedu tzh.tzh_version[0] = ZIC_VERSION; 15782059035fStedu convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt); 15792059035fStedu convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt); 15802059035fStedu convert(eitol(thisleapcnt), tzh.tzh_leapcnt); 15812059035fStedu convert(eitol(thistimecnt), tzh.tzh_timecnt); 15822059035fStedu convert(eitol(thistypecnt), tzh.tzh_typecnt); 15832059035fStedu convert(eitol(thischarcnt), tzh.tzh_charcnt); 15842059035fStedu DO(tzh_magic); 15852059035fStedu DO(tzh_version); 15862059035fStedu DO(tzh_reserved); 15872059035fStedu DO(tzh_ttisgmtcnt); 15882059035fStedu DO(tzh_ttisstdcnt); 15892059035fStedu DO(tzh_leapcnt); 15902059035fStedu DO(tzh_timecnt); 15912059035fStedu DO(tzh_typecnt); 15922059035fStedu DO(tzh_charcnt); 15932059035fStedu #undef DO 15942059035fStedu for (i = thistimei; i < thistimelim; ++i) 15952059035fStedu if (pass == 1) 15962059035fStedu puttzcode((long) ats[i], fp); 1597f0dc9fbaSderaadt else 1598f0dc9fbaSderaadt puttzcode64(ats[i], fp); 15992059035fStedu for (i = thistimei; i < thistimelim; ++i) { 16002059035fStedu unsigned char uc; 16012059035fStedu 16022059035fStedu uc = typemap[types[i]]; 160375b1bb69Stedu fwrite(&uc, sizeof uc, 1, fp); 16042059035fStedu } 16052059035fStedu for (i = 0; i < typecnt; ++i) 16062059035fStedu if (writetype[i]) { 16072059035fStedu puttzcode(gmtoffs[i], fp); 160875b1bb69Stedu putc(isdsts[i], fp); 160975b1bb69Stedu putc((unsigned char)indmap[abbrinds[i]], fp); 16102059035fStedu } 16112059035fStedu if (thischarcnt != 0) 161275b1bb69Stedu fwrite(thischars, sizeof thischars[0], thischarcnt, fp); 16132059035fStedu for (i = thisleapi; i < thisleaplim; ++i) { 161466e58127Stedu zic_t todo; 16152059035fStedu 16162059035fStedu if (roll[i]) { 16172059035fStedu if (timecnt == 0 || trans[i] < ats[0]) { 16182059035fStedu j = 0; 16192059035fStedu while (isdsts[j]) 16202059035fStedu if (++j >= typecnt) { 16212059035fStedu j = 0; 16222059035fStedu break; 16232059035fStedu } 16242059035fStedu } else { 16252059035fStedu j = 1; 16262059035fStedu while (j < timecnt && 16272059035fStedu trans[i] >= ats[j]) 16282059035fStedu ++j; 16292059035fStedu j = types[j - 1]; 16302059035fStedu } 16312059035fStedu todo = tadd(trans[i], -gmtoffs[j]); 1632f0dc9fbaSderaadt } else 1633f0dc9fbaSderaadt todo = trans[i]; 16342059035fStedu if (pass == 1) 16352059035fStedu puttzcode((long) todo, fp); 1636f0dc9fbaSderaadt else 1637f0dc9fbaSderaadt puttzcode64(todo, fp); 16382059035fStedu puttzcode(corr[i], fp); 16392059035fStedu } 16402059035fStedu for (i = 0; i < typecnt; ++i) 16412059035fStedu if (writetype[i]) 164275b1bb69Stedu putc(ttisstds[i], fp); 16432059035fStedu for (i = 0; i < typecnt; ++i) 16442059035fStedu if (writetype[i]) 164575b1bb69Stedu putc(ttisgmts[i], fp); 16462059035fStedu } 164775b1bb69Stedu fprintf(fp, "\n%s\n", string); 1648f0dc9fbaSderaadt if (ferror(fp) || fclose(fp)) 16494dc71ee2Stedu errx(1, "Error writing %s", fullname); 16502059035fStedu } 16512059035fStedu 1652*6a4024dcSmillert static char const * 1653*6a4024dcSmillert abbroffset(char *buf, zic_t offset) 1654*6a4024dcSmillert { 1655*6a4024dcSmillert char sign = '+'; 1656*6a4024dcSmillert int seconds, minutes; 1657*6a4024dcSmillert 1658*6a4024dcSmillert if (offset < 0) { 1659*6a4024dcSmillert offset = -offset; 1660*6a4024dcSmillert sign = '-'; 1661*6a4024dcSmillert } 1662*6a4024dcSmillert 1663*6a4024dcSmillert seconds = offset % SECSPERMIN; 1664*6a4024dcSmillert offset /= SECSPERMIN; 1665*6a4024dcSmillert minutes = offset % MINSPERHOUR; 1666*6a4024dcSmillert offset /= MINSPERHOUR; 1667*6a4024dcSmillert if (100 <= offset) { 1668*6a4024dcSmillert error("%%z UTC offset magnitude exceeds 99:59:59"); 1669*6a4024dcSmillert return "%z"; 1670*6a4024dcSmillert } else { 1671*6a4024dcSmillert char *p = buf; 1672*6a4024dcSmillert *p++ = sign; 1673*6a4024dcSmillert *p++ = '0' + offset / 10; 1674*6a4024dcSmillert *p++ = '0' + offset % 10; 1675*6a4024dcSmillert if (minutes | seconds) { 1676*6a4024dcSmillert *p++ = '0' + minutes / 10; 1677*6a4024dcSmillert *p++ = '0' + minutes % 10; 1678*6a4024dcSmillert if (seconds) { 1679*6a4024dcSmillert *p++ = '0' + seconds / 10; 1680*6a4024dcSmillert *p++ = '0' + seconds % 10; 1681*6a4024dcSmillert } 1682*6a4024dcSmillert } 1683*6a4024dcSmillert *p = '\0'; 1684*6a4024dcSmillert return buf; 1685*6a4024dcSmillert } 1686*6a4024dcSmillert } 1687*6a4024dcSmillert 16882059035fStedu static void 1689*6a4024dcSmillert doabbr(char *abbr, size_t size, struct zone const *zp, const char *letters, 1690f0dc9fbaSderaadt int isdst, int doquotes) 16912059035fStedu { 1692f0dc9fbaSderaadt char *cp, *slashp; 1693*6a4024dcSmillert size_t len; 1694*6a4024dcSmillert char const *format = zp->z_format; 16952059035fStedu 16962059035fStedu slashp = strchr(format, '/'); 16972059035fStedu if (slashp == NULL) { 1698*6a4024dcSmillert char letterbuf[PERCENT_Z_LEN_BOUND + 1]; 1699*6a4024dcSmillert if (zp->z_format_specifier == 'z') 1700*6a4024dcSmillert letters = abbroffset(letterbuf, -zp->z_gmtoff); 1701*6a4024dcSmillert else if (letters == NULL) 1702*6a4024dcSmillert letters = "%s"; 1703f0dc9fbaSderaadt snprintf(abbr, size, format, letters); 17042059035fStedu } else if (isdst) { 170575b1bb69Stedu strlcpy(abbr, slashp + 1, size); 17062059035fStedu } else { 17072059035fStedu if (slashp - format + 1 < size) 17082059035fStedu size = slashp - format + 1; 170975b1bb69Stedu strlcpy(abbr, format, size); 17102059035fStedu } 17112059035fStedu if (!doquotes) 17122059035fStedu return; 17132059035fStedu for (cp = abbr; *cp != '\0'; ++cp) 17142059035fStedu if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL && 17152059035fStedu strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL) 17162059035fStedu break; 17172059035fStedu len = strlen(abbr); 17182059035fStedu if (len > 0 && *cp == '\0') 17192059035fStedu return; 17202059035fStedu abbr[len + 2] = '\0'; 17212059035fStedu abbr[len + 1] = '>'; 17222059035fStedu for ( ; len > 0; --len) 17232059035fStedu abbr[len] = abbr[len - 1]; 17242059035fStedu abbr[0] = '<'; 17252059035fStedu } 17262059035fStedu 17272059035fStedu static void 1728738fad2fStedu updateminmax(int x) 17292059035fStedu { 17302059035fStedu if (min_year > x) 17312059035fStedu min_year = x; 17322059035fStedu if (max_year < x) 17332059035fStedu max_year = x; 17342059035fStedu } 17352059035fStedu 17362059035fStedu static int 1737738fad2fStedu stringoffset(char *result, size_t size, long offset) 17382059035fStedu { 1739f0dc9fbaSderaadt int hours, minutes, seconds; 174066e58127Stedu char *ep; 17412059035fStedu 17422059035fStedu result[0] = '\0'; 17432059035fStedu if (offset < 0) { 174475b1bb69Stedu strlcpy(result, "-", size); 17452059035fStedu offset = -offset; 17462059035fStedu } 17472059035fStedu seconds = offset % SECSPERMIN; 17482059035fStedu offset /= SECSPERMIN; 17492059035fStedu minutes = offset % MINSPERHOUR; 17502059035fStedu offset /= MINSPERHOUR; 17512059035fStedu hours = offset; 17522059035fStedu if (hours >= HOURSPERDAY) { 17532059035fStedu result[0] = '\0'; 17542059035fStedu return -1; 17552059035fStedu } 17562059035fStedu ep = end(result, size); 175775b1bb69Stedu snprintf(ep, size - (ep - result), "%d", hours); 17582059035fStedu if (minutes != 0 || seconds != 0) { 17592059035fStedu ep = end(result, size); 176075b1bb69Stedu snprintf(ep, size - (ep - result), ":%02d", minutes); 17612059035fStedu if (seconds != 0) { 17622059035fStedu ep = end(result, size); 176375b1bb69Stedu snprintf(ep, size - (ep - result), ":%02d", seconds); 17642059035fStedu } 17652059035fStedu } 17662059035fStedu return 0; 17672059035fStedu } 17682059035fStedu 17692059035fStedu static int 1770738fad2fStedu stringrule(char *result, size_t size, const struct rule *rp, long dstoff, long gmtoff) 17712059035fStedu { 177266e58127Stedu long tod; 177366e58127Stedu char *ep; 17742059035fStedu 17752059035fStedu ep = end(result, size); 17762059035fStedu size -= ep - result; 17772059035fStedu result = ep; 17782059035fStedu if (rp->r_dycode == DC_DOM) { 177966e58127Stedu int month, total; 17802059035fStedu 17812059035fStedu if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY) 17822059035fStedu return -1; 17832059035fStedu total = 0; 17842059035fStedu for (month = 0; month < rp->r_month; ++month) 17852059035fStedu total += len_months[0][month]; 178675b1bb69Stedu snprintf(result, size, "J%d", total + rp->r_dayofmonth); 17872059035fStedu } else { 178866e58127Stedu int week; 17892059035fStedu 17902059035fStedu if (rp->r_dycode == DC_DOWGEQ) { 17912059035fStedu if ((rp->r_dayofmonth % DAYSPERWEEK) != 1) 17922059035fStedu return -1; 17932059035fStedu week = 1 + rp->r_dayofmonth / DAYSPERWEEK; 17942059035fStedu } else if (rp->r_dycode == DC_DOWLEQ) { 17952059035fStedu if (rp->r_dayofmonth == len_months[1][rp->r_month]) 17962059035fStedu week = 5; 17972059035fStedu else { 17982059035fStedu if ((rp->r_dayofmonth % DAYSPERWEEK) != 0) 17992059035fStedu return -1; 18002059035fStedu week = rp->r_dayofmonth / DAYSPERWEEK; 18012059035fStedu } 1802f0dc9fbaSderaadt } else 1803f0dc9fbaSderaadt return -1; /* "cannot happen" */ 180475b1bb69Stedu snprintf(result, size, "M%d.%d.%d", 18052059035fStedu rp->r_month + 1, week, rp->r_wday); 18062059035fStedu } 18072059035fStedu tod = rp->r_tod; 18082059035fStedu if (rp->r_todisgmt) 18092059035fStedu tod += gmtoff; 18102059035fStedu if (rp->r_todisstd && rp->r_stdoff == 0) 18112059035fStedu tod += dstoff; 18122059035fStedu if (tod < 0) { 18132059035fStedu result[0] = '\0'; 18142059035fStedu return -1; 18152059035fStedu } 18162059035fStedu if (tod != 2 * SECSPERMIN * MINSPERHOUR) { 181775b1bb69Stedu strlcat(result, "/", size); 18182059035fStedu ep = end(result, size); 18192059035fStedu if (stringoffset(ep, size - (ep - result), tod) != 0) 18202059035fStedu return -1; 18212059035fStedu } 18222059035fStedu return 0; 18232059035fStedu } 18242059035fStedu 18252059035fStedu static void 1826738fad2fStedu stringzone(char *result, size_t size, const struct zone *zpfirst, int zonecount) 18272059035fStedu { 182866e58127Stedu const struct zone *zp; 1829f0dc9fbaSderaadt struct rule *rp, *stdrp, *dstrp; 183066e58127Stedu int i; 183166e58127Stedu const char *abbrvar; 183266e58127Stedu char *ep; 18332059035fStedu 18342059035fStedu result[0] = '\0'; 18352059035fStedu zp = zpfirst + zonecount - 1; 18362059035fStedu stdrp = dstrp = NULL; 18372059035fStedu for (i = 0; i < zp->z_nrules; ++i) { 18382059035fStedu rp = &zp->z_rules[i]; 18392059035fStedu if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX) 18402059035fStedu continue; 18412059035fStedu if (rp->r_stdoff == 0) { 18422059035fStedu if (stdrp == NULL) 18432059035fStedu stdrp = rp; 1844f0dc9fbaSderaadt else 1845f0dc9fbaSderaadt return; 18462059035fStedu } else { 18472059035fStedu if (dstrp == NULL) 18482059035fStedu dstrp = rp; 1849f0dc9fbaSderaadt else 1850f0dc9fbaSderaadt return; 18512059035fStedu } 18522059035fStedu } 18532059035fStedu if (stdrp == NULL && dstrp == NULL) { 18542059035fStedu /* 18552059035fStedu ** There are no rules running through "max". 18562059035fStedu ** Let's find the latest rule. 18572059035fStedu */ 18582059035fStedu for (i = 0; i < zp->z_nrules; ++i) { 18592059035fStedu rp = &zp->z_rules[i]; 18602059035fStedu if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear || 18612059035fStedu (rp->r_hiyear == stdrp->r_hiyear && 18622059035fStedu rp->r_month > stdrp->r_month)) 18632059035fStedu stdrp = rp; 18642059035fStedu } 18652059035fStedu if (stdrp != NULL && stdrp->r_stdoff != 0) 18662059035fStedu return; /* We end up in DST (a POSIX no-no). */ 18672059035fStedu /* 18682059035fStedu ** Horrid special case: if year is 2037, 18692059035fStedu ** presume this is a zone handled on a year-by-year basis; 18702059035fStedu ** do not try to apply a rule to the zone. 18712059035fStedu */ 18722059035fStedu if (stdrp != NULL && stdrp->r_hiyear == 2037) 18732059035fStedu return; 18742059035fStedu } 18752059035fStedu if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) 18762059035fStedu return; 18772059035fStedu abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; 1878*6a4024dcSmillert doabbr(result, size, zp, abbrvar, FALSE, TRUE); 18792059035fStedu ep = end(result, size); 18802059035fStedu if (stringoffset(ep, size - (ep - result), -zp->z_gmtoff) != 0) { 18812059035fStedu result[0] = '\0'; 18822059035fStedu return; 18832059035fStedu } 18842059035fStedu if (dstrp == NULL) 18852059035fStedu return; 18862059035fStedu ep = end(result, size); 1887*6a4024dcSmillert doabbr(ep, size - (ep - result), zp, dstrp->r_abbrvar, TRUE, TRUE); 18882059035fStedu if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) { 18892059035fStedu ep = end(result, size); 18902059035fStedu if (stringoffset(ep, size - (ep - result), 18912059035fStedu -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) { 18922059035fStedu result[0] = '\0'; 18932059035fStedu return; 18942059035fStedu } 18952059035fStedu } 189675b1bb69Stedu strlcat(result, ",", size); 18972059035fStedu if (stringrule(result, size, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) { 18982059035fStedu result[0] = '\0'; 18992059035fStedu return; 19002059035fStedu } 190175b1bb69Stedu strlcat(result, ",", size); 19022059035fStedu if (stringrule(result, size, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) { 19032059035fStedu result[0] = '\0'; 19042059035fStedu return; 19052059035fStedu } 19062059035fStedu } 19072059035fStedu 19082059035fStedu static void 1909738fad2fStedu outzone(const struct zone *zpfirst, int zonecount) 19102059035fStedu { 191166e58127Stedu const struct zone *zp; 191266e58127Stedu struct rule *rp; 1913f0dc9fbaSderaadt int i, j, usestart, useuntil, type; 1914f0dc9fbaSderaadt zic_t starttime = 0, untiltime = 0; 1915f0dc9fbaSderaadt long gmtoff, stdoff, startoff; 1916f0dc9fbaSderaadt int year, startttisstd = FALSE, startttisgmt = FALSE; 1917f0dc9fbaSderaadt char *startbuf, *ab, *envvar; 1918f0dc9fbaSderaadt int max_abbr_len, max_envvar_len; 191966e58127Stedu int prodstic; /* all rules are min to max */ 19202059035fStedu 19212059035fStedu max_abbr_len = 2 + max_format_len + max_abbrvar_len; 19222059035fStedu max_envvar_len = 2 * max_abbr_len + 5 * 9; 19232059035fStedu startbuf = emalloc(max_abbr_len + 1); 19242059035fStedu ab = emalloc(max_abbr_len + 1); 19252059035fStedu envvar = emalloc(max_envvar_len + 1); 19262059035fStedu /* 19272059035fStedu ** Now. . .finally. . .generate some useful data! 19282059035fStedu */ 19292059035fStedu timecnt = 0; 19302059035fStedu typecnt = 0; 19312059035fStedu charcnt = 0; 19322059035fStedu prodstic = zonecount == 1; 19332059035fStedu /* 19342059035fStedu ** Thanks to Earl Chew 19352059035fStedu ** for noting the need to unconditionally initialize startttisstd. 19362059035fStedu */ 19372059035fStedu min_year = max_year = EPOCH_YEAR; 19382059035fStedu if (leapseen) { 19392059035fStedu updateminmax(leapminyear); 19402059035fStedu updateminmax(leapmaxyear + (leapmaxyear < INT_MAX)); 19412059035fStedu } 19422059035fStedu for (i = 0; i < zonecount; ++i) { 19432059035fStedu zp = &zpfirst[i]; 19442059035fStedu if (i < zonecount - 1) 19452059035fStedu updateminmax(zp->z_untilrule.r_loyear); 19462059035fStedu for (j = 0; j < zp->z_nrules; ++j) { 19472059035fStedu rp = &zp->z_rules[j]; 19482059035fStedu if (rp->r_lowasnum) 19492059035fStedu updateminmax(rp->r_loyear); 19502059035fStedu if (rp->r_hiwasnum) 19512059035fStedu updateminmax(rp->r_hiyear); 19522059035fStedu if (rp->r_lowasnum || rp->r_hiwasnum) 19532059035fStedu prodstic = FALSE; 19542059035fStedu } 19552059035fStedu } 19562059035fStedu /* 19572059035fStedu ** Generate lots of data if a rule can't cover all future times. 19582059035fStedu */ 19592059035fStedu stringzone(envvar, max_envvar_len + 1, zpfirst, zonecount); 19602059035fStedu if (noise && envvar[0] == '\0') { 196166e58127Stedu char * wp; 19622059035fStedu 1963f0dc9fbaSderaadt wp = ecpyalloc("no POSIX environment variable for zone"); 19642059035fStedu wp = ecatalloc(wp, " "); 19652059035fStedu wp = ecatalloc(wp, zpfirst->z_name); 19662059035fStedu warning(wp); 19672059035fStedu free(wp); 19682059035fStedu } 19692059035fStedu if (envvar[0] == '\0') { 19702059035fStedu if (min_year >= INT_MIN + YEARSPERREPEAT) 19712059035fStedu min_year -= YEARSPERREPEAT; 1972f0dc9fbaSderaadt else 1973f0dc9fbaSderaadt min_year = INT_MIN; 19742059035fStedu if (max_year <= INT_MAX - YEARSPERREPEAT) 19752059035fStedu max_year += YEARSPERREPEAT; 1976f0dc9fbaSderaadt else 1977f0dc9fbaSderaadt max_year = INT_MAX; 19782059035fStedu /* 19792059035fStedu ** Regardless of any of the above, 19802059035fStedu ** for a "proDSTic" zone which specifies that its rules 19812059035fStedu ** always have and always will be in effect, 19822059035fStedu ** we only need one cycle to define the zone. 19832059035fStedu */ 19842059035fStedu if (prodstic) { 19852059035fStedu min_year = 1900; 19862059035fStedu max_year = min_year + YEARSPERREPEAT; 19872059035fStedu } 19882059035fStedu } 19892059035fStedu /* 19902059035fStedu ** For the benefit of older systems, 19912059035fStedu ** generate data from 1900 through 2037. 19922059035fStedu */ 19932059035fStedu if (min_year > 1900) 19942059035fStedu min_year = 1900; 19952059035fStedu if (max_year < 2037) 19962059035fStedu max_year = 2037; 19972059035fStedu for (i = 0; i < zonecount; ++i) { 19982059035fStedu /* 19992059035fStedu ** A guess that may well be corrected later. 20002059035fStedu */ 20012059035fStedu stdoff = 0; 20022059035fStedu zp = &zpfirst[i]; 20032059035fStedu usestart = i > 0 && (zp - 1)->z_untiltime > min_time; 20042059035fStedu useuntil = i < (zonecount - 1); 20052059035fStedu if (useuntil && zp->z_untiltime <= min_time) 20062059035fStedu continue; 20072059035fStedu gmtoff = zp->z_gmtoff; 20082059035fStedu eat(zp->z_filename, zp->z_linenum); 20092059035fStedu *startbuf = '\0'; 20102059035fStedu startoff = zp->z_gmtoff; 20112059035fStedu if (zp->z_nrules == 0) { 20122059035fStedu stdoff = zp->z_stdoff; 2013*6a4024dcSmillert doabbr(startbuf, max_abbr_len + 1, zp, NULL, 2014*6a4024dcSmillert stdoff != 0, FALSE); 20152059035fStedu type = addtype(oadd(zp->z_gmtoff, stdoff), 20162059035fStedu startbuf, stdoff != 0, startttisstd, 20172059035fStedu startttisgmt); 20182059035fStedu if (usestart) { 20192059035fStedu addtt(starttime, type); 20202059035fStedu usestart = FALSE; 20212059035fStedu } else if (stdoff != 0) 20222059035fStedu addtt(min_time, type); 20232059035fStedu } else for (year = min_year; year <= max_year; ++year) { 20242059035fStedu if (useuntil && year > zp->z_untilrule.r_hiyear) 20252059035fStedu break; 20262059035fStedu /* 20272059035fStedu ** Mark which rules to do in the current year. 20282059035fStedu ** For those to do, calculate rpytime(rp, year); 20292059035fStedu */ 20302059035fStedu for (j = 0; j < zp->z_nrules; ++j) { 20312059035fStedu rp = &zp->z_rules[j]; 20322059035fStedu eats(zp->z_filename, zp->z_linenum, 20332059035fStedu rp->r_filename, rp->r_linenum); 20342059035fStedu rp->r_todo = year >= rp->r_loyear && 2035a188d4d1Smillert year <= rp->r_hiyear; 20362059035fStedu if (rp->r_todo) 20372059035fStedu rp->r_temp = rpytime(rp, year); 20382059035fStedu } 20392059035fStedu for ( ; ; ) { 204066e58127Stedu int k; 2041f0dc9fbaSderaadt zic_t jtime, ktime = 0; 204266e58127Stedu long offset; 20432059035fStedu 20442059035fStedu if (useuntil) { 20452059035fStedu /* 20462059035fStedu ** Turn untiltime into UTC 20472059035fStedu ** assuming the current gmtoff and 20482059035fStedu ** stdoff values. 20492059035fStedu */ 20502059035fStedu untiltime = zp->z_untiltime; 20512059035fStedu if (!zp->z_untilrule.r_todisgmt) 20522059035fStedu untiltime = tadd(untiltime, 20532059035fStedu -gmtoff); 20542059035fStedu if (!zp->z_untilrule.r_todisstd) 20552059035fStedu untiltime = tadd(untiltime, 20562059035fStedu -stdoff); 20572059035fStedu } 20582059035fStedu /* 20592059035fStedu ** Find the rule (of those to do, if any) 20602059035fStedu ** that takes effect earliest in the year. 20612059035fStedu */ 20622059035fStedu k = -1; 20632059035fStedu for (j = 0; j < zp->z_nrules; ++j) { 20642059035fStedu rp = &zp->z_rules[j]; 20652059035fStedu if (!rp->r_todo) 20662059035fStedu continue; 20672059035fStedu eats(zp->z_filename, zp->z_linenum, 20682059035fStedu rp->r_filename, rp->r_linenum); 20692059035fStedu offset = rp->r_todisgmt ? 0 : gmtoff; 20702059035fStedu if (!rp->r_todisstd) 20712059035fStedu offset = oadd(offset, stdoff); 20722059035fStedu jtime = rp->r_temp; 20732059035fStedu if (jtime == min_time || 20742059035fStedu jtime == max_time) 20752059035fStedu continue; 20762059035fStedu jtime = tadd(jtime, -offset); 20772059035fStedu if (k < 0 || jtime < ktime) { 20782059035fStedu k = j; 20792059035fStedu ktime = jtime; 20802059035fStedu } 20812059035fStedu } 20822059035fStedu if (k < 0) 20832059035fStedu break; /* go on to next year */ 20842059035fStedu rp = &zp->z_rules[k]; 20852059035fStedu rp->r_todo = FALSE; 20862059035fStedu if (useuntil && ktime >= untiltime) 20872059035fStedu break; 20882059035fStedu stdoff = rp->r_stdoff; 20892059035fStedu if (usestart && ktime == starttime) 20902059035fStedu usestart = FALSE; 20912059035fStedu if (usestart) { 20922059035fStedu if (ktime < starttime) { 20932059035fStedu startoff = oadd(zp->z_gmtoff, 20942059035fStedu stdoff); 20952059035fStedu doabbr(startbuf, 20962059035fStedu max_abbr_len + 1, 2097*6a4024dcSmillert zp, 20982059035fStedu rp->r_abbrvar, 20992059035fStedu rp->r_stdoff != 0, 21002059035fStedu FALSE); 21012059035fStedu continue; 21022059035fStedu } 21032059035fStedu if (*startbuf == '\0' && 21042059035fStedu startoff == oadd(zp->z_gmtoff, 21052059035fStedu stdoff)) { 21062059035fStedu doabbr(startbuf, 21072059035fStedu max_abbr_len + 1, 2108*6a4024dcSmillert zp, 21092059035fStedu rp->r_abbrvar, 2110f0dc9fbaSderaadt rp->r_stdoff != 0, 21112059035fStedu FALSE); 21122059035fStedu } 21132059035fStedu } 21142059035fStedu eats(zp->z_filename, zp->z_linenum, 21152059035fStedu rp->r_filename, rp->r_linenum); 2116*6a4024dcSmillert doabbr(ab, max_abbr_len + 1, zp, 2117f0dc9fbaSderaadt rp->r_abbrvar, rp->r_stdoff != 0, FALSE); 21182059035fStedu offset = oadd(zp->z_gmtoff, rp->r_stdoff); 21192059035fStedu type = addtype(offset, ab, rp->r_stdoff != 0, 21202059035fStedu rp->r_todisstd, rp->r_todisgmt); 21212059035fStedu addtt(ktime, type); 21222059035fStedu } 21232059035fStedu } 21242059035fStedu if (usestart) { 21252059035fStedu if (*startbuf == '\0' && 21262059035fStedu zp->z_format != NULL && 21272059035fStedu strchr(zp->z_format, '%') == NULL && 21282059035fStedu strchr(zp->z_format, '/') == NULL) 2129f0dc9fbaSderaadt strlcpy(startbuf, zp->z_format, max_abbr_len + 1); 21302059035fStedu eat(zp->z_filename, zp->z_linenum); 21312059035fStedu if (*startbuf == '\0') 2132f0dc9fbaSderaadt error("can't determine time zone abbreviation to use just after until time"); 2133f0dc9fbaSderaadt else 2134f0dc9fbaSderaadt addtt(starttime, 21352059035fStedu addtype(startoff, startbuf, 21362059035fStedu startoff != zp->z_gmtoff, 2137f0dc9fbaSderaadt startttisstd, startttisgmt)); 21382059035fStedu } 21392059035fStedu /* 21402059035fStedu ** Now we may get to set starttime for the next zone line. 21412059035fStedu */ 21422059035fStedu if (useuntil) { 21432059035fStedu startttisstd = zp->z_untilrule.r_todisstd; 21442059035fStedu startttisgmt = zp->z_untilrule.r_todisgmt; 21452059035fStedu starttime = zp->z_untiltime; 21462059035fStedu if (!startttisstd) 21472059035fStedu starttime = tadd(starttime, -stdoff); 21482059035fStedu if (!startttisgmt) 21492059035fStedu starttime = tadd(starttime, -gmtoff); 21502059035fStedu } 21512059035fStedu } 21522059035fStedu writezone(zpfirst->z_name, envvar); 21532059035fStedu free(startbuf); 21542059035fStedu free(ab); 21552059035fStedu free(envvar); 21562059035fStedu } 21572059035fStedu 21582059035fStedu static void 2159f0dc9fbaSderaadt addtt(const zic_t starttime, int type) 21602059035fStedu { 21612059035fStedu size_t len; 21622059035fStedu 21632059035fStedu if (starttime <= min_time || 21642059035fStedu (timecnt == 1 && attypes[0].at < min_time)) { 21652059035fStedu gmtoffs[0] = gmtoffs[type]; 21662059035fStedu isdsts[0] = isdsts[type]; 21672059035fStedu ttisstds[0] = ttisstds[type]; 21682059035fStedu ttisgmts[0] = ttisgmts[type]; 21692059035fStedu if (abbrinds[type] != 0) { 21702059035fStedu len = strlen(&chars[abbrinds[type]]) + 1; 217175b1bb69Stedu memmove(chars, &chars[abbrinds[type]], len); 21722059035fStedu } 21732059035fStedu abbrinds[0] = 0; 21742059035fStedu charcnt = strlen(chars) + 1; 21752059035fStedu typecnt = 1; 21762059035fStedu timecnt = 0; 21772059035fStedu type = 0; 21782059035fStedu } 21792059035fStedu if (timecnt >= TZ_MAX_TIMES) { 2180f0dc9fbaSderaadt error("too many transitions?!"); 21812059035fStedu exit(EXIT_FAILURE); 21822059035fStedu } 21832059035fStedu attypes[timecnt].at = starttime; 21842059035fStedu attypes[timecnt].type = type; 21852059035fStedu ++timecnt; 21862059035fStedu } 21872059035fStedu 21882059035fStedu static int 2189738fad2fStedu addtype(long gmtoff, const char *abbr, int isdst, int ttisstd, int ttisgmt) 21902059035fStedu { 219166e58127Stedu int i, j; 21922059035fStedu 21932059035fStedu if (isdst != TRUE && isdst != FALSE) { 2194f0dc9fbaSderaadt error("internal error - addtype called with bad isdst"); 21952059035fStedu exit(EXIT_FAILURE); 21962059035fStedu } 21972059035fStedu if (ttisstd != TRUE && ttisstd != FALSE) { 2198f0dc9fbaSderaadt error("internal error - addtype called with bad ttisstd"); 21992059035fStedu exit(EXIT_FAILURE); 22002059035fStedu } 22012059035fStedu if (ttisgmt != TRUE && ttisgmt != FALSE) { 2202f0dc9fbaSderaadt error("internal error - addtype called with bad ttisgmt"); 22032059035fStedu exit(EXIT_FAILURE); 22042059035fStedu } 22052059035fStedu /* 22062059035fStedu ** See if there's already an entry for this zone type. 22072059035fStedu ** If so, just return its index. 22082059035fStedu */ 22092059035fStedu for (i = 0; i < typecnt; ++i) { 22102059035fStedu if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && 22112059035fStedu strcmp(abbr, &chars[abbrinds[i]]) == 0 && 22122059035fStedu ttisstd == ttisstds[i] && 22132059035fStedu ttisgmt == ttisgmts[i]) 22142059035fStedu return i; 22152059035fStedu } 22162059035fStedu /* 22172059035fStedu ** There isn't one; add a new one, unless there are already too 22182059035fStedu ** many. 22192059035fStedu */ 22202059035fStedu if (typecnt >= TZ_MAX_TYPES) { 2221f0dc9fbaSderaadt error("too many local time types"); 22222059035fStedu exit(EXIT_FAILURE); 22232059035fStedu } 22242059035fStedu if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { 2225f0dc9fbaSderaadt error("UTC offset out of range"); 22262059035fStedu exit(EXIT_FAILURE); 22272059035fStedu } 22282059035fStedu gmtoffs[i] = gmtoff; 22292059035fStedu isdsts[i] = isdst; 22302059035fStedu ttisstds[i] = ttisstd; 22312059035fStedu ttisgmts[i] = ttisgmt; 22322059035fStedu 22332059035fStedu for (j = 0; j < charcnt; ++j) 22342059035fStedu if (strcmp(&chars[j], abbr) == 0) 22352059035fStedu break; 22362059035fStedu if (j == charcnt) 22372059035fStedu newabbr(abbr); 22382059035fStedu abbrinds[i] = j; 22392059035fStedu ++typecnt; 22402059035fStedu return i; 22412059035fStedu } 22422059035fStedu 22432059035fStedu static void 2244738fad2fStedu leapadd(zic_t t, int positive, int rolling, int count) 22452059035fStedu { 224666e58127Stedu int i, j; 22472059035fStedu 22482059035fStedu if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { 2249f0dc9fbaSderaadt error("too many leap seconds"); 22502059035fStedu exit(EXIT_FAILURE); 22512059035fStedu } 22522059035fStedu for (i = 0; i < leapcnt; ++i) 22532059035fStedu if (t <= trans[i]) { 22542059035fStedu if (t == trans[i]) { 2255f0dc9fbaSderaadt error("repeated leap second moment"); 22562059035fStedu exit(EXIT_FAILURE); 22572059035fStedu } 22582059035fStedu break; 22592059035fStedu } 22602059035fStedu do { 22612059035fStedu for (j = leapcnt; j > i; --j) { 22622059035fStedu trans[j] = trans[j - 1]; 22632059035fStedu corr[j] = corr[j - 1]; 22642059035fStedu roll[j] = roll[j - 1]; 22652059035fStedu } 22662059035fStedu trans[i] = t; 22672059035fStedu corr[i] = positive ? 1L : eitol(-count); 22682059035fStedu roll[i] = rolling; 22692059035fStedu ++leapcnt; 22702059035fStedu } while (positive && --count != 0); 22712059035fStedu } 22722059035fStedu 22732059035fStedu static void 22742059035fStedu adjleap(void) 22752059035fStedu { 227666e58127Stedu int i; 227766e58127Stedu long last = 0; 22782059035fStedu 22792059035fStedu /* 22802059035fStedu ** propagate leap seconds forward 22812059035fStedu */ 22822059035fStedu for (i = 0; i < leapcnt; ++i) { 22832059035fStedu trans[i] = tadd(trans[i], last); 22842059035fStedu last = corr[i] += last; 22852059035fStedu } 22862059035fStedu } 22872059035fStedu 2288fb356290Stedu /* this function is not strncasecmp */ 228938cd2fb1Stedu static int 2290fb356290Stedu itsabbr(const char *sabbr, const char *sword) 229138cd2fb1Stedu { 2292fb356290Stedu const unsigned char *abbr = sabbr; 2293fb356290Stedu const unsigned char *word = sword; 229438cd2fb1Stedu 2295fb356290Stedu if (tolower(*abbr) != tolower(*word)) 229638cd2fb1Stedu return FALSE; 229738cd2fb1Stedu while (*++abbr != '\0') 229838cd2fb1Stedu do { 2299fb356290Stedu ++word; 230038cd2fb1Stedu if (*word == '\0') 230138cd2fb1Stedu return FALSE; 2302fb356290Stedu } while (tolower(*word) != tolower(*abbr)); 230338cd2fb1Stedu return TRUE; 230438cd2fb1Stedu } 230538cd2fb1Stedu 23062059035fStedu static const struct lookup * 2307738fad2fStedu byword(const char *word, const struct lookup *table) 23082059035fStedu { 230966e58127Stedu const struct lookup *foundlp; 231066e58127Stedu const struct lookup *lp; 23112059035fStedu 231238cd2fb1Stedu if (word == NULL || table == NULL) 23132059035fStedu return NULL; 23142059035fStedu /* 23152059035fStedu ** Look for exact match. 23162059035fStedu */ 23172059035fStedu for (lp = table; lp->l_word != NULL; ++lp) 2318fb356290Stedu if (strcasecmp(word, lp->l_word) == 0) 23192059035fStedu return lp; 23202059035fStedu /* 23212059035fStedu ** Look for inexact match. 23222059035fStedu */ 23232059035fStedu foundlp = NULL; 23242059035fStedu for (lp = table; lp->l_word != NULL; ++lp) 232538cd2fb1Stedu if (itsabbr(word, lp->l_word)) { 23262059035fStedu if (foundlp == NULL) 23272059035fStedu foundlp = lp; 2328fb356290Stedu else 2329fb356290Stedu return NULL; /* multiple inexact matches */ 23302059035fStedu } 23312059035fStedu return foundlp; 23322059035fStedu } 23332059035fStedu 23342059035fStedu static char ** 2335738fad2fStedu getfields(char *cp) 23362059035fStedu { 233766e58127Stedu char *dp; 233866e58127Stedu char **array; 233966e58127Stedu int nsubs; 23402059035fStedu 23412059035fStedu if (cp == NULL) 23422059035fStedu return NULL; 2343cc917873Sderaadt array = ereallocarray(NULL, strlen(cp) + 1, sizeof *array); 23442059035fStedu nsubs = 0; 23452059035fStedu for ( ; ; ) { 23462059035fStedu while (isascii((unsigned char)*cp) && 23472059035fStedu isspace((unsigned char)*cp)) 23482059035fStedu ++cp; 23492059035fStedu if (*cp == '\0' || *cp == '#') 23502059035fStedu break; 23512059035fStedu array[nsubs++] = dp = cp; 23522059035fStedu do { 2353f0dc9fbaSderaadt if ((*dp = *cp++) != '"') { 23542059035fStedu ++dp; 2355f0dc9fbaSderaadt } else { 2356f0dc9fbaSderaadt while ((*dp = *cp++) != '"') { 23572059035fStedu if (*dp != '\0') 23582059035fStedu ++dp; 23592059035fStedu else { 2360f0dc9fbaSderaadt error("Odd number of quotation marks"); 2361b3cc38c2Stedu exit(EXIT_FAILURE); 23622059035fStedu } 2363f0dc9fbaSderaadt } 2364f0dc9fbaSderaadt } 23652059035fStedu } while (*cp != '\0' && *cp != '#' && 23662059035fStedu (!isascii((unsigned char)*cp) || !isspace((unsigned char)*cp))); 23672059035fStedu if (isascii((unsigned char)*cp) && isspace((unsigned char)*cp)) 23682059035fStedu ++cp; 23692059035fStedu *dp = '\0'; 23702059035fStedu } 23712059035fStedu array[nsubs] = NULL; 23722059035fStedu return array; 23732059035fStedu } 23742059035fStedu 23752059035fStedu static long 2376738fad2fStedu oadd(long t1, long t2) 23772059035fStedu { 2378f0dc9fbaSderaadt long t = t1 + t2; 23792059035fStedu 23802059035fStedu if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { 2381f0dc9fbaSderaadt error("time overflow"); 23822059035fStedu exit(EXIT_FAILURE); 23832059035fStedu } 23842059035fStedu return t; 23852059035fStedu } 23862059035fStedu 23872059035fStedu static zic_t 2388738fad2fStedu tadd(zic_t t1, long t2) 23892059035fStedu { 239066e58127Stedu zic_t t; 23912059035fStedu 23922059035fStedu if (t1 == max_time && t2 > 0) 23932059035fStedu return max_time; 23942059035fStedu if (t1 == min_time && t2 < 0) 23952059035fStedu return min_time; 23962059035fStedu t = t1 + t2; 23972059035fStedu if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { 2398f0dc9fbaSderaadt error("time overflow"); 23992059035fStedu exit(EXIT_FAILURE); 24002059035fStedu } 24012059035fStedu return t; 24022059035fStedu } 24032059035fStedu 24042059035fStedu /* 24052059035fStedu ** Given a rule, and a year, compute the date - in seconds since January 1, 24062059035fStedu ** 1970, 00:00 LOCAL time - in that year that the rule refers to. 24072059035fStedu */ 24082059035fStedu static zic_t 2409738fad2fStedu rpytime(const struct rule *rp, int wantedy) 24102059035fStedu { 241166e58127Stedu int y, m, i; 241266e58127Stedu long dayoff; /* with a nod to Margaret O. */ 241366e58127Stedu zic_t t; 24142059035fStedu 24152059035fStedu if (wantedy == INT_MIN) 24162059035fStedu return min_time; 24172059035fStedu if (wantedy == INT_MAX) 24182059035fStedu return max_time; 24192059035fStedu dayoff = 0; 24202059035fStedu m = TM_JANUARY; 24212059035fStedu y = EPOCH_YEAR; 24222059035fStedu while (wantedy != y) { 24232059035fStedu if (wantedy > y) { 24242059035fStedu i = len_years[isleap(y)]; 24252059035fStedu ++y; 24262059035fStedu } else { 24272059035fStedu --y; 24282059035fStedu i = -len_years[isleap(y)]; 24292059035fStedu } 24302059035fStedu dayoff = oadd(dayoff, eitol(i)); 24312059035fStedu } 24322059035fStedu while (m != rp->r_month) { 24332059035fStedu i = len_months[isleap(y)][m]; 24342059035fStedu dayoff = oadd(dayoff, eitol(i)); 24352059035fStedu ++m; 24362059035fStedu } 24372059035fStedu i = rp->r_dayofmonth; 24382059035fStedu if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { 24392059035fStedu if (rp->r_dycode == DC_DOWLEQ) 24402059035fStedu --i; 24412059035fStedu else { 2442f0dc9fbaSderaadt error("use of 2/29 in non leap-year"); 24432059035fStedu exit(EXIT_FAILURE); 24442059035fStedu } 24452059035fStedu } 24462059035fStedu --i; 24472059035fStedu dayoff = oadd(dayoff, eitol(i)); 24482059035fStedu if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { 244966e58127Stedu long wday; 24502059035fStedu 24512059035fStedu #define LDAYSPERWEEK ((long) DAYSPERWEEK) 24522059035fStedu wday = eitol(EPOCH_WDAY); 24532059035fStedu /* 24542059035fStedu ** Don't trust mod of negative numbers. 24552059035fStedu */ 24562059035fStedu if (dayoff >= 0) 24572059035fStedu wday = (wday + dayoff) % LDAYSPERWEEK; 24582059035fStedu else { 24592059035fStedu wday -= ((-dayoff) % LDAYSPERWEEK); 24602059035fStedu if (wday < 0) 24612059035fStedu wday += LDAYSPERWEEK; 24622059035fStedu } 24632059035fStedu while (wday != eitol(rp->r_wday)) 24642059035fStedu if (rp->r_dycode == DC_DOWGEQ) { 246575b1bb69Stedu dayoff = oadd(dayoff, 1); 24662059035fStedu if (++wday >= LDAYSPERWEEK) 24672059035fStedu wday = 0; 24682059035fStedu ++i; 24692059035fStedu } else { 247075b1bb69Stedu dayoff = oadd(dayoff, -1); 24712059035fStedu if (--wday < 0) 24722059035fStedu wday = LDAYSPERWEEK - 1; 24732059035fStedu --i; 24742059035fStedu } 24752059035fStedu } 24762059035fStedu if (dayoff < min_time / SECSPERDAY) 24772059035fStedu return min_time; 24782059035fStedu if (dayoff > max_time / SECSPERDAY) 24792059035fStedu return max_time; 24802059035fStedu t = (zic_t) dayoff * SECSPERDAY; 24812059035fStedu return tadd(t, rp->r_tod); 24822059035fStedu } 24832059035fStedu 24842059035fStedu static void 2485738fad2fStedu newabbr(const char *string) 24862059035fStedu { 248766e58127Stedu int i; 24882059035fStedu 24892059035fStedu if (strcmp(string, GRANDPARENTED) != 0) { 249066e58127Stedu const char * cp; 249166e58127Stedu char * wp; 24922059035fStedu 24932059035fStedu cp = string; 24942059035fStedu wp = NULL; 24952059035fStedu while (isascii((unsigned char)*cp) && 2496fcefcf04Smillert (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+')) 24972059035fStedu ++cp; 24982059035fStedu if (noise && cp - string > 3) 2499fcefcf04Smillert wp = "time zone abbreviation has more than 3 characters"; 25002059035fStedu if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) 2501fcefcf04Smillert wp = "time zone abbreviation has too many characters"; 25022059035fStedu if (*cp != '\0') 2503f0dc9fbaSderaadt wp = "time zone abbreviation differs from POSIX standard"; 25042059035fStedu if (wp != NULL) { 25052059035fStedu wp = ecpyalloc(wp); 25062059035fStedu wp = ecatalloc(wp, " ("); 25072059035fStedu wp = ecatalloc(wp, string); 25082059035fStedu wp = ecatalloc(wp, ")"); 25092059035fStedu warning(wp); 25102059035fStedu free(wp); 25112059035fStedu } 25122059035fStedu } 25132059035fStedu i = strlen(string) + 1; 25142059035fStedu if (charcnt + i > TZ_MAX_CHARS) { 2515f0dc9fbaSderaadt error("too many, or too long, time zone abbreviations"); 25162059035fStedu exit(EXIT_FAILURE); 25172059035fStedu } 251875b1bb69Stedu strlcpy(&chars[charcnt], string, sizeof(chars) - charcnt); 25192059035fStedu charcnt += eitol(i); 25202059035fStedu } 25212059035fStedu 25222059035fStedu static int 2523738fad2fStedu mkdirs(char *argname) 25242059035fStedu { 252566e58127Stedu char * name; 252666e58127Stedu char * cp; 25272059035fStedu 25282059035fStedu if (argname == NULL || *argname == '\0') 25292059035fStedu return 0; 25302059035fStedu cp = name = ecpyalloc(argname); 25312059035fStedu while ((cp = strchr(cp + 1, '/')) != 0) { 25322059035fStedu *cp = '\0'; 25332059035fStedu if (!itsdir(name)) { 25342059035fStedu /* 25352059035fStedu ** It doesn't seem to exist, so we try to create it. 25362059035fStedu ** Creation may fail because of the directory being 25372059035fStedu ** created by some other multiprocessor, so we get 25382059035fStedu ** to do extra checking. 25392059035fStedu */ 25402059035fStedu if (mkdir(name, MKDIR_UMASK) != 0) { 25412059035fStedu const char *e = strerror(errno); 25422059035fStedu 25432059035fStedu if (errno != EEXIST || !itsdir(name)) { 254475b1bb69Stedu fprintf(stderr, 2545f0dc9fbaSderaadt "%s: Can't create directory %s: %s\n", 2546f0dc9fbaSderaadt __progname, name, e); 25472059035fStedu free(name); 25482059035fStedu return -1; 25492059035fStedu } 25502059035fStedu } 25512059035fStedu } 25522059035fStedu *cp = '/'; 25532059035fStedu } 25542059035fStedu free(name); 25552059035fStedu return 0; 25562059035fStedu } 25572059035fStedu 25582059035fStedu static long 2559738fad2fStedu eitol(int i) 25602059035fStedu { 2561f0dc9fbaSderaadt long l = i; 25622059035fStedu 2563f0dc9fbaSderaadt if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) 25644dc71ee2Stedu errx(1, "%d did not sign extend correctly", i); 25652059035fStedu return l; 25662059035fStedu } 2567