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