1bc421551SDag-Erling Smørgrav /* Compile .zi time zone data into TZif binary files. */ 2bc421551SDag-Erling Smørgrav 3bc421551SDag-Erling Smørgrav /* 4bc421551SDag-Erling Smørgrav ** This file is in the public domain, so clarified as of 5bc421551SDag-Erling Smørgrav ** 2006-07-17 by Arthur David Olson. 6bc421551SDag-Erling Smørgrav */ 7bc421551SDag-Erling Smørgrav 8bc421551SDag-Erling Smørgrav /* Use the system 'time' function, instead of any private replacement. 9bc421551SDag-Erling Smørgrav This avoids creating an unnecessary dependency on localtime.c. */ 10bc421551SDag-Erling Smørgrav #undef EPOCH_LOCAL 11bc421551SDag-Erling Smørgrav #undef EPOCH_OFFSET 12bc421551SDag-Erling Smørgrav #undef RESERVE_STD_EXT_IDS 13bc421551SDag-Erling Smørgrav #undef time_tz 14bc421551SDag-Erling Smørgrav 15bc421551SDag-Erling Smørgrav #include "version.h" 16bc421551SDag-Erling Smørgrav #include "private.h" 17bc421551SDag-Erling Smørgrav #include "tzfile.h" 18bc421551SDag-Erling Smørgrav 19bc421551SDag-Erling Smørgrav #include <fcntl.h> 20bc421551SDag-Erling Smørgrav #include <locale.h> 21bc421551SDag-Erling Smørgrav #include <signal.h> 22bc421551SDag-Erling Smørgrav #include <stdarg.h> 23bc421551SDag-Erling Smørgrav #include <stdio.h> 24bc421551SDag-Erling Smørgrav 25bc421551SDag-Erling Smørgrav typedef int_fast64_t zic_t; 26bc421551SDag-Erling Smørgrav static zic_t const 27bc421551SDag-Erling Smørgrav ZIC_MIN = INT_FAST64_MIN, 28bc421551SDag-Erling Smørgrav ZIC_MAX = INT_FAST64_MAX, 29bc421551SDag-Erling Smørgrav ZIC32_MIN = -1 - (zic_t) 0x7fffffff, 30bc421551SDag-Erling Smørgrav ZIC32_MAX = 0x7fffffff; 31bc421551SDag-Erling Smørgrav #define SCNdZIC SCNdFAST64 32bc421551SDag-Erling Smørgrav 33bc421551SDag-Erling Smørgrav #ifndef ZIC_MAX_ABBR_LEN_WO_WARN 34bc421551SDag-Erling Smørgrav # define ZIC_MAX_ABBR_LEN_WO_WARN 6 35bc421551SDag-Erling Smørgrav #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ 36bc421551SDag-Erling Smørgrav 37bc421551SDag-Erling Smørgrav /* An upper bound on how much a format might grow due to concatenation. */ 38bc421551SDag-Erling Smørgrav enum { FORMAT_LEN_GROWTH_BOUND = 5 }; 39bc421551SDag-Erling Smørgrav 40bc421551SDag-Erling Smørgrav #ifdef HAVE_DIRECT_H 41bc421551SDag-Erling Smørgrav # include <direct.h> 42bc421551SDag-Erling Smørgrav # include <io.h> 43bc421551SDag-Erling Smørgrav # undef mkdir 44bc421551SDag-Erling Smørgrav # define mkdir(name, mode) _mkdir(name) 45bc421551SDag-Erling Smørgrav #endif 46bc421551SDag-Erling Smørgrav 47bc421551SDag-Erling Smørgrav #ifndef HAVE_GETRANDOM 48bc421551SDag-Erling Smørgrav # ifdef __has_include 49bc421551SDag-Erling Smørgrav # if __has_include(<sys/random.h>) 50bc421551SDag-Erling Smørgrav # include <sys/random.h> 51bc421551SDag-Erling Smørgrav # endif 52bc421551SDag-Erling Smørgrav # elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__) 53bc421551SDag-Erling Smørgrav # include <sys/random.h> 54bc421551SDag-Erling Smørgrav # endif 55bc421551SDag-Erling Smørgrav # define HAVE_GETRANDOM GRND_RANDOM 56bc421551SDag-Erling Smørgrav #elif HAVE_GETRANDOM 57bc421551SDag-Erling Smørgrav # include <sys/random.h> 58bc421551SDag-Erling Smørgrav #endif 59bc421551SDag-Erling Smørgrav 60bc421551SDag-Erling Smørgrav #if HAVE_SYS_STAT_H 61bc421551SDag-Erling Smørgrav # include <sys/stat.h> 62bc421551SDag-Erling Smørgrav #endif 63bc421551SDag-Erling Smørgrav #ifdef S_IRUSR 64bc421551SDag-Erling Smørgrav # define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 65bc421551SDag-Erling Smørgrav #else 66bc421551SDag-Erling Smørgrav # define MKDIR_UMASK 0755 67bc421551SDag-Erling Smørgrav #endif 68bc421551SDag-Erling Smørgrav 69bc421551SDag-Erling Smørgrav /* The minimum alignment of a type, for pre-C23 platforms. */ 70bc421551SDag-Erling Smørgrav #if __STDC_VERSION__ < 201112 71bc421551SDag-Erling Smørgrav # define alignof(type) offsetof(struct { char a; type b; }, b) 72bc421551SDag-Erling Smørgrav #elif __STDC_VERSION__ < 202311 73bc421551SDag-Erling Smørgrav # include <stdalign.h> 74bc421551SDag-Erling Smørgrav #endif 75bc421551SDag-Erling Smørgrav 76bc421551SDag-Erling Smørgrav /* The maximum length of a text line, including the trailing newline. */ 77bc421551SDag-Erling Smørgrav #ifndef _POSIX2_LINE_MAX 78bc421551SDag-Erling Smørgrav # define _POSIX2_LINE_MAX 2048 79bc421551SDag-Erling Smørgrav #endif 80bc421551SDag-Erling Smørgrav 81bc421551SDag-Erling Smørgrav /* The type for line numbers. Use PRIdMAX to format them; formerly 82bc421551SDag-Erling Smørgrav there was also "#define PRIdLINENO PRIdMAX" and formats used 83bc421551SDag-Erling Smørgrav PRIdLINENO, but xgettext cannot grok that. */ 84bc421551SDag-Erling Smørgrav typedef intmax_t lineno; 85bc421551SDag-Erling Smørgrav 86bc421551SDag-Erling Smørgrav struct rule { 87bc421551SDag-Erling Smørgrav int r_filenum; 88bc421551SDag-Erling Smørgrav lineno r_linenum; 89bc421551SDag-Erling Smørgrav const char * r_name; 90bc421551SDag-Erling Smørgrav 91bc421551SDag-Erling Smørgrav zic_t r_loyear; /* for example, 1986 */ 92bc421551SDag-Erling Smørgrav zic_t r_hiyear; /* for example, 1986 */ 93bc421551SDag-Erling Smørgrav bool r_lowasnum; 94bc421551SDag-Erling Smørgrav bool r_hiwasnum; 95bc421551SDag-Erling Smørgrav 96bc421551SDag-Erling Smørgrav int r_month; /* 0..11 */ 97bc421551SDag-Erling Smørgrav 98bc421551SDag-Erling Smørgrav int r_dycode; /* see below */ 99bc421551SDag-Erling Smørgrav int r_dayofmonth; 100bc421551SDag-Erling Smørgrav int r_wday; 101bc421551SDag-Erling Smørgrav 102bc421551SDag-Erling Smørgrav zic_t r_tod; /* time from midnight */ 103bc421551SDag-Erling Smørgrav bool r_todisstd; /* is r_tod standard time? */ 104bc421551SDag-Erling Smørgrav bool r_todisut; /* is r_tod UT? */ 105bc421551SDag-Erling Smørgrav bool r_isdst; /* is this daylight saving time? */ 106bc421551SDag-Erling Smørgrav zic_t r_save; /* offset from standard time */ 107bc421551SDag-Erling Smørgrav const char * r_abbrvar; /* variable part of abbreviation */ 108bc421551SDag-Erling Smørgrav 109bc421551SDag-Erling Smørgrav bool r_todo; /* a rule to do (used in outzone) */ 110bc421551SDag-Erling Smørgrav zic_t r_temp; /* used in outzone */ 111bc421551SDag-Erling Smørgrav }; 112bc421551SDag-Erling Smørgrav 113bc421551SDag-Erling Smørgrav /* 114bc421551SDag-Erling Smørgrav ** r_dycode r_dayofmonth r_wday 115bc421551SDag-Erling Smørgrav */ 116bc421551SDag-Erling Smørgrav enum { 117bc421551SDag-Erling Smørgrav DC_DOM, /* 1..31 */ /* unused */ 118bc421551SDag-Erling Smørgrav DC_DOWGEQ, /* 1..31 */ /* 0..6 (Sun..Sat) */ 119bc421551SDag-Erling Smørgrav DC_DOWLEQ /* 1..31 */ /* 0..6 (Sun..Sat) */ 120bc421551SDag-Erling Smørgrav }; 121bc421551SDag-Erling Smørgrav 122bc421551SDag-Erling Smørgrav struct zone { 123bc421551SDag-Erling Smørgrav int z_filenum; 124bc421551SDag-Erling Smørgrav lineno z_linenum; 125bc421551SDag-Erling Smørgrav 126bc421551SDag-Erling Smørgrav const char * z_name; 127bc421551SDag-Erling Smørgrav zic_t z_stdoff; 128bc421551SDag-Erling Smørgrav char * z_rule; 129bc421551SDag-Erling Smørgrav const char * z_format; 130bc421551SDag-Erling Smørgrav char z_format_specifier; 131bc421551SDag-Erling Smørgrav 132bc421551SDag-Erling Smørgrav bool z_isdst; 133bc421551SDag-Erling Smørgrav zic_t z_save; 134bc421551SDag-Erling Smørgrav 135bc421551SDag-Erling Smørgrav struct rule * z_rules; 136bc421551SDag-Erling Smørgrav ptrdiff_t z_nrules; 137bc421551SDag-Erling Smørgrav 138bc421551SDag-Erling Smørgrav struct rule z_untilrule; 139bc421551SDag-Erling Smørgrav zic_t z_untiltime; 140bc421551SDag-Erling Smørgrav }; 141bc421551SDag-Erling Smørgrav 142bc421551SDag-Erling Smørgrav #if !HAVE_POSIX_DECLS 143bc421551SDag-Erling Smørgrav extern int getopt(int argc, char * const argv[], 144bc421551SDag-Erling Smørgrav const char * options); 145bc421551SDag-Erling Smørgrav extern int link(const char * target, const char * linkname); 146bc421551SDag-Erling Smørgrav extern char * optarg; 147bc421551SDag-Erling Smørgrav extern int optind; 148bc421551SDag-Erling Smørgrav #endif 149bc421551SDag-Erling Smørgrav 150bc421551SDag-Erling Smørgrav #if ! HAVE_SYMLINK 151bc421551SDag-Erling Smørgrav static ssize_t 152bc421551SDag-Erling Smørgrav readlink(char const *restrict file, char *restrict buf, size_t size) 153bc421551SDag-Erling Smørgrav { 154bc421551SDag-Erling Smørgrav errno = ENOTSUP; 155bc421551SDag-Erling Smørgrav return -1; 156bc421551SDag-Erling Smørgrav } 157bc421551SDag-Erling Smørgrav static int 158bc421551SDag-Erling Smørgrav symlink(char const *target, char const *linkname) 159bc421551SDag-Erling Smørgrav { 160bc421551SDag-Erling Smørgrav errno = ENOTSUP; 161bc421551SDag-Erling Smørgrav return -1; 162bc421551SDag-Erling Smørgrav } 163bc421551SDag-Erling Smørgrav #endif 164bc421551SDag-Erling Smørgrav #ifndef AT_SYMLINK_FOLLOW 165bc421551SDag-Erling Smørgrav # if HAVE_LINK 166bc421551SDag-Erling Smørgrav # define linkat(targetdir, target, linknamedir, linkname, flag) \ 167bc421551SDag-Erling Smørgrav (itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname)) 168bc421551SDag-Erling Smørgrav # else 169bc421551SDag-Erling Smørgrav # define linkat(targetdir, target, linknamedir, linkname, flag) \ 170bc421551SDag-Erling Smørgrav (errno = ENOTSUP, -1) 171bc421551SDag-Erling Smørgrav # endif 172bc421551SDag-Erling Smørgrav #endif 173bc421551SDag-Erling Smørgrav 174bc421551SDag-Erling Smørgrav static void addtt(zic_t starttime, int type); 175bc421551SDag-Erling Smørgrav static int addtype(zic_t, char const *, bool, bool, bool); 176bc421551SDag-Erling Smørgrav static void leapadd(zic_t, int, int); 177bc421551SDag-Erling Smørgrav static void adjleap(void); 178bc421551SDag-Erling Smørgrav static void associate(void); 179bc421551SDag-Erling Smørgrav static void dolink(const char *, const char *, bool); 180bc421551SDag-Erling Smørgrav static int getfields(char *, char **, int); 181bc421551SDag-Erling Smørgrav static zic_t gethms(const char * string, const char * errstring); 182bc421551SDag-Erling Smørgrav static zic_t getsave(char *, bool *); 183bc421551SDag-Erling Smørgrav static void inexpires(char **, int); 184bc421551SDag-Erling Smørgrav static void infile(int, char const *); 185bc421551SDag-Erling Smørgrav static void inleap(char ** fields, int nfields); 186bc421551SDag-Erling Smørgrav static void inlink(char ** fields, int nfields); 187bc421551SDag-Erling Smørgrav static void inrule(char ** fields, int nfields); 188bc421551SDag-Erling Smørgrav static bool inzcont(char ** fields, int nfields); 189bc421551SDag-Erling Smørgrav static bool inzone(char ** fields, int nfields); 190bc421551SDag-Erling Smørgrav static bool inzsub(char **, int, bool); 191bc421551SDag-Erling Smørgrav static bool itssymlink(char const *); 192bc421551SDag-Erling Smørgrav static bool is_alpha(char a); 193bc421551SDag-Erling Smørgrav static char lowerit(char); 194bc421551SDag-Erling Smørgrav static void mkdirs(char const *, bool); 195bc421551SDag-Erling Smørgrav static void newabbr(const char * abbr); 196bc421551SDag-Erling Smørgrav static zic_t oadd(zic_t t1, zic_t t2); 197bc421551SDag-Erling Smørgrav static void outzone(const struct zone * zp, ptrdiff_t ntzones); 198bc421551SDag-Erling Smørgrav static zic_t rpytime(const struct rule * rp, zic_t wantedy); 199bc421551SDag-Erling Smørgrav static bool rulesub(struct rule * rp, 200bc421551SDag-Erling Smørgrav const char * loyearp, const char * hiyearp, 201bc421551SDag-Erling Smørgrav const char * typep, const char * monthp, 202bc421551SDag-Erling Smørgrav const char * dayp, const char * timep); 203bc421551SDag-Erling Smørgrav static void setgroup(gid_t *flag, const char *name); 204bc421551SDag-Erling Smørgrav static void setuser(uid_t *flag, const char *name); 205bc421551SDag-Erling Smørgrav static zic_t tadd(zic_t t1, zic_t t2); 206bc421551SDag-Erling Smørgrav 207bc421551SDag-Erling Smørgrav /* Bound on length of what %z can expand to. */ 208bc421551SDag-Erling Smørgrav enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 }; 209bc421551SDag-Erling Smørgrav 210bc421551SDag-Erling Smørgrav static int charcnt; 211bc421551SDag-Erling Smørgrav static bool errors; 212bc421551SDag-Erling Smørgrav static bool warnings; 213bc421551SDag-Erling Smørgrav static int filenum; 214bc421551SDag-Erling Smørgrav static int leapcnt; 215bc421551SDag-Erling Smørgrav static bool leapseen; 216bc421551SDag-Erling Smørgrav static zic_t leapminyear; 217bc421551SDag-Erling Smørgrav static zic_t leapmaxyear; 218bc421551SDag-Erling Smørgrav static lineno linenum; 219bc421551SDag-Erling Smørgrav static size_t max_abbrvar_len = PERCENT_Z_LEN_BOUND; 220bc421551SDag-Erling Smørgrav static int max_format_len; 221bc421551SDag-Erling Smørgrav static zic_t max_year; 222bc421551SDag-Erling Smørgrav static zic_t min_year; 223bc421551SDag-Erling Smørgrav static bool noise; 224bc421551SDag-Erling Smørgrav static int rfilenum; 225bc421551SDag-Erling Smørgrav static lineno rlinenum; 226bc421551SDag-Erling Smørgrav static const char * progname; 227bc421551SDag-Erling Smørgrav static char const * leapsec; 228bc421551SDag-Erling Smørgrav static char *const * main_argv; 229bc421551SDag-Erling Smørgrav static ptrdiff_t timecnt; 230bc421551SDag-Erling Smørgrav static ptrdiff_t timecnt_alloc; 231bc421551SDag-Erling Smørgrav static int typecnt; 232bc421551SDag-Erling Smørgrav static int unspecifiedtype; 233bc421551SDag-Erling Smørgrav 234bc421551SDag-Erling Smørgrav /* 235bc421551SDag-Erling Smørgrav ** Line codes. 236bc421551SDag-Erling Smørgrav */ 237bc421551SDag-Erling Smørgrav 238bc421551SDag-Erling Smørgrav enum { 239bc421551SDag-Erling Smørgrav LC_RULE, 240bc421551SDag-Erling Smørgrav LC_ZONE, 241bc421551SDag-Erling Smørgrav LC_LINK, 242bc421551SDag-Erling Smørgrav LC_LEAP, 243bc421551SDag-Erling Smørgrav LC_EXPIRES 244bc421551SDag-Erling Smørgrav }; 245bc421551SDag-Erling Smørgrav 246bc421551SDag-Erling Smørgrav /* 247bc421551SDag-Erling Smørgrav ** Which fields are which on a Zone line. 248bc421551SDag-Erling Smørgrav */ 249bc421551SDag-Erling Smørgrav 250bc421551SDag-Erling Smørgrav enum { 251bc421551SDag-Erling Smørgrav ZF_NAME = 1, 252bc421551SDag-Erling Smørgrav ZF_STDOFF, 253bc421551SDag-Erling Smørgrav ZF_RULE, 254bc421551SDag-Erling Smørgrav ZF_FORMAT, 255bc421551SDag-Erling Smørgrav ZF_TILYEAR, 256bc421551SDag-Erling Smørgrav ZF_TILMONTH, 257bc421551SDag-Erling Smørgrav ZF_TILDAY, 258bc421551SDag-Erling Smørgrav ZF_TILTIME, 259bc421551SDag-Erling Smørgrav ZONE_MAXFIELDS, 260bc421551SDag-Erling Smørgrav ZONE_MINFIELDS = ZF_TILYEAR 261bc421551SDag-Erling Smørgrav }; 262bc421551SDag-Erling Smørgrav 263bc421551SDag-Erling Smørgrav /* 264bc421551SDag-Erling Smørgrav ** Which fields are which on a Zone continuation line. 265bc421551SDag-Erling Smørgrav */ 266bc421551SDag-Erling Smørgrav 267bc421551SDag-Erling Smørgrav enum { 268bc421551SDag-Erling Smørgrav ZFC_STDOFF, 269bc421551SDag-Erling Smørgrav ZFC_RULE, 270bc421551SDag-Erling Smørgrav ZFC_FORMAT, 271bc421551SDag-Erling Smørgrav ZFC_TILYEAR, 272bc421551SDag-Erling Smørgrav ZFC_TILMONTH, 273bc421551SDag-Erling Smørgrav ZFC_TILDAY, 274bc421551SDag-Erling Smørgrav ZFC_TILTIME, 275bc421551SDag-Erling Smørgrav ZONEC_MAXFIELDS, 276bc421551SDag-Erling Smørgrav ZONEC_MINFIELDS = ZFC_TILYEAR 277bc421551SDag-Erling Smørgrav }; 278bc421551SDag-Erling Smørgrav 279bc421551SDag-Erling Smørgrav /* 280bc421551SDag-Erling Smørgrav ** Which files are which on a Rule line. 281bc421551SDag-Erling Smørgrav */ 282bc421551SDag-Erling Smørgrav 283bc421551SDag-Erling Smørgrav enum { 284bc421551SDag-Erling Smørgrav RF_NAME = 1, 285bc421551SDag-Erling Smørgrav RF_LOYEAR, 286bc421551SDag-Erling Smørgrav RF_HIYEAR, 287bc421551SDag-Erling Smørgrav RF_COMMAND, 288bc421551SDag-Erling Smørgrav RF_MONTH, 289bc421551SDag-Erling Smørgrav RF_DAY, 290bc421551SDag-Erling Smørgrav RF_TOD, 291bc421551SDag-Erling Smørgrav RF_SAVE, 292bc421551SDag-Erling Smørgrav RF_ABBRVAR, 293bc421551SDag-Erling Smørgrav RULE_FIELDS 294bc421551SDag-Erling Smørgrav }; 295bc421551SDag-Erling Smørgrav 296bc421551SDag-Erling Smørgrav /* 297bc421551SDag-Erling Smørgrav ** Which fields are which on a Link line. 298bc421551SDag-Erling Smørgrav */ 299bc421551SDag-Erling Smørgrav 300bc421551SDag-Erling Smørgrav enum { 301bc421551SDag-Erling Smørgrav LF_TARGET = 1, 302bc421551SDag-Erling Smørgrav LF_LINKNAME, 303bc421551SDag-Erling Smørgrav LINK_FIELDS 304bc421551SDag-Erling Smørgrav }; 305bc421551SDag-Erling Smørgrav 306bc421551SDag-Erling Smørgrav /* 307bc421551SDag-Erling Smørgrav ** Which fields are which on a Leap line. 308bc421551SDag-Erling Smørgrav */ 309bc421551SDag-Erling Smørgrav 310bc421551SDag-Erling Smørgrav enum { 311bc421551SDag-Erling Smørgrav LP_YEAR = 1, 312bc421551SDag-Erling Smørgrav LP_MONTH, 313bc421551SDag-Erling Smørgrav LP_DAY, 314bc421551SDag-Erling Smørgrav LP_TIME, 315bc421551SDag-Erling Smørgrav LP_CORR, 316bc421551SDag-Erling Smørgrav LP_ROLL, 317bc421551SDag-Erling Smørgrav LEAP_FIELDS, 318bc421551SDag-Erling Smørgrav 319bc421551SDag-Erling Smørgrav /* Expires lines are like Leap lines, except without CORR and ROLL fields. */ 320bc421551SDag-Erling Smørgrav EXPIRES_FIELDS = LP_TIME + 1 321bc421551SDag-Erling Smørgrav }; 322bc421551SDag-Erling Smørgrav 323bc421551SDag-Erling Smørgrav /* The maximum number of fields on any of the above lines. 324bc421551SDag-Erling Smørgrav (The "+"s pacify gcc -Wenum-compare.) */ 325bc421551SDag-Erling Smørgrav enum { 326bc421551SDag-Erling Smørgrav MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS), 327bc421551SDag-Erling Smørgrav max(+LEAP_FIELDS, +EXPIRES_FIELDS)) 328bc421551SDag-Erling Smørgrav }; 329bc421551SDag-Erling Smørgrav 330bc421551SDag-Erling Smørgrav /* 331bc421551SDag-Erling Smørgrav ** Year synonyms. 332bc421551SDag-Erling Smørgrav */ 333bc421551SDag-Erling Smørgrav 334bc421551SDag-Erling Smørgrav enum { 335bc421551SDag-Erling Smørgrav YR_MINIMUM, 336bc421551SDag-Erling Smørgrav YR_MAXIMUM, 337bc421551SDag-Erling Smørgrav YR_ONLY 338bc421551SDag-Erling Smørgrav }; 339bc421551SDag-Erling Smørgrav 340bc421551SDag-Erling Smørgrav static struct rule * rules; 341bc421551SDag-Erling Smørgrav static ptrdiff_t nrules; /* number of rules */ 342bc421551SDag-Erling Smørgrav static ptrdiff_t nrules_alloc; 343bc421551SDag-Erling Smørgrav 344bc421551SDag-Erling Smørgrav static struct zone * zones; 345bc421551SDag-Erling Smørgrav static ptrdiff_t nzones; /* number of zones */ 346bc421551SDag-Erling Smørgrav static ptrdiff_t nzones_alloc; 347bc421551SDag-Erling Smørgrav 348bc421551SDag-Erling Smørgrav struct link { 349bc421551SDag-Erling Smørgrav int l_filenum; 350bc421551SDag-Erling Smørgrav lineno l_linenum; 351bc421551SDag-Erling Smørgrav const char * l_target; 352bc421551SDag-Erling Smørgrav const char * l_linkname; 353bc421551SDag-Erling Smørgrav }; 354bc421551SDag-Erling Smørgrav 355bc421551SDag-Erling Smørgrav static struct link * links; 356bc421551SDag-Erling Smørgrav static ptrdiff_t nlinks; 357bc421551SDag-Erling Smørgrav static ptrdiff_t nlinks_alloc; 358bc421551SDag-Erling Smørgrav 359bc421551SDag-Erling Smørgrav struct lookup { 360bc421551SDag-Erling Smørgrav const char * l_word; 361bc421551SDag-Erling Smørgrav const int l_value; 362bc421551SDag-Erling Smørgrav }; 363bc421551SDag-Erling Smørgrav 364bc421551SDag-Erling Smørgrav static struct lookup const * byword(const char * string, 365bc421551SDag-Erling Smørgrav const struct lookup * lp); 366bc421551SDag-Erling Smørgrav 367bc421551SDag-Erling Smørgrav static struct lookup const zi_line_codes[] = { 368bc421551SDag-Erling Smørgrav { "Rule", LC_RULE }, 369bc421551SDag-Erling Smørgrav { "Zone", LC_ZONE }, 370bc421551SDag-Erling Smørgrav { "Link", LC_LINK }, 371bc421551SDag-Erling Smørgrav { NULL, 0 } 372bc421551SDag-Erling Smørgrav }; 373bc421551SDag-Erling Smørgrav static struct lookup const leap_line_codes[] = { 374bc421551SDag-Erling Smørgrav { "Leap", LC_LEAP }, 375bc421551SDag-Erling Smørgrav { "Expires", LC_EXPIRES }, 376bc421551SDag-Erling Smørgrav { NULL, 0} 377bc421551SDag-Erling Smørgrav }; 378bc421551SDag-Erling Smørgrav 379bc421551SDag-Erling Smørgrav static struct lookup const mon_names[] = { 380bc421551SDag-Erling Smørgrav { "January", TM_JANUARY }, 381bc421551SDag-Erling Smørgrav { "February", TM_FEBRUARY }, 382bc421551SDag-Erling Smørgrav { "March", TM_MARCH }, 383bc421551SDag-Erling Smørgrav { "April", TM_APRIL }, 384bc421551SDag-Erling Smørgrav { "May", TM_MAY }, 385bc421551SDag-Erling Smørgrav { "June", TM_JUNE }, 386bc421551SDag-Erling Smørgrav { "July", TM_JULY }, 387bc421551SDag-Erling Smørgrav { "August", TM_AUGUST }, 388bc421551SDag-Erling Smørgrav { "September", TM_SEPTEMBER }, 389bc421551SDag-Erling Smørgrav { "October", TM_OCTOBER }, 390bc421551SDag-Erling Smørgrav { "November", TM_NOVEMBER }, 391bc421551SDag-Erling Smørgrav { "December", TM_DECEMBER }, 392bc421551SDag-Erling Smørgrav { NULL, 0 } 393bc421551SDag-Erling Smørgrav }; 394bc421551SDag-Erling Smørgrav 395bc421551SDag-Erling Smørgrav static struct lookup const wday_names[] = { 396bc421551SDag-Erling Smørgrav { "Sunday", TM_SUNDAY }, 397bc421551SDag-Erling Smørgrav { "Monday", TM_MONDAY }, 398bc421551SDag-Erling Smørgrav { "Tuesday", TM_TUESDAY }, 399bc421551SDag-Erling Smørgrav { "Wednesday", TM_WEDNESDAY }, 400bc421551SDag-Erling Smørgrav { "Thursday", TM_THURSDAY }, 401bc421551SDag-Erling Smørgrav { "Friday", TM_FRIDAY }, 402bc421551SDag-Erling Smørgrav { "Saturday", TM_SATURDAY }, 403bc421551SDag-Erling Smørgrav { NULL, 0 } 404bc421551SDag-Erling Smørgrav }; 405bc421551SDag-Erling Smørgrav 406bc421551SDag-Erling Smørgrav static struct lookup const lasts[] = { 407bc421551SDag-Erling Smørgrav { "last-Sunday", TM_SUNDAY }, 408bc421551SDag-Erling Smørgrav { "last-Monday", TM_MONDAY }, 409bc421551SDag-Erling Smørgrav { "last-Tuesday", TM_TUESDAY }, 410bc421551SDag-Erling Smørgrav { "last-Wednesday", TM_WEDNESDAY }, 411bc421551SDag-Erling Smørgrav { "last-Thursday", TM_THURSDAY }, 412bc421551SDag-Erling Smørgrav { "last-Friday", TM_FRIDAY }, 413bc421551SDag-Erling Smørgrav { "last-Saturday", TM_SATURDAY }, 414bc421551SDag-Erling Smørgrav { NULL, 0 } 415bc421551SDag-Erling Smørgrav }; 416bc421551SDag-Erling Smørgrav 417bc421551SDag-Erling Smørgrav static struct lookup const begin_years[] = { 418bc421551SDag-Erling Smørgrav { "minimum", YR_MINIMUM }, 419bc421551SDag-Erling Smørgrav { "maximum", YR_MAXIMUM }, 420bc421551SDag-Erling Smørgrav { NULL, 0 } 421bc421551SDag-Erling Smørgrav }; 422bc421551SDag-Erling Smørgrav 423bc421551SDag-Erling Smørgrav static struct lookup const end_years[] = { 424bc421551SDag-Erling Smørgrav { "minimum", YR_MINIMUM }, 425bc421551SDag-Erling Smørgrav { "maximum", YR_MAXIMUM }, 426bc421551SDag-Erling Smørgrav { "only", YR_ONLY }, 427bc421551SDag-Erling Smørgrav { NULL, 0 } 428bc421551SDag-Erling Smørgrav }; 429bc421551SDag-Erling Smørgrav 430bc421551SDag-Erling Smørgrav static struct lookup const leap_types[] = { 431bc421551SDag-Erling Smørgrav { "Rolling", true }, 432bc421551SDag-Erling Smørgrav { "Stationary", false }, 433bc421551SDag-Erling Smørgrav { NULL, 0 } 434bc421551SDag-Erling Smørgrav }; 435bc421551SDag-Erling Smørgrav 436bc421551SDag-Erling Smørgrav static const int len_months[2][MONSPERYEAR] = { 437bc421551SDag-Erling Smørgrav { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 438bc421551SDag-Erling Smørgrav { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 439bc421551SDag-Erling Smørgrav }; 440bc421551SDag-Erling Smørgrav 441bc421551SDag-Erling Smørgrav static const int len_years[2] = { 442bc421551SDag-Erling Smørgrav DAYSPERNYEAR, DAYSPERLYEAR 443bc421551SDag-Erling Smørgrav }; 444bc421551SDag-Erling Smørgrav 445bc421551SDag-Erling Smørgrav static struct attype { 446bc421551SDag-Erling Smørgrav zic_t at; 447bc421551SDag-Erling Smørgrav bool dontmerge; 448bc421551SDag-Erling Smørgrav unsigned char type; 449bc421551SDag-Erling Smørgrav } * attypes; 450bc421551SDag-Erling Smørgrav static zic_t utoffs[TZ_MAX_TYPES]; 451bc421551SDag-Erling Smørgrav static char isdsts[TZ_MAX_TYPES]; 452bc421551SDag-Erling Smørgrav static unsigned char desigidx[TZ_MAX_TYPES]; 453bc421551SDag-Erling Smørgrav static bool ttisstds[TZ_MAX_TYPES]; 454bc421551SDag-Erling Smørgrav static bool ttisuts[TZ_MAX_TYPES]; 455bc421551SDag-Erling Smørgrav static char chars[TZ_MAX_CHARS]; 456bc421551SDag-Erling Smørgrav static zic_t trans[TZ_MAX_LEAPS]; 457bc421551SDag-Erling Smørgrav static zic_t corr[TZ_MAX_LEAPS]; 458bc421551SDag-Erling Smørgrav static char roll[TZ_MAX_LEAPS]; 459bc421551SDag-Erling Smørgrav 460bc421551SDag-Erling Smørgrav /* 461bc421551SDag-Erling Smørgrav ** Memory allocation. 462bc421551SDag-Erling Smørgrav */ 463bc421551SDag-Erling Smørgrav 464bc421551SDag-Erling Smørgrav static ATTRIBUTE_NORETURN void 465bc421551SDag-Erling Smørgrav memory_exhausted(const char *msg) 466bc421551SDag-Erling Smørgrav { 467bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg); 468bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 469bc421551SDag-Erling Smørgrav } 470bc421551SDag-Erling Smørgrav 471bc421551SDag-Erling Smørgrav static ATTRIBUTE_NORETURN void 472bc421551SDag-Erling Smørgrav size_overflow(void) 473bc421551SDag-Erling Smørgrav { 474bc421551SDag-Erling Smørgrav memory_exhausted(_("size overflow")); 475bc421551SDag-Erling Smørgrav } 476bc421551SDag-Erling Smørgrav 477*d5c85ac6SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE size_t 478bc421551SDag-Erling Smørgrav size_sum(size_t a, size_t b) 479bc421551SDag-Erling Smørgrav { 480bc421551SDag-Erling Smørgrav #ifdef ckd_add 481*d5c85ac6SDag-Erling Smørgrav size_t sum; 482*d5c85ac6SDag-Erling Smørgrav if (!ckd_add(&sum, a, b)) 483bc421551SDag-Erling Smørgrav return sum; 484bc421551SDag-Erling Smørgrav #else 485*d5c85ac6SDag-Erling Smørgrav if (b <= SIZE_MAX - a) 486bc421551SDag-Erling Smørgrav return a + b; 487bc421551SDag-Erling Smørgrav #endif 488bc421551SDag-Erling Smørgrav size_overflow(); 489bc421551SDag-Erling Smørgrav } 490bc421551SDag-Erling Smørgrav 491*d5c85ac6SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE size_t 492*d5c85ac6SDag-Erling Smørgrav size_product(size_t nitems, size_t itemsize) 493bc421551SDag-Erling Smørgrav { 494bc421551SDag-Erling Smørgrav #ifdef ckd_mul 495*d5c85ac6SDag-Erling Smørgrav size_t product; 496*d5c85ac6SDag-Erling Smørgrav if (!ckd_mul(&product, nitems, itemsize)) 497bc421551SDag-Erling Smørgrav return product; 498bc421551SDag-Erling Smørgrav #else 499*d5c85ac6SDag-Erling Smørgrav if (nitems <= SIZE_MAX / itemsize) 500bc421551SDag-Erling Smørgrav return nitems * itemsize; 501bc421551SDag-Erling Smørgrav #endif 502bc421551SDag-Erling Smørgrav size_overflow(); 503bc421551SDag-Erling Smørgrav } 504bc421551SDag-Erling Smørgrav 505*d5c85ac6SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE size_t 506*d5c85ac6SDag-Erling Smørgrav align_to(size_t size, size_t alignment) 507bc421551SDag-Erling Smørgrav { 508*d5c85ac6SDag-Erling Smørgrav size_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits); 509bc421551SDag-Erling Smørgrav return sum & ~lo_bits; 510bc421551SDag-Erling Smørgrav } 511bc421551SDag-Erling Smørgrav 512bc421551SDag-Erling Smørgrav #if !HAVE_STRDUP 513bc421551SDag-Erling Smørgrav static char * 514bc421551SDag-Erling Smørgrav strdup(char const *str) 515bc421551SDag-Erling Smørgrav { 516bc421551SDag-Erling Smørgrav char *result = malloc(strlen(str) + 1); 517bc421551SDag-Erling Smørgrav return result ? strcpy(result, str) : result; 518bc421551SDag-Erling Smørgrav } 519bc421551SDag-Erling Smørgrav #endif 520bc421551SDag-Erling Smørgrav 521bc421551SDag-Erling Smørgrav static void * 522bc421551SDag-Erling Smørgrav memcheck(void *ptr) 523bc421551SDag-Erling Smørgrav { 524bc421551SDag-Erling Smørgrav if (ptr == NULL) 525bc421551SDag-Erling Smørgrav memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM)); 526bc421551SDag-Erling Smørgrav return ptr; 527bc421551SDag-Erling Smørgrav } 528bc421551SDag-Erling Smørgrav 529bc421551SDag-Erling Smørgrav static void * ATTRIBUTE_MALLOC 530bc421551SDag-Erling Smørgrav emalloc(size_t size) 531bc421551SDag-Erling Smørgrav { 532bc421551SDag-Erling Smørgrav return memcheck(malloc(size)); 533bc421551SDag-Erling Smørgrav } 534bc421551SDag-Erling Smørgrav 535bc421551SDag-Erling Smørgrav static void * 536bc421551SDag-Erling Smørgrav erealloc(void *ptr, size_t size) 537bc421551SDag-Erling Smørgrav { 538bc421551SDag-Erling Smørgrav return memcheck(realloc(ptr, size)); 539bc421551SDag-Erling Smørgrav } 540bc421551SDag-Erling Smørgrav 541bc421551SDag-Erling Smørgrav static char * ATTRIBUTE_MALLOC 542bc421551SDag-Erling Smørgrav estrdup(char const *str) 543bc421551SDag-Erling Smørgrav { 544bc421551SDag-Erling Smørgrav return memcheck(strdup(str)); 545bc421551SDag-Erling Smørgrav } 546bc421551SDag-Erling Smørgrav 547bc421551SDag-Erling Smørgrav static ptrdiff_t 548bc421551SDag-Erling Smørgrav grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize) 549bc421551SDag-Erling Smørgrav { 550bc421551SDag-Erling Smørgrav ptrdiff_t addend = (*nitems_alloc >> 1) + 1; 551bc421551SDag-Erling Smørgrav #if defined ckd_add && defined ckd_mul 552bc421551SDag-Erling Smørgrav ptrdiff_t product; 553bc421551SDag-Erling Smørgrav if (!ckd_add(nitems_alloc, *nitems_alloc, addend) 554bc421551SDag-Erling Smørgrav && !ckd_mul(&product, *nitems_alloc, itemsize) /* && product <= SIZE_MAX */) 555bc421551SDag-Erling Smørgrav return product; 556bc421551SDag-Erling Smørgrav #else 557bc421551SDag-Erling Smørgrav ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX); 558bc421551SDag-Erling Smørgrav if (*nitems_alloc <= ((amax - 1) / 3 * 2) / itemsize) { 559bc421551SDag-Erling Smørgrav *nitems_alloc += addend; 560bc421551SDag-Erling Smørgrav return *nitems_alloc * itemsize; 561bc421551SDag-Erling Smørgrav } 562bc421551SDag-Erling Smørgrav #endif 563bc421551SDag-Erling Smørgrav memory_exhausted(_("integer overflow")); 564bc421551SDag-Erling Smørgrav } 565bc421551SDag-Erling Smørgrav 566bc421551SDag-Erling Smørgrav static void * 567bc421551SDag-Erling Smørgrav growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems, 568bc421551SDag-Erling Smørgrav ptrdiff_t *nitems_alloc) 569bc421551SDag-Erling Smørgrav { 570bc421551SDag-Erling Smørgrav return (nitems < *nitems_alloc 571bc421551SDag-Erling Smørgrav ? ptr 572bc421551SDag-Erling Smørgrav : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize))); 573bc421551SDag-Erling Smørgrav } 574bc421551SDag-Erling Smørgrav 575bc421551SDag-Erling Smørgrav /* 576bc421551SDag-Erling Smørgrav ** Error handling. 577bc421551SDag-Erling Smørgrav */ 578bc421551SDag-Erling Smørgrav 579bc421551SDag-Erling Smørgrav /* In most of the code, an input file name is represented by its index 580bc421551SDag-Erling Smørgrav into the main argument vector, except that LEAPSEC_FILENUM stands 581bc421551SDag-Erling Smørgrav for leapsec and COMMAND_LINE_FILENUM stands for the command line. */ 582bc421551SDag-Erling Smørgrav enum { LEAPSEC_FILENUM = -2, COMMAND_LINE_FILENUM = -1 }; 583bc421551SDag-Erling Smørgrav 584bc421551SDag-Erling Smørgrav /* Return the name of the Ith input file, for diagnostics. */ 585bc421551SDag-Erling Smørgrav static char const * 586bc421551SDag-Erling Smørgrav filename(int i) 587bc421551SDag-Erling Smørgrav { 588bc421551SDag-Erling Smørgrav if (i == COMMAND_LINE_FILENUM) 589bc421551SDag-Erling Smørgrav return _("command line"); 590bc421551SDag-Erling Smørgrav else { 591bc421551SDag-Erling Smørgrav char const *fname = i == LEAPSEC_FILENUM ? leapsec : main_argv[i]; 592bc421551SDag-Erling Smørgrav return strcmp(fname, "-") == 0 ? _("standard input") : fname; 593bc421551SDag-Erling Smørgrav } 594bc421551SDag-Erling Smørgrav } 595bc421551SDag-Erling Smørgrav 596bc421551SDag-Erling Smørgrav static void 597bc421551SDag-Erling Smørgrav eats(int fnum, lineno num, int rfnum, lineno rnum) 598bc421551SDag-Erling Smørgrav { 599bc421551SDag-Erling Smørgrav filenum = fnum; 600bc421551SDag-Erling Smørgrav linenum = num; 601bc421551SDag-Erling Smørgrav rfilenum = rfnum; 602bc421551SDag-Erling Smørgrav rlinenum = rnum; 603bc421551SDag-Erling Smørgrav } 604bc421551SDag-Erling Smørgrav 605bc421551SDag-Erling Smørgrav static void 606bc421551SDag-Erling Smørgrav eat(int fnum, lineno num) 607bc421551SDag-Erling Smørgrav { 608bc421551SDag-Erling Smørgrav eats(fnum, num, 0, -1); 609bc421551SDag-Erling Smørgrav } 610bc421551SDag-Erling Smørgrav 611bc421551SDag-Erling Smørgrav static void ATTRIBUTE_FORMAT((printf, 1, 0)) 612bc421551SDag-Erling Smørgrav verror(const char *const string, va_list args) 613bc421551SDag-Erling Smørgrav { 614bc421551SDag-Erling Smørgrav /* 615bc421551SDag-Erling Smørgrav ** Match the format of "cc" to allow sh users to 616bc421551SDag-Erling Smørgrav ** zic ... 2>&1 | error -t "*" -v 617bc421551SDag-Erling Smørgrav ** on BSD systems. 618bc421551SDag-Erling Smørgrav */ 619bc421551SDag-Erling Smørgrav if (filenum) 620bc421551SDag-Erling Smørgrav fprintf(stderr, _("\"%s\", line %"PRIdMAX": "), 621bc421551SDag-Erling Smørgrav filename(filenum), linenum); 622bc421551SDag-Erling Smørgrav vfprintf(stderr, string, args); 623bc421551SDag-Erling Smørgrav if (rfilenum) 624bc421551SDag-Erling Smørgrav fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"), 625bc421551SDag-Erling Smørgrav filename(rfilenum), rlinenum); 626bc421551SDag-Erling Smørgrav fprintf(stderr, "\n"); 627bc421551SDag-Erling Smørgrav } 628bc421551SDag-Erling Smørgrav 629bc421551SDag-Erling Smørgrav static void ATTRIBUTE_FORMAT((printf, 1, 2)) 630bc421551SDag-Erling Smørgrav error(const char *const string, ...) 631bc421551SDag-Erling Smørgrav { 632bc421551SDag-Erling Smørgrav va_list args; 633bc421551SDag-Erling Smørgrav va_start(args, string); 634bc421551SDag-Erling Smørgrav verror(string, args); 635bc421551SDag-Erling Smørgrav va_end(args); 636bc421551SDag-Erling Smørgrav errors = true; 637bc421551SDag-Erling Smørgrav } 638bc421551SDag-Erling Smørgrav 639bc421551SDag-Erling Smørgrav static void ATTRIBUTE_FORMAT((printf, 1, 2)) 640bc421551SDag-Erling Smørgrav warning(const char *const string, ...) 641bc421551SDag-Erling Smørgrav { 642bc421551SDag-Erling Smørgrav va_list args; 643bc421551SDag-Erling Smørgrav fprintf(stderr, _("warning: ")); 644bc421551SDag-Erling Smørgrav va_start(args, string); 645bc421551SDag-Erling Smørgrav verror(string, args); 646bc421551SDag-Erling Smørgrav va_end(args); 647bc421551SDag-Erling Smørgrav warnings = true; 648bc421551SDag-Erling Smørgrav } 649bc421551SDag-Erling Smørgrav 650bc421551SDag-Erling Smørgrav /* Close STREAM. If it had an I/O error, report it against DIR/NAME, 651bc421551SDag-Erling Smørgrav remove TEMPNAME if nonnull, and then exit. */ 652bc421551SDag-Erling Smørgrav static void 653bc421551SDag-Erling Smørgrav close_file(FILE *stream, char const *dir, char const *name, 654bc421551SDag-Erling Smørgrav char const *tempname) 655bc421551SDag-Erling Smørgrav { 656bc421551SDag-Erling Smørgrav char const *e = (ferror(stream) ? _("I/O error") 657bc421551SDag-Erling Smørgrav : fclose(stream) != 0 ? strerror(errno) : NULL); 658bc421551SDag-Erling Smørgrav if (e) { 659bc421551SDag-Erling Smørgrav fprintf(stderr, "%s: %s%s%s%s%s\n", progname, 660bc421551SDag-Erling Smørgrav dir ? dir : "", dir ? "/" : "", 661bc421551SDag-Erling Smørgrav name ? name : "", name ? ": " : "", 662bc421551SDag-Erling Smørgrav e); 663bc421551SDag-Erling Smørgrav if (tempname) 664bc421551SDag-Erling Smørgrav remove(tempname); 665bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 666bc421551SDag-Erling Smørgrav } 667bc421551SDag-Erling Smørgrav } 668bc421551SDag-Erling Smørgrav 669bc421551SDag-Erling Smørgrav static ATTRIBUTE_NORETURN void 670bc421551SDag-Erling Smørgrav usage(FILE *stream, int status) 671bc421551SDag-Erling Smørgrav { 672bc421551SDag-Erling Smørgrav fprintf(stream, 673bc421551SDag-Erling Smørgrav _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n" 674bc421551SDag-Erling Smørgrav "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]" 675bc421551SDag-Erling Smørgrav " [ -L leapseconds ] \\\n" 676bc421551SDag-Erling Smørgrav "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n" 677bc421551SDag-Erling Smørgrav "\t[ -t localtime-link ] [ -D ] [ -g gid ] [ -u uid ] \\\n" 678bc421551SDag-Erling Smørgrav "\t[ filename ... ]\n\n" 679bc421551SDag-Erling Smørgrav "Report bugs to %s.\n"), 680bc421551SDag-Erling Smørgrav progname, progname, REPORT_BUGS_TO); 681bc421551SDag-Erling Smørgrav if (status == EXIT_SUCCESS) 682bc421551SDag-Erling Smørgrav close_file(stream, NULL, NULL, NULL); 683bc421551SDag-Erling Smørgrav exit(status); 684bc421551SDag-Erling Smørgrav } 685bc421551SDag-Erling Smørgrav 686bc421551SDag-Erling Smørgrav /* Change the working directory to DIR, possibly creating DIR and its 687bc421551SDag-Erling Smørgrav ancestors. After this is done, all files are accessed with names 688bc421551SDag-Erling Smørgrav relative to DIR. */ 689bc421551SDag-Erling Smørgrav static void 690bc421551SDag-Erling Smørgrav change_directory(char const *dir) 691bc421551SDag-Erling Smørgrav { 692bc421551SDag-Erling Smørgrav if (chdir(dir) != 0) { 693bc421551SDag-Erling Smørgrav int chdir_errno = errno; 694bc421551SDag-Erling Smørgrav if (chdir_errno == ENOENT) { 695bc421551SDag-Erling Smørgrav mkdirs(dir, false); 696bc421551SDag-Erling Smørgrav chdir_errno = chdir(dir) == 0 ? 0 : errno; 697bc421551SDag-Erling Smørgrav } 698bc421551SDag-Erling Smørgrav if (chdir_errno != 0) { 699bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Can't chdir to %s: %s\n"), 700bc421551SDag-Erling Smørgrav progname, dir, strerror(chdir_errno)); 701bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 702bc421551SDag-Erling Smørgrav } 703bc421551SDag-Erling Smørgrav } 704bc421551SDag-Erling Smørgrav } 705bc421551SDag-Erling Smørgrav 706bc421551SDag-Erling Smørgrav /* Compare the two links A and B, for a stable sort by link name. */ 707bc421551SDag-Erling Smørgrav static int 708bc421551SDag-Erling Smørgrav qsort_linkcmp(void const *a, void const *b) 709bc421551SDag-Erling Smørgrav { 710bc421551SDag-Erling Smørgrav struct link const *l = a; 711bc421551SDag-Erling Smørgrav struct link const *m = b; 712bc421551SDag-Erling Smørgrav int cmp = strcmp(l->l_linkname, m->l_linkname); 713bc421551SDag-Erling Smørgrav if (cmp) 714bc421551SDag-Erling Smørgrav return cmp; 715bc421551SDag-Erling Smørgrav 716bc421551SDag-Erling Smørgrav /* The link names are the same. Make the sort stable by comparing 717bc421551SDag-Erling Smørgrav file numbers (where subtraction cannot overflow) and possibly 718bc421551SDag-Erling Smørgrav line numbers (where it can). */ 719bc421551SDag-Erling Smørgrav cmp = l->l_filenum - m->l_filenum; 720bc421551SDag-Erling Smørgrav if (cmp) 721bc421551SDag-Erling Smørgrav return cmp; 722bc421551SDag-Erling Smørgrav return (l->l_linenum > m->l_linenum) - (l->l_linenum < m->l_linenum); 723bc421551SDag-Erling Smørgrav } 724bc421551SDag-Erling Smørgrav 725bc421551SDag-Erling Smørgrav /* Compare the string KEY to the link B, for bsearch. */ 726bc421551SDag-Erling Smørgrav static int 727bc421551SDag-Erling Smørgrav bsearch_linkcmp(void const *key, void const *b) 728bc421551SDag-Erling Smørgrav { 729bc421551SDag-Erling Smørgrav struct link const *m = b; 730bc421551SDag-Erling Smørgrav return strcmp(key, m->l_linkname); 731bc421551SDag-Erling Smørgrav } 732bc421551SDag-Erling Smørgrav 733bc421551SDag-Erling Smørgrav /* Make the links specified by the Link lines. */ 734bc421551SDag-Erling Smørgrav static void 735bc421551SDag-Erling Smørgrav make_links(void) 736bc421551SDag-Erling Smørgrav { 737bc421551SDag-Erling Smørgrav ptrdiff_t i, j, nalinks, pass_size; 738bc421551SDag-Erling Smørgrav if (1 < nlinks) 739bc421551SDag-Erling Smørgrav qsort(links, nlinks, sizeof *links, qsort_linkcmp); 740bc421551SDag-Erling Smørgrav 741bc421551SDag-Erling Smørgrav /* Ignore each link superseded by a later link with the same name. */ 742bc421551SDag-Erling Smørgrav j = 0; 743bc421551SDag-Erling Smørgrav for (i = 0; i < nlinks; i++) { 744bc421551SDag-Erling Smørgrav while (i + 1 < nlinks 745bc421551SDag-Erling Smørgrav && strcmp(links[i].l_linkname, links[i + 1].l_linkname) == 0) 746bc421551SDag-Erling Smørgrav i++; 747bc421551SDag-Erling Smørgrav links[j++] = links[i]; 748bc421551SDag-Erling Smørgrav } 749bc421551SDag-Erling Smørgrav nlinks = pass_size = j; 750bc421551SDag-Erling Smørgrav 751bc421551SDag-Erling Smørgrav /* Walk through the link array making links. However, 752bc421551SDag-Erling Smørgrav if a link's target has not been made yet, append a copy to the 753bc421551SDag-Erling Smørgrav end of the array. The end of the array will gradually fill 754bc421551SDag-Erling Smørgrav up with a small sorted subsequence of not-yet-made links. 755bc421551SDag-Erling Smørgrav nalinks counts all the links in the array, including copies. 756bc421551SDag-Erling Smørgrav When we reach the copied subsequence, it may still contain 757bc421551SDag-Erling Smørgrav a link to a not-yet-made link, so the process repeats. 758bc421551SDag-Erling Smørgrav At any given point in time, the link array consists of the 759bc421551SDag-Erling Smørgrav following subregions, where 0 <= i <= j <= nalinks and 760bc421551SDag-Erling Smørgrav 0 <= nlinks <= nalinks: 761bc421551SDag-Erling Smørgrav 762bc421551SDag-Erling Smørgrav 0 .. (i - 1): 763bc421551SDag-Erling Smørgrav links that either have been made, or have been copied to a 764bc421551SDag-Erling Smørgrav later point point in the array (this later point can be in 765bc421551SDag-Erling Smørgrav any of the three subregions) 766bc421551SDag-Erling Smørgrav i .. (j - 1): 767bc421551SDag-Erling Smørgrav not-yet-made links for this pass 768bc421551SDag-Erling Smørgrav j .. (nalinks - 1): 769bc421551SDag-Erling Smørgrav not-yet-made links that this pass has skipped because 770bc421551SDag-Erling Smørgrav they were links to not-yet-made links 771bc421551SDag-Erling Smørgrav 772bc421551SDag-Erling Smørgrav The first subregion might not be sorted if nlinks < i; 773bc421551SDag-Erling Smørgrav the other two subregions are sorted. This algorithm does 774bc421551SDag-Erling Smørgrav not alter entries 0 .. (nlinks - 1), which remain sorted. 775bc421551SDag-Erling Smørgrav 776bc421551SDag-Erling Smørgrav If there are L links, this algorithm is O(C*L*log(L)) where 777bc421551SDag-Erling Smørgrav C is the length of the longest link chain. Usually C is 778bc421551SDag-Erling Smørgrav short (e.g., 3) though its worst-case value is L. */ 779bc421551SDag-Erling Smørgrav 780bc421551SDag-Erling Smørgrav j = nalinks = nlinks; 781bc421551SDag-Erling Smørgrav 782bc421551SDag-Erling Smørgrav for (i = 0; i < nalinks; i++) { 783bc421551SDag-Erling Smørgrav struct link *l; 784bc421551SDag-Erling Smørgrav 785bc421551SDag-Erling Smørgrav eat(links[i].l_filenum, links[i].l_linenum); 786bc421551SDag-Erling Smørgrav 787bc421551SDag-Erling Smørgrav /* If this pass examined all its links, start the next pass. */ 788bc421551SDag-Erling Smørgrav if (i == j) { 789bc421551SDag-Erling Smørgrav if (nalinks - i == pass_size) { 790bc421551SDag-Erling Smørgrav error(_("\"Link %s %s\" is part of a link cycle"), 791bc421551SDag-Erling Smørgrav links[i].l_target, links[i].l_linkname); 792bc421551SDag-Erling Smørgrav break; 793bc421551SDag-Erling Smørgrav } 794bc421551SDag-Erling Smørgrav j = nalinks; 795bc421551SDag-Erling Smørgrav pass_size = nalinks - i; 796bc421551SDag-Erling Smørgrav } 797bc421551SDag-Erling Smørgrav 798bc421551SDag-Erling Smørgrav /* Diagnose self links, which the cycle detection algorithm would not 799bc421551SDag-Erling Smørgrav otherwise catch. */ 800bc421551SDag-Erling Smørgrav if (strcmp(links[i].l_target, links[i].l_linkname) == 0) { 801bc421551SDag-Erling Smørgrav error(_("link %s targets itself"), links[i].l_target); 802bc421551SDag-Erling Smørgrav continue; 803bc421551SDag-Erling Smørgrav } 804bc421551SDag-Erling Smørgrav 805bc421551SDag-Erling Smørgrav /* Make this link unless its target has not been made yet. */ 806bc421551SDag-Erling Smørgrav l = bsearch(links[i].l_target, &links[i + 1], j - (i + 1), 807bc421551SDag-Erling Smørgrav sizeof *links, bsearch_linkcmp); 808bc421551SDag-Erling Smørgrav if (!l) 809bc421551SDag-Erling Smørgrav l = bsearch(links[i].l_target, &links[j], nalinks - j, 810bc421551SDag-Erling Smørgrav sizeof *links, bsearch_linkcmp); 811bc421551SDag-Erling Smørgrav if (!l) 812bc421551SDag-Erling Smørgrav dolink(links[i].l_target, links[i].l_linkname, false); 813bc421551SDag-Erling Smørgrav else { 814bc421551SDag-Erling Smørgrav /* The link target has not been made yet; copy the link to the end. */ 815bc421551SDag-Erling Smørgrav links = growalloc(links, sizeof *links, nalinks, &nlinks_alloc); 816bc421551SDag-Erling Smørgrav links[nalinks++] = links[i]; 817bc421551SDag-Erling Smørgrav } 818bc421551SDag-Erling Smørgrav 819bc421551SDag-Erling Smørgrav if (noise && i < nlinks) { 820bc421551SDag-Erling Smørgrav if (l) 821bc421551SDag-Erling Smørgrav warning(_("link %s targeting link %s mishandled by pre-2023 zic"), 822bc421551SDag-Erling Smørgrav links[i].l_linkname, links[i].l_target); 823bc421551SDag-Erling Smørgrav else if (bsearch(links[i].l_target, links, nlinks, sizeof *links, 824bc421551SDag-Erling Smørgrav bsearch_linkcmp)) 825bc421551SDag-Erling Smørgrav warning(_("link %s targeting link %s"), 826bc421551SDag-Erling Smørgrav links[i].l_linkname, links[i].l_target); 827bc421551SDag-Erling Smørgrav } 828bc421551SDag-Erling Smørgrav } 829bc421551SDag-Erling Smørgrav } 830bc421551SDag-Erling Smørgrav 831bc421551SDag-Erling Smørgrav /* Simple signal handling: just set a flag that is checked 832bc421551SDag-Erling Smørgrav periodically outside critical sections. To set up the handler, 833bc421551SDag-Erling Smørgrav prefer sigaction if available to close a signal race. */ 834bc421551SDag-Erling Smørgrav 835bc421551SDag-Erling Smørgrav static sig_atomic_t got_signal; 836bc421551SDag-Erling Smørgrav 837bc421551SDag-Erling Smørgrav static void 838bc421551SDag-Erling Smørgrav signal_handler(int sig) 839bc421551SDag-Erling Smørgrav { 840bc421551SDag-Erling Smørgrav #ifndef SA_SIGINFO 841bc421551SDag-Erling Smørgrav signal(sig, signal_handler); 842bc421551SDag-Erling Smørgrav #endif 843bc421551SDag-Erling Smørgrav got_signal = sig; 844bc421551SDag-Erling Smørgrav } 845bc421551SDag-Erling Smørgrav 846bc421551SDag-Erling Smørgrav /* Arrange for SIGINT etc. to be caught by the handler. */ 847bc421551SDag-Erling Smørgrav static void 848bc421551SDag-Erling Smørgrav catch_signals(void) 849bc421551SDag-Erling Smørgrav { 850bc421551SDag-Erling Smørgrav static int const signals[] = { 851bc421551SDag-Erling Smørgrav #ifdef SIGHUP 852bc421551SDag-Erling Smørgrav SIGHUP, 853bc421551SDag-Erling Smørgrav #endif 854bc421551SDag-Erling Smørgrav SIGINT, 855bc421551SDag-Erling Smørgrav #ifdef SIGPIPE 856bc421551SDag-Erling Smørgrav SIGPIPE, 857bc421551SDag-Erling Smørgrav #endif 858bc421551SDag-Erling Smørgrav SIGTERM 859bc421551SDag-Erling Smørgrav }; 860bc421551SDag-Erling Smørgrav size_t i; 861bc421551SDag-Erling Smørgrav for (i = 0; i < sizeof signals / sizeof signals[0]; i++) { 862bc421551SDag-Erling Smørgrav #ifdef SA_SIGINFO 863bc421551SDag-Erling Smørgrav struct sigaction act0, act; 864bc421551SDag-Erling Smørgrav act.sa_handler = signal_handler; 865bc421551SDag-Erling Smørgrav sigemptyset(&act.sa_mask); 866bc421551SDag-Erling Smørgrav act.sa_flags = 0; 867bc421551SDag-Erling Smørgrav if (sigaction(signals[i], &act, &act0) == 0 868bc421551SDag-Erling Smørgrav && ! (act0.sa_flags & SA_SIGINFO) && act0.sa_handler == SIG_IGN) { 869bc421551SDag-Erling Smørgrav sigaction(signals[i], &act0, NULL); 870bc421551SDag-Erling Smørgrav got_signal = 0; 871bc421551SDag-Erling Smørgrav } 872bc421551SDag-Erling Smørgrav #else 873bc421551SDag-Erling Smørgrav if (signal(signals[i], signal_handler) == SIG_IGN) { 874bc421551SDag-Erling Smørgrav signal(signals[i], SIG_IGN); 875bc421551SDag-Erling Smørgrav got_signal = 0; 876bc421551SDag-Erling Smørgrav } 877bc421551SDag-Erling Smørgrav #endif 878bc421551SDag-Erling Smørgrav } 879bc421551SDag-Erling Smørgrav } 880bc421551SDag-Erling Smørgrav 881bc421551SDag-Erling Smørgrav /* If a signal has arrived, terminate zic with appropriate status. */ 882bc421551SDag-Erling Smørgrav static void 883bc421551SDag-Erling Smørgrav check_for_signal(void) 884bc421551SDag-Erling Smørgrav { 885bc421551SDag-Erling Smørgrav int sig = got_signal; 886bc421551SDag-Erling Smørgrav if (sig) { 887bc421551SDag-Erling Smørgrav signal(sig, SIG_DFL); 888bc421551SDag-Erling Smørgrav raise(sig); 889bc421551SDag-Erling Smørgrav abort(); /* A bug in 'raise'. */ 890bc421551SDag-Erling Smørgrav } 891bc421551SDag-Erling Smørgrav } 892bc421551SDag-Erling Smørgrav 893bc421551SDag-Erling Smørgrav enum { TIME_T_BITS_IN_FILE = 64 }; 894bc421551SDag-Erling Smørgrav 895bc421551SDag-Erling Smørgrav /* The minimum and maximum values representable in a TZif file. */ 896bc421551SDag-Erling Smørgrav static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); 897bc421551SDag-Erling Smørgrav static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); 898bc421551SDag-Erling Smørgrav 899bc421551SDag-Erling Smørgrav /* The minimum, and one less than the maximum, values specified by 900bc421551SDag-Erling Smørgrav the -r option. These default to MIN_TIME and MAX_TIME. */ 901bc421551SDag-Erling Smørgrav static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); 902bc421551SDag-Erling Smørgrav static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); 903bc421551SDag-Erling Smørgrav 904bc421551SDag-Erling Smørgrav /* The time specified by the -R option, defaulting to MIN_TIME. */ 905bc421551SDag-Erling Smørgrav static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); 906bc421551SDag-Erling Smørgrav 907bc421551SDag-Erling Smørgrav /* The time specified by an Expires line, or negative if no such line. */ 908bc421551SDag-Erling Smørgrav static zic_t leapexpires = -1; 909bc421551SDag-Erling Smørgrav 910bc421551SDag-Erling Smørgrav /* Set the time range of the output to TIMERANGE. 911bc421551SDag-Erling Smørgrav Return true if successful. */ 912bc421551SDag-Erling Smørgrav static bool 913bc421551SDag-Erling Smørgrav timerange_option(char *timerange) 914bc421551SDag-Erling Smørgrav { 915bc421551SDag-Erling Smørgrav intmax_t lo = min_time, hi = max_time; 916bc421551SDag-Erling Smørgrav char *lo_end = timerange, *hi_end; 917bc421551SDag-Erling Smørgrav if (*timerange == '@') { 918bc421551SDag-Erling Smørgrav errno = 0; 919bc421551SDag-Erling Smørgrav lo = strtoimax(timerange + 1, &lo_end, 10); 920bc421551SDag-Erling Smørgrav if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE)) 921bc421551SDag-Erling Smørgrav return false; 922bc421551SDag-Erling Smørgrav } 923bc421551SDag-Erling Smørgrav hi_end = lo_end; 924bc421551SDag-Erling Smørgrav if (lo_end[0] == '/' && lo_end[1] == '@') { 925bc421551SDag-Erling Smørgrav errno = 0; 926bc421551SDag-Erling Smørgrav hi = strtoimax(lo_end + 2, &hi_end, 10); 927bc421551SDag-Erling Smørgrav if (hi_end == lo_end + 2 || hi == INTMAX_MIN) 928bc421551SDag-Erling Smørgrav return false; 929bc421551SDag-Erling Smørgrav hi -= ! (hi == INTMAX_MAX && errno == ERANGE); 930bc421551SDag-Erling Smørgrav } 931bc421551SDag-Erling Smørgrav if (*hi_end || hi < lo || max_time < lo || hi < min_time) 932bc421551SDag-Erling Smørgrav return false; 933bc421551SDag-Erling Smørgrav lo_time = max(lo, min_time); 934bc421551SDag-Erling Smørgrav hi_time = min(hi, max_time); 935bc421551SDag-Erling Smørgrav return true; 936bc421551SDag-Erling Smørgrav } 937bc421551SDag-Erling Smørgrav 938bc421551SDag-Erling Smørgrav /* Generate redundant time stamps up to OPT. Return true if successful. */ 939bc421551SDag-Erling Smørgrav static bool 940bc421551SDag-Erling Smørgrav redundant_time_option(char *opt) 941bc421551SDag-Erling Smørgrav { 942bc421551SDag-Erling Smørgrav if (*opt == '@') { 943bc421551SDag-Erling Smørgrav intmax_t redundant; 944bc421551SDag-Erling Smørgrav char *opt_end; 945bc421551SDag-Erling Smørgrav redundant = strtoimax(opt + 1, &opt_end, 10); 946bc421551SDag-Erling Smørgrav if (opt_end != opt + 1 && !*opt_end) { 947bc421551SDag-Erling Smørgrav redundant_time = max(redundant_time, redundant); 948bc421551SDag-Erling Smørgrav return true; 949bc421551SDag-Erling Smørgrav } 950bc421551SDag-Erling Smørgrav } 951bc421551SDag-Erling Smørgrav return false; 952bc421551SDag-Erling Smørgrav } 953bc421551SDag-Erling Smørgrav 954bc421551SDag-Erling Smørgrav static const char * psxrules; 955bc421551SDag-Erling Smørgrav static const char * lcltime; 956bc421551SDag-Erling Smørgrav static const char * directory; 957bc421551SDag-Erling Smørgrav static const char * leapsec; 958bc421551SDag-Erling Smørgrav static int Dflag; 959bc421551SDag-Erling Smørgrav static uid_t uflag = (uid_t)-1; 960bc421551SDag-Erling Smørgrav static gid_t gflag = (gid_t)-1; 961bc421551SDag-Erling Smørgrav static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH 962bc421551SDag-Erling Smørgrav | S_IWUSR); 963bc421551SDag-Erling Smørgrav static const char * tzdefault; 964bc421551SDag-Erling Smørgrav 965bc421551SDag-Erling Smørgrav /* -1 if the TZif output file should be slim, 0 if default, 1 if the 966bc421551SDag-Erling Smørgrav output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT 967bc421551SDag-Erling Smørgrav determines the default. */ 968bc421551SDag-Erling Smørgrav static int bloat; 969bc421551SDag-Erling Smørgrav 970bc421551SDag-Erling Smørgrav static bool 971bc421551SDag-Erling Smørgrav want_bloat(void) 972bc421551SDag-Erling Smørgrav { 973bc421551SDag-Erling Smørgrav return 0 <= bloat; 974bc421551SDag-Erling Smørgrav } 975bc421551SDag-Erling Smørgrav 976bc421551SDag-Erling Smørgrav #ifndef ZIC_BLOAT_DEFAULT 977bc421551SDag-Erling Smørgrav # define ZIC_BLOAT_DEFAULT "slim" 978bc421551SDag-Erling Smørgrav #endif 979bc421551SDag-Erling Smørgrav 980bc421551SDag-Erling Smørgrav int 981bc421551SDag-Erling Smørgrav main(int argc, char **argv) 982bc421551SDag-Erling Smørgrav { 983bc421551SDag-Erling Smørgrav register int c, k; 984bc421551SDag-Erling Smørgrav register ptrdiff_t i, j; 985bc421551SDag-Erling Smørgrav bool timerange_given = false; 986bc421551SDag-Erling Smørgrav 987bc421551SDag-Erling Smørgrav #ifdef S_IWGRP 988bc421551SDag-Erling Smørgrav umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); 989bc421551SDag-Erling Smørgrav #endif 990bc421551SDag-Erling Smørgrav #if HAVE_GETTEXT 991bc421551SDag-Erling Smørgrav setlocale(LC_ALL, ""); 992bc421551SDag-Erling Smørgrav # ifdef TZ_DOMAINDIR 993bc421551SDag-Erling Smørgrav bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 994bc421551SDag-Erling Smørgrav # endif /* defined TEXTDOMAINDIR */ 995bc421551SDag-Erling Smørgrav textdomain(TZ_DOMAIN); 996bc421551SDag-Erling Smørgrav #endif /* HAVE_GETTEXT */ 997bc421551SDag-Erling Smørgrav main_argv = argv; 998bc421551SDag-Erling Smørgrav progname = /* argv[0] ? argv[0] : */ "zic"; 999bc421551SDag-Erling Smørgrav if (TYPE_BIT(zic_t) < 64) { 1000bc421551SDag-Erling Smørgrav fprintf(stderr, "%s: %s\n", progname, 1001bc421551SDag-Erling Smørgrav _("wild compilation-time specification of zic_t")); 1002bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1003bc421551SDag-Erling Smørgrav } 1004bc421551SDag-Erling Smørgrav for (k = 1; k < argc; k++) 1005bc421551SDag-Erling Smørgrav if (strcmp(argv[k], "--version") == 0) { 1006bc421551SDag-Erling Smørgrav printf("zic %s%s\n", PKGVERSION, TZVERSION); 1007bc421551SDag-Erling Smørgrav close_file(stdout, NULL, NULL, NULL); 1008bc421551SDag-Erling Smørgrav return EXIT_SUCCESS; 1009bc421551SDag-Erling Smørgrav } else if (strcmp(argv[k], "--help") == 0) { 1010bc421551SDag-Erling Smørgrav usage(stdout, EXIT_SUCCESS); 1011bc421551SDag-Erling Smørgrav } 1012bc421551SDag-Erling Smørgrav while ((c = getopt(argc, argv, "Db:d:g:l:L:m:p:r:R:st:u:vy:")) != EOF 1013bc421551SDag-Erling Smørgrav && c != -1) 1014bc421551SDag-Erling Smørgrav switch (c) { 1015bc421551SDag-Erling Smørgrav default: 1016bc421551SDag-Erling Smørgrav usage(stderr, EXIT_FAILURE); 1017bc421551SDag-Erling Smørgrav case 'D': 1018bc421551SDag-Erling Smørgrav Dflag = 1; 1019bc421551SDag-Erling Smørgrav break; 1020bc421551SDag-Erling Smørgrav case 'b': 1021bc421551SDag-Erling Smørgrav if (strcmp(optarg, "slim") == 0) { 1022bc421551SDag-Erling Smørgrav if (0 < bloat) 1023bc421551SDag-Erling Smørgrav error(_("incompatible -b options")); 1024bc421551SDag-Erling Smørgrav bloat = -1; 1025bc421551SDag-Erling Smørgrav } else if (strcmp(optarg, "fat") == 0) { 1026bc421551SDag-Erling Smørgrav if (bloat < 0) 1027bc421551SDag-Erling Smørgrav error(_("incompatible -b options")); 1028bc421551SDag-Erling Smørgrav bloat = 1; 1029bc421551SDag-Erling Smørgrav } else 1030bc421551SDag-Erling Smørgrav error(_("invalid option: -b '%s'"), optarg); 1031bc421551SDag-Erling Smørgrav break; 1032bc421551SDag-Erling Smørgrav case 'd': 1033bc421551SDag-Erling Smørgrav if (directory == NULL) 1034bc421551SDag-Erling Smørgrav directory = optarg; 1035bc421551SDag-Erling Smørgrav else { 1036bc421551SDag-Erling Smørgrav fprintf(stderr, 1037bc421551SDag-Erling Smørgrav _("%s: More than one -d option specified\n"), 1038bc421551SDag-Erling Smørgrav progname); 1039bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1040bc421551SDag-Erling Smørgrav } 1041bc421551SDag-Erling Smørgrav break; 1042bc421551SDag-Erling Smørgrav case 'g': 1043bc421551SDag-Erling Smørgrav setgroup(&gflag, optarg); 1044bc421551SDag-Erling Smørgrav break; 1045bc421551SDag-Erling Smørgrav case 'l': 1046bc421551SDag-Erling Smørgrav if (lcltime == NULL) 1047bc421551SDag-Erling Smørgrav lcltime = optarg; 1048bc421551SDag-Erling Smørgrav else { 1049bc421551SDag-Erling Smørgrav fprintf(stderr, 1050bc421551SDag-Erling Smørgrav _("%s: More than one -l option specified\n"), 1051bc421551SDag-Erling Smørgrav progname); 1052bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1053bc421551SDag-Erling Smørgrav } 1054bc421551SDag-Erling Smørgrav break; 1055bc421551SDag-Erling Smørgrav case 'm': 1056bc421551SDag-Erling Smørgrav { 1057bc421551SDag-Erling Smørgrav void *set = setmode(optarg); 1058bc421551SDag-Erling Smørgrav if (set == NULL) { 1059bc421551SDag-Erling Smørgrav fprintf(stderr, 1060bc421551SDag-Erling Smørgrav _("invalid file mode")); 1061bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1062bc421551SDag-Erling Smørgrav } 1063bc421551SDag-Erling Smørgrav mflag = getmode(set, mflag); 1064bc421551SDag-Erling Smørgrav free(set); 1065bc421551SDag-Erling Smørgrav break; 1066bc421551SDag-Erling Smørgrav } 1067bc421551SDag-Erling Smørgrav case 'p': 1068bc421551SDag-Erling Smørgrav if (psxrules == NULL) 1069bc421551SDag-Erling Smørgrav psxrules = optarg; 1070bc421551SDag-Erling Smørgrav else { 1071bc421551SDag-Erling Smørgrav fprintf(stderr, 1072bc421551SDag-Erling Smørgrav _("%s: More than one -p option specified\n"), 1073bc421551SDag-Erling Smørgrav progname); 1074bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1075bc421551SDag-Erling Smørgrav } 1076bc421551SDag-Erling Smørgrav break; 1077bc421551SDag-Erling Smørgrav case 't': 1078bc421551SDag-Erling Smørgrav if (tzdefault != NULL) { 1079bc421551SDag-Erling Smørgrav fprintf(stderr, 1080bc421551SDag-Erling Smørgrav _("%s: More than one -t option" 1081bc421551SDag-Erling Smørgrav " specified\n"), 1082bc421551SDag-Erling Smørgrav progname); 1083bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1084bc421551SDag-Erling Smørgrav } 1085bc421551SDag-Erling Smørgrav tzdefault = optarg; 1086bc421551SDag-Erling Smørgrav break; 1087bc421551SDag-Erling Smørgrav case 'u': 1088bc421551SDag-Erling Smørgrav setuser(&uflag, optarg); 1089bc421551SDag-Erling Smørgrav break; 1090bc421551SDag-Erling Smørgrav case 'y': 1091bc421551SDag-Erling Smørgrav warning(_("-y ignored")); 1092bc421551SDag-Erling Smørgrav break; 1093bc421551SDag-Erling Smørgrav case 'L': 1094bc421551SDag-Erling Smørgrav if (leapsec == NULL) 1095bc421551SDag-Erling Smørgrav leapsec = optarg; 1096bc421551SDag-Erling Smørgrav else { 1097bc421551SDag-Erling Smørgrav fprintf(stderr, 1098bc421551SDag-Erling Smørgrav _("%s: More than one -L option specified\n"), 1099bc421551SDag-Erling Smørgrav progname); 1100bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1101bc421551SDag-Erling Smørgrav } 1102bc421551SDag-Erling Smørgrav break; 1103bc421551SDag-Erling Smørgrav case 'v': 1104bc421551SDag-Erling Smørgrav noise = true; 1105bc421551SDag-Erling Smørgrav break; 1106bc421551SDag-Erling Smørgrav case 'r': 1107bc421551SDag-Erling Smørgrav if (timerange_given) { 1108bc421551SDag-Erling Smørgrav fprintf(stderr, 1109bc421551SDag-Erling Smørgrav _("%s: More than one -r option specified\n"), 1110bc421551SDag-Erling Smørgrav progname); 1111bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1112bc421551SDag-Erling Smørgrav } 1113bc421551SDag-Erling Smørgrav if (! timerange_option(optarg)) { 1114bc421551SDag-Erling Smørgrav fprintf(stderr, 1115bc421551SDag-Erling Smørgrav _("%s: invalid time range: %s\n"), 1116bc421551SDag-Erling Smørgrav progname, optarg); 1117bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1118bc421551SDag-Erling Smørgrav } 1119bc421551SDag-Erling Smørgrav timerange_given = true; 1120bc421551SDag-Erling Smørgrav break; 1121bc421551SDag-Erling Smørgrav case 'R': 1122bc421551SDag-Erling Smørgrav if (! redundant_time_option(optarg)) { 1123bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: invalid time: %s\n"), 1124bc421551SDag-Erling Smørgrav progname, optarg); 1125bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1126bc421551SDag-Erling Smørgrav } 1127bc421551SDag-Erling Smørgrav break; 1128bc421551SDag-Erling Smørgrav case 's': 1129bc421551SDag-Erling Smørgrav warning(_("-s ignored")); 1130bc421551SDag-Erling Smørgrav break; 1131bc421551SDag-Erling Smørgrav } 1132bc421551SDag-Erling Smørgrav if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) 1133bc421551SDag-Erling Smørgrav usage(stderr, EXIT_FAILURE); /* usage message by request */ 1134bc421551SDag-Erling Smørgrav if (hi_time + (hi_time < ZIC_MAX) < redundant_time) { 1135bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname); 1136bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1137bc421551SDag-Erling Smørgrav } 1138bc421551SDag-Erling Smørgrav if (bloat == 0) { 1139bc421551SDag-Erling Smørgrav static char const bloat_default[] = ZIC_BLOAT_DEFAULT; 1140bc421551SDag-Erling Smørgrav if (strcmp(bloat_default, "slim") == 0) 1141bc421551SDag-Erling Smørgrav bloat = -1; 1142bc421551SDag-Erling Smørgrav else if (strcmp(bloat_default, "fat") == 0) 1143bc421551SDag-Erling Smørgrav bloat = 1; 1144bc421551SDag-Erling Smørgrav else 1145bc421551SDag-Erling Smørgrav abort(); /* Configuration error. */ 1146bc421551SDag-Erling Smørgrav } 1147bc421551SDag-Erling Smørgrav if (directory == NULL) 1148bc421551SDag-Erling Smørgrav directory = TZDIR; 1149bc421551SDag-Erling Smørgrav if (tzdefault == NULL) 1150bc421551SDag-Erling Smørgrav tzdefault = TZDEFAULT; 1151bc421551SDag-Erling Smørgrav 1152bc421551SDag-Erling Smørgrav if (optind < argc && leapsec != NULL) { 1153bc421551SDag-Erling Smørgrav infile(LEAPSEC_FILENUM, leapsec); 1154bc421551SDag-Erling Smørgrav adjleap(); 1155bc421551SDag-Erling Smørgrav } 1156bc421551SDag-Erling Smørgrav 1157bc421551SDag-Erling Smørgrav for (k = optind; k < argc; k++) 1158bc421551SDag-Erling Smørgrav infile(k, argv[k]); 1159bc421551SDag-Erling Smørgrav if (errors) 1160bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1161bc421551SDag-Erling Smørgrav associate(); 1162bc421551SDag-Erling Smørgrav change_directory(directory); 1163bc421551SDag-Erling Smørgrav catch_signals(); 1164bc421551SDag-Erling Smørgrav for (i = 0; i < nzones; i = j) { 1165bc421551SDag-Erling Smørgrav /* 1166bc421551SDag-Erling Smørgrav ** Find the next non-continuation zone entry. 1167bc421551SDag-Erling Smørgrav */ 1168bc421551SDag-Erling Smørgrav for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) 1169bc421551SDag-Erling Smørgrav continue; 1170bc421551SDag-Erling Smørgrav outzone(&zones[i], j - i); 1171bc421551SDag-Erling Smørgrav } 1172bc421551SDag-Erling Smørgrav make_links(); 1173bc421551SDag-Erling Smørgrav if (lcltime != NULL) { 1174bc421551SDag-Erling Smørgrav eat(COMMAND_LINE_FILENUM, 1); 1175bc421551SDag-Erling Smørgrav dolink(lcltime, tzdefault, true); 1176bc421551SDag-Erling Smørgrav } 1177bc421551SDag-Erling Smørgrav if (psxrules != NULL) { 1178bc421551SDag-Erling Smørgrav eat(COMMAND_LINE_FILENUM, 1); 1179bc421551SDag-Erling Smørgrav dolink(psxrules, TZDEFRULES, true); 1180bc421551SDag-Erling Smørgrav } 1181bc421551SDag-Erling Smørgrav if (warnings && (ferror(stderr) || fclose(stderr) != 0)) 1182bc421551SDag-Erling Smørgrav return EXIT_FAILURE; 1183bc421551SDag-Erling Smørgrav return errors ? EXIT_FAILURE : EXIT_SUCCESS; 1184bc421551SDag-Erling Smørgrav } 1185bc421551SDag-Erling Smørgrav 1186bc421551SDag-Erling Smørgrav static bool 1187bc421551SDag-Erling Smørgrav componentcheck(char const *name, char const *component, 1188bc421551SDag-Erling Smørgrav char const *component_end) 1189bc421551SDag-Erling Smørgrav { 1190bc421551SDag-Erling Smørgrav enum { component_len_max = 14 }; 1191bc421551SDag-Erling Smørgrav ptrdiff_t component_len = component_end - component; 1192bc421551SDag-Erling Smørgrav if (component_len == 0) { 1193bc421551SDag-Erling Smørgrav if (!*name) 1194bc421551SDag-Erling Smørgrav error(_("empty file name")); 1195bc421551SDag-Erling Smørgrav else 1196bc421551SDag-Erling Smørgrav error(_(component == name 1197bc421551SDag-Erling Smørgrav ? "file name '%s' begins with '/'" 1198bc421551SDag-Erling Smørgrav : *component_end 1199bc421551SDag-Erling Smørgrav ? "file name '%s' contains '//'" 1200bc421551SDag-Erling Smørgrav : "file name '%s' ends with '/'"), 1201bc421551SDag-Erling Smørgrav name); 1202bc421551SDag-Erling Smørgrav return false; 1203bc421551SDag-Erling Smørgrav } 1204bc421551SDag-Erling Smørgrav if (0 < component_len && component_len <= 2 1205bc421551SDag-Erling Smørgrav && component[0] == '.' && component_end[-1] == '.') { 1206bc421551SDag-Erling Smørgrav int len = component_len; 1207bc421551SDag-Erling Smørgrav error(_("file name '%s' contains '%.*s' component"), 1208bc421551SDag-Erling Smørgrav name, len, component); 1209bc421551SDag-Erling Smørgrav return false; 1210bc421551SDag-Erling Smørgrav } 1211bc421551SDag-Erling Smørgrav if (noise) { 1212bc421551SDag-Erling Smørgrav if (0 < component_len && component[0] == '-') 1213bc421551SDag-Erling Smørgrav warning(_("file name '%s' component contains leading '-'"), 1214bc421551SDag-Erling Smørgrav name); 1215bc421551SDag-Erling Smørgrav if (component_len_max < component_len) 1216bc421551SDag-Erling Smørgrav warning(_("file name '%s' contains overlength component" 1217bc421551SDag-Erling Smørgrav " '%.*s...'"), 1218bc421551SDag-Erling Smørgrav name, component_len_max, component); 1219bc421551SDag-Erling Smørgrav } 1220bc421551SDag-Erling Smørgrav return true; 1221bc421551SDag-Erling Smørgrav } 1222bc421551SDag-Erling Smørgrav 1223bc421551SDag-Erling Smørgrav static bool 1224bc421551SDag-Erling Smørgrav namecheck(const char *name) 1225bc421551SDag-Erling Smørgrav { 1226bc421551SDag-Erling Smørgrav register char const *cp; 1227bc421551SDag-Erling Smørgrav 1228bc421551SDag-Erling Smørgrav /* Benign characters in a portable file name. */ 1229bc421551SDag-Erling Smørgrav static char const benign[] = 1230bc421551SDag-Erling Smørgrav "-/_" 1231bc421551SDag-Erling Smørgrav "abcdefghijklmnopqrstuvwxyz" 1232bc421551SDag-Erling Smørgrav "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 1233bc421551SDag-Erling Smørgrav 1234bc421551SDag-Erling Smørgrav /* Non-control chars in the POSIX portable character set, 1235bc421551SDag-Erling Smørgrav excluding the benign characters. */ 1236bc421551SDag-Erling Smørgrav static char const printable_and_not_benign[] = 1237bc421551SDag-Erling Smørgrav " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~"; 1238bc421551SDag-Erling Smørgrav 1239bc421551SDag-Erling Smørgrav register char const *component = name; 1240bc421551SDag-Erling Smørgrav for (cp = name; *cp; cp++) { 1241bc421551SDag-Erling Smørgrav unsigned char c = *cp; 1242bc421551SDag-Erling Smørgrav if (noise && !strchr(benign, c)) { 1243bc421551SDag-Erling Smørgrav warning((strchr(printable_and_not_benign, c) 1244bc421551SDag-Erling Smørgrav ? _("file name '%s' contains byte '%c'") 1245bc421551SDag-Erling Smørgrav : _("file name '%s' contains byte '\\%o'")), 1246bc421551SDag-Erling Smørgrav name, c); 1247bc421551SDag-Erling Smørgrav } 1248bc421551SDag-Erling Smørgrav if (c == '/') { 1249bc421551SDag-Erling Smørgrav if (!componentcheck(name, component, cp)) 1250bc421551SDag-Erling Smørgrav return false; 1251bc421551SDag-Erling Smørgrav component = cp + 1; 1252bc421551SDag-Erling Smørgrav } 1253bc421551SDag-Erling Smørgrav } 1254bc421551SDag-Erling Smørgrav return componentcheck(name, component, cp); 1255bc421551SDag-Erling Smørgrav } 1256bc421551SDag-Erling Smørgrav 1257bc421551SDag-Erling Smørgrav /* Return a random uint_fast64_t. */ 1258bc421551SDag-Erling Smørgrav static uint_fast64_t 1259bc421551SDag-Erling Smørgrav get_rand_u64(void) 1260bc421551SDag-Erling Smørgrav { 1261bc421551SDag-Erling Smørgrav #if HAVE_GETRANDOM 1262bc421551SDag-Erling Smørgrav static uint_fast64_t entropy_buffer[max(1, 256 / sizeof(uint_fast64_t))]; 1263bc421551SDag-Erling Smørgrav static int nwords; 1264bc421551SDag-Erling Smørgrav if (!nwords) { 1265bc421551SDag-Erling Smørgrav ssize_t s; 1266bc421551SDag-Erling Smørgrav do 1267bc421551SDag-Erling Smørgrav s = getrandom(entropy_buffer, sizeof entropy_buffer, 0); 1268bc421551SDag-Erling Smørgrav while (s < 0 && errno == EINTR); 1269bc421551SDag-Erling Smørgrav 1270*d5c85ac6SDag-Erling Smørgrav if (s < 0) 1271*d5c85ac6SDag-Erling Smørgrav nwords = -1; 1272*d5c85ac6SDag-Erling Smørgrav else 1273*d5c85ac6SDag-Erling Smørgrav nwords = s / sizeof *entropy_buffer; 1274bc421551SDag-Erling Smørgrav } 1275bc421551SDag-Erling Smørgrav if (0 < nwords) 1276bc421551SDag-Erling Smørgrav return entropy_buffer[--nwords]; 1277bc421551SDag-Erling Smørgrav #endif 1278bc421551SDag-Erling Smørgrav 1279bc421551SDag-Erling Smørgrav /* getrandom didn't work, so fall back on portable code that is 1280bc421551SDag-Erling Smørgrav not the best because the seed isn't cryptographically random and 1281bc421551SDag-Erling Smørgrav 'rand' might not be cryptographically secure. */ 1282bc421551SDag-Erling Smørgrav { 1283bc421551SDag-Erling Smørgrav static bool initialized; 1284bc421551SDag-Erling Smørgrav if (!initialized) { 1285bc421551SDag-Erling Smørgrav srand(time(NULL)); 1286bc421551SDag-Erling Smørgrav initialized = true; 1287bc421551SDag-Erling Smørgrav } 1288bc421551SDag-Erling Smørgrav } 1289bc421551SDag-Erling Smørgrav 1290bc421551SDag-Erling Smørgrav /* Return a random number if rand() yields a random number and in 1291bc421551SDag-Erling Smørgrav the typical case where RAND_MAX is one less than a power of two. 1292bc421551SDag-Erling Smørgrav In other cases this code yields a sort-of-random number. */ 1293bc421551SDag-Erling Smørgrav { 1294bc421551SDag-Erling Smørgrav uint_fast64_t rand_max = RAND_MAX, 1295bc421551SDag-Erling Smørgrav nrand = rand_max < UINT_FAST64_MAX ? rand_max + 1 : 0, 1296bc421551SDag-Erling Smørgrav rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1, 1297bc421551SDag-Erling Smørgrav r = 0, rmax = 0; 1298bc421551SDag-Erling Smørgrav 1299bc421551SDag-Erling Smørgrav do { 1300bc421551SDag-Erling Smørgrav uint_fast64_t rmax1 = rmax; 1301bc421551SDag-Erling Smørgrav if (rmod) { 1302bc421551SDag-Erling Smørgrav /* Avoid signed integer overflow on theoretical platforms 1303bc421551SDag-Erling Smørgrav where uint_fast64_t promotes to int. */ 1304bc421551SDag-Erling Smørgrav rmax1 %= rmod; 1305bc421551SDag-Erling Smørgrav r %= rmod; 1306bc421551SDag-Erling Smørgrav } 1307bc421551SDag-Erling Smørgrav rmax1 = nrand * rmax1 + rand_max; 1308bc421551SDag-Erling Smørgrav r = nrand * r + rand(); 1309bc421551SDag-Erling Smørgrav rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX; 1310bc421551SDag-Erling Smørgrav } while (rmax < UINT_FAST64_MAX); 1311bc421551SDag-Erling Smørgrav 1312bc421551SDag-Erling Smørgrav return r; 1313bc421551SDag-Erling Smørgrav } 1314bc421551SDag-Erling Smørgrav } 1315bc421551SDag-Erling Smørgrav 1316bc421551SDag-Erling Smørgrav /* Generate a randomish name in the same directory as *NAME. If 1317bc421551SDag-Erling Smørgrav *NAMEALLOC, put the name into *NAMEALLOC which is assumed to be 1318bc421551SDag-Erling Smørgrav that returned by a previous call and is thus already almost set up 1319bc421551SDag-Erling Smørgrav and equal to *NAME; otherwise, allocate a new name and put its 1320bc421551SDag-Erling Smørgrav address into both *NAMEALLOC and *NAME. */ 1321bc421551SDag-Erling Smørgrav static void 1322bc421551SDag-Erling Smørgrav random_dirent(char const **name, char **namealloc) 1323bc421551SDag-Erling Smørgrav { 1324bc421551SDag-Erling Smørgrav char const *src = *name; 1325bc421551SDag-Erling Smørgrav char *dst = *namealloc; 1326bc421551SDag-Erling Smørgrav static char const prefix[] = ".zic"; 1327bc421551SDag-Erling Smørgrav static char const alphabet[] = 1328bc421551SDag-Erling Smørgrav "abcdefghijklmnopqrstuvwxyz" 1329bc421551SDag-Erling Smørgrav "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1330bc421551SDag-Erling Smørgrav "0123456789"; 1331bc421551SDag-Erling Smørgrav enum { prefixlen = sizeof prefix - 1, alphabetlen = sizeof alphabet - 1 }; 1332bc421551SDag-Erling Smørgrav int suffixlen = 6; 1333bc421551SDag-Erling Smørgrav char const *lastslash = strrchr(src, '/'); 1334bc421551SDag-Erling Smørgrav ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0; 1335bc421551SDag-Erling Smørgrav int i; 1336bc421551SDag-Erling Smørgrav uint_fast64_t r; 1337bc421551SDag-Erling Smørgrav uint_fast64_t base = alphabetlen; 1338bc421551SDag-Erling Smørgrav 1339bc421551SDag-Erling Smørgrav /* BASE**6 */ 1340bc421551SDag-Erling Smørgrav uint_fast64_t base__6 = base * base * base * base * base * base; 1341bc421551SDag-Erling Smørgrav 1342bc421551SDag-Erling Smørgrav /* The largest uintmax_t that is a multiple of BASE**6. Any random 1343bc421551SDag-Erling Smørgrav uintmax_t value that is this value or greater, yields a biased 1344bc421551SDag-Erling Smørgrav remainder when divided by BASE**6. UNFAIR_MIN equals the 1345bc421551SDag-Erling Smørgrav mathematical value of ((UINTMAX_MAX + 1) - (UINTMAX_MAX + 1) % BASE**6) 1346bc421551SDag-Erling Smørgrav computed without overflow. */ 1347bc421551SDag-Erling Smørgrav uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6); 1348bc421551SDag-Erling Smørgrav 1349bc421551SDag-Erling Smørgrav if (!dst) { 1350bc421551SDag-Erling Smørgrav dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1)); 1351bc421551SDag-Erling Smørgrav memcpy(dst, src, dirlen); 1352bc421551SDag-Erling Smørgrav memcpy(dst + dirlen, prefix, prefixlen); 1353bc421551SDag-Erling Smørgrav dst[dirlen + prefixlen + suffixlen] = '\0'; 1354bc421551SDag-Erling Smørgrav *name = *namealloc = dst; 1355bc421551SDag-Erling Smørgrav } 1356bc421551SDag-Erling Smørgrav 1357bc421551SDag-Erling Smørgrav do 1358bc421551SDag-Erling Smørgrav r = get_rand_u64(); 1359bc421551SDag-Erling Smørgrav while (unfair_min <= r); 1360bc421551SDag-Erling Smørgrav 1361bc421551SDag-Erling Smørgrav for (i = 0; i < suffixlen; i++) { 1362bc421551SDag-Erling Smørgrav dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen]; 1363bc421551SDag-Erling Smørgrav r /= alphabetlen; 1364bc421551SDag-Erling Smørgrav } 1365bc421551SDag-Erling Smørgrav } 1366bc421551SDag-Erling Smørgrav 1367bc421551SDag-Erling Smørgrav /* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the 1368bc421551SDag-Erling Smørgrav name of the temporary file that will eventually be renamed to 1369bc421551SDag-Erling Smørgrav *OUTNAME. Assign the temporary file's name to both *OUTNAME and 1370bc421551SDag-Erling Smørgrav *TEMPNAME. If *TEMPNAME is null, allocate the name of any such 1371bc421551SDag-Erling Smørgrav temporary file; otherwise, reuse *TEMPNAME's storage, which is 1372bc421551SDag-Erling Smørgrav already set up and only needs its trailing suffix updated. */ 1373bc421551SDag-Erling Smørgrav static FILE * 1374bc421551SDag-Erling Smørgrav open_outfile(char const **outname, char **tempname) 1375bc421551SDag-Erling Smørgrav { 1376bc421551SDag-Erling Smørgrav #if __STDC_VERSION__ < 201112 1377bc421551SDag-Erling Smørgrav static char const fopen_mode[] = "wb"; 1378bc421551SDag-Erling Smørgrav #else 1379bc421551SDag-Erling Smørgrav static char const fopen_mode[] = "wbx"; 1380bc421551SDag-Erling Smørgrav #endif 1381bc421551SDag-Erling Smørgrav 1382bc421551SDag-Erling Smørgrav FILE *fp; 1383bc421551SDag-Erling Smørgrav bool dirs_made = false; 1384bc421551SDag-Erling Smørgrav if (!*tempname) 1385bc421551SDag-Erling Smørgrav random_dirent(outname, tempname); 1386bc421551SDag-Erling Smørgrav 1387bc421551SDag-Erling Smørgrav /* 1388bc421551SDag-Erling Smørgrav * Remove old file, if any, to snap links. 1389bc421551SDag-Erling Smørgrav */ 1390bc421551SDag-Erling Smørgrav if (remove(*outname) != 0 && errno != ENOENT && errno != EISDIR) { 1391bc421551SDag-Erling Smørgrav fprintf(stderr, _("can't remove %s"), *outname); 1392bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1393bc421551SDag-Erling Smørgrav } 1394bc421551SDag-Erling Smørgrav 1395bc421551SDag-Erling Smørgrav while (! (fp = fopen(*outname, fopen_mode))) { 1396bc421551SDag-Erling Smørgrav int fopen_errno = errno; 1397bc421551SDag-Erling Smørgrav if (fopen_errno == ENOENT && !dirs_made) { 1398bc421551SDag-Erling Smørgrav mkdirs(*outname, true); 1399bc421551SDag-Erling Smørgrav dirs_made = true; 1400bc421551SDag-Erling Smørgrav } else if (fopen_errno == EEXIST) 1401bc421551SDag-Erling Smørgrav random_dirent(outname, tempname); 1402bc421551SDag-Erling Smørgrav else { 1403bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Can't create %s/%s: %s\n"), 1404bc421551SDag-Erling Smørgrav progname, directory, *outname, strerror(fopen_errno)); 1405bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1406bc421551SDag-Erling Smørgrav } 1407bc421551SDag-Erling Smørgrav } 1408bc421551SDag-Erling Smørgrav 1409bc421551SDag-Erling Smørgrav return fp; 1410bc421551SDag-Erling Smørgrav } 1411bc421551SDag-Erling Smørgrav 1412bc421551SDag-Erling Smørgrav /* If TEMPNAME, the result is in the temporary file TEMPNAME even 1413bc421551SDag-Erling Smørgrav though the user wanted it in NAME, so rename TEMPNAME to NAME. 1414bc421551SDag-Erling Smørgrav Report an error and exit if there is trouble. Also, free TEMPNAME. */ 1415bc421551SDag-Erling Smørgrav static void 1416bc421551SDag-Erling Smørgrav rename_dest(char *tempname, char const *name) 1417bc421551SDag-Erling Smørgrav { 1418bc421551SDag-Erling Smørgrav if (tempname) { 1419bc421551SDag-Erling Smørgrav if (rename(tempname, name) != 0) { 1420bc421551SDag-Erling Smørgrav int rename_errno = errno; 1421bc421551SDag-Erling Smørgrav remove(tempname); 1422bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: rename to %s/%s: %s\n"), 1423bc421551SDag-Erling Smørgrav progname, directory, name, strerror(rename_errno)); 1424bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1425bc421551SDag-Erling Smørgrav } 1426bc421551SDag-Erling Smørgrav free(tempname); 1427bc421551SDag-Erling Smørgrav } 1428bc421551SDag-Erling Smørgrav } 1429bc421551SDag-Erling Smørgrav 1430bc421551SDag-Erling Smørgrav /* Create symlink contents suitable for symlinking FROM to TO, as a 1431bc421551SDag-Erling Smørgrav freshly allocated string. FROM should be a relative file name, and 1432bc421551SDag-Erling Smørgrav is relative to the global variable DIRECTORY. TO can be either 1433bc421551SDag-Erling Smørgrav relative or absolute. */ 1434bc421551SDag-Erling Smørgrav static char * 1435bc421551SDag-Erling Smørgrav relname(char const *target, char const *linkname) 1436bc421551SDag-Erling Smørgrav { 1437bc421551SDag-Erling Smørgrav size_t i, taillen, dir_len = 0, dotdots = 0; 1438bc421551SDag-Erling Smørgrav ptrdiff_t dotdotetcsize, linksize = min(PTRDIFF_MAX, SIZE_MAX); 1439bc421551SDag-Erling Smørgrav char const *f = target; 1440bc421551SDag-Erling Smørgrav char *result = NULL; 1441bc421551SDag-Erling Smørgrav if (*linkname == '/') { 1442bc421551SDag-Erling Smørgrav /* Make F absolute too. */ 1443bc421551SDag-Erling Smørgrav size_t len = strlen(directory); 1444bc421551SDag-Erling Smørgrav size_t lenslash = len + (len && directory[len - 1] != '/'); 1445bc421551SDag-Erling Smørgrav size_t targetsize = strlen(target) + 1; 1446bc421551SDag-Erling Smørgrav linksize = size_sum(lenslash, targetsize); 1447bc421551SDag-Erling Smørgrav f = result = emalloc(linksize); 1448bc421551SDag-Erling Smørgrav memcpy(result, directory, len); 1449bc421551SDag-Erling Smørgrav result[len] = '/'; 1450bc421551SDag-Erling Smørgrav memcpy(result + lenslash, target, targetsize); 1451bc421551SDag-Erling Smørgrav } 1452bc421551SDag-Erling Smørgrav for (i = 0; f[i] && f[i] == linkname[i]; i++) 1453bc421551SDag-Erling Smørgrav if (f[i] == '/') 1454bc421551SDag-Erling Smørgrav dir_len = i + 1; 1455bc421551SDag-Erling Smørgrav for (; linkname[i]; i++) 1456bc421551SDag-Erling Smørgrav dotdots += linkname[i] == '/' && linkname[i - 1] != '/'; 1457bc421551SDag-Erling Smørgrav taillen = strlen(f + dir_len); 1458bc421551SDag-Erling Smørgrav dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1); 1459bc421551SDag-Erling Smørgrav if (dotdotetcsize <= linksize) { 1460bc421551SDag-Erling Smørgrav if (!result) 1461bc421551SDag-Erling Smørgrav result = emalloc(dotdotetcsize); 1462bc421551SDag-Erling Smørgrav for (i = 0; i < dotdots; i++) 1463bc421551SDag-Erling Smørgrav memcpy(result + 3 * i, "../", 3); 1464bc421551SDag-Erling Smørgrav memmove(result + 3 * dotdots, f + dir_len, taillen + 1); 1465bc421551SDag-Erling Smørgrav } 1466bc421551SDag-Erling Smørgrav return result; 1467bc421551SDag-Erling Smørgrav } 1468bc421551SDag-Erling Smørgrav 1469bc421551SDag-Erling Smørgrav static void 1470bc421551SDag-Erling Smørgrav dolink(char const *target, char const *linkname, bool staysymlink) 1471bc421551SDag-Erling Smørgrav { 1472bc421551SDag-Erling Smørgrav bool linkdirs_made = false; 1473bc421551SDag-Erling Smørgrav int link_errno; 1474bc421551SDag-Erling Smørgrav char *tempname = NULL; 1475bc421551SDag-Erling Smørgrav char const *outname = linkname; 1476bc421551SDag-Erling Smørgrav 1477bc421551SDag-Erling Smørgrav check_for_signal(); 1478bc421551SDag-Erling Smørgrav 1479bc421551SDag-Erling Smørgrav if (strcmp(target, "-") == 0) { 1480bc421551SDag-Erling Smørgrav if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR) 1481bc421551SDag-Erling Smørgrav return; 1482bc421551SDag-Erling Smørgrav else { 1483bc421551SDag-Erling Smørgrav char const *e = strerror(errno); 1484bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"), 1485bc421551SDag-Erling Smørgrav progname, directory, linkname, e); 1486bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1487bc421551SDag-Erling Smørgrav } 1488bc421551SDag-Erling Smørgrav } 1489bc421551SDag-Erling Smørgrav 1490bc421551SDag-Erling Smørgrav while (true) { 1491bc421551SDag-Erling Smørgrav if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW) 1492bc421551SDag-Erling Smørgrav == 0) { 1493bc421551SDag-Erling Smørgrav link_errno = 0; 1494bc421551SDag-Erling Smørgrav break; 1495bc421551SDag-Erling Smørgrav } 1496bc421551SDag-Erling Smørgrav link_errno = errno; 1497bc421551SDag-Erling Smørgrav if (link_errno == EXDEV || link_errno == ENOTSUP) 1498bc421551SDag-Erling Smørgrav break; 1499bc421551SDag-Erling Smørgrav 1500bc421551SDag-Erling Smørgrav if (link_errno == EEXIST) { 1501bc421551SDag-Erling Smørgrav staysymlink &= !tempname; 1502bc421551SDag-Erling Smørgrav random_dirent(&outname, &tempname); 1503bc421551SDag-Erling Smørgrav if (staysymlink && itssymlink(linkname)) 1504bc421551SDag-Erling Smørgrav break; 1505bc421551SDag-Erling Smørgrav } else if (link_errno == ENOENT && !linkdirs_made) { 1506bc421551SDag-Erling Smørgrav mkdirs(linkname, true); 1507bc421551SDag-Erling Smørgrav linkdirs_made = true; 1508bc421551SDag-Erling Smørgrav } else { 1509bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"), 1510bc421551SDag-Erling Smørgrav progname, directory, target, directory, outname, 1511bc421551SDag-Erling Smørgrav strerror(link_errno)); 1512bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1513bc421551SDag-Erling Smørgrav } 1514bc421551SDag-Erling Smørgrav } 1515bc421551SDag-Erling Smørgrav if (link_errno != 0) { 1516bc421551SDag-Erling Smørgrav bool absolute = *target == '/'; 1517bc421551SDag-Erling Smørgrav char *linkalloc = absolute ? NULL : relname(target, linkname); 1518bc421551SDag-Erling Smørgrav char const *contents = absolute ? target : linkalloc; 1519bc421551SDag-Erling Smørgrav int symlink_errno; 1520bc421551SDag-Erling Smørgrav 1521bc421551SDag-Erling Smørgrav while (true) { 1522bc421551SDag-Erling Smørgrav if (symlink(contents, outname) == 0) { 1523bc421551SDag-Erling Smørgrav symlink_errno = 0; 1524bc421551SDag-Erling Smørgrav break; 1525bc421551SDag-Erling Smørgrav } 1526bc421551SDag-Erling Smørgrav symlink_errno = errno; 1527bc421551SDag-Erling Smørgrav if (symlink_errno == EEXIST) 1528bc421551SDag-Erling Smørgrav random_dirent(&outname, &tempname); 1529bc421551SDag-Erling Smørgrav else if (symlink_errno == ENOENT && !linkdirs_made) { 1530bc421551SDag-Erling Smørgrav mkdirs(linkname, true); 1531bc421551SDag-Erling Smørgrav linkdirs_made = true; 1532bc421551SDag-Erling Smørgrav } else 1533bc421551SDag-Erling Smørgrav break; 1534bc421551SDag-Erling Smørgrav } 1535bc421551SDag-Erling Smørgrav free(linkalloc); 1536bc421551SDag-Erling Smørgrav if (symlink_errno == 0) { 1537bc421551SDag-Erling Smørgrav if (link_errno != ENOTSUP && link_errno != EEXIST) 1538bc421551SDag-Erling Smørgrav warning(_("symbolic link used because hard link failed: %s"), 1539bc421551SDag-Erling Smørgrav strerror(link_errno)); 1540bc421551SDag-Erling Smørgrav } else { 1541bc421551SDag-Erling Smørgrav FILE *fp, *tp; 1542bc421551SDag-Erling Smørgrav int c; 1543bc421551SDag-Erling Smørgrav fp = fopen(target, "rb"); 1544bc421551SDag-Erling Smørgrav if (!fp) { 1545bc421551SDag-Erling Smørgrav char const *e = strerror(errno); 1546bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Can't read %s/%s: %s\n"), 1547bc421551SDag-Erling Smørgrav progname, directory, target, e); 1548bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1549bc421551SDag-Erling Smørgrav } 1550bc421551SDag-Erling Smørgrav tp = open_outfile(&outname, &tempname); 1551bc421551SDag-Erling Smørgrav while ((c = getc(fp)) != EOF) 1552bc421551SDag-Erling Smørgrav putc(c, tp); 1553bc421551SDag-Erling Smørgrav close_file(tp, directory, linkname, tempname); 1554bc421551SDag-Erling Smørgrav close_file(fp, directory, target, NULL); 1555bc421551SDag-Erling Smørgrav if (link_errno != ENOTSUP) 1556bc421551SDag-Erling Smørgrav warning(_("copy used because hard link failed: %s"), 1557bc421551SDag-Erling Smørgrav strerror(link_errno)); 1558bc421551SDag-Erling Smørgrav else if (symlink_errno != ENOTSUP) 1559bc421551SDag-Erling Smørgrav warning(_("copy used because symbolic link failed: %s"), 1560bc421551SDag-Erling Smørgrav strerror(symlink_errno)); 1561bc421551SDag-Erling Smørgrav } 1562bc421551SDag-Erling Smørgrav } 1563bc421551SDag-Erling Smørgrav rename_dest(tempname, linkname); 1564bc421551SDag-Erling Smørgrav } 1565bc421551SDag-Erling Smørgrav 1566bc421551SDag-Erling Smørgrav /* Return true if NAME is a symbolic link. */ 1567bc421551SDag-Erling Smørgrav static bool 1568bc421551SDag-Erling Smørgrav itssymlink(char const *name) 1569bc421551SDag-Erling Smørgrav { 1570bc421551SDag-Erling Smørgrav char c; 1571bc421551SDag-Erling Smørgrav return 0 <= readlink(name, &c, 1); 1572bc421551SDag-Erling Smørgrav } 1573bc421551SDag-Erling Smørgrav 1574bc421551SDag-Erling Smørgrav /* 1575bc421551SDag-Erling Smørgrav ** Associate sets of rules with zones. 1576bc421551SDag-Erling Smørgrav */ 1577bc421551SDag-Erling Smørgrav 1578bc421551SDag-Erling Smørgrav /* 1579bc421551SDag-Erling Smørgrav ** Sort by rule name. 1580bc421551SDag-Erling Smørgrav */ 1581bc421551SDag-Erling Smørgrav 1582bc421551SDag-Erling Smørgrav static int 1583bc421551SDag-Erling Smørgrav rcomp(const void *cp1, const void *cp2) 1584bc421551SDag-Erling Smørgrav { 1585bc421551SDag-Erling Smørgrav struct rule const *r1 = cp1, *r2 = cp2; 1586bc421551SDag-Erling Smørgrav return strcmp(r1->r_name, r2->r_name); 1587bc421551SDag-Erling Smørgrav } 1588bc421551SDag-Erling Smørgrav 1589bc421551SDag-Erling Smørgrav static void 1590bc421551SDag-Erling Smørgrav associate(void) 1591bc421551SDag-Erling Smørgrav { 1592bc421551SDag-Erling Smørgrav register struct zone * zp; 1593bc421551SDag-Erling Smørgrav register struct rule * rp; 1594bc421551SDag-Erling Smørgrav register ptrdiff_t i, j, base, out; 1595bc421551SDag-Erling Smørgrav 1596bc421551SDag-Erling Smørgrav if (1 < nrules) { 1597bc421551SDag-Erling Smørgrav qsort(rules, nrules, sizeof *rules, rcomp); 1598bc421551SDag-Erling Smørgrav for (i = 0; i < nrules - 1; ++i) { 1599bc421551SDag-Erling Smørgrav if (strcmp(rules[i].r_name, 1600bc421551SDag-Erling Smørgrav rules[i + 1].r_name) != 0) 1601bc421551SDag-Erling Smørgrav continue; 1602bc421551SDag-Erling Smørgrav if (rules[i].r_filenum == rules[i + 1].r_filenum) 1603bc421551SDag-Erling Smørgrav continue; 1604bc421551SDag-Erling Smørgrav eat(rules[i].r_filenum, rules[i].r_linenum); 1605bc421551SDag-Erling Smørgrav warning(_("same rule name in multiple files")); 1606bc421551SDag-Erling Smørgrav eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum); 1607bc421551SDag-Erling Smørgrav warning(_("same rule name in multiple files")); 1608bc421551SDag-Erling Smørgrav for (j = i + 2; j < nrules; ++j) { 1609bc421551SDag-Erling Smørgrav if (strcmp(rules[i].r_name, 1610bc421551SDag-Erling Smørgrav rules[j].r_name) != 0) 1611bc421551SDag-Erling Smørgrav break; 1612bc421551SDag-Erling Smørgrav if (rules[i].r_filenum == rules[j].r_filenum) 1613bc421551SDag-Erling Smørgrav continue; 1614bc421551SDag-Erling Smørgrav if (rules[i + 1].r_filenum 1615bc421551SDag-Erling Smørgrav == rules[j].r_filenum) 1616bc421551SDag-Erling Smørgrav continue; 1617bc421551SDag-Erling Smørgrav break; 1618bc421551SDag-Erling Smørgrav } 1619bc421551SDag-Erling Smørgrav i = j - 1; 1620bc421551SDag-Erling Smørgrav } 1621bc421551SDag-Erling Smørgrav } 1622bc421551SDag-Erling Smørgrav for (i = 0; i < nzones; ++i) { 1623bc421551SDag-Erling Smørgrav zp = &zones[i]; 1624bc421551SDag-Erling Smørgrav zp->z_rules = NULL; 1625bc421551SDag-Erling Smørgrav zp->z_nrules = 0; 1626bc421551SDag-Erling Smørgrav } 1627bc421551SDag-Erling Smørgrav for (base = 0; base < nrules; base = out) { 1628bc421551SDag-Erling Smørgrav rp = &rules[base]; 1629bc421551SDag-Erling Smørgrav for (out = base + 1; out < nrules; ++out) 1630bc421551SDag-Erling Smørgrav if (strcmp(rp->r_name, rules[out].r_name) != 0) 1631bc421551SDag-Erling Smørgrav break; 1632bc421551SDag-Erling Smørgrav for (i = 0; i < nzones; ++i) { 1633bc421551SDag-Erling Smørgrav zp = &zones[i]; 1634bc421551SDag-Erling Smørgrav if (strcmp(zp->z_rule, rp->r_name) != 0) 1635bc421551SDag-Erling Smørgrav continue; 1636bc421551SDag-Erling Smørgrav zp->z_rules = rp; 1637bc421551SDag-Erling Smørgrav zp->z_nrules = out - base; 1638bc421551SDag-Erling Smørgrav } 1639bc421551SDag-Erling Smørgrav } 1640bc421551SDag-Erling Smørgrav for (i = 0; i < nzones; ++i) { 1641bc421551SDag-Erling Smørgrav zp = &zones[i]; 1642bc421551SDag-Erling Smørgrav if (zp->z_nrules == 0) { 1643bc421551SDag-Erling Smørgrav /* 1644bc421551SDag-Erling Smørgrav ** Maybe we have a local standard time offset. 1645bc421551SDag-Erling Smørgrav */ 1646bc421551SDag-Erling Smørgrav eat(zp->z_filenum, zp->z_linenum); 1647bc421551SDag-Erling Smørgrav zp->z_save = getsave(zp->z_rule, &zp->z_isdst); 1648bc421551SDag-Erling Smørgrav /* 1649bc421551SDag-Erling Smørgrav ** Note, though, that if there's no rule, 1650bc421551SDag-Erling Smørgrav ** a '%s' in the format is a bad thing. 1651bc421551SDag-Erling Smørgrav */ 1652bc421551SDag-Erling Smørgrav if (zp->z_format_specifier == 's') 1653bc421551SDag-Erling Smørgrav error("%s", _("%s in ruleless zone")); 1654bc421551SDag-Erling Smørgrav } 1655bc421551SDag-Erling Smørgrav } 1656bc421551SDag-Erling Smørgrav if (errors) 1657bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1658bc421551SDag-Erling Smørgrav } 1659bc421551SDag-Erling Smørgrav 1660bc421551SDag-Erling Smørgrav /* Read a text line from FP into BUF, which is of size BUFSIZE. 1661bc421551SDag-Erling Smørgrav Terminate it with a NUL byte instead of a newline. 1662bc421551SDag-Erling Smørgrav Return true if successful, false if EOF. 1663bc421551SDag-Erling Smørgrav On error, report the error and exit. */ 1664bc421551SDag-Erling Smørgrav static bool 1665bc421551SDag-Erling Smørgrav inputline(FILE *fp, char *buf, ptrdiff_t bufsize) 1666bc421551SDag-Erling Smørgrav { 1667bc421551SDag-Erling Smørgrav ptrdiff_t linelen = 0, ch; 1668bc421551SDag-Erling Smørgrav while ((ch = getc(fp)) != '\n') { 1669bc421551SDag-Erling Smørgrav if (ch < 0) { 1670bc421551SDag-Erling Smørgrav if (ferror(fp)) { 1671bc421551SDag-Erling Smørgrav error(_("input error")); 1672bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1673bc421551SDag-Erling Smørgrav } 1674bc421551SDag-Erling Smørgrav if (linelen == 0) 1675bc421551SDag-Erling Smørgrav return false; 1676bc421551SDag-Erling Smørgrav error(_("unterminated line")); 1677bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1678bc421551SDag-Erling Smørgrav } 1679bc421551SDag-Erling Smørgrav if (!ch) { 1680bc421551SDag-Erling Smørgrav error(_("NUL input byte")); 1681bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1682bc421551SDag-Erling Smørgrav } 1683bc421551SDag-Erling Smørgrav buf[linelen++] = ch; 1684bc421551SDag-Erling Smørgrav if (linelen == bufsize) { 1685bc421551SDag-Erling Smørgrav error(_("line too long")); 1686bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1687bc421551SDag-Erling Smørgrav } 1688bc421551SDag-Erling Smørgrav } 1689bc421551SDag-Erling Smørgrav buf[linelen] = '\0'; 1690bc421551SDag-Erling Smørgrav return true; 1691bc421551SDag-Erling Smørgrav } 1692bc421551SDag-Erling Smørgrav 1693bc421551SDag-Erling Smørgrav static void 1694bc421551SDag-Erling Smørgrav infile(int fnum, char const *name) 1695bc421551SDag-Erling Smørgrav { 1696bc421551SDag-Erling Smørgrav register FILE * fp; 1697bc421551SDag-Erling Smørgrav register const struct lookup * lp; 1698bc421551SDag-Erling Smørgrav register bool wantcont; 1699bc421551SDag-Erling Smørgrav register lineno num; 1700bc421551SDag-Erling Smørgrav 1701bc421551SDag-Erling Smørgrav if (strcmp(name, "-") == 0) { 1702bc421551SDag-Erling Smørgrav fp = stdin; 1703bc421551SDag-Erling Smørgrav } else if ((fp = fopen(name, "r")) == NULL) { 1704bc421551SDag-Erling Smørgrav const char *e = strerror(errno); 1705bc421551SDag-Erling Smørgrav 1706bc421551SDag-Erling Smørgrav fprintf(stderr, _("%s: Can't open %s: %s\n"), 1707bc421551SDag-Erling Smørgrav progname, name, e); 1708bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 1709bc421551SDag-Erling Smørgrav } 1710bc421551SDag-Erling Smørgrav wantcont = false; 1711bc421551SDag-Erling Smørgrav for (num = 1; ; ++num) { 1712bc421551SDag-Erling Smørgrav enum { bufsize_bound 1713bc421551SDag-Erling Smørgrav = (min(INT_MAX, min(PTRDIFF_MAX, SIZE_MAX)) 1714bc421551SDag-Erling Smørgrav / FORMAT_LEN_GROWTH_BOUND) }; 1715bc421551SDag-Erling Smørgrav char buf[min(_POSIX2_LINE_MAX, bufsize_bound)]; 1716bc421551SDag-Erling Smørgrav int nfields; 1717bc421551SDag-Erling Smørgrav char *fields[MAX_FIELDS]; 1718bc421551SDag-Erling Smørgrav eat(fnum, num); 1719bc421551SDag-Erling Smørgrav if (!inputline(fp, buf, sizeof buf)) 1720bc421551SDag-Erling Smørgrav break; 1721bc421551SDag-Erling Smørgrav nfields = getfields(buf, fields, 1722bc421551SDag-Erling Smørgrav sizeof fields / sizeof *fields); 1723bc421551SDag-Erling Smørgrav if (nfields == 0) { 1724bc421551SDag-Erling Smørgrav /* nothing to do */ 1725bc421551SDag-Erling Smørgrav } else if (wantcont) { 1726bc421551SDag-Erling Smørgrav wantcont = inzcont(fields, nfields); 1727bc421551SDag-Erling Smørgrav } else { 1728bc421551SDag-Erling Smørgrav struct lookup const *line_codes 1729bc421551SDag-Erling Smørgrav = fnum < 0 ? leap_line_codes : zi_line_codes; 1730bc421551SDag-Erling Smørgrav lp = byword(fields[0], line_codes); 1731bc421551SDag-Erling Smørgrav if (lp == NULL) 1732bc421551SDag-Erling Smørgrav error(_("input line of unknown type")); 1733bc421551SDag-Erling Smørgrav else switch (lp->l_value) { 1734bc421551SDag-Erling Smørgrav case LC_RULE: 1735bc421551SDag-Erling Smørgrav inrule(fields, nfields); 1736bc421551SDag-Erling Smørgrav wantcont = false; 1737bc421551SDag-Erling Smørgrav break; 1738bc421551SDag-Erling Smørgrav case LC_ZONE: 1739bc421551SDag-Erling Smørgrav wantcont = inzone(fields, nfields); 1740bc421551SDag-Erling Smørgrav break; 1741bc421551SDag-Erling Smørgrav case LC_LINK: 1742bc421551SDag-Erling Smørgrav inlink(fields, nfields); 1743bc421551SDag-Erling Smørgrav wantcont = false; 1744bc421551SDag-Erling Smørgrav break; 1745bc421551SDag-Erling Smørgrav case LC_LEAP: 1746bc421551SDag-Erling Smørgrav inleap(fields, nfields); 1747bc421551SDag-Erling Smørgrav wantcont = false; 1748bc421551SDag-Erling Smørgrav break; 1749bc421551SDag-Erling Smørgrav case LC_EXPIRES: 1750bc421551SDag-Erling Smørgrav inexpires(fields, nfields); 1751bc421551SDag-Erling Smørgrav wantcont = false; 1752bc421551SDag-Erling Smørgrav break; 1753bc421551SDag-Erling Smørgrav default: unreachable(); 1754bc421551SDag-Erling Smørgrav } 1755bc421551SDag-Erling Smørgrav } 1756bc421551SDag-Erling Smørgrav } 1757bc421551SDag-Erling Smørgrav close_file(fp, NULL, filename(fnum), NULL); 1758bc421551SDag-Erling Smørgrav if (wantcont) 1759bc421551SDag-Erling Smørgrav error(_("expected continuation line not found")); 1760bc421551SDag-Erling Smørgrav } 1761bc421551SDag-Erling Smørgrav 1762bc421551SDag-Erling Smørgrav /* 1763bc421551SDag-Erling Smørgrav ** Convert a string of one of the forms 1764bc421551SDag-Erling Smørgrav ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss 1765bc421551SDag-Erling Smørgrav ** into a number of seconds. 1766bc421551SDag-Erling Smørgrav ** A null string maps to zero. 1767bc421551SDag-Erling Smørgrav ** Call error with errstring and return zero on errors. 1768bc421551SDag-Erling Smørgrav */ 1769bc421551SDag-Erling Smørgrav 1770bc421551SDag-Erling Smørgrav static zic_t 1771bc421551SDag-Erling Smørgrav gethms(char const *string, char const *errstring) 1772bc421551SDag-Erling Smørgrav { 1773bc421551SDag-Erling Smørgrav zic_t hh; 1774bc421551SDag-Erling Smørgrav int sign, mm = 0, ss = 0; 1775bc421551SDag-Erling Smørgrav char hhx, mmx, ssx, xr = '0', xs; 1776bc421551SDag-Erling Smørgrav int tenths = 0; 1777bc421551SDag-Erling Smørgrav bool ok = true; 1778bc421551SDag-Erling Smørgrav 1779bc421551SDag-Erling Smørgrav if (string == NULL || *string == '\0') 1780bc421551SDag-Erling Smørgrav return 0; 1781bc421551SDag-Erling Smørgrav if (*string == '-') { 1782bc421551SDag-Erling Smørgrav sign = -1; 1783bc421551SDag-Erling Smørgrav ++string; 1784bc421551SDag-Erling Smørgrav } else sign = 1; 1785bc421551SDag-Erling Smørgrav switch (sscanf(string, 1786bc421551SDag-Erling Smørgrav "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c", 1787bc421551SDag-Erling Smørgrav &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) { 1788bc421551SDag-Erling Smørgrav default: ok = false; break; 1789bc421551SDag-Erling Smørgrav case 8: 1790bc421551SDag-Erling Smørgrav ok = '0' <= xr && xr <= '9'; 1791bc421551SDag-Erling Smørgrav ATTRIBUTE_FALLTHROUGH; 1792bc421551SDag-Erling Smørgrav case 7: 1793bc421551SDag-Erling Smørgrav ok &= ssx == '.'; 1794bc421551SDag-Erling Smørgrav if (ok && noise) 1795bc421551SDag-Erling Smørgrav warning(_("fractional seconds rejected by" 1796bc421551SDag-Erling Smørgrav " pre-2018 versions of zic")); 1797bc421551SDag-Erling Smørgrav ATTRIBUTE_FALLTHROUGH; 1798bc421551SDag-Erling Smørgrav case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH; 1799bc421551SDag-Erling Smørgrav case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH; 1800bc421551SDag-Erling Smørgrav case 1: break; 1801bc421551SDag-Erling Smørgrav } 1802bc421551SDag-Erling Smørgrav if (!ok) { 1803bc421551SDag-Erling Smørgrav error("%s", errstring); 1804bc421551SDag-Erling Smørgrav return 0; 1805bc421551SDag-Erling Smørgrav } 1806bc421551SDag-Erling Smørgrav if (hh < 0 || 1807bc421551SDag-Erling Smørgrav mm < 0 || mm >= MINSPERHOUR || 1808bc421551SDag-Erling Smørgrav ss < 0 || ss > SECSPERMIN) { 1809bc421551SDag-Erling Smørgrav error("%s", errstring); 1810bc421551SDag-Erling Smørgrav return 0; 1811bc421551SDag-Erling Smørgrav } 1812bc421551SDag-Erling Smørgrav if (ZIC_MAX / SECSPERHOUR < hh) { 1813bc421551SDag-Erling Smørgrav error(_("time overflow")); 1814bc421551SDag-Erling Smørgrav return 0; 1815bc421551SDag-Erling Smørgrav } 1816bc421551SDag-Erling Smørgrav ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */ 1817bc421551SDag-Erling Smørgrav if (noise && (hh > HOURSPERDAY || 1818bc421551SDag-Erling Smørgrav (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) 1819bc421551SDag-Erling Smørgrav warning(_("values over 24 hours not handled by pre-2007 versions of zic")); 1820bc421551SDag-Erling Smørgrav return oadd(sign * hh * SECSPERHOUR, 1821bc421551SDag-Erling Smørgrav sign * (mm * SECSPERMIN + ss)); 1822bc421551SDag-Erling Smørgrav } 1823bc421551SDag-Erling Smørgrav 1824bc421551SDag-Erling Smørgrav static zic_t 1825bc421551SDag-Erling Smørgrav getsave(char *field, bool *isdst) 1826bc421551SDag-Erling Smørgrav { 1827bc421551SDag-Erling Smørgrav int dst = -1; 1828bc421551SDag-Erling Smørgrav zic_t save; 1829bc421551SDag-Erling Smørgrav ptrdiff_t fieldlen = strlen(field); 1830bc421551SDag-Erling Smørgrav if (fieldlen != 0) { 1831bc421551SDag-Erling Smørgrav char *ep = field + fieldlen - 1; 1832bc421551SDag-Erling Smørgrav switch (*ep) { 1833bc421551SDag-Erling Smørgrav case 'd': dst = 1; *ep = '\0'; break; 1834bc421551SDag-Erling Smørgrav case 's': dst = 0; *ep = '\0'; break; 1835bc421551SDag-Erling Smørgrav } 1836bc421551SDag-Erling Smørgrav } 1837bc421551SDag-Erling Smørgrav save = gethms(field, _("invalid saved time")); 1838bc421551SDag-Erling Smørgrav *isdst = dst < 0 ? save != 0 : dst; 1839bc421551SDag-Erling Smørgrav return save; 1840bc421551SDag-Erling Smørgrav } 1841bc421551SDag-Erling Smørgrav 1842bc421551SDag-Erling Smørgrav static void 1843bc421551SDag-Erling Smørgrav inrule(char **fields, int nfields) 1844bc421551SDag-Erling Smørgrav { 1845bc421551SDag-Erling Smørgrav struct rule r; 1846bc421551SDag-Erling Smørgrav 1847bc421551SDag-Erling Smørgrav if (nfields != RULE_FIELDS) { 1848bc421551SDag-Erling Smørgrav error(_("wrong number of fields on Rule line")); 1849bc421551SDag-Erling Smørgrav return; 1850bc421551SDag-Erling Smørgrav } 1851bc421551SDag-Erling Smørgrav switch (*fields[RF_NAME]) { 1852bc421551SDag-Erling Smørgrav case '\0': 1853bc421551SDag-Erling Smørgrav case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': 1854bc421551SDag-Erling Smørgrav case '+': case '-': 1855bc421551SDag-Erling Smørgrav case '0': case '1': case '2': case '3': case '4': 1856bc421551SDag-Erling Smørgrav case '5': case '6': case '7': case '8': case '9': 1857bc421551SDag-Erling Smørgrav error(_("Invalid rule name \"%s\""), fields[RF_NAME]); 1858bc421551SDag-Erling Smørgrav return; 1859bc421551SDag-Erling Smørgrav } 1860bc421551SDag-Erling Smørgrav r.r_filenum = filenum; 1861bc421551SDag-Erling Smørgrav r.r_linenum = linenum; 1862bc421551SDag-Erling Smørgrav r.r_save = getsave(fields[RF_SAVE], &r.r_isdst); 1863bc421551SDag-Erling Smørgrav if (!rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], 1864bc421551SDag-Erling Smørgrav fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY], 1865bc421551SDag-Erling Smørgrav fields[RF_TOD])) 1866bc421551SDag-Erling Smørgrav return; 1867bc421551SDag-Erling Smørgrav r.r_name = estrdup(fields[RF_NAME]); 1868bc421551SDag-Erling Smørgrav r.r_abbrvar = estrdup(fields[RF_ABBRVAR]); 1869bc421551SDag-Erling Smørgrav if (max_abbrvar_len < strlen(r.r_abbrvar)) 1870bc421551SDag-Erling Smørgrav max_abbrvar_len = strlen(r.r_abbrvar); 1871bc421551SDag-Erling Smørgrav rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc); 1872bc421551SDag-Erling Smørgrav rules[nrules++] = r; 1873bc421551SDag-Erling Smørgrav } 1874bc421551SDag-Erling Smørgrav 1875bc421551SDag-Erling Smørgrav static bool 1876bc421551SDag-Erling Smørgrav inzone(char **fields, int nfields) 1877bc421551SDag-Erling Smørgrav { 1878bc421551SDag-Erling Smørgrav register ptrdiff_t i; 1879bc421551SDag-Erling Smørgrav 1880bc421551SDag-Erling Smørgrav if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { 1881bc421551SDag-Erling Smørgrav error(_("wrong number of fields on Zone line")); 1882bc421551SDag-Erling Smørgrav return false; 1883bc421551SDag-Erling Smørgrav } 1884bc421551SDag-Erling Smørgrav if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) { 1885bc421551SDag-Erling Smørgrav error( 1886bc421551SDag-Erling Smørgrav _("\"Zone %s\" line and -l option are mutually exclusive"), 1887bc421551SDag-Erling Smørgrav tzdefault); 1888bc421551SDag-Erling Smørgrav return false; 1889bc421551SDag-Erling Smørgrav } 1890bc421551SDag-Erling Smørgrav if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { 1891bc421551SDag-Erling Smørgrav error( 1892bc421551SDag-Erling Smørgrav _("\"Zone %s\" line and -p option are mutually exclusive"), 1893bc421551SDag-Erling Smørgrav TZDEFRULES); 1894bc421551SDag-Erling Smørgrav return false; 1895bc421551SDag-Erling Smørgrav } 1896bc421551SDag-Erling Smørgrav for (i = 0; i < nzones; ++i) 1897bc421551SDag-Erling Smørgrav if (zones[i].z_name != NULL && 1898bc421551SDag-Erling Smørgrav strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { 1899bc421551SDag-Erling Smørgrav error(_("duplicate zone name %s" 1900bc421551SDag-Erling Smørgrav " (file \"%s\", line %"PRIdMAX")"), 1901bc421551SDag-Erling Smørgrav fields[ZF_NAME], 1902bc421551SDag-Erling Smørgrav filename(zones[i].z_filenum), 1903bc421551SDag-Erling Smørgrav zones[i].z_linenum); 1904bc421551SDag-Erling Smørgrav return false; 1905bc421551SDag-Erling Smørgrav } 1906bc421551SDag-Erling Smørgrav return inzsub(fields, nfields, false); 1907bc421551SDag-Erling Smørgrav } 1908bc421551SDag-Erling Smørgrav 1909bc421551SDag-Erling Smørgrav static bool 1910bc421551SDag-Erling Smørgrav inzcont(char **fields, int nfields) 1911bc421551SDag-Erling Smørgrav { 1912bc421551SDag-Erling Smørgrav if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { 1913bc421551SDag-Erling Smørgrav error(_("wrong number of fields on Zone continuation line")); 1914bc421551SDag-Erling Smørgrav return false; 1915bc421551SDag-Erling Smørgrav } 1916bc421551SDag-Erling Smørgrav return inzsub(fields, nfields, true); 1917bc421551SDag-Erling Smørgrav } 1918bc421551SDag-Erling Smørgrav 1919bc421551SDag-Erling Smørgrav static bool 1920bc421551SDag-Erling Smørgrav inzsub(char **fields, int nfields, bool iscont) 1921bc421551SDag-Erling Smørgrav { 1922bc421551SDag-Erling Smørgrav register char * cp; 1923bc421551SDag-Erling Smørgrav char * cp1; 1924bc421551SDag-Erling Smørgrav struct zone z; 1925bc421551SDag-Erling Smørgrav int format_len; 1926bc421551SDag-Erling Smørgrav register int i_stdoff, i_rule, i_format; 1927bc421551SDag-Erling Smørgrav register int i_untilyear, i_untilmonth; 1928bc421551SDag-Erling Smørgrav register int i_untilday, i_untiltime; 1929bc421551SDag-Erling Smørgrav register bool hasuntil; 1930bc421551SDag-Erling Smørgrav 1931bc421551SDag-Erling Smørgrav if (iscont) { 1932bc421551SDag-Erling Smørgrav i_stdoff = ZFC_STDOFF; 1933bc421551SDag-Erling Smørgrav i_rule = ZFC_RULE; 1934bc421551SDag-Erling Smørgrav i_format = ZFC_FORMAT; 1935bc421551SDag-Erling Smørgrav i_untilyear = ZFC_TILYEAR; 1936bc421551SDag-Erling Smørgrav i_untilmonth = ZFC_TILMONTH; 1937bc421551SDag-Erling Smørgrav i_untilday = ZFC_TILDAY; 1938bc421551SDag-Erling Smørgrav i_untiltime = ZFC_TILTIME; 1939bc421551SDag-Erling Smørgrav } else if (!namecheck(fields[ZF_NAME])) 1940bc421551SDag-Erling Smørgrav return false; 1941bc421551SDag-Erling Smørgrav else { 1942bc421551SDag-Erling Smørgrav i_stdoff = ZF_STDOFF; 1943bc421551SDag-Erling Smørgrav i_rule = ZF_RULE; 1944bc421551SDag-Erling Smørgrav i_format = ZF_FORMAT; 1945bc421551SDag-Erling Smørgrav i_untilyear = ZF_TILYEAR; 1946bc421551SDag-Erling Smørgrav i_untilmonth = ZF_TILMONTH; 1947bc421551SDag-Erling Smørgrav i_untilday = ZF_TILDAY; 1948bc421551SDag-Erling Smørgrav i_untiltime = ZF_TILTIME; 1949bc421551SDag-Erling Smørgrav } 1950bc421551SDag-Erling Smørgrav z.z_filenum = filenum; 1951bc421551SDag-Erling Smørgrav z.z_linenum = linenum; 1952bc421551SDag-Erling Smørgrav z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset")); 1953bc421551SDag-Erling Smørgrav if ((cp = strchr(fields[i_format], '%')) != 0) { 1954bc421551SDag-Erling Smørgrav if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') 1955bc421551SDag-Erling Smørgrav || strchr(fields[i_format], '/')) { 1956bc421551SDag-Erling Smørgrav error(_("invalid abbreviation format")); 1957bc421551SDag-Erling Smørgrav return false; 1958bc421551SDag-Erling Smørgrav } 1959bc421551SDag-Erling Smørgrav } 1960bc421551SDag-Erling Smørgrav z.z_format_specifier = cp ? *cp : '\0'; 1961bc421551SDag-Erling Smørgrav format_len = strlen(fields[i_format]); 1962bc421551SDag-Erling Smørgrav if (max_format_len < format_len) 1963bc421551SDag-Erling Smørgrav max_format_len = format_len; 1964bc421551SDag-Erling Smørgrav hasuntil = nfields > i_untilyear; 1965bc421551SDag-Erling Smørgrav if (hasuntil) { 1966bc421551SDag-Erling Smørgrav z.z_untilrule.r_filenum = filenum; 1967bc421551SDag-Erling Smørgrav z.z_untilrule.r_linenum = linenum; 1968bc421551SDag-Erling Smørgrav if (!rulesub( 1969bc421551SDag-Erling Smørgrav &z.z_untilrule, 1970bc421551SDag-Erling Smørgrav fields[i_untilyear], 1971bc421551SDag-Erling Smørgrav "only", 1972bc421551SDag-Erling Smørgrav "", 1973bc421551SDag-Erling Smørgrav (nfields > i_untilmonth) ? 1974bc421551SDag-Erling Smørgrav fields[i_untilmonth] : "Jan", 1975bc421551SDag-Erling Smørgrav (nfields > i_untilday) ? fields[i_untilday] : "1", 1976bc421551SDag-Erling Smørgrav (nfields > i_untiltime) ? fields[i_untiltime] : "0")) 1977bc421551SDag-Erling Smørgrav return false; 1978bc421551SDag-Erling Smørgrav z.z_untiltime = rpytime(&z.z_untilrule, 1979bc421551SDag-Erling Smørgrav z.z_untilrule.r_loyear); 1980bc421551SDag-Erling Smørgrav if (iscont && nzones > 0 && 1981bc421551SDag-Erling Smørgrav z.z_untiltime > min_time && 1982bc421551SDag-Erling Smørgrav z.z_untiltime < max_time && 1983bc421551SDag-Erling Smørgrav zones[nzones - 1].z_untiltime > min_time && 1984bc421551SDag-Erling Smørgrav zones[nzones - 1].z_untiltime < max_time && 1985bc421551SDag-Erling Smørgrav zones[nzones - 1].z_untiltime >= z.z_untiltime) { 1986bc421551SDag-Erling Smørgrav error(_( 1987bc421551SDag-Erling Smørgrav "Zone continuation line end time is not after end time of previous line" 1988bc421551SDag-Erling Smørgrav )); 1989bc421551SDag-Erling Smørgrav return false; 1990bc421551SDag-Erling Smørgrav } 1991bc421551SDag-Erling Smørgrav } 1992bc421551SDag-Erling Smørgrav z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]); 1993bc421551SDag-Erling Smørgrav z.z_rule = estrdup(fields[i_rule]); 1994bc421551SDag-Erling Smørgrav z.z_format = cp1 = estrdup(fields[i_format]); 1995bc421551SDag-Erling Smørgrav if (z.z_format_specifier == 'z') { 1996bc421551SDag-Erling Smørgrav cp1[cp - fields[i_format]] = 's'; 1997bc421551SDag-Erling Smørgrav if (noise) 1998bc421551SDag-Erling Smørgrav warning(_("format '%s' not handled by pre-2015 versions of zic"), 1999bc421551SDag-Erling Smørgrav fields[i_format]); 2000bc421551SDag-Erling Smørgrav } 2001bc421551SDag-Erling Smørgrav zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc); 2002bc421551SDag-Erling Smørgrav zones[nzones++] = z; 2003bc421551SDag-Erling Smørgrav /* 2004bc421551SDag-Erling Smørgrav ** If there was an UNTIL field on this line, 2005bc421551SDag-Erling Smørgrav ** there's more information about the zone on the next line. 2006bc421551SDag-Erling Smørgrav */ 2007bc421551SDag-Erling Smørgrav return hasuntil; 2008bc421551SDag-Erling Smørgrav } 2009bc421551SDag-Erling Smørgrav 2010bc421551SDag-Erling Smørgrav static zic_t 2011bc421551SDag-Erling Smørgrav getleapdatetime(char **fields, bool expire_line) 2012bc421551SDag-Erling Smørgrav { 2013bc421551SDag-Erling Smørgrav register const char * cp; 2014bc421551SDag-Erling Smørgrav register const struct lookup * lp; 2015bc421551SDag-Erling Smørgrav register zic_t i, j; 2016bc421551SDag-Erling Smørgrav zic_t year; 2017bc421551SDag-Erling Smørgrav int month, day; 2018bc421551SDag-Erling Smørgrav zic_t dayoff, tod; 2019bc421551SDag-Erling Smørgrav zic_t t; 2020bc421551SDag-Erling Smørgrav char xs; 2021bc421551SDag-Erling Smørgrav 2022bc421551SDag-Erling Smørgrav dayoff = 0; 2023bc421551SDag-Erling Smørgrav cp = fields[LP_YEAR]; 2024bc421551SDag-Erling Smørgrav if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) { 2025bc421551SDag-Erling Smørgrav /* 2026bc421551SDag-Erling Smørgrav ** Leapin' Lizards! 2027bc421551SDag-Erling Smørgrav */ 2028bc421551SDag-Erling Smørgrav error(_("invalid leaping year")); 2029bc421551SDag-Erling Smørgrav return -1; 2030bc421551SDag-Erling Smørgrav } 2031bc421551SDag-Erling Smørgrav if (!expire_line) { 2032bc421551SDag-Erling Smørgrav if (!leapseen || leapmaxyear < year) 2033bc421551SDag-Erling Smørgrav leapmaxyear = year; 2034bc421551SDag-Erling Smørgrav if (!leapseen || leapminyear > year) 2035bc421551SDag-Erling Smørgrav leapminyear = year; 2036bc421551SDag-Erling Smørgrav leapseen = true; 2037bc421551SDag-Erling Smørgrav } 2038bc421551SDag-Erling Smørgrav j = EPOCH_YEAR; 2039bc421551SDag-Erling Smørgrav while (j != year) { 2040bc421551SDag-Erling Smørgrav if (year > j) { 2041bc421551SDag-Erling Smørgrav i = len_years[isleap(j)]; 2042bc421551SDag-Erling Smørgrav ++j; 2043bc421551SDag-Erling Smørgrav } else { 2044bc421551SDag-Erling Smørgrav --j; 2045bc421551SDag-Erling Smørgrav i = -len_years[isleap(j)]; 2046bc421551SDag-Erling Smørgrav } 2047bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, i); 2048bc421551SDag-Erling Smørgrav } 2049bc421551SDag-Erling Smørgrav if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { 2050bc421551SDag-Erling Smørgrav error(_("invalid month name")); 2051bc421551SDag-Erling Smørgrav return -1; 2052bc421551SDag-Erling Smørgrav } 2053bc421551SDag-Erling Smørgrav month = lp->l_value; 2054bc421551SDag-Erling Smørgrav j = TM_JANUARY; 2055bc421551SDag-Erling Smørgrav while (j != month) { 2056bc421551SDag-Erling Smørgrav i = len_months[isleap(year)][j]; 2057bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, i); 2058bc421551SDag-Erling Smørgrav ++j; 2059bc421551SDag-Erling Smørgrav } 2060bc421551SDag-Erling Smørgrav cp = fields[LP_DAY]; 2061bc421551SDag-Erling Smørgrav if (sscanf(cp, "%d%c", &day, &xs) != 1 || 2062bc421551SDag-Erling Smørgrav day <= 0 || day > len_months[isleap(year)][month]) { 2063bc421551SDag-Erling Smørgrav error(_("invalid day of month")); 2064bc421551SDag-Erling Smørgrav return -1; 2065bc421551SDag-Erling Smørgrav } 2066bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, day - 1); 2067bc421551SDag-Erling Smørgrav if (dayoff < min_time / SECSPERDAY) { 2068bc421551SDag-Erling Smørgrav error(_("time too small")); 2069bc421551SDag-Erling Smørgrav return -1; 2070bc421551SDag-Erling Smørgrav } 2071bc421551SDag-Erling Smørgrav if (dayoff > max_time / SECSPERDAY) { 2072bc421551SDag-Erling Smørgrav error(_("time too large")); 2073bc421551SDag-Erling Smørgrav return -1; 2074bc421551SDag-Erling Smørgrav } 2075bc421551SDag-Erling Smørgrav t = dayoff * SECSPERDAY; 2076bc421551SDag-Erling Smørgrav tod = gethms(fields[LP_TIME], _("invalid time of day")); 2077bc421551SDag-Erling Smørgrav t = tadd(t, tod); 2078bc421551SDag-Erling Smørgrav if (t < 0) 2079bc421551SDag-Erling Smørgrav error(_("leap second precedes Epoch")); 2080bc421551SDag-Erling Smørgrav return t; 2081bc421551SDag-Erling Smørgrav } 2082bc421551SDag-Erling Smørgrav 2083bc421551SDag-Erling Smørgrav static void 2084bc421551SDag-Erling Smørgrav inleap(char **fields, int nfields) 2085bc421551SDag-Erling Smørgrav { 2086bc421551SDag-Erling Smørgrav if (nfields != LEAP_FIELDS) 2087bc421551SDag-Erling Smørgrav error(_("wrong number of fields on Leap line")); 2088bc421551SDag-Erling Smørgrav else { 2089bc421551SDag-Erling Smørgrav zic_t t = getleapdatetime(fields, false); 2090bc421551SDag-Erling Smørgrav if (0 <= t) { 2091bc421551SDag-Erling Smørgrav struct lookup const *lp = byword(fields[LP_ROLL], leap_types); 2092bc421551SDag-Erling Smørgrav if (!lp) 2093bc421551SDag-Erling Smørgrav error(_("invalid Rolling/Stationary field on Leap line")); 2094bc421551SDag-Erling Smørgrav else { 2095bc421551SDag-Erling Smørgrav int correction = 0; 2096bc421551SDag-Erling Smørgrav if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */ 2097bc421551SDag-Erling Smørgrav correction = -1; 2098bc421551SDag-Erling Smørgrav else if (strcmp(fields[LP_CORR], "+") == 0) 2099bc421551SDag-Erling Smørgrav correction = 1; 2100bc421551SDag-Erling Smørgrav else 2101bc421551SDag-Erling Smørgrav error(_("invalid CORRECTION field on Leap line")); 2102bc421551SDag-Erling Smørgrav if (correction) 2103bc421551SDag-Erling Smørgrav leapadd(t, correction, lp->l_value); 2104bc421551SDag-Erling Smørgrav } 2105bc421551SDag-Erling Smørgrav } 2106bc421551SDag-Erling Smørgrav } 2107bc421551SDag-Erling Smørgrav } 2108bc421551SDag-Erling Smørgrav 2109bc421551SDag-Erling Smørgrav static void 2110bc421551SDag-Erling Smørgrav inexpires(char **fields, int nfields) 2111bc421551SDag-Erling Smørgrav { 2112bc421551SDag-Erling Smørgrav if (nfields != EXPIRES_FIELDS) 2113bc421551SDag-Erling Smørgrav error(_("wrong number of fields on Expires line")); 2114bc421551SDag-Erling Smørgrav else if (0 <= leapexpires) 2115bc421551SDag-Erling Smørgrav error(_("multiple Expires lines")); 2116bc421551SDag-Erling Smørgrav else 2117bc421551SDag-Erling Smørgrav leapexpires = getleapdatetime(fields, true); 2118bc421551SDag-Erling Smørgrav } 2119bc421551SDag-Erling Smørgrav 2120bc421551SDag-Erling Smørgrav static void 2121bc421551SDag-Erling Smørgrav inlink(char **fields, int nfields) 2122bc421551SDag-Erling Smørgrav { 2123bc421551SDag-Erling Smørgrav struct link l; 2124bc421551SDag-Erling Smørgrav 2125bc421551SDag-Erling Smørgrav if (nfields != LINK_FIELDS) { 2126bc421551SDag-Erling Smørgrav error(_("wrong number of fields on Link line")); 2127bc421551SDag-Erling Smørgrav return; 2128bc421551SDag-Erling Smørgrav } 2129bc421551SDag-Erling Smørgrav if (*fields[LF_TARGET] == '\0') { 2130bc421551SDag-Erling Smørgrav error(_("blank TARGET field on Link line")); 2131bc421551SDag-Erling Smørgrav return; 2132bc421551SDag-Erling Smørgrav } 2133bc421551SDag-Erling Smørgrav if (! namecheck(fields[LF_LINKNAME])) 2134bc421551SDag-Erling Smørgrav return; 2135bc421551SDag-Erling Smørgrav l.l_filenum = filenum; 2136bc421551SDag-Erling Smørgrav l.l_linenum = linenum; 2137bc421551SDag-Erling Smørgrav l.l_target = estrdup(fields[LF_TARGET]); 2138bc421551SDag-Erling Smørgrav l.l_linkname = estrdup(fields[LF_LINKNAME]); 2139bc421551SDag-Erling Smørgrav links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc); 2140bc421551SDag-Erling Smørgrav links[nlinks++] = l; 2141bc421551SDag-Erling Smørgrav } 2142bc421551SDag-Erling Smørgrav 2143bc421551SDag-Erling Smørgrav static bool 2144bc421551SDag-Erling Smørgrav rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, 2145bc421551SDag-Erling Smørgrav const char *typep, const char *monthp, const char *dayp, 2146bc421551SDag-Erling Smørgrav const char *timep) 2147bc421551SDag-Erling Smørgrav { 2148bc421551SDag-Erling Smørgrav register const struct lookup * lp; 2149bc421551SDag-Erling Smørgrav register const char * cp; 2150bc421551SDag-Erling Smørgrav register char * dp; 2151bc421551SDag-Erling Smørgrav register char * ep; 2152bc421551SDag-Erling Smørgrav char xs; 2153bc421551SDag-Erling Smørgrav 2154bc421551SDag-Erling Smørgrav if ((lp = byword(monthp, mon_names)) == NULL) { 2155bc421551SDag-Erling Smørgrav error(_("invalid month name")); 2156bc421551SDag-Erling Smørgrav return false; 2157bc421551SDag-Erling Smørgrav } 2158bc421551SDag-Erling Smørgrav rp->r_month = lp->l_value; 2159bc421551SDag-Erling Smørgrav rp->r_todisstd = false; 2160bc421551SDag-Erling Smørgrav rp->r_todisut = false; 2161bc421551SDag-Erling Smørgrav dp = estrdup(timep); 2162bc421551SDag-Erling Smørgrav if (*dp != '\0') { 2163bc421551SDag-Erling Smørgrav ep = dp + strlen(dp) - 1; 2164bc421551SDag-Erling Smørgrav switch (lowerit(*ep)) { 2165bc421551SDag-Erling Smørgrav case 's': /* Standard */ 2166bc421551SDag-Erling Smørgrav rp->r_todisstd = true; 2167bc421551SDag-Erling Smørgrav rp->r_todisut = false; 2168bc421551SDag-Erling Smørgrav *ep = '\0'; 2169bc421551SDag-Erling Smørgrav break; 2170bc421551SDag-Erling Smørgrav case 'w': /* Wall */ 2171bc421551SDag-Erling Smørgrav rp->r_todisstd = false; 2172bc421551SDag-Erling Smørgrav rp->r_todisut = false; 2173bc421551SDag-Erling Smørgrav *ep = '\0'; 2174bc421551SDag-Erling Smørgrav break; 2175bc421551SDag-Erling Smørgrav case 'g': /* Greenwich */ 2176bc421551SDag-Erling Smørgrav case 'u': /* Universal */ 2177bc421551SDag-Erling Smørgrav case 'z': /* Zulu */ 2178bc421551SDag-Erling Smørgrav rp->r_todisstd = true; 2179bc421551SDag-Erling Smørgrav rp->r_todisut = true; 2180bc421551SDag-Erling Smørgrav *ep = '\0'; 2181bc421551SDag-Erling Smørgrav break; 2182bc421551SDag-Erling Smørgrav } 2183bc421551SDag-Erling Smørgrav } 2184bc421551SDag-Erling Smørgrav rp->r_tod = gethms(dp, _("invalid time of day")); 2185bc421551SDag-Erling Smørgrav free(dp); 2186bc421551SDag-Erling Smørgrav /* 2187bc421551SDag-Erling Smørgrav ** Year work. 2188bc421551SDag-Erling Smørgrav */ 2189bc421551SDag-Erling Smørgrav cp = loyearp; 2190bc421551SDag-Erling Smørgrav lp = byword(cp, begin_years); 2191bc421551SDag-Erling Smørgrav rp->r_lowasnum = lp == NULL; 2192bc421551SDag-Erling Smørgrav if (!rp->r_lowasnum) switch (lp->l_value) { 2193bc421551SDag-Erling Smørgrav case YR_MINIMUM: 2194bc421551SDag-Erling Smørgrav rp->r_loyear = ZIC_MIN; 2195bc421551SDag-Erling Smørgrav break; 2196bc421551SDag-Erling Smørgrav case YR_MAXIMUM: 2197bc421551SDag-Erling Smørgrav rp->r_loyear = ZIC_MAX; 2198bc421551SDag-Erling Smørgrav break; 2199bc421551SDag-Erling Smørgrav default: unreachable(); 2200bc421551SDag-Erling Smørgrav } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) { 2201bc421551SDag-Erling Smørgrav error(_("invalid starting year")); 2202bc421551SDag-Erling Smørgrav return false; 2203bc421551SDag-Erling Smørgrav } 2204bc421551SDag-Erling Smørgrav cp = hiyearp; 2205bc421551SDag-Erling Smørgrav lp = byword(cp, end_years); 2206bc421551SDag-Erling Smørgrav rp->r_hiwasnum = lp == NULL; 2207bc421551SDag-Erling Smørgrav if (!rp->r_hiwasnum) switch (lp->l_value) { 2208bc421551SDag-Erling Smørgrav case YR_MINIMUM: 2209bc421551SDag-Erling Smørgrav rp->r_hiyear = ZIC_MIN; 2210bc421551SDag-Erling Smørgrav break; 2211bc421551SDag-Erling Smørgrav case YR_MAXIMUM: 2212bc421551SDag-Erling Smørgrav rp->r_hiyear = ZIC_MAX; 2213bc421551SDag-Erling Smørgrav break; 2214bc421551SDag-Erling Smørgrav case YR_ONLY: 2215bc421551SDag-Erling Smørgrav rp->r_hiyear = rp->r_loyear; 2216bc421551SDag-Erling Smørgrav break; 2217bc421551SDag-Erling Smørgrav default: unreachable(); 2218bc421551SDag-Erling Smørgrav } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) { 2219bc421551SDag-Erling Smørgrav error(_("invalid ending year")); 2220bc421551SDag-Erling Smørgrav return false; 2221bc421551SDag-Erling Smørgrav } 2222bc421551SDag-Erling Smørgrav if (rp->r_loyear > rp->r_hiyear) { 2223bc421551SDag-Erling Smørgrav error(_("starting year greater than ending year")); 2224bc421551SDag-Erling Smørgrav return false; 2225bc421551SDag-Erling Smørgrav } 2226bc421551SDag-Erling Smørgrav if (*typep != '\0') { 2227bc421551SDag-Erling Smørgrav error(_("year type \"%s\" is unsupported; use \"-\" instead"), 2228bc421551SDag-Erling Smørgrav typep); 2229bc421551SDag-Erling Smørgrav return false; 2230bc421551SDag-Erling Smørgrav } 2231bc421551SDag-Erling Smørgrav /* 2232bc421551SDag-Erling Smørgrav ** Day work. 2233bc421551SDag-Erling Smørgrav ** Accept things such as: 2234bc421551SDag-Erling Smørgrav ** 1 2235bc421551SDag-Erling Smørgrav ** lastSunday 2236bc421551SDag-Erling Smørgrav ** last-Sunday (undocumented; warn about this) 2237bc421551SDag-Erling Smørgrav ** Sun<=20 2238bc421551SDag-Erling Smørgrav ** Sun>=7 2239bc421551SDag-Erling Smørgrav */ 2240bc421551SDag-Erling Smørgrav dp = estrdup(dayp); 2241bc421551SDag-Erling Smørgrav if ((lp = byword(dp, lasts)) != NULL) { 2242bc421551SDag-Erling Smørgrav rp->r_dycode = DC_DOWLEQ; 2243bc421551SDag-Erling Smørgrav rp->r_wday = lp->l_value; 2244bc421551SDag-Erling Smørgrav rp->r_dayofmonth = len_months[1][rp->r_month]; 2245bc421551SDag-Erling Smørgrav } else { 2246bc421551SDag-Erling Smørgrav if ((ep = strchr(dp, '<')) != 0) 2247bc421551SDag-Erling Smørgrav rp->r_dycode = DC_DOWLEQ; 2248bc421551SDag-Erling Smørgrav else if ((ep = strchr(dp, '>')) != 0) 2249bc421551SDag-Erling Smørgrav rp->r_dycode = DC_DOWGEQ; 2250bc421551SDag-Erling Smørgrav else { 2251bc421551SDag-Erling Smørgrav ep = dp; 2252bc421551SDag-Erling Smørgrav rp->r_dycode = DC_DOM; 2253bc421551SDag-Erling Smørgrav } 2254bc421551SDag-Erling Smørgrav if (rp->r_dycode != DC_DOM) { 2255bc421551SDag-Erling Smørgrav *ep++ = 0; 2256bc421551SDag-Erling Smørgrav if (*ep++ != '=') { 2257bc421551SDag-Erling Smørgrav error(_("invalid day of month")); 2258bc421551SDag-Erling Smørgrav free(dp); 2259bc421551SDag-Erling Smørgrav return false; 2260bc421551SDag-Erling Smørgrav } 2261bc421551SDag-Erling Smørgrav if ((lp = byword(dp, wday_names)) == NULL) { 2262bc421551SDag-Erling Smørgrav error(_("invalid weekday name")); 2263bc421551SDag-Erling Smørgrav free(dp); 2264bc421551SDag-Erling Smørgrav return false; 2265bc421551SDag-Erling Smørgrav } 2266bc421551SDag-Erling Smørgrav rp->r_wday = lp->l_value; 2267bc421551SDag-Erling Smørgrav } 2268bc421551SDag-Erling Smørgrav if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 || 2269bc421551SDag-Erling Smørgrav rp->r_dayofmonth <= 0 || 2270bc421551SDag-Erling Smørgrav (rp->r_dayofmonth > len_months[1][rp->r_month])) { 2271bc421551SDag-Erling Smørgrav error(_("invalid day of month")); 2272bc421551SDag-Erling Smørgrav free(dp); 2273bc421551SDag-Erling Smørgrav return false; 2274bc421551SDag-Erling Smørgrav } 2275bc421551SDag-Erling Smørgrav } 2276bc421551SDag-Erling Smørgrav free(dp); 2277bc421551SDag-Erling Smørgrav return true; 2278bc421551SDag-Erling Smørgrav } 2279bc421551SDag-Erling Smørgrav 2280bc421551SDag-Erling Smørgrav static void 2281bc421551SDag-Erling Smørgrav convert(uint_fast32_t val, char *buf) 2282bc421551SDag-Erling Smørgrav { 2283bc421551SDag-Erling Smørgrav register int i; 2284bc421551SDag-Erling Smørgrav register int shift; 2285bc421551SDag-Erling Smørgrav unsigned char *const b = (unsigned char *) buf; 2286bc421551SDag-Erling Smørgrav 2287bc421551SDag-Erling Smørgrav for (i = 0, shift = 24; i < 4; ++i, shift -= 8) 2288bc421551SDag-Erling Smørgrav b[i] = (val >> shift) & 0xff; 2289bc421551SDag-Erling Smørgrav } 2290bc421551SDag-Erling Smørgrav 2291bc421551SDag-Erling Smørgrav static void 2292bc421551SDag-Erling Smørgrav convert64(uint_fast64_t val, char *buf) 2293bc421551SDag-Erling Smørgrav { 2294bc421551SDag-Erling Smørgrav register int i; 2295bc421551SDag-Erling Smørgrav register int shift; 2296bc421551SDag-Erling Smørgrav unsigned char *const b = (unsigned char *) buf; 2297bc421551SDag-Erling Smørgrav 2298bc421551SDag-Erling Smørgrav for (i = 0, shift = 56; i < 8; ++i, shift -= 8) 2299bc421551SDag-Erling Smørgrav b[i] = (val >> shift) & 0xff; 2300bc421551SDag-Erling Smørgrav } 2301bc421551SDag-Erling Smørgrav 2302bc421551SDag-Erling Smørgrav static void 2303bc421551SDag-Erling Smørgrav puttzcode(zic_t val, FILE *fp) 2304bc421551SDag-Erling Smørgrav { 2305bc421551SDag-Erling Smørgrav char buf[4]; 2306bc421551SDag-Erling Smørgrav 2307bc421551SDag-Erling Smørgrav convert(val, buf); 2308bc421551SDag-Erling Smørgrav fwrite(buf, sizeof buf, 1, fp); 2309bc421551SDag-Erling Smørgrav } 2310bc421551SDag-Erling Smørgrav 2311bc421551SDag-Erling Smørgrav static void 2312bc421551SDag-Erling Smørgrav puttzcodepass(zic_t val, FILE *fp, int pass) 2313bc421551SDag-Erling Smørgrav { 2314bc421551SDag-Erling Smørgrav if (pass == 1) 2315bc421551SDag-Erling Smørgrav puttzcode(val, fp); 2316bc421551SDag-Erling Smørgrav else { 2317bc421551SDag-Erling Smørgrav char buf[8]; 2318bc421551SDag-Erling Smørgrav 2319bc421551SDag-Erling Smørgrav convert64(val, buf); 2320bc421551SDag-Erling Smørgrav fwrite(buf, sizeof buf, 1, fp); 2321bc421551SDag-Erling Smørgrav } 2322bc421551SDag-Erling Smørgrav } 2323bc421551SDag-Erling Smørgrav 2324bc421551SDag-Erling Smørgrav static int 2325bc421551SDag-Erling Smørgrav atcomp(const void *avp, const void *bvp) 2326bc421551SDag-Erling Smørgrav { 2327bc421551SDag-Erling Smørgrav struct attype const *ap = avp, *bp = bvp; 2328bc421551SDag-Erling Smørgrav zic_t a = ap->at, b = bp->at; 2329bc421551SDag-Erling Smørgrav return a < b ? -1 : a > b; 2330bc421551SDag-Erling Smørgrav } 2331bc421551SDag-Erling Smørgrav 2332bc421551SDag-Erling Smørgrav struct timerange { 2333bc421551SDag-Erling Smørgrav int defaulttype; 2334bc421551SDag-Erling Smørgrav ptrdiff_t base, count; 2335bc421551SDag-Erling Smørgrav int leapbase, leapcount; 2336bc421551SDag-Erling Smørgrav bool leapexpiry; 2337bc421551SDag-Erling Smørgrav }; 2338bc421551SDag-Erling Smørgrav 2339bc421551SDag-Erling Smørgrav static struct timerange 2340bc421551SDag-Erling Smørgrav limitrange(struct timerange r, zic_t lo, zic_t hi, 2341bc421551SDag-Erling Smørgrav zic_t const *ats, unsigned char const *types) 2342bc421551SDag-Erling Smørgrav { 2343bc421551SDag-Erling Smørgrav /* Omit ordinary transitions < LO. */ 2344bc421551SDag-Erling Smørgrav while (0 < r.count && ats[r.base] < lo) { 2345bc421551SDag-Erling Smørgrav r.defaulttype = types[r.base]; 2346bc421551SDag-Erling Smørgrav r.count--; 2347bc421551SDag-Erling Smørgrav r.base++; 2348bc421551SDag-Erling Smørgrav } 2349bc421551SDag-Erling Smørgrav 2350bc421551SDag-Erling Smørgrav /* Omit as many initial leap seconds as possible, such that the 2351bc421551SDag-Erling Smørgrav first leap second in the truncated list is <= LO, and is a 2352bc421551SDag-Erling Smørgrav positive leap second if and only if it has a positive correction. 2353bc421551SDag-Erling Smørgrav This supports common TZif readers that assume that the first leap 2354bc421551SDag-Erling Smørgrav second is positive if and only if its correction is positive. */ 2355bc421551SDag-Erling Smørgrav while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) { 2356bc421551SDag-Erling Smørgrav r.leapcount--; 2357bc421551SDag-Erling Smørgrav r.leapbase++; 2358bc421551SDag-Erling Smørgrav } 2359bc421551SDag-Erling Smørgrav while (0 < r.leapbase 2360bc421551SDag-Erling Smørgrav && ((corr[r.leapbase - 1] < corr[r.leapbase]) 2361bc421551SDag-Erling Smørgrav != (0 < corr[r.leapbase]))) { 2362bc421551SDag-Erling Smørgrav r.leapcount++; 2363bc421551SDag-Erling Smørgrav r.leapbase--; 2364bc421551SDag-Erling Smørgrav } 2365bc421551SDag-Erling Smørgrav 2366bc421551SDag-Erling Smørgrav 2367bc421551SDag-Erling Smørgrav /* Omit ordinary and leap second transitions greater than HI + 1. */ 2368bc421551SDag-Erling Smørgrav if (hi < max_time) { 2369bc421551SDag-Erling Smørgrav while (0 < r.count && hi + 1 < ats[r.base + r.count - 1]) 2370bc421551SDag-Erling Smørgrav r.count--; 2371bc421551SDag-Erling Smørgrav while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1]) 2372bc421551SDag-Erling Smørgrav r.leapcount--; 2373bc421551SDag-Erling Smørgrav } 2374bc421551SDag-Erling Smørgrav 2375bc421551SDag-Erling Smørgrav /* Determine whether to append an expiration to the leap second table. */ 2376bc421551SDag-Erling Smørgrav r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi; 2377bc421551SDag-Erling Smørgrav 2378bc421551SDag-Erling Smørgrav return r; 2379bc421551SDag-Erling Smørgrav } 2380bc421551SDag-Erling Smørgrav 2381bc421551SDag-Erling Smørgrav static void 2382bc421551SDag-Erling Smørgrav writezone(const char *const name, const char *const string, char version, 2383bc421551SDag-Erling Smørgrav int defaulttype) 2384bc421551SDag-Erling Smørgrav { 2385bc421551SDag-Erling Smørgrav register FILE * fp; 2386bc421551SDag-Erling Smørgrav register ptrdiff_t i, j; 2387bc421551SDag-Erling Smørgrav register size_t u; 2388bc421551SDag-Erling Smørgrav register int pass; 2389bc421551SDag-Erling Smørgrav char *tempname = NULL; 2390bc421551SDag-Erling Smørgrav char const *outname = name; 2391bc421551SDag-Erling Smørgrav 2392bc421551SDag-Erling Smørgrav /* Allocate the ATS and TYPES arrays via a single malloc, 2393bc421551SDag-Erling Smørgrav as this is a bit faster. Do not malloc(0) if !timecnt, 2394bc421551SDag-Erling Smørgrav as that might return NULL even on success. */ 2395bc421551SDag-Erling Smørgrav zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt, 2396bc421551SDag-Erling Smørgrav sizeof *ats + 1), 2397bc421551SDag-Erling Smørgrav alignof(zic_t))); 2398bc421551SDag-Erling Smørgrav void *typesptr = ats + timecnt; 2399bc421551SDag-Erling Smørgrav unsigned char *types = typesptr; 2400bc421551SDag-Erling Smørgrav struct timerange rangeall = {0}, range32, range64; 2401bc421551SDag-Erling Smørgrav 2402bc421551SDag-Erling Smørgrav /* 2403bc421551SDag-Erling Smørgrav ** Sort. 2404bc421551SDag-Erling Smørgrav */ 2405bc421551SDag-Erling Smørgrav if (timecnt > 1) 2406bc421551SDag-Erling Smørgrav qsort(attypes, timecnt, sizeof *attypes, atcomp); 2407bc421551SDag-Erling Smørgrav /* 2408bc421551SDag-Erling Smørgrav ** Optimize. 2409bc421551SDag-Erling Smørgrav */ 2410bc421551SDag-Erling Smørgrav { 2411bc421551SDag-Erling Smørgrav ptrdiff_t fromi, toi; 2412bc421551SDag-Erling Smørgrav 2413bc421551SDag-Erling Smørgrav toi = 0; 2414bc421551SDag-Erling Smørgrav fromi = 0; 2415bc421551SDag-Erling Smørgrav for ( ; fromi < timecnt; ++fromi) { 2416bc421551SDag-Erling Smørgrav if (toi != 0 2417bc421551SDag-Erling Smørgrav && ((attypes[fromi].at 2418bc421551SDag-Erling Smørgrav + utoffs[attypes[toi - 1].type]) 2419bc421551SDag-Erling Smørgrav <= (attypes[toi - 1].at 2420bc421551SDag-Erling Smørgrav + utoffs[toi == 1 ? 0 2421bc421551SDag-Erling Smørgrav : attypes[toi - 2].type]))) { 2422bc421551SDag-Erling Smørgrav attypes[toi - 1].type = 2423bc421551SDag-Erling Smørgrav attypes[fromi].type; 2424bc421551SDag-Erling Smørgrav continue; 2425bc421551SDag-Erling Smørgrav } 2426bc421551SDag-Erling Smørgrav if (toi == 0 2427bc421551SDag-Erling Smørgrav || attypes[fromi].dontmerge 2428bc421551SDag-Erling Smørgrav || (utoffs[attypes[toi - 1].type] 2429bc421551SDag-Erling Smørgrav != utoffs[attypes[fromi].type]) 2430bc421551SDag-Erling Smørgrav || (isdsts[attypes[toi - 1].type] 2431bc421551SDag-Erling Smørgrav != isdsts[attypes[fromi].type]) 2432bc421551SDag-Erling Smørgrav || (desigidx[attypes[toi - 1].type] 2433bc421551SDag-Erling Smørgrav != desigidx[attypes[fromi].type])) 2434bc421551SDag-Erling Smørgrav attypes[toi++] = attypes[fromi]; 2435bc421551SDag-Erling Smørgrav } 2436bc421551SDag-Erling Smørgrav timecnt = toi; 2437bc421551SDag-Erling Smørgrav } 2438bc421551SDag-Erling Smørgrav 2439bc421551SDag-Erling Smørgrav if (noise && timecnt > 1200) { 2440bc421551SDag-Erling Smørgrav if (timecnt > TZ_MAX_TIMES) 2441bc421551SDag-Erling Smørgrav warning(_("reference clients mishandle" 2442bc421551SDag-Erling Smørgrav " more than %d transition times"), 2443bc421551SDag-Erling Smørgrav TZ_MAX_TIMES); 2444bc421551SDag-Erling Smørgrav else 2445bc421551SDag-Erling Smørgrav warning(_("pre-2014 clients may mishandle" 2446bc421551SDag-Erling Smørgrav " more than 1200 transition times")); 2447bc421551SDag-Erling Smørgrav } 2448bc421551SDag-Erling Smørgrav /* 2449bc421551SDag-Erling Smørgrav ** Transfer. 2450bc421551SDag-Erling Smørgrav */ 2451bc421551SDag-Erling Smørgrav for (i = 0; i < timecnt; ++i) { 2452bc421551SDag-Erling Smørgrav ats[i] = attypes[i].at; 2453bc421551SDag-Erling Smørgrav types[i] = attypes[i].type; 2454bc421551SDag-Erling Smørgrav } 2455bc421551SDag-Erling Smørgrav 2456bc421551SDag-Erling Smørgrav /* 2457bc421551SDag-Erling Smørgrav ** Correct for leap seconds. 2458bc421551SDag-Erling Smørgrav */ 2459bc421551SDag-Erling Smørgrav for (i = 0; i < timecnt; ++i) { 2460bc421551SDag-Erling Smørgrav j = leapcnt; 2461bc421551SDag-Erling Smørgrav while (--j >= 0) 2462bc421551SDag-Erling Smørgrav if (ats[i] > trans[j] - corr[j]) { 2463bc421551SDag-Erling Smørgrav ats[i] = tadd(ats[i], corr[j]); 2464bc421551SDag-Erling Smørgrav break; 2465bc421551SDag-Erling Smørgrav } 2466bc421551SDag-Erling Smørgrav } 2467bc421551SDag-Erling Smørgrav 2468bc421551SDag-Erling Smørgrav rangeall.defaulttype = defaulttype; 2469bc421551SDag-Erling Smørgrav rangeall.count = timecnt; 2470bc421551SDag-Erling Smørgrav rangeall.leapcount = leapcnt; 2471bc421551SDag-Erling Smørgrav range64 = limitrange(rangeall, lo_time, 2472bc421551SDag-Erling Smørgrav max(hi_time, 2473bc421551SDag-Erling Smørgrav redundant_time - (ZIC_MIN < redundant_time)), 2474bc421551SDag-Erling Smørgrav ats, types); 2475bc421551SDag-Erling Smørgrav range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types); 2476bc421551SDag-Erling Smørgrav 2477bc421551SDag-Erling Smørgrav /* TZif version 4 is needed if a no-op transition is appended to 2478bc421551SDag-Erling Smørgrav indicate the expiration of the leap second table, or if the first 2479bc421551SDag-Erling Smørgrav leap second transition is not to a +1 or -1 correction. */ 2480bc421551SDag-Erling Smørgrav for (pass = 1; pass <= 2; pass++) { 2481bc421551SDag-Erling Smørgrav struct timerange const *r = pass == 1 ? &range32 : &range64; 2482bc421551SDag-Erling Smørgrav if (pass == 1 && !want_bloat()) 2483bc421551SDag-Erling Smørgrav continue; 2484bc421551SDag-Erling Smørgrav if (r->leapexpiry) { 2485bc421551SDag-Erling Smørgrav if (noise) 2486bc421551SDag-Erling Smørgrav warning(_("%s: pre-2021b clients may mishandle" 2487bc421551SDag-Erling Smørgrav " leap second expiry"), 2488bc421551SDag-Erling Smørgrav name); 2489bc421551SDag-Erling Smørgrav version = '4'; 2490bc421551SDag-Erling Smørgrav } 2491bc421551SDag-Erling Smørgrav if (0 < r->leapcount 2492bc421551SDag-Erling Smørgrav && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) { 2493bc421551SDag-Erling Smørgrav if (noise) 2494bc421551SDag-Erling Smørgrav warning(_("%s: pre-2021b clients may mishandle" 2495bc421551SDag-Erling Smørgrav " leap second table truncation"), 2496bc421551SDag-Erling Smørgrav name); 2497bc421551SDag-Erling Smørgrav version = '4'; 2498bc421551SDag-Erling Smørgrav } 2499bc421551SDag-Erling Smørgrav if (version == '4') 2500bc421551SDag-Erling Smørgrav break; 2501bc421551SDag-Erling Smørgrav } 2502bc421551SDag-Erling Smørgrav 2503bc421551SDag-Erling Smørgrav fp = open_outfile(&outname, &tempname); 2504bc421551SDag-Erling Smørgrav 2505bc421551SDag-Erling Smørgrav for (pass = 1; pass <= 2; ++pass) { 2506bc421551SDag-Erling Smørgrav register ptrdiff_t thistimei, thistimecnt, thistimelim; 2507bc421551SDag-Erling Smørgrav register int thisleapi, thisleapcnt, thisleaplim; 2508bc421551SDag-Erling Smørgrav struct tzhead tzh; 2509bc421551SDag-Erling Smørgrav int pretranstype = -1, thisdefaulttype; 2510bc421551SDag-Erling Smørgrav bool locut, hicut, thisleapexpiry; 2511bc421551SDag-Erling Smørgrav zic_t lo, thismin, thismax; 2512bc421551SDag-Erling Smørgrav int old0; 2513bc421551SDag-Erling Smørgrav char omittype[TZ_MAX_TYPES]; 2514bc421551SDag-Erling Smørgrav int typemap[TZ_MAX_TYPES]; 2515bc421551SDag-Erling Smørgrav int thistypecnt, stdcnt, utcnt; 2516bc421551SDag-Erling Smørgrav char thischars[TZ_MAX_CHARS]; 2517bc421551SDag-Erling Smørgrav int thischarcnt; 2518bc421551SDag-Erling Smørgrav bool toomanytimes; 2519bc421551SDag-Erling Smørgrav int indmap[TZ_MAX_CHARS]; 2520bc421551SDag-Erling Smørgrav 2521bc421551SDag-Erling Smørgrav if (pass == 1) { 2522bc421551SDag-Erling Smørgrav thisdefaulttype = range32.defaulttype; 2523bc421551SDag-Erling Smørgrav thistimei = range32.base; 2524bc421551SDag-Erling Smørgrav thistimecnt = range32.count; 2525bc421551SDag-Erling Smørgrav toomanytimes = thistimecnt >> 31 >> 1 != 0; 2526bc421551SDag-Erling Smørgrav thisleapi = range32.leapbase; 2527bc421551SDag-Erling Smørgrav thisleapcnt = range32.leapcount; 2528bc421551SDag-Erling Smørgrav thisleapexpiry = range32.leapexpiry; 2529bc421551SDag-Erling Smørgrav thismin = ZIC32_MIN; 2530bc421551SDag-Erling Smørgrav thismax = ZIC32_MAX; 2531bc421551SDag-Erling Smørgrav } else { 2532bc421551SDag-Erling Smørgrav thisdefaulttype = range64.defaulttype; 2533bc421551SDag-Erling Smørgrav thistimei = range64.base; 2534bc421551SDag-Erling Smørgrav thistimecnt = range64.count; 2535bc421551SDag-Erling Smørgrav toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0; 2536bc421551SDag-Erling Smørgrav thisleapi = range64.leapbase; 2537bc421551SDag-Erling Smørgrav thisleapcnt = range64.leapcount; 2538bc421551SDag-Erling Smørgrav thisleapexpiry = range64.leapexpiry; 2539bc421551SDag-Erling Smørgrav thismin = min_time; 2540bc421551SDag-Erling Smørgrav thismax = max_time; 2541bc421551SDag-Erling Smørgrav } 2542bc421551SDag-Erling Smørgrav if (toomanytimes) 2543bc421551SDag-Erling Smørgrav error(_("too many transition times")); 2544bc421551SDag-Erling Smørgrav 2545bc421551SDag-Erling Smørgrav locut = thismin < lo_time && lo_time <= thismax; 2546bc421551SDag-Erling Smørgrav hicut = thismin <= hi_time && hi_time < thismax; 2547bc421551SDag-Erling Smørgrav thistimelim = thistimei + thistimecnt; 2548bc421551SDag-Erling Smørgrav memset(omittype, true, typecnt); 2549bc421551SDag-Erling Smørgrav 2550bc421551SDag-Erling Smørgrav /* Determine whether to output a transition before the first 2551bc421551SDag-Erling Smørgrav transition in range. This is needed when the output is 2552bc421551SDag-Erling Smørgrav truncated at the start, and is also useful when catering to 2553bc421551SDag-Erling Smørgrav buggy 32-bit clients that do not use time type 0 for 2554bc421551SDag-Erling Smørgrav timestamps before the first transition. */ 2555bc421551SDag-Erling Smørgrav if ((locut || (pass == 1 && thistimei)) 2556bc421551SDag-Erling Smørgrav && ! (thistimecnt && ats[thistimei] == lo_time)) { 2557bc421551SDag-Erling Smørgrav pretranstype = thisdefaulttype; 2558bc421551SDag-Erling Smørgrav omittype[pretranstype] = false; 2559bc421551SDag-Erling Smørgrav } 2560bc421551SDag-Erling Smørgrav 2561bc421551SDag-Erling Smørgrav /* Arguably the default time type in the 32-bit data 2562bc421551SDag-Erling Smørgrav should be range32.defaulttype, which is suited for 2563bc421551SDag-Erling Smørgrav timestamps just before ZIC32_MIN. However, zic 2564bc421551SDag-Erling Smørgrav traditionally used the time type of the indefinite 2565bc421551SDag-Erling Smørgrav past instead. Internet RFC 8532 says readers should 2566bc421551SDag-Erling Smørgrav ignore 32-bit data, so this discrepancy matters only 2567bc421551SDag-Erling Smørgrav to obsolete readers where the traditional type might 2568bc421551SDag-Erling Smørgrav be more appropriate even if it's "wrong". So, use 2569bc421551SDag-Erling Smørgrav the historical zic value, unless -r specifies a low 2570bc421551SDag-Erling Smørgrav cutoff that excludes some 32-bit timestamps. */ 2571bc421551SDag-Erling Smørgrav if (pass == 1 && lo_time <= thismin) 2572bc421551SDag-Erling Smørgrav thisdefaulttype = range64.defaulttype; 2573bc421551SDag-Erling Smørgrav 2574bc421551SDag-Erling Smørgrav if (locut) 2575bc421551SDag-Erling Smørgrav thisdefaulttype = unspecifiedtype; 2576bc421551SDag-Erling Smørgrav omittype[thisdefaulttype] = false; 2577bc421551SDag-Erling Smørgrav for (i = thistimei; i < thistimelim; i++) 2578bc421551SDag-Erling Smørgrav omittype[types[i]] = false; 2579bc421551SDag-Erling Smørgrav if (hicut) 2580bc421551SDag-Erling Smørgrav omittype[unspecifiedtype] = false; 2581bc421551SDag-Erling Smørgrav 2582bc421551SDag-Erling Smørgrav /* Reorder types to make THISDEFAULTTYPE type 0. 2583bc421551SDag-Erling Smørgrav Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that 2584bc421551SDag-Erling Smørgrav THISDEFAULTTYPE appears as type 0 in the output instead 2585bc421551SDag-Erling Smørgrav of OLD0. TYPEMAP also omits unused types. */ 2586bc421551SDag-Erling Smørgrav old0 = strlen(omittype); 2587bc421551SDag-Erling Smørgrav 2588bc421551SDag-Erling Smørgrav #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH 2589bc421551SDag-Erling Smørgrav /* 2590bc421551SDag-Erling Smørgrav ** For some pre-2011 systems: if the last-to-be-written 2591bc421551SDag-Erling Smørgrav ** standard (or daylight) type has an offset different from the 2592bc421551SDag-Erling Smørgrav ** most recently used offset, 2593bc421551SDag-Erling Smørgrav ** append an (unused) copy of the most recently used type 2594bc421551SDag-Erling Smørgrav ** (to help get global "altzone" and "timezone" variables 2595bc421551SDag-Erling Smørgrav ** set correctly). 2596bc421551SDag-Erling Smørgrav */ 2597bc421551SDag-Erling Smørgrav if (want_bloat()) { 2598bc421551SDag-Erling Smørgrav register int mrudst, mrustd, hidst, histd, type; 2599bc421551SDag-Erling Smørgrav 2600bc421551SDag-Erling Smørgrav hidst = histd = mrudst = mrustd = -1; 2601bc421551SDag-Erling Smørgrav if (0 <= pretranstype) { 2602bc421551SDag-Erling Smørgrav if (isdsts[pretranstype]) 2603bc421551SDag-Erling Smørgrav mrudst = pretranstype; 2604bc421551SDag-Erling Smørgrav else 2605bc421551SDag-Erling Smørgrav mrustd = pretranstype; 2606bc421551SDag-Erling Smørgrav } 2607bc421551SDag-Erling Smørgrav for (i = thistimei; i < thistimelim; i++) 2608bc421551SDag-Erling Smørgrav if (isdsts[types[i]]) 2609bc421551SDag-Erling Smørgrav mrudst = types[i]; 2610bc421551SDag-Erling Smørgrav else mrustd = types[i]; 2611bc421551SDag-Erling Smørgrav for (i = old0; i < typecnt; i++) { 2612bc421551SDag-Erling Smørgrav int h = (i == old0 ? thisdefaulttype 2613bc421551SDag-Erling Smørgrav : i == thisdefaulttype ? old0 : i); 2614bc421551SDag-Erling Smørgrav if (!omittype[h]) { 2615bc421551SDag-Erling Smørgrav if (isdsts[h]) 2616bc421551SDag-Erling Smørgrav hidst = i; 2617bc421551SDag-Erling Smørgrav else 2618bc421551SDag-Erling Smørgrav histd = i; 2619bc421551SDag-Erling Smørgrav } 2620bc421551SDag-Erling Smørgrav } 2621bc421551SDag-Erling Smørgrav if (hidst >= 0 && mrudst >= 0 && hidst != mrudst && 2622bc421551SDag-Erling Smørgrav utoffs[hidst] != utoffs[mrudst]) { 2623bc421551SDag-Erling Smørgrav isdsts[mrudst] = -1; 2624bc421551SDag-Erling Smørgrav type = addtype(utoffs[mrudst], 2625bc421551SDag-Erling Smørgrav &chars[desigidx[mrudst]], 2626bc421551SDag-Erling Smørgrav true, 2627bc421551SDag-Erling Smørgrav ttisstds[mrudst], 2628bc421551SDag-Erling Smørgrav ttisuts[mrudst]); 2629bc421551SDag-Erling Smørgrav isdsts[mrudst] = 1; 2630bc421551SDag-Erling Smørgrav omittype[type] = false; 2631bc421551SDag-Erling Smørgrav } 2632bc421551SDag-Erling Smørgrav if (histd >= 0 && mrustd >= 0 && histd != mrustd && 2633bc421551SDag-Erling Smørgrav utoffs[histd] != utoffs[mrustd]) { 2634bc421551SDag-Erling Smørgrav isdsts[mrustd] = -1; 2635bc421551SDag-Erling Smørgrav type = addtype(utoffs[mrustd], 2636bc421551SDag-Erling Smørgrav &chars[desigidx[mrustd]], 2637bc421551SDag-Erling Smørgrav false, 2638bc421551SDag-Erling Smørgrav ttisstds[mrustd], 2639bc421551SDag-Erling Smørgrav ttisuts[mrustd]); 2640bc421551SDag-Erling Smørgrav isdsts[mrustd] = 0; 2641bc421551SDag-Erling Smørgrav omittype[type] = false; 2642bc421551SDag-Erling Smørgrav } 2643bc421551SDag-Erling Smørgrav } 2644bc421551SDag-Erling Smørgrav #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ 2645bc421551SDag-Erling Smørgrav thistypecnt = 0; 2646bc421551SDag-Erling Smørgrav for (i = old0; i < typecnt; i++) 2647bc421551SDag-Erling Smørgrav if (!omittype[i]) 2648bc421551SDag-Erling Smørgrav typemap[i == old0 ? thisdefaulttype 2649bc421551SDag-Erling Smørgrav : i == thisdefaulttype ? old0 : i] 2650bc421551SDag-Erling Smørgrav = thistypecnt++; 2651bc421551SDag-Erling Smørgrav 2652bc421551SDag-Erling Smørgrav for (u = 0; u < sizeof indmap / sizeof indmap[0]; ++u) 2653bc421551SDag-Erling Smørgrav indmap[u] = -1; 2654bc421551SDag-Erling Smørgrav thischarcnt = stdcnt = utcnt = 0; 2655bc421551SDag-Erling Smørgrav for (i = old0; i < typecnt; i++) { 2656bc421551SDag-Erling Smørgrav register char * thisabbr; 2657bc421551SDag-Erling Smørgrav 2658bc421551SDag-Erling Smørgrav if (omittype[i]) 2659bc421551SDag-Erling Smørgrav continue; 2660bc421551SDag-Erling Smørgrav if (ttisstds[i]) 2661bc421551SDag-Erling Smørgrav stdcnt = thistypecnt; 2662bc421551SDag-Erling Smørgrav if (ttisuts[i]) 2663bc421551SDag-Erling Smørgrav utcnt = thistypecnt; 2664bc421551SDag-Erling Smørgrav if (indmap[desigidx[i]] >= 0) 2665bc421551SDag-Erling Smørgrav continue; 2666bc421551SDag-Erling Smørgrav thisabbr = &chars[desigidx[i]]; 2667bc421551SDag-Erling Smørgrav for (j = 0; j < thischarcnt; ++j) 2668bc421551SDag-Erling Smørgrav if (strcmp(&thischars[j], thisabbr) == 0) 2669bc421551SDag-Erling Smørgrav break; 2670bc421551SDag-Erling Smørgrav if (j == thischarcnt) { 2671bc421551SDag-Erling Smørgrav strcpy(&thischars[thischarcnt], thisabbr); 2672bc421551SDag-Erling Smørgrav thischarcnt += strlen(thisabbr) + 1; 2673bc421551SDag-Erling Smørgrav } 2674bc421551SDag-Erling Smørgrav indmap[desigidx[i]] = j; 2675bc421551SDag-Erling Smørgrav } 2676bc421551SDag-Erling Smørgrav if (pass == 1 && !want_bloat()) { 2677bc421551SDag-Erling Smørgrav hicut = thisleapexpiry = false; 2678bc421551SDag-Erling Smørgrav pretranstype = -1; 2679bc421551SDag-Erling Smørgrav thistimecnt = thisleapcnt = 0; 2680bc421551SDag-Erling Smørgrav thistypecnt = thischarcnt = 1; 2681bc421551SDag-Erling Smørgrav } 2682bc421551SDag-Erling Smørgrav #define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp) 2683bc421551SDag-Erling Smørgrav memset(&tzh, 0, sizeof tzh); 2684bc421551SDag-Erling Smørgrav memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); 2685bc421551SDag-Erling Smørgrav tzh.tzh_version[0] = version; 2686bc421551SDag-Erling Smørgrav convert(utcnt, tzh.tzh_ttisutcnt); 2687bc421551SDag-Erling Smørgrav convert(stdcnt, tzh.tzh_ttisstdcnt); 2688bc421551SDag-Erling Smørgrav convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt); 2689bc421551SDag-Erling Smørgrav convert((0 <= pretranstype) + thistimecnt + hicut, 2690bc421551SDag-Erling Smørgrav tzh.tzh_timecnt); 2691bc421551SDag-Erling Smørgrav convert(thistypecnt, tzh.tzh_typecnt); 2692bc421551SDag-Erling Smørgrav convert(thischarcnt, tzh.tzh_charcnt); 2693bc421551SDag-Erling Smørgrav DO(tzh_magic); 2694bc421551SDag-Erling Smørgrav DO(tzh_version); 2695bc421551SDag-Erling Smørgrav DO(tzh_reserved); 2696bc421551SDag-Erling Smørgrav DO(tzh_ttisutcnt); 2697bc421551SDag-Erling Smørgrav DO(tzh_ttisstdcnt); 2698bc421551SDag-Erling Smørgrav DO(tzh_leapcnt); 2699bc421551SDag-Erling Smørgrav DO(tzh_timecnt); 2700bc421551SDag-Erling Smørgrav DO(tzh_typecnt); 2701bc421551SDag-Erling Smørgrav DO(tzh_charcnt); 2702bc421551SDag-Erling Smørgrav #undef DO 2703bc421551SDag-Erling Smørgrav if (pass == 1 && !want_bloat()) { 2704bc421551SDag-Erling Smørgrav /* Output a minimal data block with just one time type. */ 2705bc421551SDag-Erling Smørgrav puttzcode(0, fp); /* utoff */ 2706bc421551SDag-Erling Smørgrav putc(0, fp); /* dst */ 2707bc421551SDag-Erling Smørgrav putc(0, fp); /* index of abbreviation */ 2708bc421551SDag-Erling Smørgrav putc(0, fp); /* empty-string abbreviation */ 2709bc421551SDag-Erling Smørgrav continue; 2710bc421551SDag-Erling Smørgrav } 2711bc421551SDag-Erling Smørgrav 2712bc421551SDag-Erling Smørgrav /* Output a LO_TIME transition if needed; see limitrange. 2713bc421551SDag-Erling Smørgrav But do not go below the minimum representable value 2714bc421551SDag-Erling Smørgrav for this pass. */ 2715bc421551SDag-Erling Smørgrav lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time; 2716bc421551SDag-Erling Smørgrav 2717bc421551SDag-Erling Smørgrav if (0 <= pretranstype) 2718bc421551SDag-Erling Smørgrav puttzcodepass(lo, fp, pass); 2719bc421551SDag-Erling Smørgrav for (i = thistimei; i < thistimelim; ++i) { 2720bc421551SDag-Erling Smørgrav puttzcodepass(ats[i], fp, pass); 2721bc421551SDag-Erling Smørgrav } 2722bc421551SDag-Erling Smørgrav if (hicut) 2723bc421551SDag-Erling Smørgrav puttzcodepass(hi_time + 1, fp, pass); 2724bc421551SDag-Erling Smørgrav if (0 <= pretranstype) 2725bc421551SDag-Erling Smørgrav putc(typemap[pretranstype], fp); 2726bc421551SDag-Erling Smørgrav for (i = thistimei; i < thistimelim; i++) 2727bc421551SDag-Erling Smørgrav putc(typemap[types[i]], fp); 2728bc421551SDag-Erling Smørgrav if (hicut) 2729bc421551SDag-Erling Smørgrav putc(typemap[unspecifiedtype], fp); 2730bc421551SDag-Erling Smørgrav 2731bc421551SDag-Erling Smørgrav for (i = old0; i < typecnt; i++) { 2732bc421551SDag-Erling Smørgrav int h = (i == old0 ? thisdefaulttype 2733bc421551SDag-Erling Smørgrav : i == thisdefaulttype ? old0 : i); 2734bc421551SDag-Erling Smørgrav if (!omittype[h]) { 2735bc421551SDag-Erling Smørgrav puttzcode(utoffs[h], fp); 2736bc421551SDag-Erling Smørgrav putc(isdsts[h], fp); 2737bc421551SDag-Erling Smørgrav putc(indmap[desigidx[h]], fp); 2738bc421551SDag-Erling Smørgrav } 2739bc421551SDag-Erling Smørgrav } 2740bc421551SDag-Erling Smørgrav if (thischarcnt != 0) 2741bc421551SDag-Erling Smørgrav fwrite(thischars, sizeof thischars[0], 2742bc421551SDag-Erling Smørgrav thischarcnt, fp); 2743bc421551SDag-Erling Smørgrav thisleaplim = thisleapi + thisleapcnt; 2744bc421551SDag-Erling Smørgrav for (i = thisleapi; i < thisleaplim; ++i) { 2745bc421551SDag-Erling Smørgrav register zic_t todo; 2746bc421551SDag-Erling Smørgrav 2747bc421551SDag-Erling Smørgrav if (roll[i]) { 2748bc421551SDag-Erling Smørgrav if (timecnt == 0 || trans[i] < ats[0]) { 2749bc421551SDag-Erling Smørgrav j = 0; 2750bc421551SDag-Erling Smørgrav while (isdsts[j]) 2751bc421551SDag-Erling Smørgrav if (++j >= typecnt) { 2752bc421551SDag-Erling Smørgrav j = 0; 2753bc421551SDag-Erling Smørgrav break; 2754bc421551SDag-Erling Smørgrav } 2755bc421551SDag-Erling Smørgrav } else { 2756bc421551SDag-Erling Smørgrav j = 1; 2757bc421551SDag-Erling Smørgrav while (j < timecnt && 2758bc421551SDag-Erling Smørgrav trans[i] >= ats[j]) 2759bc421551SDag-Erling Smørgrav ++j; 2760bc421551SDag-Erling Smørgrav j = types[j - 1]; 2761bc421551SDag-Erling Smørgrav } 2762bc421551SDag-Erling Smørgrav todo = tadd(trans[i], -utoffs[j]); 2763bc421551SDag-Erling Smørgrav } else todo = trans[i]; 2764bc421551SDag-Erling Smørgrav puttzcodepass(todo, fp, pass); 2765bc421551SDag-Erling Smørgrav puttzcode(corr[i], fp); 2766bc421551SDag-Erling Smørgrav } 2767bc421551SDag-Erling Smørgrav if (thisleapexpiry) { 2768bc421551SDag-Erling Smørgrav /* Append a no-op leap correction indicating when the leap 2769bc421551SDag-Erling Smørgrav second table expires. Although this does not conform to 2770bc421551SDag-Erling Smørgrav Internet RFC 8536, most clients seem to accept this and 2771bc421551SDag-Erling Smørgrav the plan is to amend the RFC to allow this in version 4 2772bc421551SDag-Erling Smørgrav TZif files. */ 2773bc421551SDag-Erling Smørgrav puttzcodepass(leapexpires, fp, pass); 2774bc421551SDag-Erling Smørgrav puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp); 2775bc421551SDag-Erling Smørgrav } 2776bc421551SDag-Erling Smørgrav if (stdcnt != 0) 2777bc421551SDag-Erling Smørgrav for (i = old0; i < typecnt; i++) 2778bc421551SDag-Erling Smørgrav if (!omittype[i]) 2779bc421551SDag-Erling Smørgrav putc(ttisstds[i], fp); 2780bc421551SDag-Erling Smørgrav if (utcnt != 0) 2781bc421551SDag-Erling Smørgrav for (i = old0; i < typecnt; i++) 2782bc421551SDag-Erling Smørgrav if (!omittype[i]) 2783bc421551SDag-Erling Smørgrav putc(ttisuts[i], fp); 2784bc421551SDag-Erling Smørgrav } 2785bc421551SDag-Erling Smørgrav fprintf(fp, "\n%s\n", string); 2786bc421551SDag-Erling Smørgrav close_file(fp, directory, name, tempname); 2787bc421551SDag-Erling Smørgrav if (chmod(tempname, mflag) < 0) { 2788bc421551SDag-Erling Smørgrav fprintf(stderr, _("cannot change mode of %s to %03o"), 2789bc421551SDag-Erling Smørgrav tempname, (unsigned)mflag); 2790bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 2791bc421551SDag-Erling Smørgrav } 2792bc421551SDag-Erling Smørgrav if ((uflag != (uid_t)-1 || gflag != (gid_t)-1) 2793bc421551SDag-Erling Smørgrav && chown(tempname, uflag, gflag) < 0) { 2794bc421551SDag-Erling Smørgrav fprintf(stderr, _("cannot change ownership of %s"), 2795bc421551SDag-Erling Smørgrav tempname); 2796bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 2797bc421551SDag-Erling Smørgrav } 2798bc421551SDag-Erling Smørgrav rename_dest(tempname, name); 2799bc421551SDag-Erling Smørgrav free(ats); 2800bc421551SDag-Erling Smørgrav } 2801bc421551SDag-Erling Smørgrav 2802bc421551SDag-Erling Smørgrav static char const * 2803bc421551SDag-Erling Smørgrav abbroffset(char *buf, zic_t offset) 2804bc421551SDag-Erling Smørgrav { 2805bc421551SDag-Erling Smørgrav char sign = '+'; 2806bc421551SDag-Erling Smørgrav int seconds, minutes; 2807bc421551SDag-Erling Smørgrav 2808bc421551SDag-Erling Smørgrav if (offset < 0) { 2809bc421551SDag-Erling Smørgrav offset = -offset; 2810bc421551SDag-Erling Smørgrav sign = '-'; 2811bc421551SDag-Erling Smørgrav } 2812bc421551SDag-Erling Smørgrav 2813bc421551SDag-Erling Smørgrav seconds = offset % SECSPERMIN; 2814bc421551SDag-Erling Smørgrav offset /= SECSPERMIN; 2815bc421551SDag-Erling Smørgrav minutes = offset % MINSPERHOUR; 2816bc421551SDag-Erling Smørgrav offset /= MINSPERHOUR; 2817bc421551SDag-Erling Smørgrav if (100 <= offset) { 2818bc421551SDag-Erling Smørgrav error(_("%%z UT offset magnitude exceeds 99:59:59")); 2819bc421551SDag-Erling Smørgrav return "%z"; 2820bc421551SDag-Erling Smørgrav } else { 2821bc421551SDag-Erling Smørgrav char *p = buf; 2822bc421551SDag-Erling Smørgrav *p++ = sign; 2823bc421551SDag-Erling Smørgrav *p++ = '0' + offset / 10; 2824bc421551SDag-Erling Smørgrav *p++ = '0' + offset % 10; 2825bc421551SDag-Erling Smørgrav if (minutes | seconds) { 2826bc421551SDag-Erling Smørgrav *p++ = '0' + minutes / 10; 2827bc421551SDag-Erling Smørgrav *p++ = '0' + minutes % 10; 2828bc421551SDag-Erling Smørgrav if (seconds) { 2829bc421551SDag-Erling Smørgrav *p++ = '0' + seconds / 10; 2830bc421551SDag-Erling Smørgrav *p++ = '0' + seconds % 10; 2831bc421551SDag-Erling Smørgrav } 2832bc421551SDag-Erling Smørgrav } 2833bc421551SDag-Erling Smørgrav *p = '\0'; 2834bc421551SDag-Erling Smørgrav return buf; 2835bc421551SDag-Erling Smørgrav } 2836bc421551SDag-Erling Smørgrav } 2837bc421551SDag-Erling Smørgrav 2838bc421551SDag-Erling Smørgrav static char const disable_percent_s[] = ""; 2839bc421551SDag-Erling Smørgrav 2840bc421551SDag-Erling Smørgrav static ptrdiff_t 2841bc421551SDag-Erling Smørgrav doabbr(char *abbr, struct zone const *zp, char const *letters, 2842bc421551SDag-Erling Smørgrav bool isdst, zic_t save, bool doquotes) 2843bc421551SDag-Erling Smørgrav { 2844bc421551SDag-Erling Smørgrav register char * cp; 2845bc421551SDag-Erling Smørgrav register char * slashp; 2846bc421551SDag-Erling Smørgrav ptrdiff_t len; 2847bc421551SDag-Erling Smørgrav char const *format = zp->z_format; 2848bc421551SDag-Erling Smørgrav 2849bc421551SDag-Erling Smørgrav slashp = strchr(format, '/'); 2850bc421551SDag-Erling Smørgrav if (slashp == NULL) { 2851bc421551SDag-Erling Smørgrav char letterbuf[PERCENT_Z_LEN_BOUND + 1]; 2852bc421551SDag-Erling Smørgrav if (zp->z_format_specifier == 'z') 2853bc421551SDag-Erling Smørgrav letters = abbroffset(letterbuf, zp->z_stdoff + save); 2854bc421551SDag-Erling Smørgrav else if (!letters) 2855bc421551SDag-Erling Smørgrav letters = "%s"; 2856bc421551SDag-Erling Smørgrav else if (letters == disable_percent_s) 2857bc421551SDag-Erling Smørgrav return 0; 2858bc421551SDag-Erling Smørgrav sprintf(abbr, format, letters); 2859bc421551SDag-Erling Smørgrav } else if (isdst) { 2860bc421551SDag-Erling Smørgrav strcpy(abbr, slashp + 1); 2861bc421551SDag-Erling Smørgrav } else { 2862bc421551SDag-Erling Smørgrav memcpy(abbr, format, slashp - format); 2863bc421551SDag-Erling Smørgrav abbr[slashp - format] = '\0'; 2864bc421551SDag-Erling Smørgrav } 2865bc421551SDag-Erling Smørgrav len = strlen(abbr); 2866bc421551SDag-Erling Smørgrav if (!doquotes) 2867bc421551SDag-Erling Smørgrav return len; 2868bc421551SDag-Erling Smørgrav for (cp = abbr; is_alpha(*cp); cp++) 2869bc421551SDag-Erling Smørgrav continue; 2870bc421551SDag-Erling Smørgrav if (len > 0 && *cp == '\0') 2871bc421551SDag-Erling Smørgrav return len; 2872bc421551SDag-Erling Smørgrav abbr[len + 2] = '\0'; 2873bc421551SDag-Erling Smørgrav abbr[len + 1] = '>'; 2874bc421551SDag-Erling Smørgrav memmove(abbr + 1, abbr, len); 2875bc421551SDag-Erling Smørgrav abbr[0] = '<'; 2876bc421551SDag-Erling Smørgrav return len + 2; 2877bc421551SDag-Erling Smørgrav } 2878bc421551SDag-Erling Smørgrav 2879bc421551SDag-Erling Smørgrav static void 2880bc421551SDag-Erling Smørgrav updateminmax(const zic_t x) 2881bc421551SDag-Erling Smørgrav { 2882bc421551SDag-Erling Smørgrav if (min_year > x) 2883bc421551SDag-Erling Smørgrav min_year = x; 2884bc421551SDag-Erling Smørgrav if (max_year < x) 2885bc421551SDag-Erling Smørgrav max_year = x; 2886bc421551SDag-Erling Smørgrav } 2887bc421551SDag-Erling Smørgrav 2888bc421551SDag-Erling Smørgrav static int 2889bc421551SDag-Erling Smørgrav stringoffset(char *result, zic_t offset) 2890bc421551SDag-Erling Smørgrav { 2891bc421551SDag-Erling Smørgrav register int hours; 2892bc421551SDag-Erling Smørgrav register int minutes; 2893bc421551SDag-Erling Smørgrav register int seconds; 2894bc421551SDag-Erling Smørgrav bool negative = offset < 0; 2895bc421551SDag-Erling Smørgrav int len = negative; 2896bc421551SDag-Erling Smørgrav 2897bc421551SDag-Erling Smørgrav if (negative) { 2898bc421551SDag-Erling Smørgrav offset = -offset; 2899bc421551SDag-Erling Smørgrav result[0] = '-'; 2900bc421551SDag-Erling Smørgrav } 2901bc421551SDag-Erling Smørgrav seconds = offset % SECSPERMIN; 2902bc421551SDag-Erling Smørgrav offset /= SECSPERMIN; 2903bc421551SDag-Erling Smørgrav minutes = offset % MINSPERHOUR; 2904bc421551SDag-Erling Smørgrav offset /= MINSPERHOUR; 2905bc421551SDag-Erling Smørgrav hours = offset; 2906bc421551SDag-Erling Smørgrav if (hours >= HOURSPERDAY * DAYSPERWEEK) { 2907bc421551SDag-Erling Smørgrav result[0] = '\0'; 2908bc421551SDag-Erling Smørgrav return 0; 2909bc421551SDag-Erling Smørgrav } 2910bc421551SDag-Erling Smørgrav len += sprintf(result + len, "%d", hours); 2911bc421551SDag-Erling Smørgrav if (minutes != 0 || seconds != 0) { 2912bc421551SDag-Erling Smørgrav len += sprintf(result + len, ":%02d", minutes); 2913bc421551SDag-Erling Smørgrav if (seconds != 0) 2914bc421551SDag-Erling Smørgrav len += sprintf(result + len, ":%02d", seconds); 2915bc421551SDag-Erling Smørgrav } 2916bc421551SDag-Erling Smørgrav return len; 2917bc421551SDag-Erling Smørgrav } 2918bc421551SDag-Erling Smørgrav 2919bc421551SDag-Erling Smørgrav static int 2920bc421551SDag-Erling Smørgrav stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff) 2921bc421551SDag-Erling Smørgrav { 2922bc421551SDag-Erling Smørgrav register zic_t tod = rp->r_tod; 2923bc421551SDag-Erling Smørgrav register int compat = 0; 2924bc421551SDag-Erling Smørgrav 2925bc421551SDag-Erling Smørgrav if (rp->r_dycode == DC_DOM) { 2926bc421551SDag-Erling Smørgrav register int month, total; 2927bc421551SDag-Erling Smørgrav 2928bc421551SDag-Erling Smørgrav if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY) 2929bc421551SDag-Erling Smørgrav return -1; 2930bc421551SDag-Erling Smørgrav total = 0; 2931bc421551SDag-Erling Smørgrav for (month = 0; month < rp->r_month; ++month) 2932bc421551SDag-Erling Smørgrav total += len_months[0][month]; 2933bc421551SDag-Erling Smørgrav /* Omit the "J" in Jan and Feb, as that's shorter. */ 2934bc421551SDag-Erling Smørgrav if (rp->r_month <= 1) 2935bc421551SDag-Erling Smørgrav result += sprintf(result, "%d", total + rp->r_dayofmonth - 1); 2936bc421551SDag-Erling Smørgrav else 2937bc421551SDag-Erling Smørgrav result += sprintf(result, "J%d", total + rp->r_dayofmonth); 2938bc421551SDag-Erling Smørgrav } else { 2939bc421551SDag-Erling Smørgrav register int week; 2940bc421551SDag-Erling Smørgrav register int wday = rp->r_wday; 2941bc421551SDag-Erling Smørgrav register int wdayoff; 2942bc421551SDag-Erling Smørgrav 2943bc421551SDag-Erling Smørgrav if (rp->r_dycode == DC_DOWGEQ) { 2944bc421551SDag-Erling Smørgrav wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK; 2945bc421551SDag-Erling Smørgrav if (wdayoff) 2946bc421551SDag-Erling Smørgrav compat = 2013; 2947bc421551SDag-Erling Smørgrav wday -= wdayoff; 2948bc421551SDag-Erling Smørgrav tod += wdayoff * SECSPERDAY; 2949bc421551SDag-Erling Smørgrav week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK; 2950bc421551SDag-Erling Smørgrav } else if (rp->r_dycode == DC_DOWLEQ) { 2951bc421551SDag-Erling Smørgrav if (rp->r_dayofmonth == len_months[1][rp->r_month]) 2952bc421551SDag-Erling Smørgrav week = 5; 2953bc421551SDag-Erling Smørgrav else { 2954bc421551SDag-Erling Smørgrav wdayoff = rp->r_dayofmonth % DAYSPERWEEK; 2955bc421551SDag-Erling Smørgrav if (wdayoff) 2956bc421551SDag-Erling Smørgrav compat = 2013; 2957bc421551SDag-Erling Smørgrav wday -= wdayoff; 2958bc421551SDag-Erling Smørgrav tod += wdayoff * SECSPERDAY; 2959bc421551SDag-Erling Smørgrav week = rp->r_dayofmonth / DAYSPERWEEK; 2960bc421551SDag-Erling Smørgrav } 2961bc421551SDag-Erling Smørgrav } else return -1; /* "cannot happen" */ 2962bc421551SDag-Erling Smørgrav if (wday < 0) 2963bc421551SDag-Erling Smørgrav wday += DAYSPERWEEK; 2964bc421551SDag-Erling Smørgrav result += sprintf(result, "M%d.%d.%d", 2965bc421551SDag-Erling Smørgrav rp->r_month + 1, week, wday); 2966bc421551SDag-Erling Smørgrav } 2967bc421551SDag-Erling Smørgrav if (rp->r_todisut) 2968bc421551SDag-Erling Smørgrav tod += stdoff; 2969bc421551SDag-Erling Smørgrav if (rp->r_todisstd && !rp->r_isdst) 2970bc421551SDag-Erling Smørgrav tod += save; 2971bc421551SDag-Erling Smørgrav if (tod != 2 * SECSPERMIN * MINSPERHOUR) { 2972bc421551SDag-Erling Smørgrav *result++ = '/'; 2973bc421551SDag-Erling Smørgrav if (! stringoffset(result, tod)) 2974bc421551SDag-Erling Smørgrav return -1; 2975bc421551SDag-Erling Smørgrav if (tod < 0) { 2976bc421551SDag-Erling Smørgrav if (compat < 2013) 2977bc421551SDag-Erling Smørgrav compat = 2013; 2978bc421551SDag-Erling Smørgrav } else if (SECSPERDAY <= tod) { 2979bc421551SDag-Erling Smørgrav if (compat < 1994) 2980bc421551SDag-Erling Smørgrav compat = 1994; 2981bc421551SDag-Erling Smørgrav } 2982bc421551SDag-Erling Smørgrav } 2983bc421551SDag-Erling Smørgrav return compat; 2984bc421551SDag-Erling Smørgrav } 2985bc421551SDag-Erling Smørgrav 2986bc421551SDag-Erling Smørgrav static int 2987bc421551SDag-Erling Smørgrav rule_cmp(struct rule const *a, struct rule const *b) 2988bc421551SDag-Erling Smørgrav { 2989bc421551SDag-Erling Smørgrav if (!a) 2990bc421551SDag-Erling Smørgrav return -!!b; 2991bc421551SDag-Erling Smørgrav if (!b) 2992bc421551SDag-Erling Smørgrav return 1; 2993bc421551SDag-Erling Smørgrav if (a->r_hiyear != b->r_hiyear) 2994bc421551SDag-Erling Smørgrav return a->r_hiyear < b->r_hiyear ? -1 : 1; 2995bc421551SDag-Erling Smørgrav if (a->r_hiyear == ZIC_MAX) 2996bc421551SDag-Erling Smørgrav return 0; 2997bc421551SDag-Erling Smørgrav if (a->r_month - b->r_month != 0) 2998bc421551SDag-Erling Smørgrav return a->r_month - b->r_month; 2999bc421551SDag-Erling Smørgrav return a->r_dayofmonth - b->r_dayofmonth; 3000bc421551SDag-Erling Smørgrav } 3001bc421551SDag-Erling Smørgrav 3002bc421551SDag-Erling Smørgrav static int 3003bc421551SDag-Erling Smørgrav stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) 3004bc421551SDag-Erling Smørgrav { 3005bc421551SDag-Erling Smørgrav register const struct zone * zp; 3006bc421551SDag-Erling Smørgrav register struct rule * rp; 3007bc421551SDag-Erling Smørgrav register struct rule * stdrp; 3008bc421551SDag-Erling Smørgrav register struct rule * dstrp; 3009bc421551SDag-Erling Smørgrav register ptrdiff_t i; 3010bc421551SDag-Erling Smørgrav register int compat = 0; 3011bc421551SDag-Erling Smørgrav register int c; 3012bc421551SDag-Erling Smørgrav int offsetlen; 3013bc421551SDag-Erling Smørgrav struct rule stdr, dstr; 3014bc421551SDag-Erling Smørgrav ptrdiff_t len; 3015bc421551SDag-Erling Smørgrav int dstcmp; 3016bc421551SDag-Erling Smørgrav struct rule *lastrp[2] = { NULL, NULL }; 3017bc421551SDag-Erling Smørgrav struct zone zstr[2]; 3018bc421551SDag-Erling Smørgrav struct zone const *stdzp; 3019bc421551SDag-Erling Smørgrav struct zone const *dstzp; 3020bc421551SDag-Erling Smørgrav 3021bc421551SDag-Erling Smørgrav result[0] = '\0'; 3022bc421551SDag-Erling Smørgrav 3023bc421551SDag-Erling Smørgrav /* Internet RFC 8536 section 5.1 says to use an empty TZ string if 3024bc421551SDag-Erling Smørgrav future timestamps are truncated. */ 3025bc421551SDag-Erling Smørgrav if (hi_time < max_time) 3026bc421551SDag-Erling Smørgrav return -1; 3027bc421551SDag-Erling Smørgrav 3028bc421551SDag-Erling Smørgrav zp = zpfirst + zonecount - 1; 3029bc421551SDag-Erling Smørgrav for (i = 0; i < zp->z_nrules; ++i) { 3030bc421551SDag-Erling Smørgrav struct rule **last; 3031bc421551SDag-Erling Smørgrav int cmp; 3032bc421551SDag-Erling Smørgrav rp = &zp->z_rules[i]; 3033bc421551SDag-Erling Smørgrav last = &lastrp[rp->r_isdst]; 3034bc421551SDag-Erling Smørgrav cmp = rule_cmp(*last, rp); 3035bc421551SDag-Erling Smørgrav if (cmp < 0) 3036bc421551SDag-Erling Smørgrav *last = rp; 3037bc421551SDag-Erling Smørgrav else if (cmp == 0) 3038bc421551SDag-Erling Smørgrav return -1; 3039bc421551SDag-Erling Smørgrav } 3040bc421551SDag-Erling Smørgrav stdrp = lastrp[false]; 3041bc421551SDag-Erling Smørgrav dstrp = lastrp[true]; 3042bc421551SDag-Erling Smørgrav dstcmp = zp->z_nrules ? rule_cmp(dstrp, stdrp) : zp->z_isdst ? 1 : -1; 3043bc421551SDag-Erling Smørgrav stdzp = dstzp = zp; 3044bc421551SDag-Erling Smørgrav 3045bc421551SDag-Erling Smørgrav if (dstcmp < 0) { 3046bc421551SDag-Erling Smørgrav /* Standard time all year. */ 3047bc421551SDag-Erling Smørgrav dstrp = NULL; 3048bc421551SDag-Erling Smørgrav } else if (0 < dstcmp) { 3049bc421551SDag-Erling Smørgrav /* DST all year. Use an abbreviation like 3050bc421551SDag-Erling Smørgrav "XXX3EDT4,0/0,J365/23" for EDT (-04) all year. */ 3051bc421551SDag-Erling Smørgrav zic_t save = dstrp ? dstrp->r_save : zp->z_save; 3052bc421551SDag-Erling Smørgrav if (0 <= save) 3053bc421551SDag-Erling Smørgrav { 3054bc421551SDag-Erling Smørgrav /* Positive DST, the typical case for all-year DST. 3055bc421551SDag-Erling Smørgrav Fake a timezone with negative DST. */ 3056bc421551SDag-Erling Smørgrav stdzp = &zstr[0]; 3057bc421551SDag-Erling Smørgrav dstzp = &zstr[1]; 3058bc421551SDag-Erling Smørgrav zstr[0].z_stdoff = zp->z_stdoff + 2 * save; 3059bc421551SDag-Erling Smørgrav zstr[0].z_format = "XXX"; /* Any 3 letters will do. */ 3060bc421551SDag-Erling Smørgrav zstr[0].z_format_specifier = 0; 3061bc421551SDag-Erling Smørgrav zstr[1].z_stdoff = zstr[0].z_stdoff; 3062bc421551SDag-Erling Smørgrav zstr[1].z_format = zp->z_format; 3063bc421551SDag-Erling Smørgrav zstr[1].z_format_specifier = zp->z_format_specifier; 3064bc421551SDag-Erling Smørgrav } 3065bc421551SDag-Erling Smørgrav dstr.r_month = TM_JANUARY; 3066bc421551SDag-Erling Smørgrav dstr.r_dycode = DC_DOM; 3067bc421551SDag-Erling Smørgrav dstr.r_dayofmonth = 1; 3068bc421551SDag-Erling Smørgrav dstr.r_tod = 0; 3069bc421551SDag-Erling Smørgrav dstr.r_todisstd = dstr.r_todisut = false; 3070bc421551SDag-Erling Smørgrav dstr.r_isdst = true; 3071bc421551SDag-Erling Smørgrav dstr.r_save = save < 0 ? save : -save; 3072bc421551SDag-Erling Smørgrav dstr.r_abbrvar = dstrp ? dstrp->r_abbrvar : NULL; 3073bc421551SDag-Erling Smørgrav stdr.r_month = TM_DECEMBER; 3074bc421551SDag-Erling Smørgrav stdr.r_dycode = DC_DOM; 3075bc421551SDag-Erling Smørgrav stdr.r_dayofmonth = 31; 3076bc421551SDag-Erling Smørgrav stdr.r_tod = SECSPERDAY + dstr.r_save; 3077bc421551SDag-Erling Smørgrav stdr.r_todisstd = stdr.r_todisut = false; 3078bc421551SDag-Erling Smørgrav stdr.r_isdst = false; 3079bc421551SDag-Erling Smørgrav stdr.r_save = 0; 3080bc421551SDag-Erling Smørgrav stdr.r_abbrvar = save < 0 && stdrp ? stdrp->r_abbrvar : NULL; 3081bc421551SDag-Erling Smørgrav dstrp = &dstr; 3082bc421551SDag-Erling Smørgrav stdrp = &stdr; 3083bc421551SDag-Erling Smørgrav } 3084bc421551SDag-Erling Smørgrav len = doabbr(result, stdzp, stdrp ? stdrp->r_abbrvar : NULL, 3085bc421551SDag-Erling Smørgrav false, 0, true); 3086bc421551SDag-Erling Smørgrav offsetlen = stringoffset(result + len, - stdzp->z_stdoff); 3087bc421551SDag-Erling Smørgrav if (! offsetlen) { 3088bc421551SDag-Erling Smørgrav result[0] = '\0'; 3089bc421551SDag-Erling Smørgrav return -1; 3090bc421551SDag-Erling Smørgrav } 3091bc421551SDag-Erling Smørgrav len += offsetlen; 3092bc421551SDag-Erling Smørgrav if (dstrp == NULL) 3093bc421551SDag-Erling Smørgrav return compat; 3094bc421551SDag-Erling Smørgrav len += doabbr(result + len, dstzp, dstrp->r_abbrvar, 3095bc421551SDag-Erling Smørgrav dstrp->r_isdst, dstrp->r_save, true); 3096bc421551SDag-Erling Smørgrav if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) { 3097bc421551SDag-Erling Smørgrav offsetlen = stringoffset(result + len, 3098bc421551SDag-Erling Smørgrav - (dstzp->z_stdoff + dstrp->r_save)); 3099bc421551SDag-Erling Smørgrav if (! offsetlen) { 3100bc421551SDag-Erling Smørgrav result[0] = '\0'; 3101bc421551SDag-Erling Smørgrav return -1; 3102bc421551SDag-Erling Smørgrav } 3103bc421551SDag-Erling Smørgrav len += offsetlen; 3104bc421551SDag-Erling Smørgrav } 3105bc421551SDag-Erling Smørgrav result[len++] = ','; 3106bc421551SDag-Erling Smørgrav c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff); 3107bc421551SDag-Erling Smørgrav if (c < 0) { 3108bc421551SDag-Erling Smørgrav result[0] = '\0'; 3109bc421551SDag-Erling Smørgrav return -1; 3110bc421551SDag-Erling Smørgrav } 3111bc421551SDag-Erling Smørgrav if (compat < c) 3112bc421551SDag-Erling Smørgrav compat = c; 3113bc421551SDag-Erling Smørgrav len += strlen(result + len); 3114bc421551SDag-Erling Smørgrav result[len++] = ','; 3115bc421551SDag-Erling Smørgrav c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff); 3116bc421551SDag-Erling Smørgrav if (c < 0) { 3117bc421551SDag-Erling Smørgrav result[0] = '\0'; 3118bc421551SDag-Erling Smørgrav return -1; 3119bc421551SDag-Erling Smørgrav } 3120bc421551SDag-Erling Smørgrav if (compat < c) 3121bc421551SDag-Erling Smørgrav compat = c; 3122bc421551SDag-Erling Smørgrav return compat; 3123bc421551SDag-Erling Smørgrav } 3124bc421551SDag-Erling Smørgrav 3125bc421551SDag-Erling Smørgrav static void 3126bc421551SDag-Erling Smørgrav outzone(const struct zone *zpfirst, ptrdiff_t zonecount) 3127bc421551SDag-Erling Smørgrav { 3128bc421551SDag-Erling Smørgrav register ptrdiff_t i, j; 3129bc421551SDag-Erling Smørgrav register zic_t starttime, untiltime; 3130bc421551SDag-Erling Smørgrav register bool startttisstd; 3131bc421551SDag-Erling Smørgrav register bool startttisut; 3132bc421551SDag-Erling Smørgrav register char * startbuf; 3133bc421551SDag-Erling Smørgrav register char * ab; 3134bc421551SDag-Erling Smørgrav register char * envvar; 3135bc421551SDag-Erling Smørgrav register int max_abbr_len; 3136bc421551SDag-Erling Smørgrav register int max_envvar_len; 3137bc421551SDag-Erling Smørgrav register bool prodstic; /* all rules are min to max */ 3138bc421551SDag-Erling Smørgrav register int compat; 3139bc421551SDag-Erling Smørgrav register bool do_extend; 3140bc421551SDag-Erling Smørgrav register char version; 3141bc421551SDag-Erling Smørgrav ptrdiff_t lastatmax = -1; 3142bc421551SDag-Erling Smørgrav zic_t max_year0; 3143bc421551SDag-Erling Smørgrav int defaulttype = -1; 3144bc421551SDag-Erling Smørgrav 3145bc421551SDag-Erling Smørgrav check_for_signal(); 3146bc421551SDag-Erling Smørgrav 3147bc421551SDag-Erling Smørgrav /* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND. */ 3148bc421551SDag-Erling Smørgrav max_abbr_len = 2 + max_format_len + max_abbrvar_len; 3149bc421551SDag-Erling Smørgrav max_envvar_len = 2 * max_abbr_len + 5 * 9; 3150bc421551SDag-Erling Smørgrav 3151bc421551SDag-Erling Smørgrav startbuf = emalloc(max_abbr_len + 1); 3152bc421551SDag-Erling Smørgrav ab = emalloc(max_abbr_len + 1); 3153bc421551SDag-Erling Smørgrav envvar = emalloc(max_envvar_len + 1); 3154bc421551SDag-Erling Smørgrav INITIALIZE(untiltime); 3155bc421551SDag-Erling Smørgrav INITIALIZE(starttime); 3156bc421551SDag-Erling Smørgrav /* 3157bc421551SDag-Erling Smørgrav ** Now. . .finally. . .generate some useful data! 3158bc421551SDag-Erling Smørgrav */ 3159bc421551SDag-Erling Smørgrav timecnt = 0; 3160bc421551SDag-Erling Smørgrav typecnt = 0; 3161bc421551SDag-Erling Smørgrav charcnt = 0; 3162bc421551SDag-Erling Smørgrav prodstic = zonecount == 1; 3163bc421551SDag-Erling Smørgrav /* 3164bc421551SDag-Erling Smørgrav ** Thanks to Earl Chew 3165bc421551SDag-Erling Smørgrav ** for noting the need to unconditionally initialize startttisstd. 3166bc421551SDag-Erling Smørgrav */ 3167bc421551SDag-Erling Smørgrav startttisstd = false; 3168bc421551SDag-Erling Smørgrav startttisut = false; 3169bc421551SDag-Erling Smørgrav min_year = max_year = EPOCH_YEAR; 3170bc421551SDag-Erling Smørgrav if (leapseen) { 3171bc421551SDag-Erling Smørgrav updateminmax(leapminyear); 3172bc421551SDag-Erling Smørgrav updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX)); 3173bc421551SDag-Erling Smørgrav } 3174bc421551SDag-Erling Smørgrav for (i = 0; i < zonecount; ++i) { 3175bc421551SDag-Erling Smørgrav struct zone const *zp = &zpfirst[i]; 3176bc421551SDag-Erling Smørgrav if (i < zonecount - 1) 3177bc421551SDag-Erling Smørgrav updateminmax(zp->z_untilrule.r_loyear); 3178bc421551SDag-Erling Smørgrav for (j = 0; j < zp->z_nrules; ++j) { 3179bc421551SDag-Erling Smørgrav struct rule *rp = &zp->z_rules[j]; 3180bc421551SDag-Erling Smørgrav if (rp->r_lowasnum) 3181bc421551SDag-Erling Smørgrav updateminmax(rp->r_loyear); 3182bc421551SDag-Erling Smørgrav if (rp->r_hiwasnum) 3183bc421551SDag-Erling Smørgrav updateminmax(rp->r_hiyear); 3184bc421551SDag-Erling Smørgrav if (rp->r_lowasnum || rp->r_hiwasnum) 3185bc421551SDag-Erling Smørgrav prodstic = false; 3186bc421551SDag-Erling Smørgrav } 3187bc421551SDag-Erling Smørgrav } 3188bc421551SDag-Erling Smørgrav /* 3189bc421551SDag-Erling Smørgrav ** Generate lots of data if a rule can't cover all future times. 3190bc421551SDag-Erling Smørgrav */ 3191bc421551SDag-Erling Smørgrav compat = stringzone(envvar, zpfirst, zonecount); 3192bc421551SDag-Erling Smørgrav version = compat < 2013 ? '2' : '3'; 3193bc421551SDag-Erling Smørgrav do_extend = compat < 0; 3194bc421551SDag-Erling Smørgrav if (noise) { 3195bc421551SDag-Erling Smørgrav if (!*envvar) 3196bc421551SDag-Erling Smørgrav warning("%s %s", 3197bc421551SDag-Erling Smørgrav _("no POSIX environment variable for zone"), 3198bc421551SDag-Erling Smørgrav zpfirst->z_name); 3199bc421551SDag-Erling Smørgrav else if (compat != 0) { 3200bc421551SDag-Erling Smørgrav /* Circa-COMPAT clients, and earlier clients, might 3201bc421551SDag-Erling Smørgrav not work for this zone when given dates before 3202bc421551SDag-Erling Smørgrav 1970 or after 2038. */ 3203bc421551SDag-Erling Smørgrav warning(_("%s: pre-%d clients may mishandle" 3204bc421551SDag-Erling Smørgrav " distant timestamps"), 3205bc421551SDag-Erling Smørgrav zpfirst->z_name, compat); 3206bc421551SDag-Erling Smørgrav } 3207bc421551SDag-Erling Smørgrav } 3208bc421551SDag-Erling Smørgrav if (do_extend) { 3209bc421551SDag-Erling Smørgrav /* 3210bc421551SDag-Erling Smørgrav ** Search through a couple of extra years past the obvious 3211bc421551SDag-Erling Smørgrav ** 400, to avoid edge cases. For example, suppose a non-POSIX 3212bc421551SDag-Erling Smørgrav ** rule applies from 2012 onwards and has transitions in March 3213bc421551SDag-Erling Smørgrav ** and September, plus some one-off transitions in November 3214bc421551SDag-Erling Smørgrav ** 2013. If zic looked only at the last 400 years, it would 3215bc421551SDag-Erling Smørgrav ** set max_year=2413, with the intent that the 400 years 2014 3216bc421551SDag-Erling Smørgrav ** through 2413 will be repeated. The last transition listed 3217bc421551SDag-Erling Smørgrav ** in the tzfile would be in 2413-09, less than 400 years 3218bc421551SDag-Erling Smørgrav ** after the last one-off transition in 2013-11. Two years 3219bc421551SDag-Erling Smørgrav ** might be overkill, but with the kind of edge cases 3220bc421551SDag-Erling Smørgrav ** available we're not sure that one year would suffice. 3221bc421551SDag-Erling Smørgrav */ 3222bc421551SDag-Erling Smørgrav enum { years_of_observations = YEARSPERREPEAT + 2 }; 3223bc421551SDag-Erling Smørgrav 3224bc421551SDag-Erling Smørgrav if (min_year >= ZIC_MIN + years_of_observations) 3225bc421551SDag-Erling Smørgrav min_year -= years_of_observations; 3226bc421551SDag-Erling Smørgrav else min_year = ZIC_MIN; 3227bc421551SDag-Erling Smørgrav if (max_year <= ZIC_MAX - years_of_observations) 3228bc421551SDag-Erling Smørgrav max_year += years_of_observations; 3229bc421551SDag-Erling Smørgrav else max_year = ZIC_MAX; 3230bc421551SDag-Erling Smørgrav /* 3231bc421551SDag-Erling Smørgrav ** Regardless of any of the above, 3232bc421551SDag-Erling Smørgrav ** for a "proDSTic" zone which specifies that its rules 3233bc421551SDag-Erling Smørgrav ** always have and always will be in effect, 3234bc421551SDag-Erling Smørgrav ** we only need one cycle to define the zone. 3235bc421551SDag-Erling Smørgrav */ 3236bc421551SDag-Erling Smørgrav if (prodstic) { 3237bc421551SDag-Erling Smørgrav min_year = 1900; 3238bc421551SDag-Erling Smørgrav max_year = min_year + years_of_observations; 3239bc421551SDag-Erling Smørgrav } 3240bc421551SDag-Erling Smørgrav } 3241bc421551SDag-Erling Smørgrav max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR) 3242bc421551SDag-Erling Smørgrav + EPOCH_YEAR + 1)); 3243bc421551SDag-Erling Smørgrav max_year0 = max_year; 3244bc421551SDag-Erling Smørgrav if (want_bloat()) { 3245bc421551SDag-Erling Smørgrav /* For the benefit of older systems, 3246bc421551SDag-Erling Smørgrav generate data from 1900 through 2038. */ 3247bc421551SDag-Erling Smørgrav if (min_year > 1900) 3248bc421551SDag-Erling Smørgrav min_year = 1900; 3249bc421551SDag-Erling Smørgrav if (max_year < 2038) 3250bc421551SDag-Erling Smørgrav max_year = 2038; 3251bc421551SDag-Erling Smørgrav } 3252bc421551SDag-Erling Smørgrav 3253bc421551SDag-Erling Smørgrav if (min_time < lo_time || hi_time < max_time) 3254bc421551SDag-Erling Smørgrav unspecifiedtype = addtype(0, "-00", false, false, false); 3255bc421551SDag-Erling Smørgrav 3256bc421551SDag-Erling Smørgrav for (i = 0; i < zonecount; ++i) { 3257bc421551SDag-Erling Smørgrav struct rule *prevrp = NULL; 3258bc421551SDag-Erling Smørgrav /* 3259bc421551SDag-Erling Smørgrav ** A guess that may well be corrected later. 3260bc421551SDag-Erling Smørgrav */ 3261bc421551SDag-Erling Smørgrav zic_t save = 0; 3262bc421551SDag-Erling Smørgrav struct zone const *zp = &zpfirst[i]; 3263bc421551SDag-Erling Smørgrav bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time; 3264bc421551SDag-Erling Smørgrav bool useuntil = i < (zonecount - 1); 3265bc421551SDag-Erling Smørgrav zic_t stdoff = zp->z_stdoff; 3266bc421551SDag-Erling Smørgrav zic_t startoff = stdoff; 3267bc421551SDag-Erling Smørgrav zic_t prevktime; 3268bc421551SDag-Erling Smørgrav INITIALIZE(prevktime); 3269bc421551SDag-Erling Smørgrav if (useuntil && zp->z_untiltime <= min_time) 3270bc421551SDag-Erling Smørgrav continue; 3271bc421551SDag-Erling Smørgrav eat(zp->z_filenum, zp->z_linenum); 3272bc421551SDag-Erling Smørgrav *startbuf = '\0'; 3273bc421551SDag-Erling Smørgrav if (zp->z_nrules == 0) { 3274bc421551SDag-Erling Smørgrav int type; 3275bc421551SDag-Erling Smørgrav save = zp->z_save; 3276bc421551SDag-Erling Smørgrav doabbr(startbuf, zp, NULL, zp->z_isdst, save, false); 3277bc421551SDag-Erling Smørgrav type = addtype(oadd(zp->z_stdoff, save), 3278bc421551SDag-Erling Smørgrav startbuf, zp->z_isdst, startttisstd, 3279bc421551SDag-Erling Smørgrav startttisut); 3280bc421551SDag-Erling Smørgrav if (usestart) { 3281bc421551SDag-Erling Smørgrav addtt(starttime, type); 3282bc421551SDag-Erling Smørgrav usestart = false; 3283bc421551SDag-Erling Smørgrav } else 3284bc421551SDag-Erling Smørgrav defaulttype = type; 3285bc421551SDag-Erling Smørgrav } else { 3286bc421551SDag-Erling Smørgrav zic_t year; 3287bc421551SDag-Erling Smørgrav for (year = min_year; year <= max_year; ++year) { 3288bc421551SDag-Erling Smørgrav if (useuntil && year > zp->z_untilrule.r_hiyear) 3289bc421551SDag-Erling Smørgrav break; 3290bc421551SDag-Erling Smørgrav /* 3291bc421551SDag-Erling Smørgrav ** Mark which rules to do in the current year. 3292bc421551SDag-Erling Smørgrav ** For those to do, calculate rpytime(rp, year); 3293bc421551SDag-Erling Smørgrav ** The former TYPE field was also considered here. 3294bc421551SDag-Erling Smørgrav */ 3295bc421551SDag-Erling Smørgrav for (j = 0; j < zp->z_nrules; ++j) { 3296bc421551SDag-Erling Smørgrav zic_t one = 1; 3297bc421551SDag-Erling Smørgrav zic_t y2038_boundary = one << 31; 3298bc421551SDag-Erling Smørgrav struct rule *rp = &zp->z_rules[j]; 3299bc421551SDag-Erling Smørgrav eats(zp->z_filenum, zp->z_linenum, 3300bc421551SDag-Erling Smørgrav rp->r_filenum, rp->r_linenum); 3301bc421551SDag-Erling Smørgrav rp->r_todo = year >= rp->r_loyear && 3302bc421551SDag-Erling Smørgrav year <= rp->r_hiyear; 3303bc421551SDag-Erling Smørgrav if (rp->r_todo) { 3304bc421551SDag-Erling Smørgrav rp->r_temp = rpytime(rp, year); 3305bc421551SDag-Erling Smørgrav rp->r_todo 3306bc421551SDag-Erling Smørgrav = (rp->r_temp < y2038_boundary 3307bc421551SDag-Erling Smørgrav || year <= max_year0); 3308bc421551SDag-Erling Smørgrav } 3309bc421551SDag-Erling Smørgrav } 3310bc421551SDag-Erling Smørgrav for ( ; ; ) { 3311bc421551SDag-Erling Smørgrav register ptrdiff_t k; 3312bc421551SDag-Erling Smørgrav register zic_t jtime, ktime; 3313bc421551SDag-Erling Smørgrav register zic_t offset; 3314bc421551SDag-Erling Smørgrav struct rule *rp; 3315bc421551SDag-Erling Smørgrav int type; 3316bc421551SDag-Erling Smørgrav 3317bc421551SDag-Erling Smørgrav INITIALIZE(ktime); 3318bc421551SDag-Erling Smørgrav if (useuntil) { 3319bc421551SDag-Erling Smørgrav /* 3320bc421551SDag-Erling Smørgrav ** Turn untiltime into UT 3321bc421551SDag-Erling Smørgrav ** assuming the current stdoff and 3322bc421551SDag-Erling Smørgrav ** save values. 3323bc421551SDag-Erling Smørgrav */ 3324bc421551SDag-Erling Smørgrav untiltime = zp->z_untiltime; 3325bc421551SDag-Erling Smørgrav if (!zp->z_untilrule.r_todisut) 3326bc421551SDag-Erling Smørgrav untiltime = tadd(untiltime, 3327bc421551SDag-Erling Smørgrav -stdoff); 3328bc421551SDag-Erling Smørgrav if (!zp->z_untilrule.r_todisstd) 3329bc421551SDag-Erling Smørgrav untiltime = tadd(untiltime, 3330bc421551SDag-Erling Smørgrav -save); 3331bc421551SDag-Erling Smørgrav } 3332bc421551SDag-Erling Smørgrav /* 3333bc421551SDag-Erling Smørgrav ** Find the rule (of those to do, if any) 3334bc421551SDag-Erling Smørgrav ** that takes effect earliest in the year. 3335bc421551SDag-Erling Smørgrav */ 3336bc421551SDag-Erling Smørgrav k = -1; 3337bc421551SDag-Erling Smørgrav for (j = 0; j < zp->z_nrules; ++j) { 3338bc421551SDag-Erling Smørgrav struct rule *r = &zp->z_rules[j]; 3339bc421551SDag-Erling Smørgrav if (!r->r_todo) 3340bc421551SDag-Erling Smørgrav continue; 3341bc421551SDag-Erling Smørgrav eats(zp->z_filenum, zp->z_linenum, 3342bc421551SDag-Erling Smørgrav r->r_filenum, r->r_linenum); 3343bc421551SDag-Erling Smørgrav offset = r->r_todisut ? 0 : stdoff; 3344bc421551SDag-Erling Smørgrav if (!r->r_todisstd) 3345bc421551SDag-Erling Smørgrav offset = oadd(offset, save); 3346bc421551SDag-Erling Smørgrav jtime = r->r_temp; 3347bc421551SDag-Erling Smørgrav if (jtime == min_time || 3348bc421551SDag-Erling Smørgrav jtime == max_time) 3349bc421551SDag-Erling Smørgrav continue; 3350bc421551SDag-Erling Smørgrav jtime = tadd(jtime, -offset); 3351bc421551SDag-Erling Smørgrav if (k < 0 || jtime < ktime) { 3352bc421551SDag-Erling Smørgrav k = j; 3353bc421551SDag-Erling Smørgrav ktime = jtime; 3354bc421551SDag-Erling Smørgrav } else if (jtime == ktime) { 3355bc421551SDag-Erling Smørgrav char const *dup_rules_msg = 3356bc421551SDag-Erling Smørgrav _("two rules for same instant"); 3357bc421551SDag-Erling Smørgrav eats(zp->z_filenum, zp->z_linenum, 3358bc421551SDag-Erling Smørgrav r->r_filenum, r->r_linenum); 3359bc421551SDag-Erling Smørgrav warning("%s", dup_rules_msg); 3360bc421551SDag-Erling Smørgrav r = &zp->z_rules[k]; 3361bc421551SDag-Erling Smørgrav eats(zp->z_filenum, zp->z_linenum, 3362bc421551SDag-Erling Smørgrav r->r_filenum, r->r_linenum); 3363bc421551SDag-Erling Smørgrav error("%s", dup_rules_msg); 3364bc421551SDag-Erling Smørgrav } 3365bc421551SDag-Erling Smørgrav } 3366bc421551SDag-Erling Smørgrav if (k < 0) 3367bc421551SDag-Erling Smørgrav break; /* go on to next year */ 3368bc421551SDag-Erling Smørgrav rp = &zp->z_rules[k]; 3369bc421551SDag-Erling Smørgrav rp->r_todo = false; 3370bc421551SDag-Erling Smørgrav if (useuntil && ktime >= untiltime) { 3371bc421551SDag-Erling Smørgrav if (!*startbuf 3372bc421551SDag-Erling Smørgrav && (oadd(zp->z_stdoff, rp->r_save) 3373bc421551SDag-Erling Smørgrav == startoff)) 3374bc421551SDag-Erling Smørgrav doabbr(startbuf, zp, rp->r_abbrvar, 3375bc421551SDag-Erling Smørgrav rp->r_isdst, rp->r_save, 3376bc421551SDag-Erling Smørgrav false); 3377bc421551SDag-Erling Smørgrav break; 3378bc421551SDag-Erling Smørgrav } 3379bc421551SDag-Erling Smørgrav save = rp->r_save; 3380bc421551SDag-Erling Smørgrav if (usestart && ktime == starttime) 3381bc421551SDag-Erling Smørgrav usestart = false; 3382bc421551SDag-Erling Smørgrav if (usestart) { 3383bc421551SDag-Erling Smørgrav if (ktime < starttime) { 3384bc421551SDag-Erling Smørgrav startoff = oadd(zp->z_stdoff, 3385bc421551SDag-Erling Smørgrav save); 3386bc421551SDag-Erling Smørgrav doabbr(startbuf, zp, 3387bc421551SDag-Erling Smørgrav rp->r_abbrvar, 3388bc421551SDag-Erling Smørgrav rp->r_isdst, 3389bc421551SDag-Erling Smørgrav rp->r_save, 3390bc421551SDag-Erling Smørgrav false); 3391bc421551SDag-Erling Smørgrav continue; 3392bc421551SDag-Erling Smørgrav } 3393bc421551SDag-Erling Smørgrav if (*startbuf == '\0' 3394bc421551SDag-Erling Smørgrav && startoff == oadd(zp->z_stdoff, 3395bc421551SDag-Erling Smørgrav save)) { 3396bc421551SDag-Erling Smørgrav doabbr(startbuf, 3397bc421551SDag-Erling Smørgrav zp, 3398bc421551SDag-Erling Smørgrav rp->r_abbrvar, 3399bc421551SDag-Erling Smørgrav rp->r_isdst, 3400bc421551SDag-Erling Smørgrav rp->r_save, 3401bc421551SDag-Erling Smørgrav false); 3402bc421551SDag-Erling Smørgrav } 3403bc421551SDag-Erling Smørgrav } 3404bc421551SDag-Erling Smørgrav eats(zp->z_filenum, zp->z_linenum, 3405bc421551SDag-Erling Smørgrav rp->r_filenum, rp->r_linenum); 3406bc421551SDag-Erling Smørgrav doabbr(ab, zp, rp->r_abbrvar, 3407bc421551SDag-Erling Smørgrav rp->r_isdst, rp->r_save, false); 3408bc421551SDag-Erling Smørgrav offset = oadd(zp->z_stdoff, rp->r_save); 3409bc421551SDag-Erling Smørgrav if (!want_bloat() && !useuntil && !do_extend 3410bc421551SDag-Erling Smørgrav && prevrp && lo_time <= prevktime 3411bc421551SDag-Erling Smørgrav && redundant_time <= ktime 3412bc421551SDag-Erling Smørgrav && rp->r_hiyear == ZIC_MAX 3413bc421551SDag-Erling Smørgrav && prevrp->r_hiyear == ZIC_MAX) 3414bc421551SDag-Erling Smørgrav break; 3415bc421551SDag-Erling Smørgrav type = addtype(offset, ab, rp->r_isdst, 3416bc421551SDag-Erling Smørgrav rp->r_todisstd, rp->r_todisut); 3417bc421551SDag-Erling Smørgrav if (defaulttype < 0 && !rp->r_isdst) 3418bc421551SDag-Erling Smørgrav defaulttype = type; 3419bc421551SDag-Erling Smørgrav if (rp->r_hiyear == ZIC_MAX 3420bc421551SDag-Erling Smørgrav && ! (0 <= lastatmax 3421bc421551SDag-Erling Smørgrav && ktime < attypes[lastatmax].at)) 3422bc421551SDag-Erling Smørgrav lastatmax = timecnt; 3423bc421551SDag-Erling Smørgrav addtt(ktime, type); 3424bc421551SDag-Erling Smørgrav prevrp = rp; 3425bc421551SDag-Erling Smørgrav prevktime = ktime; 3426bc421551SDag-Erling Smørgrav } 3427bc421551SDag-Erling Smørgrav } 3428bc421551SDag-Erling Smørgrav } 3429bc421551SDag-Erling Smørgrav if (usestart) { 3430bc421551SDag-Erling Smørgrav bool isdst = startoff != zp->z_stdoff; 3431bc421551SDag-Erling Smørgrav if (*startbuf == '\0' && zp->z_format) 3432bc421551SDag-Erling Smørgrav doabbr(startbuf, zp, disable_percent_s, 3433bc421551SDag-Erling Smørgrav isdst, save, false); 3434bc421551SDag-Erling Smørgrav eat(zp->z_filenum, zp->z_linenum); 3435bc421551SDag-Erling Smørgrav if (*startbuf == '\0') 3436bc421551SDag-Erling Smørgrav error(_("can't determine time zone abbreviation to use just after until time")); 3437bc421551SDag-Erling Smørgrav else { 3438bc421551SDag-Erling Smørgrav int type = addtype(startoff, startbuf, isdst, 3439bc421551SDag-Erling Smørgrav startttisstd, startttisut); 3440bc421551SDag-Erling Smørgrav if (defaulttype < 0 && !isdst) 3441bc421551SDag-Erling Smørgrav defaulttype = type; 3442bc421551SDag-Erling Smørgrav addtt(starttime, type); 3443bc421551SDag-Erling Smørgrav } 3444bc421551SDag-Erling Smørgrav } 3445bc421551SDag-Erling Smørgrav /* 3446bc421551SDag-Erling Smørgrav ** Now we may get to set starttime for the next zone line. 3447bc421551SDag-Erling Smørgrav */ 3448bc421551SDag-Erling Smørgrav if (useuntil) { 3449bc421551SDag-Erling Smørgrav startttisstd = zp->z_untilrule.r_todisstd; 3450bc421551SDag-Erling Smørgrav startttisut = zp->z_untilrule.r_todisut; 3451bc421551SDag-Erling Smørgrav starttime = zp->z_untiltime; 3452bc421551SDag-Erling Smørgrav if (!startttisstd) 3453bc421551SDag-Erling Smørgrav starttime = tadd(starttime, -save); 3454bc421551SDag-Erling Smørgrav if (!startttisut) 3455bc421551SDag-Erling Smørgrav starttime = tadd(starttime, -stdoff); 3456bc421551SDag-Erling Smørgrav } 3457bc421551SDag-Erling Smørgrav } 3458bc421551SDag-Erling Smørgrav if (defaulttype < 0) 3459bc421551SDag-Erling Smørgrav defaulttype = 0; 3460bc421551SDag-Erling Smørgrav if (0 <= lastatmax) 3461bc421551SDag-Erling Smørgrav attypes[lastatmax].dontmerge = true; 3462bc421551SDag-Erling Smørgrav if (do_extend) { 3463bc421551SDag-Erling Smørgrav /* 3464bc421551SDag-Erling Smørgrav ** If we're extending the explicitly listed observations 3465bc421551SDag-Erling Smørgrav ** for 400 years because we can't fill the POSIX-TZ field, 3466bc421551SDag-Erling Smørgrav ** check whether we actually ended up explicitly listing 3467bc421551SDag-Erling Smørgrav ** observations through that period. If there aren't any 3468bc421551SDag-Erling Smørgrav ** near the end of the 400-year period, add a redundant 3469bc421551SDag-Erling Smørgrav ** one at the end of the final year, to make it clear 3470bc421551SDag-Erling Smørgrav ** that we are claiming to have definite knowledge of 3471bc421551SDag-Erling Smørgrav ** the lack of transitions up to that point. 3472bc421551SDag-Erling Smørgrav */ 3473bc421551SDag-Erling Smørgrav struct rule xr; 3474bc421551SDag-Erling Smørgrav struct attype *lastat; 3475bc421551SDag-Erling Smørgrav xr.r_month = TM_JANUARY; 3476bc421551SDag-Erling Smørgrav xr.r_dycode = DC_DOM; 3477bc421551SDag-Erling Smørgrav xr.r_dayofmonth = 1; 3478bc421551SDag-Erling Smørgrav xr.r_tod = 0; 3479bc421551SDag-Erling Smørgrav for (lastat = attypes, i = 1; i < timecnt; i++) 3480bc421551SDag-Erling Smørgrav if (attypes[i].at > lastat->at) 3481bc421551SDag-Erling Smørgrav lastat = &attypes[i]; 3482bc421551SDag-Erling Smørgrav if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) { 3483bc421551SDag-Erling Smørgrav addtt(rpytime(&xr, max_year + 1), 3484bc421551SDag-Erling Smørgrav lastat ? lastat->type : defaulttype); 3485bc421551SDag-Erling Smørgrav attypes[timecnt - 1].dontmerge = true; 3486bc421551SDag-Erling Smørgrav } 3487bc421551SDag-Erling Smørgrav } 3488bc421551SDag-Erling Smørgrav writezone(zpfirst->z_name, envvar, version, defaulttype); 3489bc421551SDag-Erling Smørgrav free(startbuf); 3490bc421551SDag-Erling Smørgrav free(ab); 3491bc421551SDag-Erling Smørgrav free(envvar); 3492bc421551SDag-Erling Smørgrav } 3493bc421551SDag-Erling Smørgrav 3494bc421551SDag-Erling Smørgrav static void 3495bc421551SDag-Erling Smørgrav addtt(zic_t starttime, int type) 3496bc421551SDag-Erling Smørgrav { 3497bc421551SDag-Erling Smørgrav attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc); 3498bc421551SDag-Erling Smørgrav attypes[timecnt].at = starttime; 3499bc421551SDag-Erling Smørgrav attypes[timecnt].dontmerge = false; 3500bc421551SDag-Erling Smørgrav attypes[timecnt].type = type; 3501bc421551SDag-Erling Smørgrav ++timecnt; 3502bc421551SDag-Erling Smørgrav } 3503bc421551SDag-Erling Smørgrav 3504bc421551SDag-Erling Smørgrav static int 3505bc421551SDag-Erling Smørgrav addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) 3506bc421551SDag-Erling Smørgrav { 3507bc421551SDag-Erling Smørgrav register int i, j; 3508bc421551SDag-Erling Smørgrav 3509bc421551SDag-Erling Smørgrav if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) { 3510bc421551SDag-Erling Smørgrav error(_("UT offset out of range")); 3511bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3512bc421551SDag-Erling Smørgrav } 3513bc421551SDag-Erling Smørgrav if (!want_bloat()) 3514bc421551SDag-Erling Smørgrav ttisstd = ttisut = false; 3515bc421551SDag-Erling Smørgrav 3516bc421551SDag-Erling Smørgrav for (j = 0; j < charcnt; ++j) 3517bc421551SDag-Erling Smørgrav if (strcmp(&chars[j], abbr) == 0) 3518bc421551SDag-Erling Smørgrav break; 3519bc421551SDag-Erling Smørgrav if (j == charcnt) 3520bc421551SDag-Erling Smørgrav newabbr(abbr); 3521bc421551SDag-Erling Smørgrav else { 3522bc421551SDag-Erling Smørgrav /* If there's already an entry, return its index. */ 3523bc421551SDag-Erling Smørgrav for (i = 0; i < typecnt; i++) 3524bc421551SDag-Erling Smørgrav if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i] 3525bc421551SDag-Erling Smørgrav && ttisstd == ttisstds[i] && ttisut == ttisuts[i]) 3526bc421551SDag-Erling Smørgrav return i; 3527bc421551SDag-Erling Smørgrav } 3528bc421551SDag-Erling Smørgrav /* 3529bc421551SDag-Erling Smørgrav ** There isn't one; add a new one, unless there are already too 3530bc421551SDag-Erling Smørgrav ** many. 3531bc421551SDag-Erling Smørgrav */ 3532bc421551SDag-Erling Smørgrav if (typecnt >= TZ_MAX_TYPES) { 3533bc421551SDag-Erling Smørgrav error(_("too many local time types")); 3534bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3535bc421551SDag-Erling Smørgrav } 3536bc421551SDag-Erling Smørgrav i = typecnt++; 3537bc421551SDag-Erling Smørgrav utoffs[i] = utoff; 3538bc421551SDag-Erling Smørgrav isdsts[i] = isdst; 3539bc421551SDag-Erling Smørgrav ttisstds[i] = ttisstd; 3540bc421551SDag-Erling Smørgrav ttisuts[i] = ttisut; 3541bc421551SDag-Erling Smørgrav desigidx[i] = j; 3542bc421551SDag-Erling Smørgrav return i; 3543bc421551SDag-Erling Smørgrav } 3544bc421551SDag-Erling Smørgrav 3545bc421551SDag-Erling Smørgrav static void 3546bc421551SDag-Erling Smørgrav leapadd(zic_t t, int correction, int rolling) 3547bc421551SDag-Erling Smørgrav { 3548bc421551SDag-Erling Smørgrav register int i; 3549bc421551SDag-Erling Smørgrav 3550bc421551SDag-Erling Smørgrav if (TZ_MAX_LEAPS <= leapcnt) { 3551bc421551SDag-Erling Smørgrav error(_("too many leap seconds")); 3552bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3553bc421551SDag-Erling Smørgrav } 3554bc421551SDag-Erling Smørgrav if (rolling && (lo_time != min_time || hi_time != max_time)) { 3555bc421551SDag-Erling Smørgrav error(_("Rolling leap seconds not supported with -r")); 3556bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3557bc421551SDag-Erling Smørgrav } 3558bc421551SDag-Erling Smørgrav for (i = 0; i < leapcnt; ++i) 3559bc421551SDag-Erling Smørgrav if (t <= trans[i]) 3560bc421551SDag-Erling Smørgrav break; 3561bc421551SDag-Erling Smørgrav memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans); 3562bc421551SDag-Erling Smørgrav memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr); 3563bc421551SDag-Erling Smørgrav memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll); 3564bc421551SDag-Erling Smørgrav trans[i] = t; 3565bc421551SDag-Erling Smørgrav corr[i] = correction; 3566bc421551SDag-Erling Smørgrav roll[i] = rolling; 3567bc421551SDag-Erling Smørgrav ++leapcnt; 3568bc421551SDag-Erling Smørgrav } 3569bc421551SDag-Erling Smørgrav 3570bc421551SDag-Erling Smørgrav static void 3571bc421551SDag-Erling Smørgrav adjleap(void) 3572bc421551SDag-Erling Smørgrav { 3573bc421551SDag-Erling Smørgrav register int i; 3574bc421551SDag-Erling Smørgrav register zic_t last = 0; 3575bc421551SDag-Erling Smørgrav register zic_t prevtrans = 0; 3576bc421551SDag-Erling Smørgrav 3577bc421551SDag-Erling Smørgrav /* 3578bc421551SDag-Erling Smørgrav ** propagate leap seconds forward 3579bc421551SDag-Erling Smørgrav */ 3580bc421551SDag-Erling Smørgrav for (i = 0; i < leapcnt; ++i) { 3581bc421551SDag-Erling Smørgrav if (trans[i] - prevtrans < 28 * SECSPERDAY) { 3582bc421551SDag-Erling Smørgrav error(_("Leap seconds too close together")); 3583bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3584bc421551SDag-Erling Smørgrav } 3585bc421551SDag-Erling Smørgrav prevtrans = trans[i]; 3586bc421551SDag-Erling Smørgrav trans[i] = tadd(trans[i], last); 3587bc421551SDag-Erling Smørgrav last = corr[i] += last; 3588bc421551SDag-Erling Smørgrav } 3589bc421551SDag-Erling Smørgrav 3590bc421551SDag-Erling Smørgrav if (0 <= leapexpires) { 3591bc421551SDag-Erling Smørgrav leapexpires = oadd(leapexpires, last); 3592bc421551SDag-Erling Smørgrav if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) { 3593bc421551SDag-Erling Smørgrav error(_("last Leap time does not precede Expires time")); 3594bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3595bc421551SDag-Erling Smørgrav } 3596bc421551SDag-Erling Smørgrav } 3597bc421551SDag-Erling Smørgrav } 3598bc421551SDag-Erling Smørgrav 3599bc421551SDag-Erling Smørgrav /* Is A a space character in the C locale? */ 3600bc421551SDag-Erling Smørgrav static bool 3601bc421551SDag-Erling Smørgrav is_space(char a) 3602bc421551SDag-Erling Smørgrav { 3603bc421551SDag-Erling Smørgrav switch (a) { 3604bc421551SDag-Erling Smørgrav default: 3605bc421551SDag-Erling Smørgrav return false; 3606bc421551SDag-Erling Smørgrav case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': 3607bc421551SDag-Erling Smørgrav return true; 3608bc421551SDag-Erling Smørgrav } 3609bc421551SDag-Erling Smørgrav } 3610bc421551SDag-Erling Smørgrav 3611bc421551SDag-Erling Smørgrav /* Is A an alphabetic character in the C locale? */ 3612bc421551SDag-Erling Smørgrav static bool 3613bc421551SDag-Erling Smørgrav is_alpha(char a) 3614bc421551SDag-Erling Smørgrav { 3615bc421551SDag-Erling Smørgrav switch (a) { 3616bc421551SDag-Erling Smørgrav default: 3617bc421551SDag-Erling Smørgrav return false; 3618bc421551SDag-Erling Smørgrav case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 3619bc421551SDag-Erling Smørgrav case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 3620bc421551SDag-Erling Smørgrav case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 3621bc421551SDag-Erling Smørgrav case 'V': case 'W': case 'X': case 'Y': case 'Z': 3622bc421551SDag-Erling Smørgrav case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 3623bc421551SDag-Erling Smørgrav case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 3624bc421551SDag-Erling Smørgrav case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 3625bc421551SDag-Erling Smørgrav case 'v': case 'w': case 'x': case 'y': case 'z': 3626bc421551SDag-Erling Smørgrav return true; 3627bc421551SDag-Erling Smørgrav } 3628bc421551SDag-Erling Smørgrav } 3629bc421551SDag-Erling Smørgrav 3630bc421551SDag-Erling Smørgrav /* If A is an uppercase character in the C locale, return its lowercase 3631bc421551SDag-Erling Smørgrav counterpart. Otherwise, return A. */ 3632bc421551SDag-Erling Smørgrav static char 3633bc421551SDag-Erling Smørgrav lowerit(char a) 3634bc421551SDag-Erling Smørgrav { 3635bc421551SDag-Erling Smørgrav switch (a) { 3636bc421551SDag-Erling Smørgrav default: return a; 3637bc421551SDag-Erling Smørgrav case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c'; 3638bc421551SDag-Erling Smørgrav case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f'; 3639bc421551SDag-Erling Smørgrav case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i'; 3640bc421551SDag-Erling Smørgrav case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l'; 3641bc421551SDag-Erling Smørgrav case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o'; 3642bc421551SDag-Erling Smørgrav case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r'; 3643bc421551SDag-Erling Smørgrav case 'S': return 's'; case 'T': return 't'; case 'U': return 'u'; 3644bc421551SDag-Erling Smørgrav case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x'; 3645bc421551SDag-Erling Smørgrav case 'Y': return 'y'; case 'Z': return 'z'; 3646bc421551SDag-Erling Smørgrav } 3647bc421551SDag-Erling Smørgrav } 3648bc421551SDag-Erling Smørgrav 3649bc421551SDag-Erling Smørgrav /* case-insensitive equality */ 3650bc421551SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE bool 3651bc421551SDag-Erling Smørgrav ciequal(register const char *ap, register const char *bp) 3652bc421551SDag-Erling Smørgrav { 3653bc421551SDag-Erling Smørgrav while (lowerit(*ap) == lowerit(*bp++)) 3654bc421551SDag-Erling Smørgrav if (*ap++ == '\0') 3655bc421551SDag-Erling Smørgrav return true; 3656bc421551SDag-Erling Smørgrav return false; 3657bc421551SDag-Erling Smørgrav } 3658bc421551SDag-Erling Smørgrav 3659bc421551SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE bool 3660bc421551SDag-Erling Smørgrav itsabbr(register const char *abbr, register const char *word) 3661bc421551SDag-Erling Smørgrav { 3662bc421551SDag-Erling Smørgrav if (lowerit(*abbr) != lowerit(*word)) 3663bc421551SDag-Erling Smørgrav return false; 3664bc421551SDag-Erling Smørgrav ++word; 3665bc421551SDag-Erling Smørgrav while (*++abbr != '\0') 3666bc421551SDag-Erling Smørgrav do { 3667bc421551SDag-Erling Smørgrav if (*word == '\0') 3668bc421551SDag-Erling Smørgrav return false; 3669bc421551SDag-Erling Smørgrav } while (lowerit(*word++) != lowerit(*abbr)); 3670bc421551SDag-Erling Smørgrav return true; 3671bc421551SDag-Erling Smørgrav } 3672bc421551SDag-Erling Smørgrav 3673bc421551SDag-Erling Smørgrav /* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */ 3674bc421551SDag-Erling Smørgrav 3675bc421551SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE bool 3676bc421551SDag-Erling Smørgrav ciprefix(char const *abbr, char const *word) 3677bc421551SDag-Erling Smørgrav { 3678bc421551SDag-Erling Smørgrav do 3679bc421551SDag-Erling Smørgrav if (!*abbr) 3680bc421551SDag-Erling Smørgrav return true; 3681bc421551SDag-Erling Smørgrav while (lowerit(*abbr++) == lowerit(*word++)); 3682bc421551SDag-Erling Smørgrav 3683bc421551SDag-Erling Smørgrav return false; 3684bc421551SDag-Erling Smørgrav } 3685bc421551SDag-Erling Smørgrav 3686bc421551SDag-Erling Smørgrav static const struct lookup * 3687bc421551SDag-Erling Smørgrav byword(const char *word, const struct lookup *table) 3688bc421551SDag-Erling Smørgrav { 3689bc421551SDag-Erling Smørgrav register const struct lookup * foundlp; 3690bc421551SDag-Erling Smørgrav register const struct lookup * lp; 3691bc421551SDag-Erling Smørgrav 3692bc421551SDag-Erling Smørgrav if (word == NULL || table == NULL) 3693bc421551SDag-Erling Smørgrav return NULL; 3694bc421551SDag-Erling Smørgrav 3695bc421551SDag-Erling Smørgrav /* If TABLE is LASTS and the word starts with "last" followed 3696bc421551SDag-Erling Smørgrav by a non-'-', skip the "last" and look in WDAY_NAMES instead. 3697bc421551SDag-Erling Smørgrav Warn about any usage of the undocumented prefix "last-". */ 3698bc421551SDag-Erling Smørgrav if (table == lasts && ciprefix("last", word) && word[4]) { 3699bc421551SDag-Erling Smørgrav if (word[4] == '-') 3700bc421551SDag-Erling Smørgrav warning(_("\"%s\" is undocumented; use \"last%s\" instead"), 3701bc421551SDag-Erling Smørgrav word, word + 5); 3702bc421551SDag-Erling Smørgrav else { 3703bc421551SDag-Erling Smørgrav word += 4; 3704bc421551SDag-Erling Smørgrav table = wday_names; 3705bc421551SDag-Erling Smørgrav } 3706bc421551SDag-Erling Smørgrav } 3707bc421551SDag-Erling Smørgrav 3708bc421551SDag-Erling Smørgrav /* 3709bc421551SDag-Erling Smørgrav ** Look for exact match. 3710bc421551SDag-Erling Smørgrav */ 3711bc421551SDag-Erling Smørgrav for (lp = table; lp->l_word != NULL; ++lp) 3712bc421551SDag-Erling Smørgrav if (ciequal(word, lp->l_word)) 3713bc421551SDag-Erling Smørgrav return lp; 3714bc421551SDag-Erling Smørgrav /* 3715bc421551SDag-Erling Smørgrav ** Look for inexact match. 3716bc421551SDag-Erling Smørgrav */ 3717bc421551SDag-Erling Smørgrav foundlp = NULL; 3718bc421551SDag-Erling Smørgrav for (lp = table; lp->l_word != NULL; ++lp) 3719bc421551SDag-Erling Smørgrav if (ciprefix(word, lp->l_word)) { 3720bc421551SDag-Erling Smørgrav if (foundlp == NULL) 3721bc421551SDag-Erling Smørgrav foundlp = lp; 3722bc421551SDag-Erling Smørgrav else return NULL; /* multiple inexact matches */ 3723bc421551SDag-Erling Smørgrav } 3724bc421551SDag-Erling Smørgrav 3725bc421551SDag-Erling Smørgrav if (foundlp && noise) { 3726bc421551SDag-Erling Smørgrav /* Warn about any backward-compatibility issue with pre-2017c zic. */ 3727bc421551SDag-Erling Smørgrav bool pre_2017c_match = false; 3728bc421551SDag-Erling Smørgrav for (lp = table; lp->l_word; lp++) 3729bc421551SDag-Erling Smørgrav if (itsabbr(word, lp->l_word)) { 3730bc421551SDag-Erling Smørgrav if (pre_2017c_match) { 3731bc421551SDag-Erling Smørgrav warning(_("\"%s\" is ambiguous in pre-2017c zic"), word); 3732bc421551SDag-Erling Smørgrav break; 3733bc421551SDag-Erling Smørgrav } 3734bc421551SDag-Erling Smørgrav pre_2017c_match = true; 3735bc421551SDag-Erling Smørgrav } 3736bc421551SDag-Erling Smørgrav } 3737bc421551SDag-Erling Smørgrav 3738bc421551SDag-Erling Smørgrav return foundlp; 3739bc421551SDag-Erling Smørgrav } 3740bc421551SDag-Erling Smørgrav 3741bc421551SDag-Erling Smørgrav static int 3742bc421551SDag-Erling Smørgrav getfields(char *cp, char **array, int arrayelts) 3743bc421551SDag-Erling Smørgrav { 3744bc421551SDag-Erling Smørgrav register char * dp; 3745bc421551SDag-Erling Smørgrav register int nsubs; 3746bc421551SDag-Erling Smørgrav 3747bc421551SDag-Erling Smørgrav nsubs = 0; 3748bc421551SDag-Erling Smørgrav for ( ; ; ) { 3749bc421551SDag-Erling Smørgrav char *dstart; 3750bc421551SDag-Erling Smørgrav while (is_space(*cp)) 3751bc421551SDag-Erling Smørgrav ++cp; 3752bc421551SDag-Erling Smørgrav if (*cp == '\0' || *cp == '#') 3753bc421551SDag-Erling Smørgrav break; 3754bc421551SDag-Erling Smørgrav dstart = dp = cp; 3755bc421551SDag-Erling Smørgrav do { 3756bc421551SDag-Erling Smørgrav if ((*dp = *cp++) != '"') 3757bc421551SDag-Erling Smørgrav ++dp; 3758bc421551SDag-Erling Smørgrav else while ((*dp = *cp++) != '"') 3759bc421551SDag-Erling Smørgrav if (*dp != '\0') 3760bc421551SDag-Erling Smørgrav ++dp; 3761bc421551SDag-Erling Smørgrav else { 3762bc421551SDag-Erling Smørgrav error(_("Odd number of quotation marks")); 3763bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3764bc421551SDag-Erling Smørgrav } 3765bc421551SDag-Erling Smørgrav } while (*cp && *cp != '#' && !is_space(*cp)); 3766bc421551SDag-Erling Smørgrav if (is_space(*cp)) 3767bc421551SDag-Erling Smørgrav ++cp; 3768bc421551SDag-Erling Smørgrav *dp = '\0'; 3769bc421551SDag-Erling Smørgrav if (nsubs == arrayelts) { 3770bc421551SDag-Erling Smørgrav error(_("Too many input fields")); 3771bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3772bc421551SDag-Erling Smørgrav } 3773bc421551SDag-Erling Smørgrav array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1); 3774bc421551SDag-Erling Smørgrav } 3775bc421551SDag-Erling Smørgrav return nsubs; 3776bc421551SDag-Erling Smørgrav } 3777bc421551SDag-Erling Smørgrav 3778bc421551SDag-Erling Smørgrav static ATTRIBUTE_NORETURN void 3779bc421551SDag-Erling Smørgrav time_overflow(void) 3780bc421551SDag-Erling Smørgrav { 3781bc421551SDag-Erling Smørgrav error(_("time overflow")); 3782bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3783bc421551SDag-Erling Smørgrav } 3784bc421551SDag-Erling Smørgrav 3785bc421551SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE zic_t 3786bc421551SDag-Erling Smørgrav oadd(zic_t t1, zic_t t2) 3787bc421551SDag-Erling Smørgrav { 3788bc421551SDag-Erling Smørgrav #ifdef ckd_add 3789bc421551SDag-Erling Smørgrav zic_t sum; 3790bc421551SDag-Erling Smørgrav if (!ckd_add(&sum, t1, t2)) 3791bc421551SDag-Erling Smørgrav return sum; 3792bc421551SDag-Erling Smørgrav #else 3793bc421551SDag-Erling Smørgrav if (t1 < 0 ? ZIC_MIN - t1 <= t2 : t2 <= ZIC_MAX - t1) 3794bc421551SDag-Erling Smørgrav return t1 + t2; 3795bc421551SDag-Erling Smørgrav #endif 3796bc421551SDag-Erling Smørgrav time_overflow(); 3797bc421551SDag-Erling Smørgrav } 3798bc421551SDag-Erling Smørgrav 3799bc421551SDag-Erling Smørgrav static ATTRIBUTE_REPRODUCIBLE zic_t 3800bc421551SDag-Erling Smørgrav tadd(zic_t t1, zic_t t2) 3801bc421551SDag-Erling Smørgrav { 3802bc421551SDag-Erling Smørgrav #ifdef ckd_add 3803bc421551SDag-Erling Smørgrav zic_t sum; 3804bc421551SDag-Erling Smørgrav if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time) 3805bc421551SDag-Erling Smørgrav return sum; 3806bc421551SDag-Erling Smørgrav #else 3807bc421551SDag-Erling Smørgrav if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1) 3808bc421551SDag-Erling Smørgrav return t1 + t2; 3809bc421551SDag-Erling Smørgrav #endif 3810bc421551SDag-Erling Smørgrav if (t1 == min_time || t1 == max_time) 3811bc421551SDag-Erling Smørgrav return t1; 3812bc421551SDag-Erling Smørgrav time_overflow(); 3813bc421551SDag-Erling Smørgrav } 3814bc421551SDag-Erling Smørgrav 3815bc421551SDag-Erling Smørgrav /* 3816bc421551SDag-Erling Smørgrav ** Given a rule, and a year, compute the date (in seconds since January 1, 3817bc421551SDag-Erling Smørgrav ** 1970, 00:00 LOCAL time) in that year that the rule refers to. 3818bc421551SDag-Erling Smørgrav */ 3819bc421551SDag-Erling Smørgrav 3820bc421551SDag-Erling Smørgrav static zic_t 3821bc421551SDag-Erling Smørgrav rpytime(const struct rule *rp, zic_t wantedy) 3822bc421551SDag-Erling Smørgrav { 3823bc421551SDag-Erling Smørgrav register int m, i; 3824bc421551SDag-Erling Smørgrav register zic_t dayoff; /* with a nod to Margaret O. */ 3825bc421551SDag-Erling Smørgrav register zic_t t, y; 3826bc421551SDag-Erling Smørgrav int yrem; 3827bc421551SDag-Erling Smørgrav 3828bc421551SDag-Erling Smørgrav if (wantedy == ZIC_MIN) 3829bc421551SDag-Erling Smørgrav return min_time; 3830bc421551SDag-Erling Smørgrav if (wantedy == ZIC_MAX) 3831bc421551SDag-Erling Smørgrav return max_time; 3832bc421551SDag-Erling Smørgrav m = TM_JANUARY; 3833bc421551SDag-Erling Smørgrav y = EPOCH_YEAR; 3834bc421551SDag-Erling Smørgrav 3835bc421551SDag-Erling Smørgrav /* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT, 3836bc421551SDag-Erling Smørgrav sans overflow. */ 3837bc421551SDag-Erling Smørgrav yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT; 3838bc421551SDag-Erling Smørgrav dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT 3839bc421551SDag-Erling Smørgrav + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0)) 3840bc421551SDag-Erling Smørgrav * DAYSPERREPEAT); 3841bc421551SDag-Erling Smørgrav /* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow. */ 3842bc421551SDag-Erling Smørgrav wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT; 3843bc421551SDag-Erling Smørgrav 3844bc421551SDag-Erling Smørgrav while (wantedy != y) { 3845bc421551SDag-Erling Smørgrav i = len_years[isleap(y)]; 3846bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, i); 3847bc421551SDag-Erling Smørgrav y++; 3848bc421551SDag-Erling Smørgrav } 3849bc421551SDag-Erling Smørgrav while (m != rp->r_month) { 3850bc421551SDag-Erling Smørgrav i = len_months[isleap(y)][m]; 3851bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, i); 3852bc421551SDag-Erling Smørgrav ++m; 3853bc421551SDag-Erling Smørgrav } 3854bc421551SDag-Erling Smørgrav i = rp->r_dayofmonth; 3855bc421551SDag-Erling Smørgrav if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { 3856bc421551SDag-Erling Smørgrav if (rp->r_dycode == DC_DOWLEQ) 3857bc421551SDag-Erling Smørgrav --i; 3858bc421551SDag-Erling Smørgrav else { 3859bc421551SDag-Erling Smørgrav error(_("use of 2/29 in non leap-year")); 3860bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3861bc421551SDag-Erling Smørgrav } 3862bc421551SDag-Erling Smørgrav } 3863bc421551SDag-Erling Smørgrav --i; 3864bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, i); 3865bc421551SDag-Erling Smørgrav if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { 3866bc421551SDag-Erling Smørgrav /* 3867bc421551SDag-Erling Smørgrav ** Don't trust mod of negative numbers. 3868bc421551SDag-Erling Smørgrav */ 3869bc421551SDag-Erling Smørgrav zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK) 3870bc421551SDag-Erling Smørgrav % DAYSPERWEEK); 3871bc421551SDag-Erling Smørgrav while (wday != rp->r_wday) 3872bc421551SDag-Erling Smørgrav if (rp->r_dycode == DC_DOWGEQ) { 3873bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, 1); 3874bc421551SDag-Erling Smørgrav if (++wday >= DAYSPERWEEK) 3875bc421551SDag-Erling Smørgrav wday = 0; 3876bc421551SDag-Erling Smørgrav ++i; 3877bc421551SDag-Erling Smørgrav } else { 3878bc421551SDag-Erling Smørgrav dayoff = oadd(dayoff, -1); 3879bc421551SDag-Erling Smørgrav if (--wday < 0) 3880bc421551SDag-Erling Smørgrav wday = DAYSPERWEEK - 1; 3881bc421551SDag-Erling Smørgrav --i; 3882bc421551SDag-Erling Smørgrav } 3883bc421551SDag-Erling Smørgrav if (i < 0 || i >= len_months[isleap(y)][m]) { 3884bc421551SDag-Erling Smørgrav if (noise) 3885bc421551SDag-Erling Smørgrav warning(_("rule goes past start/end of month; \ 3886bc421551SDag-Erling Smørgrav will not work with pre-2004 versions of zic")); 3887bc421551SDag-Erling Smørgrav } 3888bc421551SDag-Erling Smørgrav } 3889bc421551SDag-Erling Smørgrav if (dayoff < min_time / SECSPERDAY) 3890bc421551SDag-Erling Smørgrav return min_time; 3891bc421551SDag-Erling Smørgrav if (dayoff > max_time / SECSPERDAY) 3892bc421551SDag-Erling Smørgrav return max_time; 3893bc421551SDag-Erling Smørgrav t = (zic_t) dayoff * SECSPERDAY; 3894bc421551SDag-Erling Smørgrav return tadd(t, rp->r_tod); 3895bc421551SDag-Erling Smørgrav } 3896bc421551SDag-Erling Smørgrav 3897bc421551SDag-Erling Smørgrav static void 3898bc421551SDag-Erling Smørgrav newabbr(const char *string) 3899bc421551SDag-Erling Smørgrav { 3900bc421551SDag-Erling Smørgrav register int i; 3901bc421551SDag-Erling Smørgrav 3902bc421551SDag-Erling Smørgrav if (strcmp(string, GRANDPARENTED) != 0) { 3903bc421551SDag-Erling Smørgrav register const char * cp; 3904bc421551SDag-Erling Smørgrav const char * mp; 3905bc421551SDag-Erling Smørgrav 3906bc421551SDag-Erling Smørgrav cp = string; 3907bc421551SDag-Erling Smørgrav mp = NULL; 3908bc421551SDag-Erling Smørgrav while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9') 3909bc421551SDag-Erling Smørgrav || *cp == '-' || *cp == '+') 3910bc421551SDag-Erling Smørgrav ++cp; 3911bc421551SDag-Erling Smørgrav if (noise && cp - string < 3) 3912bc421551SDag-Erling Smørgrav mp = _("time zone abbreviation has fewer than 3 characters"); 3913bc421551SDag-Erling Smørgrav if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) 3914bc421551SDag-Erling Smørgrav mp = _("time zone abbreviation has too many characters"); 3915bc421551SDag-Erling Smørgrav if (*cp != '\0') 3916bc421551SDag-Erling Smørgrav mp = _("time zone abbreviation differs from POSIX standard"); 3917bc421551SDag-Erling Smørgrav if (mp != NULL) 3918bc421551SDag-Erling Smørgrav warning("%s (%s)", mp, string); 3919bc421551SDag-Erling Smørgrav } 3920bc421551SDag-Erling Smørgrav i = strlen(string) + 1; 3921bc421551SDag-Erling Smørgrav if (charcnt + i > TZ_MAX_CHARS) { 3922bc421551SDag-Erling Smørgrav error(_("too many, or too long, time zone abbreviations")); 3923bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3924bc421551SDag-Erling Smørgrav } 3925bc421551SDag-Erling Smørgrav strcpy(&chars[charcnt], string); 3926bc421551SDag-Erling Smørgrav charcnt += i; 3927bc421551SDag-Erling Smørgrav } 3928bc421551SDag-Erling Smørgrav 3929bc421551SDag-Erling Smørgrav /* Ensure that the directories of ARGNAME exist, by making any missing 3930bc421551SDag-Erling Smørgrav ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise, 3931bc421551SDag-Erling Smørgrav do it for ARGNAME too. Exit with failure if there is trouble. 3932bc421551SDag-Erling Smørgrav Do not consider an existing file to be trouble. */ 3933bc421551SDag-Erling Smørgrav static void 3934bc421551SDag-Erling Smørgrav mkdirs(char const *argname, bool ancestors) 3935bc421551SDag-Erling Smørgrav { 3936bc421551SDag-Erling Smørgrav char *name = estrdup(argname); 3937bc421551SDag-Erling Smørgrav char *cp = name; 3938bc421551SDag-Erling Smørgrav 3939bc421551SDag-Erling Smørgrav /* On MS-Windows systems, do not worry about drive letters or 3940bc421551SDag-Erling Smørgrav backslashes, as this should suffice in practice. Time zone 3941bc421551SDag-Erling Smørgrav names do not use drive letters and backslashes. If the -d 3942bc421551SDag-Erling Smørgrav option of zic does not name an already-existing directory, 3943bc421551SDag-Erling Smørgrav it can use slashes to separate the already-existing 3944bc421551SDag-Erling Smørgrav ancestor prefix from the to-be-created subdirectories. */ 3945bc421551SDag-Erling Smørgrav 3946bc421551SDag-Erling Smørgrav /* 3947bc421551SDag-Erling Smørgrav * If -D was specified, do not create directories. A subsequent 3948bc421551SDag-Erling Smørgrav * file operation will fail and produce an appropriate error 3949bc421551SDag-Erling Smørgrav * message. 3950bc421551SDag-Erling Smørgrav */ 3951bc421551SDag-Erling Smørgrav if (Dflag) 3952bc421551SDag-Erling Smørgrav return; 3953bc421551SDag-Erling Smørgrav /* Do not mkdir a root directory, as it must exist. */ 3954bc421551SDag-Erling Smørgrav while (*cp == '/') 3955bc421551SDag-Erling Smørgrav cp++; 3956bc421551SDag-Erling Smørgrav 3957bc421551SDag-Erling Smørgrav while (cp && ((cp = strchr(cp, '/')) || !ancestors)) { 3958bc421551SDag-Erling Smørgrav if (cp) 3959bc421551SDag-Erling Smørgrav *cp = '\0'; 3960bc421551SDag-Erling Smørgrav /* 3961bc421551SDag-Erling Smørgrav ** Try to create it. It's OK if creation fails because 3962bc421551SDag-Erling Smørgrav ** the directory already exists, perhaps because some 3963bc421551SDag-Erling Smørgrav ** other process just created it. For simplicity do 3964bc421551SDag-Erling Smørgrav ** not check first whether it already exists, as that 3965bc421551SDag-Erling Smørgrav ** is checked anyway if the mkdir fails. 3966bc421551SDag-Erling Smørgrav */ 3967bc421551SDag-Erling Smørgrav if (mkdir(name, MKDIR_UMASK) != 0) { 3968bc421551SDag-Erling Smørgrav /* Do not report an error if err == EEXIST, because 3969bc421551SDag-Erling Smørgrav some other process might have made the directory 3970bc421551SDag-Erling Smørgrav in the meantime. Likewise for ENOSYS, because 3971bc421551SDag-Erling Smørgrav Solaris 10 mkdir fails with ENOSYS if the 3972bc421551SDag-Erling Smørgrav directory is an automounted mount point. 3973bc421551SDag-Erling Smørgrav Likewise for EACCES, since mkdir can fail 3974bc421551SDag-Erling Smørgrav with EACCES merely because the parent directory 3975bc421551SDag-Erling Smørgrav is unwritable. Likewise for most other error 3976bc421551SDag-Erling Smørgrav numbers. */ 3977bc421551SDag-Erling Smørgrav int err = errno; 3978bc421551SDag-Erling Smørgrav if (err == ELOOP || err == ENAMETOOLONG 3979bc421551SDag-Erling Smørgrav || err == ENOENT || err == ENOTDIR) { 3980bc421551SDag-Erling Smørgrav error(_("%s: Can't create directory %s: %s"), 3981bc421551SDag-Erling Smørgrav progname, name, strerror(err)); 3982bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 3983bc421551SDag-Erling Smørgrav } 3984bc421551SDag-Erling Smørgrav } 3985bc421551SDag-Erling Smørgrav if (cp) 3986bc421551SDag-Erling Smørgrav *cp++ = '/'; 3987bc421551SDag-Erling Smørgrav } 3988bc421551SDag-Erling Smørgrav free(name); 3989bc421551SDag-Erling Smørgrav } 3990bc421551SDag-Erling Smørgrav 3991bc421551SDag-Erling Smørgrav #include <grp.h> 3992bc421551SDag-Erling Smørgrav #include <pwd.h> 3993bc421551SDag-Erling Smørgrav 3994bc421551SDag-Erling Smørgrav static void 3995*d5c85ac6SDag-Erling Smørgrav setgroup(gid_t *flag, const char *name) 3996bc421551SDag-Erling Smørgrav { 3997bc421551SDag-Erling Smørgrav struct group *gr; 3998bc421551SDag-Erling Smørgrav 3999bc421551SDag-Erling Smørgrav if (*flag != (gid_t)-1) { 4000bc421551SDag-Erling Smørgrav fprintf(stderr, _("multiple -g flags specified")); 4001bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 4002bc421551SDag-Erling Smørgrav } 4003bc421551SDag-Erling Smørgrav 4004bc421551SDag-Erling Smørgrav gr = getgrnam(name); 4005bc421551SDag-Erling Smørgrav if (gr == 0) { 4006bc421551SDag-Erling Smørgrav char *ep; 4007bc421551SDag-Erling Smørgrav unsigned long ul; 4008bc421551SDag-Erling Smørgrav 4009bc421551SDag-Erling Smørgrav ul = strtoul(name, &ep, 10); 4010bc421551SDag-Erling Smørgrav if (ul == (unsigned long)(gid_t)ul && *ep == '\0') { 4011bc421551SDag-Erling Smørgrav *flag = ul; 4012bc421551SDag-Erling Smørgrav return; 4013bc421551SDag-Erling Smørgrav } 4014bc421551SDag-Erling Smørgrav fprintf(stderr, _("group `%s' not found"), name); 4015bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 4016bc421551SDag-Erling Smørgrav } 4017bc421551SDag-Erling Smørgrav *flag = gr->gr_gid; 4018bc421551SDag-Erling Smørgrav } 4019bc421551SDag-Erling Smørgrav 4020bc421551SDag-Erling Smørgrav static void 4021*d5c85ac6SDag-Erling Smørgrav setuser(uid_t *flag, const char *name) 4022bc421551SDag-Erling Smørgrav { 4023bc421551SDag-Erling Smørgrav struct passwd *pw; 4024bc421551SDag-Erling Smørgrav 4025bc421551SDag-Erling Smørgrav if (*flag != (gid_t)-1) { 4026bc421551SDag-Erling Smørgrav fprintf(stderr, _("multiple -u flags specified")); 4027bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 4028bc421551SDag-Erling Smørgrav } 4029bc421551SDag-Erling Smørgrav 4030bc421551SDag-Erling Smørgrav pw = getpwnam(name); 4031bc421551SDag-Erling Smørgrav if (pw == 0) { 4032bc421551SDag-Erling Smørgrav char *ep; 4033bc421551SDag-Erling Smørgrav unsigned long ul; 4034bc421551SDag-Erling Smørgrav 4035bc421551SDag-Erling Smørgrav ul = strtoul(name, &ep, 10); 4036bc421551SDag-Erling Smørgrav if (ul == (unsigned long)(gid_t)ul && *ep == '\0') { 4037bc421551SDag-Erling Smørgrav *flag = ul; 4038bc421551SDag-Erling Smørgrav return; 4039bc421551SDag-Erling Smørgrav } 4040bc421551SDag-Erling Smørgrav fprintf(stderr, _("user `%s' not found"), name); 4041bc421551SDag-Erling Smørgrav exit(EXIT_FAILURE); 4042bc421551SDag-Erling Smørgrav } 4043bc421551SDag-Erling Smørgrav *flag = pw->pw_uid; 4044bc421551SDag-Erling Smørgrav } 4045bc421551SDag-Erling Smørgrav 4046bc421551SDag-Erling Smørgrav /* 4047bc421551SDag-Erling Smørgrav ** UNIX was a registered trademark of The Open Group in 2003. 4048bc421551SDag-Erling Smørgrav */ 4049