Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* |
michael@0 | 2 | ** This file is in the public domain, so clarified as of |
michael@0 | 3 | ** 2006-07-17 by Arthur David Olson. |
michael@0 | 4 | */ |
michael@0 | 5 | |
michael@0 | 6 | static char elsieid[] = "@(#)zic.c 8.18"; |
michael@0 | 7 | |
michael@0 | 8 | #include "private.h" |
michael@0 | 9 | #include "locale.h" |
michael@0 | 10 | #include "tzfile.h" |
michael@0 | 11 | |
michael@0 | 12 | #define ZIC_VERSION '2' |
michael@0 | 13 | |
michael@0 | 14 | typedef int_fast64_t zic_t; |
michael@0 | 15 | |
michael@0 | 16 | #ifndef ZIC_MAX_ABBR_LEN_WO_WARN |
michael@0 | 17 | #define ZIC_MAX_ABBR_LEN_WO_WARN 6 |
michael@0 | 18 | #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ |
michael@0 | 19 | |
michael@0 | 20 | #if HAVE_SYS_STAT_H |
michael@0 | 21 | #include "sys/stat.h" |
michael@0 | 22 | #endif |
michael@0 | 23 | #ifdef S_IRUSR |
michael@0 | 24 | #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) |
michael@0 | 25 | #else |
michael@0 | 26 | #define MKDIR_UMASK 0755 |
michael@0 | 27 | #endif |
michael@0 | 28 | |
michael@0 | 29 | /* Enable extensions and modifications for ICU. */ |
michael@0 | 30 | #define ICU |
michael@0 | 31 | |
michael@0 | 32 | /* Continue executing after link failure. Even if ICU is undefined |
michael@0 | 33 | * (for vanilla zic behavior), ICU_LINKS should be defined, since zic |
michael@0 | 34 | * appears to fail on the 2003 data the first time through during the |
michael@0 | 35 | * linking phase. Running zic twice, with ICU_LINKS defined, causes |
michael@0 | 36 | * links to be handled correctly. */ |
michael@0 | 37 | #define ICU_LINKS |
michael@0 | 38 | |
michael@0 | 39 | #ifdef ICU |
michael@0 | 40 | #include "tz2icu.h" |
michael@0 | 41 | #endif |
michael@0 | 42 | |
michael@0 | 43 | /* |
michael@0 | 44 | ** On some ancient hosts, predicates like `isspace(C)' are defined |
michael@0 | 45 | ** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, |
michael@0 | 46 | ** which says they are defined only if C == ((unsigned char) C) || C == EOF. |
michael@0 | 47 | ** Neither the C Standard nor Posix require that `isascii' exist. |
michael@0 | 48 | ** For portability, we check both ancient and modern requirements. |
michael@0 | 49 | ** If isascii is not defined, the isascii check succeeds trivially. |
michael@0 | 50 | */ |
michael@0 | 51 | #include "ctype.h" |
michael@0 | 52 | #ifndef isascii |
michael@0 | 53 | #define isascii(x) 1 |
michael@0 | 54 | #endif |
michael@0 | 55 | |
michael@0 | 56 | #define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long)) |
michael@0 | 57 | #define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */ |
michael@0 | 58 | |
michael@0 | 59 | #define end(cp) (strchr((cp), '\0')) |
michael@0 | 60 | |
michael@0 | 61 | struct rule { |
michael@0 | 62 | const char * r_filename; |
michael@0 | 63 | int r_linenum; |
michael@0 | 64 | const char * r_name; |
michael@0 | 65 | |
michael@0 | 66 | int r_loyear; /* for example, 1986 */ |
michael@0 | 67 | int r_hiyear; /* for example, 1986 */ |
michael@0 | 68 | const char * r_yrtype; |
michael@0 | 69 | int r_lowasnum; |
michael@0 | 70 | int r_hiwasnum; |
michael@0 | 71 | |
michael@0 | 72 | int r_month; /* 0..11 */ |
michael@0 | 73 | |
michael@0 | 74 | int r_dycode; /* see below */ |
michael@0 | 75 | int r_dayofmonth; |
michael@0 | 76 | int r_wday; |
michael@0 | 77 | |
michael@0 | 78 | long r_tod; /* time from midnight */ |
michael@0 | 79 | int r_todisstd; /* above is standard time if TRUE */ |
michael@0 | 80 | /* or wall clock time if FALSE */ |
michael@0 | 81 | int r_todisgmt; /* above is GMT if TRUE */ |
michael@0 | 82 | /* or local time if FALSE */ |
michael@0 | 83 | long r_stdoff; /* offset from standard time */ |
michael@0 | 84 | const char * r_abbrvar; /* variable part of abbreviation */ |
michael@0 | 85 | |
michael@0 | 86 | int r_todo; /* a rule to do (used in outzone) */ |
michael@0 | 87 | zic_t r_temp; /* used in outzone */ |
michael@0 | 88 | }; |
michael@0 | 89 | |
michael@0 | 90 | /* |
michael@0 | 91 | ** r_dycode r_dayofmonth r_wday |
michael@0 | 92 | */ |
michael@0 | 93 | |
michael@0 | 94 | #define DC_DOM 0 /* 1..31 */ /* unused */ |
michael@0 | 95 | #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ |
michael@0 | 96 | #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ |
michael@0 | 97 | |
michael@0 | 98 | struct zone { |
michael@0 | 99 | const char * z_filename; |
michael@0 | 100 | int z_linenum; |
michael@0 | 101 | |
michael@0 | 102 | const char * z_name; |
michael@0 | 103 | long z_gmtoff; |
michael@0 | 104 | const char * z_rule; |
michael@0 | 105 | const char * z_format; |
michael@0 | 106 | |
michael@0 | 107 | long z_stdoff; |
michael@0 | 108 | |
michael@0 | 109 | struct rule * z_rules; |
michael@0 | 110 | int z_nrules; |
michael@0 | 111 | |
michael@0 | 112 | struct rule z_untilrule; |
michael@0 | 113 | zic_t z_untiltime; |
michael@0 | 114 | }; |
michael@0 | 115 | |
michael@0 | 116 | extern int getopt(int argc, char * const argv[], |
michael@0 | 117 | const char * options); |
michael@0 | 118 | extern int link(const char * fromname, const char * toname); |
michael@0 | 119 | extern char * optarg; |
michael@0 | 120 | extern int optind; |
michael@0 | 121 | |
michael@0 | 122 | static void addtt(zic_t starttime, int type); |
michael@0 | 123 | #ifdef ICU |
michael@0 | 124 | static int addtype(long gmtoff, long rawoff, long dstoff, |
michael@0 | 125 | const char * abbr, int isdst, |
michael@0 | 126 | int ttisstd, int ttisgmt); |
michael@0 | 127 | #else |
michael@0 | 128 | static int addtype(long gmtoff, const char * abbr, int isdst, |
michael@0 | 129 | int ttisstd, int ttisgmt); |
michael@0 | 130 | #endif |
michael@0 | 131 | static void leapadd(zic_t t, int positive, int rolling, int count); |
michael@0 | 132 | static void adjleap(void); |
michael@0 | 133 | static void associate(void); |
michael@0 | 134 | static int ciequal(const char * ap, const char * bp); |
michael@0 | 135 | static void convert(long val, char * buf); |
michael@0 | 136 | static void convert64(zic_t val, char * buf); |
michael@0 | 137 | static void dolink(const char * fromfield, const char * tofield); |
michael@0 | 138 | static void doabbr(char * abbr, const char * format, |
michael@0 | 139 | const char * letters, int isdst, int doquotes); |
michael@0 | 140 | static void eat(const char * name, int num); |
michael@0 | 141 | static void eats(const char * name, int num, |
michael@0 | 142 | const char * rname, int rnum); |
michael@0 | 143 | static long eitol(int i); |
michael@0 | 144 | static void error(const char * message); |
michael@0 | 145 | static char ** getfields(char * buf); |
michael@0 | 146 | static long gethms(const char * string, const char * errstrng, |
michael@0 | 147 | int signable); |
michael@0 | 148 | static void infile(const char * filename); |
michael@0 | 149 | static void inleap(char ** fields, int nfields); |
michael@0 | 150 | static void inlink(char ** fields, int nfields); |
michael@0 | 151 | static void inrule(char ** fields, int nfields); |
michael@0 | 152 | static int inzcont(char ** fields, int nfields); |
michael@0 | 153 | static int inzone(char ** fields, int nfields); |
michael@0 | 154 | static int inzsub(char ** fields, int nfields, int iscont); |
michael@0 | 155 | static int is32(zic_t x); |
michael@0 | 156 | static int itsabbr(const char * abbr, const char * word); |
michael@0 | 157 | static int itsdir(const char * name); |
michael@0 | 158 | static int lowerit(int c); |
michael@0 | 159 | static char * memcheck(char * tocheck); |
michael@0 | 160 | static int mkdirs(char * filename); |
michael@0 | 161 | static void newabbr(const char * abbr); |
michael@0 | 162 | static long oadd(long t1, long t2); |
michael@0 | 163 | static void outzone(const struct zone * zp, int ntzones); |
michael@0 | 164 | static void puttzcode(long code, FILE * fp); |
michael@0 | 165 | static void puttzcode64(zic_t code, FILE * fp); |
michael@0 | 166 | static int rcomp(const void * leftp, const void * rightp); |
michael@0 | 167 | static zic_t rpytime(const struct rule * rp, int wantedy); |
michael@0 | 168 | static void rulesub(struct rule * rp, |
michael@0 | 169 | const char * loyearp, const char * hiyearp, |
michael@0 | 170 | const char * typep, const char * monthp, |
michael@0 | 171 | const char * dayp, const char * timep); |
michael@0 | 172 | static int stringoffset(char * result, long offset); |
michael@0 | 173 | static int stringrule(char * result, const struct rule * rp, |
michael@0 | 174 | long dstoff, long gmtoff); |
michael@0 | 175 | static void stringzone(char * result, |
michael@0 | 176 | const struct zone * zp, int ntzones); |
michael@0 | 177 | static void setboundaries(void); |
michael@0 | 178 | static zic_t tadd(zic_t t1, long t2); |
michael@0 | 179 | static void usage(FILE *stream, int status); |
michael@0 | 180 | static void writezone(const char * name, const char * string); |
michael@0 | 181 | static int yearistype(int year, const char * type); |
michael@0 | 182 | #ifdef ICU |
michael@0 | 183 | static void emit_icu_zone(FILE* f, const char* zoneName, int zoneOffset, |
michael@0 | 184 | const struct rule* rule, |
michael@0 | 185 | int ruleIndex, int startYear); |
michael@0 | 186 | static void emit_icu_link(FILE* f, const char* from, const char* to); |
michael@0 | 187 | static void emit_icu_rule(FILE* f, const struct rule* r, int ruleIndex); |
michael@0 | 188 | static int add_icu_final_rules(const struct rule* r1, const struct rule* r2); |
michael@0 | 189 | #endif |
michael@0 | 190 | |
michael@0 | 191 | static int charcnt; |
michael@0 | 192 | static int errors; |
michael@0 | 193 | static const char * filename; |
michael@0 | 194 | static int leapcnt; |
michael@0 | 195 | static int leapseen; |
michael@0 | 196 | static int leapminyear; |
michael@0 | 197 | static int leapmaxyear; |
michael@0 | 198 | static int linenum; |
michael@0 | 199 | static int max_abbrvar_len; |
michael@0 | 200 | static int max_format_len; |
michael@0 | 201 | static zic_t max_time; |
michael@0 | 202 | static int max_year; |
michael@0 | 203 | static zic_t min_time; |
michael@0 | 204 | static int min_year; |
michael@0 | 205 | static int noise; |
michael@0 | 206 | static const char * rfilename; |
michael@0 | 207 | static int rlinenum; |
michael@0 | 208 | static const char * progname; |
michael@0 | 209 | static int timecnt; |
michael@0 | 210 | static int typecnt; |
michael@0 | 211 | |
michael@0 | 212 | /* |
michael@0 | 213 | ** Line codes. |
michael@0 | 214 | */ |
michael@0 | 215 | |
michael@0 | 216 | #define LC_RULE 0 |
michael@0 | 217 | #define LC_ZONE 1 |
michael@0 | 218 | #define LC_LINK 2 |
michael@0 | 219 | #define LC_LEAP 3 |
michael@0 | 220 | |
michael@0 | 221 | /* |
michael@0 | 222 | ** Which fields are which on a Zone line. |
michael@0 | 223 | */ |
michael@0 | 224 | |
michael@0 | 225 | #define ZF_NAME 1 |
michael@0 | 226 | #define ZF_GMTOFF 2 |
michael@0 | 227 | #define ZF_RULE 3 |
michael@0 | 228 | #define ZF_FORMAT 4 |
michael@0 | 229 | #define ZF_TILYEAR 5 |
michael@0 | 230 | #define ZF_TILMONTH 6 |
michael@0 | 231 | #define ZF_TILDAY 7 |
michael@0 | 232 | #define ZF_TILTIME 8 |
michael@0 | 233 | #define ZONE_MINFIELDS 5 |
michael@0 | 234 | #define ZONE_MAXFIELDS 9 |
michael@0 | 235 | |
michael@0 | 236 | /* |
michael@0 | 237 | ** Which fields are which on a Zone continuation line. |
michael@0 | 238 | */ |
michael@0 | 239 | |
michael@0 | 240 | #define ZFC_GMTOFF 0 |
michael@0 | 241 | #define ZFC_RULE 1 |
michael@0 | 242 | #define ZFC_FORMAT 2 |
michael@0 | 243 | #define ZFC_TILYEAR 3 |
michael@0 | 244 | #define ZFC_TILMONTH 4 |
michael@0 | 245 | #define ZFC_TILDAY 5 |
michael@0 | 246 | #define ZFC_TILTIME 6 |
michael@0 | 247 | #define ZONEC_MINFIELDS 3 |
michael@0 | 248 | #define ZONEC_MAXFIELDS 7 |
michael@0 | 249 | |
michael@0 | 250 | /* |
michael@0 | 251 | ** Which files are which on a Rule line. |
michael@0 | 252 | */ |
michael@0 | 253 | |
michael@0 | 254 | #define RF_NAME 1 |
michael@0 | 255 | #define RF_LOYEAR 2 |
michael@0 | 256 | #define RF_HIYEAR 3 |
michael@0 | 257 | #define RF_COMMAND 4 |
michael@0 | 258 | #define RF_MONTH 5 |
michael@0 | 259 | #define RF_DAY 6 |
michael@0 | 260 | #define RF_TOD 7 |
michael@0 | 261 | #define RF_STDOFF 8 |
michael@0 | 262 | #define RF_ABBRVAR 9 |
michael@0 | 263 | #define RULE_FIELDS 10 |
michael@0 | 264 | |
michael@0 | 265 | /* |
michael@0 | 266 | ** Which fields are which on a Link line. |
michael@0 | 267 | */ |
michael@0 | 268 | |
michael@0 | 269 | #define LF_FROM 1 |
michael@0 | 270 | #define LF_TO 2 |
michael@0 | 271 | #define LINK_FIELDS 3 |
michael@0 | 272 | |
michael@0 | 273 | /* |
michael@0 | 274 | ** Which fields are which on a Leap line. |
michael@0 | 275 | */ |
michael@0 | 276 | |
michael@0 | 277 | #define LP_YEAR 1 |
michael@0 | 278 | #define LP_MONTH 2 |
michael@0 | 279 | #define LP_DAY 3 |
michael@0 | 280 | #define LP_TIME 4 |
michael@0 | 281 | #define LP_CORR 5 |
michael@0 | 282 | #define LP_ROLL 6 |
michael@0 | 283 | #define LEAP_FIELDS 7 |
michael@0 | 284 | |
michael@0 | 285 | /* |
michael@0 | 286 | ** Year synonyms. |
michael@0 | 287 | */ |
michael@0 | 288 | |
michael@0 | 289 | #define YR_MINIMUM 0 |
michael@0 | 290 | #define YR_MAXIMUM 1 |
michael@0 | 291 | #define YR_ONLY 2 |
michael@0 | 292 | |
michael@0 | 293 | static struct rule * rules; |
michael@0 | 294 | static int nrules; /* number of rules */ |
michael@0 | 295 | |
michael@0 | 296 | static struct zone * zones; |
michael@0 | 297 | static int nzones; /* number of zones */ |
michael@0 | 298 | |
michael@0 | 299 | struct link { |
michael@0 | 300 | const char * l_filename; |
michael@0 | 301 | int l_linenum; |
michael@0 | 302 | const char * l_from; |
michael@0 | 303 | const char * l_to; |
michael@0 | 304 | }; |
michael@0 | 305 | |
michael@0 | 306 | static struct link * links; |
michael@0 | 307 | static int nlinks; |
michael@0 | 308 | |
michael@0 | 309 | struct lookup { |
michael@0 | 310 | const char * l_word; |
michael@0 | 311 | const int l_value; |
michael@0 | 312 | }; |
michael@0 | 313 | |
michael@0 | 314 | #ifdef ICU |
michael@0 | 315 | /* Indices into rules[] for final rules. They will occur in pairs, |
michael@0 | 316 | * with finalRules[i] occurring before finalRules[i+1] in the year. |
michael@0 | 317 | * Each zone need only store a start year, a standard offset, and an |
michael@0 | 318 | * index into finalRules[]. FinalRules[] are aliases into rules[]. */ |
michael@0 | 319 | static const struct rule ** finalRules; |
michael@0 | 320 | static int finalRulesCount; |
michael@0 | 321 | #endif |
michael@0 | 322 | |
michael@0 | 323 | static struct lookup const * byword(const char * string, |
michael@0 | 324 | const struct lookup * lp); |
michael@0 | 325 | |
michael@0 | 326 | static struct lookup const line_codes[] = { |
michael@0 | 327 | { "Rule", LC_RULE }, |
michael@0 | 328 | { "Zone", LC_ZONE }, |
michael@0 | 329 | { "Link", LC_LINK }, |
michael@0 | 330 | { "Leap", LC_LEAP }, |
michael@0 | 331 | { NULL, 0} |
michael@0 | 332 | }; |
michael@0 | 333 | |
michael@0 | 334 | static struct lookup const mon_names[] = { |
michael@0 | 335 | { "January", TM_JANUARY }, |
michael@0 | 336 | { "February", TM_FEBRUARY }, |
michael@0 | 337 | { "March", TM_MARCH }, |
michael@0 | 338 | { "April", TM_APRIL }, |
michael@0 | 339 | { "May", TM_MAY }, |
michael@0 | 340 | { "June", TM_JUNE }, |
michael@0 | 341 | { "July", TM_JULY }, |
michael@0 | 342 | { "August", TM_AUGUST }, |
michael@0 | 343 | { "September", TM_SEPTEMBER }, |
michael@0 | 344 | { "October", TM_OCTOBER }, |
michael@0 | 345 | { "November", TM_NOVEMBER }, |
michael@0 | 346 | { "December", TM_DECEMBER }, |
michael@0 | 347 | { NULL, 0 } |
michael@0 | 348 | }; |
michael@0 | 349 | |
michael@0 | 350 | static struct lookup const wday_names[] = { |
michael@0 | 351 | { "Sunday", TM_SUNDAY }, |
michael@0 | 352 | { "Monday", TM_MONDAY }, |
michael@0 | 353 | { "Tuesday", TM_TUESDAY }, |
michael@0 | 354 | { "Wednesday", TM_WEDNESDAY }, |
michael@0 | 355 | { "Thursday", TM_THURSDAY }, |
michael@0 | 356 | { "Friday", TM_FRIDAY }, |
michael@0 | 357 | { "Saturday", TM_SATURDAY }, |
michael@0 | 358 | { NULL, 0 } |
michael@0 | 359 | }; |
michael@0 | 360 | |
michael@0 | 361 | static struct lookup const lasts[] = { |
michael@0 | 362 | { "last-Sunday", TM_SUNDAY }, |
michael@0 | 363 | { "last-Monday", TM_MONDAY }, |
michael@0 | 364 | { "last-Tuesday", TM_TUESDAY }, |
michael@0 | 365 | { "last-Wednesday", TM_WEDNESDAY }, |
michael@0 | 366 | { "last-Thursday", TM_THURSDAY }, |
michael@0 | 367 | { "last-Friday", TM_FRIDAY }, |
michael@0 | 368 | { "last-Saturday", TM_SATURDAY }, |
michael@0 | 369 | { NULL, 0 } |
michael@0 | 370 | }; |
michael@0 | 371 | |
michael@0 | 372 | static struct lookup const begin_years[] = { |
michael@0 | 373 | { "minimum", YR_MINIMUM }, |
michael@0 | 374 | { "maximum", YR_MAXIMUM }, |
michael@0 | 375 | { NULL, 0 } |
michael@0 | 376 | }; |
michael@0 | 377 | |
michael@0 | 378 | static struct lookup const end_years[] = { |
michael@0 | 379 | { "minimum", YR_MINIMUM }, |
michael@0 | 380 | { "maximum", YR_MAXIMUM }, |
michael@0 | 381 | { "only", YR_ONLY }, |
michael@0 | 382 | { NULL, 0 } |
michael@0 | 383 | }; |
michael@0 | 384 | |
michael@0 | 385 | static struct lookup const leap_types[] = { |
michael@0 | 386 | { "Rolling", TRUE }, |
michael@0 | 387 | { "Stationary", FALSE }, |
michael@0 | 388 | { NULL, 0 } |
michael@0 | 389 | }; |
michael@0 | 390 | |
michael@0 | 391 | static const int len_months[2][MONSPERYEAR] = { |
michael@0 | 392 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, |
michael@0 | 393 | { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } |
michael@0 | 394 | }; |
michael@0 | 395 | |
michael@0 | 396 | static const int len_years[2] = { |
michael@0 | 397 | DAYSPERNYEAR, DAYSPERLYEAR |
michael@0 | 398 | }; |
michael@0 | 399 | |
michael@0 | 400 | static struct attype { |
michael@0 | 401 | zic_t at; |
michael@0 | 402 | unsigned char type; |
michael@0 | 403 | } attypes[TZ_MAX_TIMES]; |
michael@0 | 404 | static long gmtoffs[TZ_MAX_TYPES]; |
michael@0 | 405 | #ifdef ICU |
michael@0 | 406 | /* gmtoffs[i] = rawoffs[i] + dstoffs[i] */ |
michael@0 | 407 | static long rawoffs[TZ_MAX_TYPES]; |
michael@0 | 408 | static long dstoffs[TZ_MAX_TYPES]; |
michael@0 | 409 | #endif |
michael@0 | 410 | static char isdsts[TZ_MAX_TYPES]; |
michael@0 | 411 | static unsigned char abbrinds[TZ_MAX_TYPES]; |
michael@0 | 412 | static char ttisstds[TZ_MAX_TYPES]; |
michael@0 | 413 | static char ttisgmts[TZ_MAX_TYPES]; |
michael@0 | 414 | static char chars[TZ_MAX_CHARS]; |
michael@0 | 415 | static zic_t trans[TZ_MAX_LEAPS]; |
michael@0 | 416 | static long corr[TZ_MAX_LEAPS]; |
michael@0 | 417 | static char roll[TZ_MAX_LEAPS]; |
michael@0 | 418 | |
michael@0 | 419 | /* |
michael@0 | 420 | ** Memory allocation. |
michael@0 | 421 | */ |
michael@0 | 422 | |
michael@0 | 423 | static char * |
michael@0 | 424 | memcheck(ptr) |
michael@0 | 425 | char * const ptr; |
michael@0 | 426 | { |
michael@0 | 427 | if (ptr == NULL) { |
michael@0 | 428 | const char *e = strerror(errno); |
michael@0 | 429 | |
michael@0 | 430 | (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"), |
michael@0 | 431 | progname, e); |
michael@0 | 432 | exit(EXIT_FAILURE); |
michael@0 | 433 | } |
michael@0 | 434 | return ptr; |
michael@0 | 435 | } |
michael@0 | 436 | |
michael@0 | 437 | #define emalloc(size) memcheck(imalloc(size)) |
michael@0 | 438 | #define erealloc(ptr, size) memcheck(irealloc((ptr), (size))) |
michael@0 | 439 | #define ecpyalloc(ptr) memcheck(icpyalloc(ptr)) |
michael@0 | 440 | #define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp))) |
michael@0 | 441 | |
michael@0 | 442 | /* |
michael@0 | 443 | ** Error handling. |
michael@0 | 444 | */ |
michael@0 | 445 | |
michael@0 | 446 | static void |
michael@0 | 447 | eats(name, num, rname, rnum) |
michael@0 | 448 | const char * const name; |
michael@0 | 449 | const int num; |
michael@0 | 450 | const char * const rname; |
michael@0 | 451 | const int rnum; |
michael@0 | 452 | { |
michael@0 | 453 | filename = name; |
michael@0 | 454 | linenum = num; |
michael@0 | 455 | rfilename = rname; |
michael@0 | 456 | rlinenum = rnum; |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | static void |
michael@0 | 460 | eat(name, num) |
michael@0 | 461 | const char * const name; |
michael@0 | 462 | const int num; |
michael@0 | 463 | { |
michael@0 | 464 | eats(name, num, (char *) NULL, -1); |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | static void |
michael@0 | 468 | error(string) |
michael@0 | 469 | const char * const string; |
michael@0 | 470 | { |
michael@0 | 471 | /* |
michael@0 | 472 | ** Match the format of "cc" to allow sh users to |
michael@0 | 473 | ** zic ... 2>&1 | error -t "*" -v |
michael@0 | 474 | ** on BSD systems. |
michael@0 | 475 | */ |
michael@0 | 476 | (void) fprintf(stderr, _("\"%s\", line %d: %s"), |
michael@0 | 477 | filename, linenum, string); |
michael@0 | 478 | if (rfilename != NULL) |
michael@0 | 479 | (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"), |
michael@0 | 480 | rfilename, rlinenum); |
michael@0 | 481 | (void) fprintf(stderr, "\n"); |
michael@0 | 482 | ++errors; |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | static void |
michael@0 | 486 | warning(string) |
michael@0 | 487 | const char * const string; |
michael@0 | 488 | { |
michael@0 | 489 | char * cp; |
michael@0 | 490 | |
michael@0 | 491 | cp = ecpyalloc(_("warning: ")); |
michael@0 | 492 | cp = ecatalloc(cp, string); |
michael@0 | 493 | error(cp); |
michael@0 | 494 | ifree(cp); |
michael@0 | 495 | --errors; |
michael@0 | 496 | } |
michael@0 | 497 | |
michael@0 | 498 | static void |
michael@0 | 499 | usage(FILE *stream, int status) |
michael@0 | 500 | { |
michael@0 | 501 | (void) fprintf(stream, _("%s: usage is %s \ |
michael@0 | 502 | [ --version ] [ --help ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\ |
michael@0 | 503 | \t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\ |
michael@0 | 504 | \n\ |
michael@0 | 505 | Report bugs to tz@elsie.nci.nih.gov.\n"), |
michael@0 | 506 | progname, progname); |
michael@0 | 507 | exit(status); |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | #ifdef ICU |
michael@0 | 511 | /* File into which we will write supplemental ICU data. */ |
michael@0 | 512 | static FILE * icuFile; |
michael@0 | 513 | |
michael@0 | 514 | static void |
michael@0 | 515 | emit_icu_zone(FILE* f, const char* zoneName, int zoneOffset, |
michael@0 | 516 | const struct rule* rule, |
michael@0 | 517 | int ruleIndex, int startYear) { |
michael@0 | 518 | /* machine-readable section */ |
michael@0 | 519 | fprintf(f, "zone %s %d %d %s", zoneName, zoneOffset, startYear, rule->r_name); |
michael@0 | 520 | |
michael@0 | 521 | /* human-readable section */ |
michael@0 | 522 | fprintf(f, " # zone %s, offset %d, year >= %d, rule %s (%d)\n", |
michael@0 | 523 | zoneName, zoneOffset, startYear, |
michael@0 | 524 | rule->r_name, ruleIndex); |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | static void |
michael@0 | 528 | emit_icu_link(FILE* f, const char* from, const char* to) { |
michael@0 | 529 | /* machine-readable section */ |
michael@0 | 530 | fprintf(f, "link %s %s\n", from, to); |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | static const char* DYCODE[] = {"DOM", "DOWGEQ", "DOWLEQ"}; |
michael@0 | 534 | |
michael@0 | 535 | static void |
michael@0 | 536 | emit_icu_rule(FILE* f, const struct rule* r, int ruleIndex) { |
michael@0 | 537 | if (r->r_yrtype != NULL) { |
michael@0 | 538 | warning("year types not supported by ICU"); |
michael@0 | 539 | fprintf(stderr, "rule %s, file %s, line %d\n", |
michael@0 | 540 | r->r_name, r->r_filename, r->r_linenum); |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | /* machine-readable section */ |
michael@0 | 544 | fprintf(f, "rule %s %s %d %d %d %ld %d %d %ld", |
michael@0 | 545 | r->r_name, DYCODE[r->r_dycode], |
michael@0 | 546 | r->r_month, r->r_dayofmonth, |
michael@0 | 547 | (r->r_dycode == DC_DOM ? -1 : r->r_wday), |
michael@0 | 548 | r->r_tod, r->r_todisstd, r->r_todisgmt, r->r_stdoff |
michael@0 | 549 | ); |
michael@0 | 550 | |
michael@0 | 551 | /* human-readable section */ |
michael@0 | 552 | fprintf(f, " # %d: %s, file %s, line %d", |
michael@0 | 553 | ruleIndex, r->r_name, r->r_filename, r->r_linenum); |
michael@0 | 554 | fprintf(f, ", mode %s", DYCODE[r->r_dycode]); |
michael@0 | 555 | fprintf(f, ", %s, dom %d", mon_names[r->r_month].l_word, r->r_dayofmonth); |
michael@0 | 556 | if (r->r_dycode != DC_DOM) { |
michael@0 | 557 | fprintf(f, ", %s", wday_names[r->r_wday].l_word); |
michael@0 | 558 | } |
michael@0 | 559 | fprintf(f, ", time %ld", r->r_tod); |
michael@0 | 560 | fprintf(f, ", isstd %d", r->r_todisstd); |
michael@0 | 561 | fprintf(f, ", isgmt %d", r->r_todisgmt); |
michael@0 | 562 | fprintf(f, ", offset %ld", r->r_stdoff); |
michael@0 | 563 | fprintf(f, "\n"); |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | static int |
michael@0 | 567 | add_icu_final_rules(const struct rule* r1, const struct rule* r2) { |
michael@0 | 568 | int i; |
michael@0 | 569 | |
michael@0 | 570 | for (i=0; i<finalRulesCount; ++i) { /* i+=2 should work too */ |
michael@0 | 571 | if (r1==finalRules[i]) return i; /* [sic] pointer comparison */ |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | finalRules = (const struct rule**) (void*) erealloc((char *) finalRules, |
michael@0 | 575 | (finalRulesCount + 2) * sizeof(*finalRules)); |
michael@0 | 576 | finalRules[finalRulesCount++] = r1; |
michael@0 | 577 | finalRules[finalRulesCount++] = r2; |
michael@0 | 578 | return finalRulesCount - 2; |
michael@0 | 579 | } |
michael@0 | 580 | #endif |
michael@0 | 581 | |
michael@0 | 582 | static const char * psxrules; |
michael@0 | 583 | static const char * lcltime; |
michael@0 | 584 | static const char * directory; |
michael@0 | 585 | static const char * leapsec; |
michael@0 | 586 | static const char * yitcommand; |
michael@0 | 587 | |
michael@0 | 588 | int |
michael@0 | 589 | main(argc, argv) |
michael@0 | 590 | int argc; |
michael@0 | 591 | char * argv[]; |
michael@0 | 592 | { |
michael@0 | 593 | register int i; |
michael@0 | 594 | register int j; |
michael@0 | 595 | register int c; |
michael@0 | 596 | |
michael@0 | 597 | #ifdef unix |
michael@0 | 598 | (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); |
michael@0 | 599 | #endif /* defined unix */ |
michael@0 | 600 | #if HAVE_GETTEXT |
michael@0 | 601 | (void) setlocale(LC_ALL, ""); |
michael@0 | 602 | #ifdef TZ_DOMAINDIR |
michael@0 | 603 | (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); |
michael@0 | 604 | #endif /* defined TEXTDOMAINDIR */ |
michael@0 | 605 | (void) textdomain(TZ_DOMAIN); |
michael@0 | 606 | #endif /* HAVE_GETTEXT */ |
michael@0 | 607 | progname = argv[0]; |
michael@0 | 608 | if (TYPE_BIT(zic_t) < 64) { |
michael@0 | 609 | (void) fprintf(stderr, "%s: %s\n", progname, |
michael@0 | 610 | _("wild compilation-time specification of zic_t")); |
michael@0 | 611 | exit(EXIT_FAILURE); |
michael@0 | 612 | } |
michael@0 | 613 | for (i = 1; i < argc; ++i) |
michael@0 | 614 | if (strcmp(argv[i], "--version") == 0) { |
michael@0 | 615 | (void) printf("%s\n", elsieid); |
michael@0 | 616 | exit(EXIT_SUCCESS); |
michael@0 | 617 | } else if (strcmp(argv[i], "--help") == 0) { |
michael@0 | 618 | usage(stdout, EXIT_SUCCESS); |
michael@0 | 619 | } |
michael@0 | 620 | while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) |
michael@0 | 621 | switch (c) { |
michael@0 | 622 | default: |
michael@0 | 623 | usage(stderr, EXIT_FAILURE); |
michael@0 | 624 | case 'd': |
michael@0 | 625 | if (directory == NULL) |
michael@0 | 626 | directory = optarg; |
michael@0 | 627 | else { |
michael@0 | 628 | (void) fprintf(stderr, |
michael@0 | 629 | _("%s: More than one -d option specified\n"), |
michael@0 | 630 | progname); |
michael@0 | 631 | exit(EXIT_FAILURE); |
michael@0 | 632 | } |
michael@0 | 633 | break; |
michael@0 | 634 | case 'l': |
michael@0 | 635 | if (lcltime == NULL) |
michael@0 | 636 | lcltime = optarg; |
michael@0 | 637 | else { |
michael@0 | 638 | (void) fprintf(stderr, |
michael@0 | 639 | _("%s: More than one -l option specified\n"), |
michael@0 | 640 | progname); |
michael@0 | 641 | exit(EXIT_FAILURE); |
michael@0 | 642 | } |
michael@0 | 643 | break; |
michael@0 | 644 | case 'p': |
michael@0 | 645 | if (psxrules == NULL) |
michael@0 | 646 | psxrules = optarg; |
michael@0 | 647 | else { |
michael@0 | 648 | (void) fprintf(stderr, |
michael@0 | 649 | _("%s: More than one -p option specified\n"), |
michael@0 | 650 | progname); |
michael@0 | 651 | exit(EXIT_FAILURE); |
michael@0 | 652 | } |
michael@0 | 653 | break; |
michael@0 | 654 | case 'y': |
michael@0 | 655 | if (yitcommand == NULL) |
michael@0 | 656 | yitcommand = optarg; |
michael@0 | 657 | else { |
michael@0 | 658 | (void) fprintf(stderr, |
michael@0 | 659 | _("%s: More than one -y option specified\n"), |
michael@0 | 660 | progname); |
michael@0 | 661 | exit(EXIT_FAILURE); |
michael@0 | 662 | } |
michael@0 | 663 | break; |
michael@0 | 664 | case 'L': |
michael@0 | 665 | if (leapsec == NULL) |
michael@0 | 666 | leapsec = optarg; |
michael@0 | 667 | else { |
michael@0 | 668 | (void) fprintf(stderr, |
michael@0 | 669 | _("%s: More than one -L option specified\n"), |
michael@0 | 670 | progname); |
michael@0 | 671 | exit(EXIT_FAILURE); |
michael@0 | 672 | } |
michael@0 | 673 | break; |
michael@0 | 674 | case 'v': |
michael@0 | 675 | noise = TRUE; |
michael@0 | 676 | break; |
michael@0 | 677 | case 's': |
michael@0 | 678 | (void) printf("%s: -s ignored\n", progname); |
michael@0 | 679 | break; |
michael@0 | 680 | } |
michael@0 | 681 | if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) |
michael@0 | 682 | usage(stderr, EXIT_FAILURE); /* usage message by request */ |
michael@0 | 683 | if (directory == NULL) |
michael@0 | 684 | directory = TZDIR; |
michael@0 | 685 | if (yitcommand == NULL) |
michael@0 | 686 | yitcommand = "yearistype"; |
michael@0 | 687 | |
michael@0 | 688 | setboundaries(); |
michael@0 | 689 | |
michael@0 | 690 | if (optind < argc && leapsec != NULL) { |
michael@0 | 691 | infile(leapsec); |
michael@0 | 692 | adjleap(); |
michael@0 | 693 | } |
michael@0 | 694 | |
michael@0 | 695 | #ifdef ICU |
michael@0 | 696 | if ((icuFile = fopen(ICU_ZONE_FILE, "w")) == NULL) { |
michael@0 | 697 | const char *e = strerror(errno); |
michael@0 | 698 | (void) fprintf(stderr, _("%s: Can't open %s: %s\n"), |
michael@0 | 699 | progname, ICU_ZONE_FILE, e); |
michael@0 | 700 | (void) exit(EXIT_FAILURE); |
michael@0 | 701 | } |
michael@0 | 702 | #endif |
michael@0 | 703 | for (i = optind; i < argc; ++i) |
michael@0 | 704 | infile(argv[i]); |
michael@0 | 705 | if (errors) |
michael@0 | 706 | exit(EXIT_FAILURE); |
michael@0 | 707 | associate(); |
michael@0 | 708 | for (i = 0; i < nzones; i = j) { |
michael@0 | 709 | /* |
michael@0 | 710 | ** Find the next non-continuation zone entry. |
michael@0 | 711 | */ |
michael@0 | 712 | for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) |
michael@0 | 713 | continue; |
michael@0 | 714 | outzone(&zones[i], j - i); |
michael@0 | 715 | } |
michael@0 | 716 | /* |
michael@0 | 717 | ** Make links. |
michael@0 | 718 | */ |
michael@0 | 719 | for (i = 0; i < nlinks; ++i) { |
michael@0 | 720 | eat(links[i].l_filename, links[i].l_linenum); |
michael@0 | 721 | dolink(links[i].l_from, links[i].l_to); |
michael@0 | 722 | #ifdef ICU |
michael@0 | 723 | emit_icu_link(icuFile, links[i].l_from, links[i].l_to); |
michael@0 | 724 | #endif |
michael@0 | 725 | if (noise) |
michael@0 | 726 | for (j = 0; j < nlinks; ++j) |
michael@0 | 727 | if (strcmp(links[i].l_to, |
michael@0 | 728 | links[j].l_from) == 0) |
michael@0 | 729 | warning(_("link to link")); |
michael@0 | 730 | } |
michael@0 | 731 | if (lcltime != NULL) { |
michael@0 | 732 | eat("command line", 1); |
michael@0 | 733 | dolink(lcltime, TZDEFAULT); |
michael@0 | 734 | } |
michael@0 | 735 | if (psxrules != NULL) { |
michael@0 | 736 | eat("command line", 1); |
michael@0 | 737 | dolink(psxrules, TZDEFRULES); |
michael@0 | 738 | } |
michael@0 | 739 | #ifdef ICU |
michael@0 | 740 | for (i=0; i<finalRulesCount; ++i) { |
michael@0 | 741 | emit_icu_rule(icuFile, finalRules[i], i); |
michael@0 | 742 | } |
michael@0 | 743 | #endif /*ICU*/ |
michael@0 | 744 | return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; |
michael@0 | 745 | } |
michael@0 | 746 | |
michael@0 | 747 | static void |
michael@0 | 748 | dolink(fromfield, tofield) |
michael@0 | 749 | const char * const fromfield; |
michael@0 | 750 | const char * const tofield; |
michael@0 | 751 | { |
michael@0 | 752 | register char * fromname; |
michael@0 | 753 | register char * toname; |
michael@0 | 754 | |
michael@0 | 755 | if (fromfield[0] == '/') |
michael@0 | 756 | fromname = ecpyalloc(fromfield); |
michael@0 | 757 | else { |
michael@0 | 758 | fromname = ecpyalloc(directory); |
michael@0 | 759 | fromname = ecatalloc(fromname, "/"); |
michael@0 | 760 | fromname = ecatalloc(fromname, fromfield); |
michael@0 | 761 | } |
michael@0 | 762 | if (tofield[0] == '/') |
michael@0 | 763 | toname = ecpyalloc(tofield); |
michael@0 | 764 | else { |
michael@0 | 765 | toname = ecpyalloc(directory); |
michael@0 | 766 | toname = ecatalloc(toname, "/"); |
michael@0 | 767 | toname = ecatalloc(toname, tofield); |
michael@0 | 768 | } |
michael@0 | 769 | /* |
michael@0 | 770 | ** We get to be careful here since |
michael@0 | 771 | ** there's a fair chance of root running us. |
michael@0 | 772 | */ |
michael@0 | 773 | if (!itsdir(toname)) |
michael@0 | 774 | (void) remove(toname); |
michael@0 | 775 | if (link(fromname, toname) != 0) { |
michael@0 | 776 | int result; |
michael@0 | 777 | |
michael@0 | 778 | if (mkdirs(toname) != 0) |
michael@0 | 779 | exit(EXIT_FAILURE); |
michael@0 | 780 | |
michael@0 | 781 | result = link(fromname, toname); |
michael@0 | 782 | #if HAVE_SYMLINK |
michael@0 | 783 | if (result != 0 && |
michael@0 | 784 | access(fromname, F_OK) == 0 && |
michael@0 | 785 | !itsdir(fromname)) { |
michael@0 | 786 | const char *s = tofield; |
michael@0 | 787 | register char * symlinkcontents = NULL; |
michael@0 | 788 | |
michael@0 | 789 | while ((s = strchr(s+1, '/')) != NULL) |
michael@0 | 790 | symlinkcontents = |
michael@0 | 791 | ecatalloc(symlinkcontents, |
michael@0 | 792 | "../"); |
michael@0 | 793 | symlinkcontents = |
michael@0 | 794 | ecatalloc(symlinkcontents, |
michael@0 | 795 | fromname); |
michael@0 | 796 | result = symlink(symlinkcontents, |
michael@0 | 797 | toname); |
michael@0 | 798 | if (result == 0) |
michael@0 | 799 | warning(_("hard link failed, symbolic link used")); |
michael@0 | 800 | ifree(symlinkcontents); |
michael@0 | 801 | } |
michael@0 | 802 | #endif /* HAVE_SYMLINK */ |
michael@0 | 803 | if (result != 0) { |
michael@0 | 804 | const char *e = strerror(errno); |
michael@0 | 805 | |
michael@0 | 806 | (void) fprintf(stderr, |
michael@0 | 807 | _("%s: Can't link from %s to %s: %s\n"), |
michael@0 | 808 | progname, fromname, toname, e); |
michael@0 | 809 | #ifndef ICU_LINKS |
michael@0 | 810 | exit(EXIT_FAILURE); |
michael@0 | 811 | #endif |
michael@0 | 812 | } |
michael@0 | 813 | } |
michael@0 | 814 | ifree(fromname); |
michael@0 | 815 | ifree(toname); |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | #define TIME_T_BITS_IN_FILE 64 |
michael@0 | 819 | |
michael@0 | 820 | static void |
michael@0 | 821 | setboundaries(void) |
michael@0 | 822 | { |
michael@0 | 823 | register int i; |
michael@0 | 824 | |
michael@0 | 825 | min_time = -1; |
michael@0 | 826 | for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) |
michael@0 | 827 | min_time *= 2; |
michael@0 | 828 | max_time = -(min_time + 1); |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | static int |
michael@0 | 832 | itsdir(name) |
michael@0 | 833 | const char * const name; |
michael@0 | 834 | { |
michael@0 | 835 | register char * myname; |
michael@0 | 836 | register int accres; |
michael@0 | 837 | |
michael@0 | 838 | myname = ecpyalloc(name); |
michael@0 | 839 | myname = ecatalloc(myname, "/."); |
michael@0 | 840 | accres = access(myname, F_OK); |
michael@0 | 841 | ifree(myname); |
michael@0 | 842 | return accres == 0; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | /* |
michael@0 | 846 | ** Associate sets of rules with zones. |
michael@0 | 847 | */ |
michael@0 | 848 | |
michael@0 | 849 | /* |
michael@0 | 850 | ** Sort by rule name. |
michael@0 | 851 | */ |
michael@0 | 852 | |
michael@0 | 853 | static int |
michael@0 | 854 | rcomp(cp1, cp2) |
michael@0 | 855 | const void * cp1; |
michael@0 | 856 | const void * cp2; |
michael@0 | 857 | { |
michael@0 | 858 | return strcmp(((const struct rule *) cp1)->r_name, |
michael@0 | 859 | ((const struct rule *) cp2)->r_name); |
michael@0 | 860 | } |
michael@0 | 861 | |
michael@0 | 862 | static void |
michael@0 | 863 | associate(void) |
michael@0 | 864 | { |
michael@0 | 865 | register struct zone * zp; |
michael@0 | 866 | register struct rule * rp; |
michael@0 | 867 | register int base, out; |
michael@0 | 868 | register int i, j; |
michael@0 | 869 | |
michael@0 | 870 | if (nrules != 0) { |
michael@0 | 871 | (void) qsort((void *) rules, (size_t) nrules, |
michael@0 | 872 | (size_t) sizeof *rules, rcomp); |
michael@0 | 873 | for (i = 0; i < nrules - 1; ++i) { |
michael@0 | 874 | if (strcmp(rules[i].r_name, |
michael@0 | 875 | rules[i + 1].r_name) != 0) |
michael@0 | 876 | continue; |
michael@0 | 877 | if (strcmp(rules[i].r_filename, |
michael@0 | 878 | rules[i + 1].r_filename) == 0) |
michael@0 | 879 | continue; |
michael@0 | 880 | eat(rules[i].r_filename, rules[i].r_linenum); |
michael@0 | 881 | warning(_("same rule name in multiple files")); |
michael@0 | 882 | eat(rules[i + 1].r_filename, rules[i + 1].r_linenum); |
michael@0 | 883 | warning(_("same rule name in multiple files")); |
michael@0 | 884 | for (j = i + 2; j < nrules; ++j) { |
michael@0 | 885 | if (strcmp(rules[i].r_name, |
michael@0 | 886 | rules[j].r_name) != 0) |
michael@0 | 887 | break; |
michael@0 | 888 | if (strcmp(rules[i].r_filename, |
michael@0 | 889 | rules[j].r_filename) == 0) |
michael@0 | 890 | continue; |
michael@0 | 891 | if (strcmp(rules[i + 1].r_filename, |
michael@0 | 892 | rules[j].r_filename) == 0) |
michael@0 | 893 | continue; |
michael@0 | 894 | break; |
michael@0 | 895 | } |
michael@0 | 896 | i = j - 1; |
michael@0 | 897 | } |
michael@0 | 898 | } |
michael@0 | 899 | for (i = 0; i < nzones; ++i) { |
michael@0 | 900 | zp = &zones[i]; |
michael@0 | 901 | zp->z_rules = NULL; |
michael@0 | 902 | zp->z_nrules = 0; |
michael@0 | 903 | } |
michael@0 | 904 | for (base = 0; base < nrules; base = out) { |
michael@0 | 905 | rp = &rules[base]; |
michael@0 | 906 | for (out = base + 1; out < nrules; ++out) |
michael@0 | 907 | if (strcmp(rp->r_name, rules[out].r_name) != 0) |
michael@0 | 908 | break; |
michael@0 | 909 | for (i = 0; i < nzones; ++i) { |
michael@0 | 910 | zp = &zones[i]; |
michael@0 | 911 | if (strcmp(zp->z_rule, rp->r_name) != 0) |
michael@0 | 912 | continue; |
michael@0 | 913 | zp->z_rules = rp; |
michael@0 | 914 | zp->z_nrules = out - base; |
michael@0 | 915 | } |
michael@0 | 916 | } |
michael@0 | 917 | for (i = 0; i < nzones; ++i) { |
michael@0 | 918 | zp = &zones[i]; |
michael@0 | 919 | if (zp->z_nrules == 0) { |
michael@0 | 920 | /* |
michael@0 | 921 | ** Maybe we have a local standard time offset. |
michael@0 | 922 | */ |
michael@0 | 923 | eat(zp->z_filename, zp->z_linenum); |
michael@0 | 924 | zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), |
michael@0 | 925 | TRUE); |
michael@0 | 926 | /* |
michael@0 | 927 | ** Note, though, that if there's no rule, |
michael@0 | 928 | ** a '%s' in the format is a bad thing. |
michael@0 | 929 | */ |
michael@0 | 930 | if (strchr(zp->z_format, '%') != 0) |
michael@0 | 931 | error(_("%s in ruleless zone")); |
michael@0 | 932 | } |
michael@0 | 933 | } |
michael@0 | 934 | if (errors) |
michael@0 | 935 | exit(EXIT_FAILURE); |
michael@0 | 936 | } |
michael@0 | 937 | |
michael@0 | 938 | static void |
michael@0 | 939 | infile(name) |
michael@0 | 940 | const char * name; |
michael@0 | 941 | { |
michael@0 | 942 | register FILE * fp; |
michael@0 | 943 | register char ** fields; |
michael@0 | 944 | register char * cp; |
michael@0 | 945 | register const struct lookup * lp; |
michael@0 | 946 | register int nfields; |
michael@0 | 947 | register int wantcont; |
michael@0 | 948 | register int num; |
michael@0 | 949 | char buf[BUFSIZ]; |
michael@0 | 950 | |
michael@0 | 951 | if (strcmp(name, "-") == 0) { |
michael@0 | 952 | name = _("standard input"); |
michael@0 | 953 | fp = stdin; |
michael@0 | 954 | } else if ((fp = fopen(name, "r")) == NULL) { |
michael@0 | 955 | const char *e = strerror(errno); |
michael@0 | 956 | |
michael@0 | 957 | (void) fprintf(stderr, _("%s: Can't open %s: %s\n"), |
michael@0 | 958 | progname, name, e); |
michael@0 | 959 | exit(EXIT_FAILURE); |
michael@0 | 960 | } |
michael@0 | 961 | wantcont = FALSE; |
michael@0 | 962 | for (num = 1; ; ++num) { |
michael@0 | 963 | eat(name, num); |
michael@0 | 964 | if (fgets(buf, (int) sizeof buf, fp) != buf) |
michael@0 | 965 | break; |
michael@0 | 966 | cp = strchr(buf, '\n'); |
michael@0 | 967 | if (cp == NULL) { |
michael@0 | 968 | error(_("line too long")); |
michael@0 | 969 | exit(EXIT_FAILURE); |
michael@0 | 970 | } |
michael@0 | 971 | *cp = '\0'; |
michael@0 | 972 | fields = getfields(buf); |
michael@0 | 973 | nfields = 0; |
michael@0 | 974 | while (fields[nfields] != NULL) { |
michael@0 | 975 | static char nada; |
michael@0 | 976 | |
michael@0 | 977 | if (strcmp(fields[nfields], "-") == 0) |
michael@0 | 978 | fields[nfields] = &nada; |
michael@0 | 979 | ++nfields; |
michael@0 | 980 | } |
michael@0 | 981 | if (nfields == 0) { |
michael@0 | 982 | /* nothing to do */ |
michael@0 | 983 | } else if (wantcont) { |
michael@0 | 984 | wantcont = inzcont(fields, nfields); |
michael@0 | 985 | } else { |
michael@0 | 986 | lp = byword(fields[0], line_codes); |
michael@0 | 987 | if (lp == NULL) |
michael@0 | 988 | error(_("input line of unknown type")); |
michael@0 | 989 | else switch ((int) (lp->l_value)) { |
michael@0 | 990 | case LC_RULE: |
michael@0 | 991 | inrule(fields, nfields); |
michael@0 | 992 | wantcont = FALSE; |
michael@0 | 993 | break; |
michael@0 | 994 | case LC_ZONE: |
michael@0 | 995 | wantcont = inzone(fields, nfields); |
michael@0 | 996 | break; |
michael@0 | 997 | case LC_LINK: |
michael@0 | 998 | inlink(fields, nfields); |
michael@0 | 999 | wantcont = FALSE; |
michael@0 | 1000 | break; |
michael@0 | 1001 | case LC_LEAP: |
michael@0 | 1002 | if (name != leapsec) |
michael@0 | 1003 | (void) fprintf(stderr, |
michael@0 | 1004 | _("%s: Leap line in non leap seconds file %s\n"), |
michael@0 | 1005 | progname, name); |
michael@0 | 1006 | else inleap(fields, nfields); |
michael@0 | 1007 | wantcont = FALSE; |
michael@0 | 1008 | break; |
michael@0 | 1009 | default: /* "cannot happen" */ |
michael@0 | 1010 | (void) fprintf(stderr, |
michael@0 | 1011 | _("%s: panic: Invalid l_value %d\n"), |
michael@0 | 1012 | progname, lp->l_value); |
michael@0 | 1013 | exit(EXIT_FAILURE); |
michael@0 | 1014 | } |
michael@0 | 1015 | } |
michael@0 | 1016 | ifree((char *) fields); |
michael@0 | 1017 | } |
michael@0 | 1018 | if (ferror(fp)) { |
michael@0 | 1019 | (void) fprintf(stderr, _("%s: Error reading %s\n"), |
michael@0 | 1020 | progname, filename); |
michael@0 | 1021 | exit(EXIT_FAILURE); |
michael@0 | 1022 | } |
michael@0 | 1023 | if (fp != stdin && fclose(fp)) { |
michael@0 | 1024 | const char *e = strerror(errno); |
michael@0 | 1025 | |
michael@0 | 1026 | (void) fprintf(stderr, _("%s: Error closing %s: %s\n"), |
michael@0 | 1027 | progname, filename, e); |
michael@0 | 1028 | exit(EXIT_FAILURE); |
michael@0 | 1029 | } |
michael@0 | 1030 | if (wantcont) |
michael@0 | 1031 | error(_("expected continuation line not found")); |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | /* |
michael@0 | 1035 | ** Convert a string of one of the forms |
michael@0 | 1036 | ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss |
michael@0 | 1037 | ** into a number of seconds. |
michael@0 | 1038 | ** A null string maps to zero. |
michael@0 | 1039 | ** Call error with errstring and return zero on errors. |
michael@0 | 1040 | */ |
michael@0 | 1041 | |
michael@0 | 1042 | static long |
michael@0 | 1043 | gethms(string, errstring, signable) |
michael@0 | 1044 | const char * string; |
michael@0 | 1045 | const char * const errstring; |
michael@0 | 1046 | const int signable; |
michael@0 | 1047 | { |
michael@0 | 1048 | long hh; |
michael@0 | 1049 | int mm, ss, sign; |
michael@0 | 1050 | |
michael@0 | 1051 | if (string == NULL || *string == '\0') |
michael@0 | 1052 | return 0; |
michael@0 | 1053 | if (!signable) |
michael@0 | 1054 | sign = 1; |
michael@0 | 1055 | else if (*string == '-') { |
michael@0 | 1056 | sign = -1; |
michael@0 | 1057 | ++string; |
michael@0 | 1058 | } else sign = 1; |
michael@0 | 1059 | if (sscanf(string, scheck(string, "%ld"), &hh) == 1) |
michael@0 | 1060 | mm = ss = 0; |
michael@0 | 1061 | else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2) |
michael@0 | 1062 | ss = 0; |
michael@0 | 1063 | else if (sscanf(string, scheck(string, "%ld:%d:%d"), |
michael@0 | 1064 | &hh, &mm, &ss) != 3) { |
michael@0 | 1065 | error(errstring); |
michael@0 | 1066 | return 0; |
michael@0 | 1067 | } |
michael@0 | 1068 | if (hh < 0 || |
michael@0 | 1069 | mm < 0 || mm >= MINSPERHOUR || |
michael@0 | 1070 | ss < 0 || ss > SECSPERMIN) { |
michael@0 | 1071 | error(errstring); |
michael@0 | 1072 | return 0; |
michael@0 | 1073 | } |
michael@0 | 1074 | if (LONG_MAX / SECSPERHOUR < hh) { |
michael@0 | 1075 | error(_("time overflow")); |
michael@0 | 1076 | return 0; |
michael@0 | 1077 | } |
michael@0 | 1078 | if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0) |
michael@0 | 1079 | warning(_("24:00 not handled by pre-1998 versions of zic")); |
michael@0 | 1080 | if (noise && (hh > HOURSPERDAY || |
michael@0 | 1081 | (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) |
michael@0 | 1082 | warning(_("values over 24 hours not handled by pre-2007 versions of zic")); |
michael@0 | 1083 | return oadd(eitol(sign) * hh * eitol(SECSPERHOUR), |
michael@0 | 1084 | eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))); |
michael@0 | 1085 | } |
michael@0 | 1086 | |
michael@0 | 1087 | static void |
michael@0 | 1088 | inrule(fields, nfields) |
michael@0 | 1089 | register char ** const fields; |
michael@0 | 1090 | const int nfields; |
michael@0 | 1091 | { |
michael@0 | 1092 | static struct rule r; |
michael@0 | 1093 | |
michael@0 | 1094 | if (nfields != RULE_FIELDS) { |
michael@0 | 1095 | error(_("wrong number of fields on Rule line")); |
michael@0 | 1096 | return; |
michael@0 | 1097 | } |
michael@0 | 1098 | if (*fields[RF_NAME] == '\0') { |
michael@0 | 1099 | error(_("nameless rule")); |
michael@0 | 1100 | return; |
michael@0 | 1101 | } |
michael@0 | 1102 | r.r_filename = filename; |
michael@0 | 1103 | r.r_linenum = linenum; |
michael@0 | 1104 | r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE); |
michael@0 | 1105 | rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], |
michael@0 | 1106 | fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); |
michael@0 | 1107 | r.r_name = ecpyalloc(fields[RF_NAME]); |
michael@0 | 1108 | r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); |
michael@0 | 1109 | if (max_abbrvar_len < strlen(r.r_abbrvar)) |
michael@0 | 1110 | max_abbrvar_len = strlen(r.r_abbrvar); |
michael@0 | 1111 | rules = (struct rule *) (void *) erealloc((char *) rules, |
michael@0 | 1112 | (int) ((nrules + 1) * sizeof *rules)); |
michael@0 | 1113 | rules[nrules++] = r; |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | static int |
michael@0 | 1117 | inzone(fields, nfields) |
michael@0 | 1118 | register char ** const fields; |
michael@0 | 1119 | const int nfields; |
michael@0 | 1120 | { |
michael@0 | 1121 | register int i; |
michael@0 | 1122 | static char * buf; |
michael@0 | 1123 | |
michael@0 | 1124 | if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { |
michael@0 | 1125 | error(_("wrong number of fields on Zone line")); |
michael@0 | 1126 | return FALSE; |
michael@0 | 1127 | } |
michael@0 | 1128 | if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { |
michael@0 | 1129 | buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT))); |
michael@0 | 1130 | (void) sprintf(buf, |
michael@0 | 1131 | _("\"Zone %s\" line and -l option are mutually exclusive"), |
michael@0 | 1132 | TZDEFAULT); |
michael@0 | 1133 | error(buf); |
michael@0 | 1134 | return FALSE; |
michael@0 | 1135 | } |
michael@0 | 1136 | if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { |
michael@0 | 1137 | buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES))); |
michael@0 | 1138 | (void) sprintf(buf, |
michael@0 | 1139 | _("\"Zone %s\" line and -p option are mutually exclusive"), |
michael@0 | 1140 | TZDEFRULES); |
michael@0 | 1141 | error(buf); |
michael@0 | 1142 | return FALSE; |
michael@0 | 1143 | } |
michael@0 | 1144 | for (i = 0; i < nzones; ++i) |
michael@0 | 1145 | if (zones[i].z_name != NULL && |
michael@0 | 1146 | strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { |
michael@0 | 1147 | buf = erealloc(buf, (int) (132 + |
michael@0 | 1148 | strlen(fields[ZF_NAME]) + |
michael@0 | 1149 | strlen(zones[i].z_filename))); |
michael@0 | 1150 | (void) sprintf(buf, |
michael@0 | 1151 | _("duplicate zone name %s (file \"%s\", line %d)"), |
michael@0 | 1152 | fields[ZF_NAME], |
michael@0 | 1153 | zones[i].z_filename, |
michael@0 | 1154 | zones[i].z_linenum); |
michael@0 | 1155 | error(buf); |
michael@0 | 1156 | return FALSE; |
michael@0 | 1157 | } |
michael@0 | 1158 | return inzsub(fields, nfields, FALSE); |
michael@0 | 1159 | } |
michael@0 | 1160 | |
michael@0 | 1161 | static int |
michael@0 | 1162 | inzcont(fields, nfields) |
michael@0 | 1163 | register char ** const fields; |
michael@0 | 1164 | const int nfields; |
michael@0 | 1165 | { |
michael@0 | 1166 | if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { |
michael@0 | 1167 | error(_("wrong number of fields on Zone continuation line")); |
michael@0 | 1168 | return FALSE; |
michael@0 | 1169 | } |
michael@0 | 1170 | return inzsub(fields, nfields, TRUE); |
michael@0 | 1171 | } |
michael@0 | 1172 | |
michael@0 | 1173 | static int |
michael@0 | 1174 | inzsub(fields, nfields, iscont) |
michael@0 | 1175 | register char ** const fields; |
michael@0 | 1176 | const int nfields; |
michael@0 | 1177 | const int iscont; |
michael@0 | 1178 | { |
michael@0 | 1179 | register char * cp; |
michael@0 | 1180 | static struct zone z; |
michael@0 | 1181 | register int i_gmtoff, i_rule, i_format; |
michael@0 | 1182 | register int i_untilyear, i_untilmonth; |
michael@0 | 1183 | register int i_untilday, i_untiltime; |
michael@0 | 1184 | register int hasuntil; |
michael@0 | 1185 | |
michael@0 | 1186 | if (iscont) { |
michael@0 | 1187 | i_gmtoff = ZFC_GMTOFF; |
michael@0 | 1188 | i_rule = ZFC_RULE; |
michael@0 | 1189 | i_format = ZFC_FORMAT; |
michael@0 | 1190 | i_untilyear = ZFC_TILYEAR; |
michael@0 | 1191 | i_untilmonth = ZFC_TILMONTH; |
michael@0 | 1192 | i_untilday = ZFC_TILDAY; |
michael@0 | 1193 | i_untiltime = ZFC_TILTIME; |
michael@0 | 1194 | z.z_name = NULL; |
michael@0 | 1195 | } else { |
michael@0 | 1196 | i_gmtoff = ZF_GMTOFF; |
michael@0 | 1197 | i_rule = ZF_RULE; |
michael@0 | 1198 | i_format = ZF_FORMAT; |
michael@0 | 1199 | i_untilyear = ZF_TILYEAR; |
michael@0 | 1200 | i_untilmonth = ZF_TILMONTH; |
michael@0 | 1201 | i_untilday = ZF_TILDAY; |
michael@0 | 1202 | i_untiltime = ZF_TILTIME; |
michael@0 | 1203 | z.z_name = ecpyalloc(fields[ZF_NAME]); |
michael@0 | 1204 | } |
michael@0 | 1205 | z.z_filename = filename; |
michael@0 | 1206 | z.z_linenum = linenum; |
michael@0 | 1207 | z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE); |
michael@0 | 1208 | if ((cp = strchr(fields[i_format], '%')) != 0) { |
michael@0 | 1209 | if (*++cp != 's' || strchr(cp, '%') != 0) { |
michael@0 | 1210 | error(_("invalid abbreviation format")); |
michael@0 | 1211 | return FALSE; |
michael@0 | 1212 | } |
michael@0 | 1213 | } |
michael@0 | 1214 | z.z_rule = ecpyalloc(fields[i_rule]); |
michael@0 | 1215 | z.z_format = ecpyalloc(fields[i_format]); |
michael@0 | 1216 | if (max_format_len < strlen(z.z_format)) |
michael@0 | 1217 | max_format_len = strlen(z.z_format); |
michael@0 | 1218 | hasuntil = nfields > i_untilyear; |
michael@0 | 1219 | if (hasuntil) { |
michael@0 | 1220 | z.z_untilrule.r_filename = filename; |
michael@0 | 1221 | z.z_untilrule.r_linenum = linenum; |
michael@0 | 1222 | rulesub(&z.z_untilrule, |
michael@0 | 1223 | fields[i_untilyear], |
michael@0 | 1224 | "only", |
michael@0 | 1225 | "", |
michael@0 | 1226 | (nfields > i_untilmonth) ? |
michael@0 | 1227 | fields[i_untilmonth] : "Jan", |
michael@0 | 1228 | (nfields > i_untilday) ? fields[i_untilday] : "1", |
michael@0 | 1229 | (nfields > i_untiltime) ? fields[i_untiltime] : "0"); |
michael@0 | 1230 | z.z_untiltime = rpytime(&z.z_untilrule, |
michael@0 | 1231 | z.z_untilrule.r_loyear); |
michael@0 | 1232 | if (iscont && nzones > 0 && |
michael@0 | 1233 | z.z_untiltime > min_time && |
michael@0 | 1234 | z.z_untiltime < max_time && |
michael@0 | 1235 | zones[nzones - 1].z_untiltime > min_time && |
michael@0 | 1236 | zones[nzones - 1].z_untiltime < max_time && |
michael@0 | 1237 | zones[nzones - 1].z_untiltime >= z.z_untiltime) { |
michael@0 | 1238 | error(_( |
michael@0 | 1239 | "Zone continuation line end time is not after end time of previous line" |
michael@0 | 1240 | )); |
michael@0 | 1241 | return FALSE; |
michael@0 | 1242 | } |
michael@0 | 1243 | } |
michael@0 | 1244 | zones = (struct zone *) (void *) erealloc((char *) zones, |
michael@0 | 1245 | (int) ((nzones + 1) * sizeof *zones)); |
michael@0 | 1246 | zones[nzones++] = z; |
michael@0 | 1247 | /* |
michael@0 | 1248 | ** If there was an UNTIL field on this line, |
michael@0 | 1249 | ** there's more information about the zone on the next line. |
michael@0 | 1250 | */ |
michael@0 | 1251 | return hasuntil; |
michael@0 | 1252 | } |
michael@0 | 1253 | |
michael@0 | 1254 | static void |
michael@0 | 1255 | inleap(fields, nfields) |
michael@0 | 1256 | register char ** const fields; |
michael@0 | 1257 | const int nfields; |
michael@0 | 1258 | { |
michael@0 | 1259 | register const char * cp; |
michael@0 | 1260 | register const struct lookup * lp; |
michael@0 | 1261 | register int i, j; |
michael@0 | 1262 | int year, month, day; |
michael@0 | 1263 | long dayoff, tod; |
michael@0 | 1264 | zic_t t; |
michael@0 | 1265 | |
michael@0 | 1266 | if (nfields != LEAP_FIELDS) { |
michael@0 | 1267 | error(_("wrong number of fields on Leap line")); |
michael@0 | 1268 | return; |
michael@0 | 1269 | } |
michael@0 | 1270 | dayoff = 0; |
michael@0 | 1271 | cp = fields[LP_YEAR]; |
michael@0 | 1272 | if (sscanf(cp, scheck(cp, "%d"), &year) != 1) { |
michael@0 | 1273 | /* |
michael@0 | 1274 | ** Leapin' Lizards! |
michael@0 | 1275 | */ |
michael@0 | 1276 | error(_("invalid leaping year")); |
michael@0 | 1277 | return; |
michael@0 | 1278 | } |
michael@0 | 1279 | if (!leapseen || leapmaxyear < year) |
michael@0 | 1280 | leapmaxyear = year; |
michael@0 | 1281 | if (!leapseen || leapminyear > year) |
michael@0 | 1282 | leapminyear = year; |
michael@0 | 1283 | leapseen = TRUE; |
michael@0 | 1284 | j = EPOCH_YEAR; |
michael@0 | 1285 | while (j != year) { |
michael@0 | 1286 | if (year > j) { |
michael@0 | 1287 | i = len_years[isleap(j)]; |
michael@0 | 1288 | ++j; |
michael@0 | 1289 | } else { |
michael@0 | 1290 | --j; |
michael@0 | 1291 | i = -len_years[isleap(j)]; |
michael@0 | 1292 | } |
michael@0 | 1293 | dayoff = oadd(dayoff, eitol(i)); |
michael@0 | 1294 | } |
michael@0 | 1295 | if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { |
michael@0 | 1296 | error(_("invalid month name")); |
michael@0 | 1297 | return; |
michael@0 | 1298 | } |
michael@0 | 1299 | month = lp->l_value; |
michael@0 | 1300 | j = TM_JANUARY; |
michael@0 | 1301 | while (j != month) { |
michael@0 | 1302 | i = len_months[isleap(year)][j]; |
michael@0 | 1303 | dayoff = oadd(dayoff, eitol(i)); |
michael@0 | 1304 | ++j; |
michael@0 | 1305 | } |
michael@0 | 1306 | cp = fields[LP_DAY]; |
michael@0 | 1307 | if (sscanf(cp, scheck(cp, "%d"), &day) != 1 || |
michael@0 | 1308 | day <= 0 || day > len_months[isleap(year)][month]) { |
michael@0 | 1309 | error(_("invalid day of month")); |
michael@0 | 1310 | return; |
michael@0 | 1311 | } |
michael@0 | 1312 | dayoff = oadd(dayoff, eitol(day - 1)); |
michael@0 | 1313 | if (dayoff < 0 && !TYPE_SIGNED(zic_t)) { |
michael@0 | 1314 | error(_("time before zero")); |
michael@0 | 1315 | return; |
michael@0 | 1316 | } |
michael@0 | 1317 | if (dayoff < min_time / SECSPERDAY) { |
michael@0 | 1318 | error(_("time too small")); |
michael@0 | 1319 | return; |
michael@0 | 1320 | } |
michael@0 | 1321 | if (dayoff > max_time / SECSPERDAY) { |
michael@0 | 1322 | error(_("time too large")); |
michael@0 | 1323 | return; |
michael@0 | 1324 | } |
michael@0 | 1325 | t = (zic_t) dayoff * SECSPERDAY; |
michael@0 | 1326 | tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); |
michael@0 | 1327 | cp = fields[LP_CORR]; |
michael@0 | 1328 | { |
michael@0 | 1329 | register int positive; |
michael@0 | 1330 | int count; |
michael@0 | 1331 | |
michael@0 | 1332 | if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ |
michael@0 | 1333 | positive = FALSE; |
michael@0 | 1334 | count = 1; |
michael@0 | 1335 | } else if (strcmp(cp, "--") == 0) { |
michael@0 | 1336 | positive = FALSE; |
michael@0 | 1337 | count = 2; |
michael@0 | 1338 | } else if (strcmp(cp, "+") == 0) { |
michael@0 | 1339 | positive = TRUE; |
michael@0 | 1340 | count = 1; |
michael@0 | 1341 | } else if (strcmp(cp, "++") == 0) { |
michael@0 | 1342 | positive = TRUE; |
michael@0 | 1343 | count = 2; |
michael@0 | 1344 | } else { |
michael@0 | 1345 | error(_("illegal CORRECTION field on Leap line")); |
michael@0 | 1346 | return; |
michael@0 | 1347 | } |
michael@0 | 1348 | if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { |
michael@0 | 1349 | error(_( |
michael@0 | 1350 | "illegal Rolling/Stationary field on Leap line" |
michael@0 | 1351 | )); |
michael@0 | 1352 | return; |
michael@0 | 1353 | } |
michael@0 | 1354 | leapadd(tadd(t, tod), positive, lp->l_value, count); |
michael@0 | 1355 | } |
michael@0 | 1356 | } |
michael@0 | 1357 | |
michael@0 | 1358 | static void |
michael@0 | 1359 | inlink(fields, nfields) |
michael@0 | 1360 | register char ** const fields; |
michael@0 | 1361 | const int nfields; |
michael@0 | 1362 | { |
michael@0 | 1363 | struct link l; |
michael@0 | 1364 | |
michael@0 | 1365 | if (nfields != LINK_FIELDS) { |
michael@0 | 1366 | error(_("wrong number of fields on Link line")); |
michael@0 | 1367 | return; |
michael@0 | 1368 | } |
michael@0 | 1369 | if (*fields[LF_FROM] == '\0') { |
michael@0 | 1370 | error(_("blank FROM field on Link line")); |
michael@0 | 1371 | return; |
michael@0 | 1372 | } |
michael@0 | 1373 | if (*fields[LF_TO] == '\0') { |
michael@0 | 1374 | error(_("blank TO field on Link line")); |
michael@0 | 1375 | return; |
michael@0 | 1376 | } |
michael@0 | 1377 | l.l_filename = filename; |
michael@0 | 1378 | l.l_linenum = linenum; |
michael@0 | 1379 | l.l_from = ecpyalloc(fields[LF_FROM]); |
michael@0 | 1380 | l.l_to = ecpyalloc(fields[LF_TO]); |
michael@0 | 1381 | links = (struct link *) (void *) erealloc((char *) links, |
michael@0 | 1382 | (int) ((nlinks + 1) * sizeof *links)); |
michael@0 | 1383 | links[nlinks++] = l; |
michael@0 | 1384 | } |
michael@0 | 1385 | |
michael@0 | 1386 | static void |
michael@0 | 1387 | rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep) |
michael@0 | 1388 | register struct rule * const rp; |
michael@0 | 1389 | const char * const loyearp; |
michael@0 | 1390 | const char * const hiyearp; |
michael@0 | 1391 | const char * const typep; |
michael@0 | 1392 | const char * const monthp; |
michael@0 | 1393 | const char * const dayp; |
michael@0 | 1394 | const char * const timep; |
michael@0 | 1395 | { |
michael@0 | 1396 | register const struct lookup * lp; |
michael@0 | 1397 | register const char * cp; |
michael@0 | 1398 | register char * dp; |
michael@0 | 1399 | register char * ep; |
michael@0 | 1400 | |
michael@0 | 1401 | if ((lp = byword(monthp, mon_names)) == NULL) { |
michael@0 | 1402 | error(_("invalid month name")); |
michael@0 | 1403 | return; |
michael@0 | 1404 | } |
michael@0 | 1405 | rp->r_month = lp->l_value; |
michael@0 | 1406 | rp->r_todisstd = FALSE; |
michael@0 | 1407 | rp->r_todisgmt = FALSE; |
michael@0 | 1408 | dp = ecpyalloc(timep); |
michael@0 | 1409 | if (*dp != '\0') { |
michael@0 | 1410 | ep = dp + strlen(dp) - 1; |
michael@0 | 1411 | switch (lowerit(*ep)) { |
michael@0 | 1412 | case 's': /* Standard */ |
michael@0 | 1413 | rp->r_todisstd = TRUE; |
michael@0 | 1414 | rp->r_todisgmt = FALSE; |
michael@0 | 1415 | *ep = '\0'; |
michael@0 | 1416 | break; |
michael@0 | 1417 | case 'w': /* Wall */ |
michael@0 | 1418 | rp->r_todisstd = FALSE; |
michael@0 | 1419 | rp->r_todisgmt = FALSE; |
michael@0 | 1420 | *ep = '\0'; |
michael@0 | 1421 | break; |
michael@0 | 1422 | case 'g': /* Greenwich */ |
michael@0 | 1423 | case 'u': /* Universal */ |
michael@0 | 1424 | case 'z': /* Zulu */ |
michael@0 | 1425 | rp->r_todisstd = TRUE; |
michael@0 | 1426 | rp->r_todisgmt = TRUE; |
michael@0 | 1427 | *ep = '\0'; |
michael@0 | 1428 | break; |
michael@0 | 1429 | } |
michael@0 | 1430 | } |
michael@0 | 1431 | rp->r_tod = gethms(dp, _("invalid time of day"), FALSE); |
michael@0 | 1432 | ifree(dp); |
michael@0 | 1433 | /* |
michael@0 | 1434 | ** Year work. |
michael@0 | 1435 | */ |
michael@0 | 1436 | cp = loyearp; |
michael@0 | 1437 | lp = byword(cp, begin_years); |
michael@0 | 1438 | rp->r_lowasnum = lp == NULL; |
michael@0 | 1439 | if (!rp->r_lowasnum) switch ((int) lp->l_value) { |
michael@0 | 1440 | case YR_MINIMUM: |
michael@0 | 1441 | rp->r_loyear = INT_MIN; |
michael@0 | 1442 | break; |
michael@0 | 1443 | case YR_MAXIMUM: |
michael@0 | 1444 | rp->r_loyear = INT_MAX; |
michael@0 | 1445 | break; |
michael@0 | 1446 | default: /* "cannot happen" */ |
michael@0 | 1447 | (void) fprintf(stderr, |
michael@0 | 1448 | _("%s: panic: Invalid l_value %d\n"), |
michael@0 | 1449 | progname, lp->l_value); |
michael@0 | 1450 | exit(EXIT_FAILURE); |
michael@0 | 1451 | } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { |
michael@0 | 1452 | error(_("invalid starting year")); |
michael@0 | 1453 | return; |
michael@0 | 1454 | } |
michael@0 | 1455 | cp = hiyearp; |
michael@0 | 1456 | lp = byword(cp, end_years); |
michael@0 | 1457 | rp->r_hiwasnum = lp == NULL; |
michael@0 | 1458 | if (!rp->r_hiwasnum) switch ((int) lp->l_value) { |
michael@0 | 1459 | case YR_MINIMUM: |
michael@0 | 1460 | rp->r_hiyear = INT_MIN; |
michael@0 | 1461 | break; |
michael@0 | 1462 | case YR_MAXIMUM: |
michael@0 | 1463 | rp->r_hiyear = INT_MAX; |
michael@0 | 1464 | break; |
michael@0 | 1465 | case YR_ONLY: |
michael@0 | 1466 | rp->r_hiyear = rp->r_loyear; |
michael@0 | 1467 | break; |
michael@0 | 1468 | default: /* "cannot happen" */ |
michael@0 | 1469 | (void) fprintf(stderr, |
michael@0 | 1470 | _("%s: panic: Invalid l_value %d\n"), |
michael@0 | 1471 | progname, lp->l_value); |
michael@0 | 1472 | exit(EXIT_FAILURE); |
michael@0 | 1473 | } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) { |
michael@0 | 1474 | error(_("invalid ending year")); |
michael@0 | 1475 | return; |
michael@0 | 1476 | } |
michael@0 | 1477 | if (rp->r_loyear > rp->r_hiyear) { |
michael@0 | 1478 | error(_("starting year greater than ending year")); |
michael@0 | 1479 | return; |
michael@0 | 1480 | } |
michael@0 | 1481 | if (*typep == '\0') |
michael@0 | 1482 | rp->r_yrtype = NULL; |
michael@0 | 1483 | else { |
michael@0 | 1484 | if (rp->r_loyear == rp->r_hiyear) { |
michael@0 | 1485 | error(_("typed single year")); |
michael@0 | 1486 | return; |
michael@0 | 1487 | } |
michael@0 | 1488 | rp->r_yrtype = ecpyalloc(typep); |
michael@0 | 1489 | } |
michael@0 | 1490 | /* |
michael@0 | 1491 | ** Day work. |
michael@0 | 1492 | ** Accept things such as: |
michael@0 | 1493 | ** 1 |
michael@0 | 1494 | ** last-Sunday |
michael@0 | 1495 | ** Sun<=20 |
michael@0 | 1496 | ** Sun>=7 |
michael@0 | 1497 | */ |
michael@0 | 1498 | dp = ecpyalloc(dayp); |
michael@0 | 1499 | if ((lp = byword(dp, lasts)) != NULL) { |
michael@0 | 1500 | rp->r_dycode = DC_DOWLEQ; |
michael@0 | 1501 | rp->r_wday = lp->l_value; |
michael@0 | 1502 | rp->r_dayofmonth = len_months[1][rp->r_month]; |
michael@0 | 1503 | } else { |
michael@0 | 1504 | if ((ep = strchr(dp, '<')) != 0) |
michael@0 | 1505 | rp->r_dycode = DC_DOWLEQ; |
michael@0 | 1506 | else if ((ep = strchr(dp, '>')) != 0) |
michael@0 | 1507 | rp->r_dycode = DC_DOWGEQ; |
michael@0 | 1508 | else { |
michael@0 | 1509 | ep = dp; |
michael@0 | 1510 | rp->r_dycode = DC_DOM; |
michael@0 | 1511 | } |
michael@0 | 1512 | if (rp->r_dycode != DC_DOM) { |
michael@0 | 1513 | *ep++ = 0; |
michael@0 | 1514 | if (*ep++ != '=') { |
michael@0 | 1515 | error(_("invalid day of month")); |
michael@0 | 1516 | ifree(dp); |
michael@0 | 1517 | return; |
michael@0 | 1518 | } |
michael@0 | 1519 | if ((lp = byword(dp, wday_names)) == NULL) { |
michael@0 | 1520 | error(_("invalid weekday name")); |
michael@0 | 1521 | ifree(dp); |
michael@0 | 1522 | return; |
michael@0 | 1523 | } |
michael@0 | 1524 | rp->r_wday = lp->l_value; |
michael@0 | 1525 | } |
michael@0 | 1526 | if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 || |
michael@0 | 1527 | rp->r_dayofmonth <= 0 || |
michael@0 | 1528 | (rp->r_dayofmonth > len_months[1][rp->r_month])) { |
michael@0 | 1529 | error(_("invalid day of month")); |
michael@0 | 1530 | ifree(dp); |
michael@0 | 1531 | return; |
michael@0 | 1532 | } |
michael@0 | 1533 | } |
michael@0 | 1534 | ifree(dp); |
michael@0 | 1535 | } |
michael@0 | 1536 | |
michael@0 | 1537 | static void |
michael@0 | 1538 | convert(val, buf) |
michael@0 | 1539 | const long val; |
michael@0 | 1540 | char * const buf; |
michael@0 | 1541 | { |
michael@0 | 1542 | register int i; |
michael@0 | 1543 | register int shift; |
michael@0 | 1544 | |
michael@0 | 1545 | for (i = 0, shift = 24; i < 4; ++i, shift -= 8) |
michael@0 | 1546 | buf[i] = val >> shift; |
michael@0 | 1547 | } |
michael@0 | 1548 | |
michael@0 | 1549 | static void |
michael@0 | 1550 | convert64(val, buf) |
michael@0 | 1551 | const zic_t val; |
michael@0 | 1552 | char * const buf; |
michael@0 | 1553 | { |
michael@0 | 1554 | register int i; |
michael@0 | 1555 | register int shift; |
michael@0 | 1556 | |
michael@0 | 1557 | for (i = 0, shift = 56; i < 8; ++i, shift -= 8) |
michael@0 | 1558 | buf[i] = val >> shift; |
michael@0 | 1559 | } |
michael@0 | 1560 | |
michael@0 | 1561 | static void |
michael@0 | 1562 | puttzcode(val, fp) |
michael@0 | 1563 | const long val; |
michael@0 | 1564 | FILE * const fp; |
michael@0 | 1565 | { |
michael@0 | 1566 | char buf[4]; |
michael@0 | 1567 | |
michael@0 | 1568 | convert(val, buf); |
michael@0 | 1569 | (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); |
michael@0 | 1570 | } |
michael@0 | 1571 | |
michael@0 | 1572 | static void |
michael@0 | 1573 | puttzcode64(val, fp) |
michael@0 | 1574 | const zic_t val; |
michael@0 | 1575 | FILE * const fp; |
michael@0 | 1576 | { |
michael@0 | 1577 | char buf[8]; |
michael@0 | 1578 | |
michael@0 | 1579 | convert64(val, buf); |
michael@0 | 1580 | (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); |
michael@0 | 1581 | } |
michael@0 | 1582 | |
michael@0 | 1583 | static int |
michael@0 | 1584 | atcomp(avp, bvp) |
michael@0 | 1585 | const void * avp; |
michael@0 | 1586 | const void * bvp; |
michael@0 | 1587 | { |
michael@0 | 1588 | const zic_t a = ((const struct attype *) avp)->at; |
michael@0 | 1589 | const zic_t b = ((const struct attype *) bvp)->at; |
michael@0 | 1590 | |
michael@0 | 1591 | return (a < b) ? -1 : (a > b); |
michael@0 | 1592 | } |
michael@0 | 1593 | |
michael@0 | 1594 | static int |
michael@0 | 1595 | is32(x) |
michael@0 | 1596 | const zic_t x; |
michael@0 | 1597 | { |
michael@0 | 1598 | return INT32_MIN <= x && x <= INT32_MAX; |
michael@0 | 1599 | } |
michael@0 | 1600 | |
michael@0 | 1601 | static void |
michael@0 | 1602 | writezone(name, string) |
michael@0 | 1603 | const char * const name; |
michael@0 | 1604 | const char * const string; |
michael@0 | 1605 | { |
michael@0 | 1606 | register FILE * fp; |
michael@0 | 1607 | register int i, j; |
michael@0 | 1608 | register int leapcnt32, leapi32; |
michael@0 | 1609 | register int timecnt32, timei32; |
michael@0 | 1610 | register int pass; |
michael@0 | 1611 | static char * fullname; |
michael@0 | 1612 | static const struct tzhead tzh0; |
michael@0 | 1613 | static struct tzhead tzh; |
michael@0 | 1614 | zic_t ats[TZ_MAX_TIMES]; |
michael@0 | 1615 | unsigned char types[TZ_MAX_TIMES]; |
michael@0 | 1616 | |
michael@0 | 1617 | /* |
michael@0 | 1618 | ** Sort. |
michael@0 | 1619 | */ |
michael@0 | 1620 | if (timecnt > 1) |
michael@0 | 1621 | (void) qsort((void *) attypes, (size_t) timecnt, |
michael@0 | 1622 | (size_t) sizeof *attypes, atcomp); |
michael@0 | 1623 | /* |
michael@0 | 1624 | ** Optimize. |
michael@0 | 1625 | */ |
michael@0 | 1626 | { |
michael@0 | 1627 | int fromi; |
michael@0 | 1628 | int toi; |
michael@0 | 1629 | |
michael@0 | 1630 | toi = 0; |
michael@0 | 1631 | fromi = 0; |
michael@0 | 1632 | while (fromi < timecnt && attypes[fromi].at < min_time) |
michael@0 | 1633 | ++fromi; |
michael@0 | 1634 | if (isdsts[0] == 0) |
michael@0 | 1635 | while (fromi < timecnt && attypes[fromi].type == 0) |
michael@0 | 1636 | ++fromi; /* handled by default rule */ |
michael@0 | 1637 | for ( ; fromi < timecnt; ++fromi) { |
michael@0 | 1638 | if (toi != 0 && ((attypes[fromi].at + |
michael@0 | 1639 | gmtoffs[attypes[toi - 1].type]) <= |
michael@0 | 1640 | (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 |
michael@0 | 1641 | : attypes[toi - 2].type]))) { |
michael@0 | 1642 | attypes[toi - 1].type = |
michael@0 | 1643 | attypes[fromi].type; |
michael@0 | 1644 | continue; |
michael@0 | 1645 | } |
michael@0 | 1646 | if (toi == 0 || |
michael@0 | 1647 | attypes[toi - 1].type != attypes[fromi].type) |
michael@0 | 1648 | attypes[toi++] = attypes[fromi]; |
michael@0 | 1649 | } |
michael@0 | 1650 | timecnt = toi; |
michael@0 | 1651 | } |
michael@0 | 1652 | /* |
michael@0 | 1653 | ** Transfer. |
michael@0 | 1654 | */ |
michael@0 | 1655 | for (i = 0; i < timecnt; ++i) { |
michael@0 | 1656 | ats[i] = attypes[i].at; |
michael@0 | 1657 | types[i] = attypes[i].type; |
michael@0 | 1658 | } |
michael@0 | 1659 | /* |
michael@0 | 1660 | ** Correct for leap seconds. |
michael@0 | 1661 | */ |
michael@0 | 1662 | for (i = 0; i < timecnt; ++i) { |
michael@0 | 1663 | j = leapcnt; |
michael@0 | 1664 | while (--j >= 0) |
michael@0 | 1665 | if (ats[i] > trans[j] - corr[j]) { |
michael@0 | 1666 | ats[i] = tadd(ats[i], corr[j]); |
michael@0 | 1667 | break; |
michael@0 | 1668 | } |
michael@0 | 1669 | } |
michael@0 | 1670 | /* |
michael@0 | 1671 | ** Figure out 32-bit-limited starts and counts. |
michael@0 | 1672 | */ |
michael@0 | 1673 | timecnt32 = timecnt; |
michael@0 | 1674 | timei32 = 0; |
michael@0 | 1675 | leapcnt32 = leapcnt; |
michael@0 | 1676 | leapi32 = 0; |
michael@0 | 1677 | while (timecnt32 > 0 && !is32(ats[timecnt32 - 1])) |
michael@0 | 1678 | --timecnt32; |
michael@0 | 1679 | while (timecnt32 > 0 && !is32(ats[timei32])) { |
michael@0 | 1680 | --timecnt32; |
michael@0 | 1681 | ++timei32; |
michael@0 | 1682 | } |
michael@0 | 1683 | while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) |
michael@0 | 1684 | --leapcnt32; |
michael@0 | 1685 | while (leapcnt32 > 0 && !is32(trans[leapi32])) { |
michael@0 | 1686 | --leapcnt32; |
michael@0 | 1687 | ++leapi32; |
michael@0 | 1688 | } |
michael@0 | 1689 | fullname = erealloc(fullname, |
michael@0 | 1690 | (int) (strlen(directory) + 1 + strlen(name) + 1)); |
michael@0 | 1691 | (void) sprintf(fullname, "%s/%s", directory, name); |
michael@0 | 1692 | /* |
michael@0 | 1693 | ** Remove old file, if any, to snap links. |
michael@0 | 1694 | */ |
michael@0 | 1695 | if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) { |
michael@0 | 1696 | const char *e = strerror(errno); |
michael@0 | 1697 | |
michael@0 | 1698 | (void) fprintf(stderr, _("%s: Can't remove %s: %s\n"), |
michael@0 | 1699 | progname, fullname, e); |
michael@0 | 1700 | exit(EXIT_FAILURE); |
michael@0 | 1701 | } |
michael@0 | 1702 | if ((fp = fopen(fullname, "wb")) == NULL) { |
michael@0 | 1703 | if (mkdirs(fullname) != 0) |
michael@0 | 1704 | exit(EXIT_FAILURE); |
michael@0 | 1705 | if ((fp = fopen(fullname, "wb")) == NULL) { |
michael@0 | 1706 | const char *e = strerror(errno); |
michael@0 | 1707 | |
michael@0 | 1708 | (void) fprintf(stderr, _("%s: Can't create %s: %s\n"), |
michael@0 | 1709 | progname, fullname, e); |
michael@0 | 1710 | exit(EXIT_FAILURE); |
michael@0 | 1711 | } |
michael@0 | 1712 | } |
michael@0 | 1713 | for (pass = 1; pass <= 2; ++pass) { |
michael@0 | 1714 | register int thistimei, thistimecnt; |
michael@0 | 1715 | register int thisleapi, thisleapcnt; |
michael@0 | 1716 | register int thistimelim, thisleaplim; |
michael@0 | 1717 | int writetype[TZ_MAX_TIMES]; |
michael@0 | 1718 | int typemap[TZ_MAX_TYPES]; |
michael@0 | 1719 | register int thistypecnt; |
michael@0 | 1720 | char thischars[TZ_MAX_CHARS]; |
michael@0 | 1721 | char thischarcnt; |
michael@0 | 1722 | int indmap[TZ_MAX_CHARS]; |
michael@0 | 1723 | |
michael@0 | 1724 | if (pass == 1) { |
michael@0 | 1725 | thistimei = timei32; |
michael@0 | 1726 | thistimecnt = timecnt32; |
michael@0 | 1727 | thisleapi = leapi32; |
michael@0 | 1728 | thisleapcnt = leapcnt32; |
michael@0 | 1729 | } else { |
michael@0 | 1730 | thistimei = 0; |
michael@0 | 1731 | thistimecnt = timecnt; |
michael@0 | 1732 | thisleapi = 0; |
michael@0 | 1733 | thisleapcnt = leapcnt; |
michael@0 | 1734 | } |
michael@0 | 1735 | thistimelim = thistimei + thistimecnt; |
michael@0 | 1736 | thisleaplim = thisleapi + thisleapcnt; |
michael@0 | 1737 | for (i = 0; i < typecnt; ++i) |
michael@0 | 1738 | writetype[i] = thistimecnt == timecnt; |
michael@0 | 1739 | if (thistimecnt == 0) { |
michael@0 | 1740 | /* |
michael@0 | 1741 | ** No transition times fall in the current |
michael@0 | 1742 | ** (32- or 64-bit) window. |
michael@0 | 1743 | */ |
michael@0 | 1744 | if (typecnt != 0) |
michael@0 | 1745 | writetype[typecnt - 1] = TRUE; |
michael@0 | 1746 | } else { |
michael@0 | 1747 | for (i = thistimei - 1; i < thistimelim; ++i) |
michael@0 | 1748 | if (i >= 0) |
michael@0 | 1749 | writetype[types[i]] = TRUE; |
michael@0 | 1750 | /* |
michael@0 | 1751 | ** For America/Godthab and Antarctica/Palmer |
michael@0 | 1752 | */ |
michael@0 | 1753 | if (thistimei == 0) |
michael@0 | 1754 | writetype[0] = TRUE; |
michael@0 | 1755 | } |
michael@0 | 1756 | thistypecnt = 0; |
michael@0 | 1757 | for (i = 0; i < typecnt; ++i) |
michael@0 | 1758 | typemap[i] = writetype[i] ? thistypecnt++ : -1; |
michael@0 | 1759 | for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i) |
michael@0 | 1760 | indmap[i] = -1; |
michael@0 | 1761 | thischarcnt = 0; |
michael@0 | 1762 | for (i = 0; i < typecnt; ++i) { |
michael@0 | 1763 | register char * thisabbr; |
michael@0 | 1764 | |
michael@0 | 1765 | if (!writetype[i]) |
michael@0 | 1766 | continue; |
michael@0 | 1767 | if (indmap[abbrinds[i]] >= 0) |
michael@0 | 1768 | continue; |
michael@0 | 1769 | thisabbr = &chars[abbrinds[i]]; |
michael@0 | 1770 | for (j = 0; j < thischarcnt; ++j) |
michael@0 | 1771 | if (strcmp(&thischars[j], thisabbr) == 0) |
michael@0 | 1772 | break; |
michael@0 | 1773 | if (j == thischarcnt) { |
michael@0 | 1774 | (void) strcpy(&thischars[(int) thischarcnt], |
michael@0 | 1775 | thisabbr); |
michael@0 | 1776 | thischarcnt += strlen(thisabbr) + 1; |
michael@0 | 1777 | } |
michael@0 | 1778 | indmap[abbrinds[i]] = j; |
michael@0 | 1779 | } |
michael@0 | 1780 | #define DO(field) (void) fwrite((void *) tzh.field, \ |
michael@0 | 1781 | (size_t) sizeof tzh.field, (size_t) 1, fp) |
michael@0 | 1782 | tzh = tzh0; |
michael@0 | 1783 | #ifdef ICU |
michael@0 | 1784 | * (ICUZoneinfoVersion*) &tzh.tzh_reserved = TZ_ICU_VERSION; |
michael@0 | 1785 | (void) strncpy(tzh.tzh_magic, TZ_ICU_MAGIC, sizeof tzh.tzh_magic); |
michael@0 | 1786 | #else |
michael@0 | 1787 | (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); |
michael@0 | 1788 | #endif |
michael@0 | 1789 | tzh.tzh_version[0] = ZIC_VERSION; |
michael@0 | 1790 | convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt); |
michael@0 | 1791 | convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt); |
michael@0 | 1792 | convert(eitol(thisleapcnt), tzh.tzh_leapcnt); |
michael@0 | 1793 | convert(eitol(thistimecnt), tzh.tzh_timecnt); |
michael@0 | 1794 | convert(eitol(thistypecnt), tzh.tzh_typecnt); |
michael@0 | 1795 | convert(eitol(thischarcnt), tzh.tzh_charcnt); |
michael@0 | 1796 | DO(tzh_magic); |
michael@0 | 1797 | DO(tzh_version); |
michael@0 | 1798 | DO(tzh_reserved); |
michael@0 | 1799 | DO(tzh_ttisgmtcnt); |
michael@0 | 1800 | DO(tzh_ttisstdcnt); |
michael@0 | 1801 | DO(tzh_leapcnt); |
michael@0 | 1802 | DO(tzh_timecnt); |
michael@0 | 1803 | DO(tzh_typecnt); |
michael@0 | 1804 | DO(tzh_charcnt); |
michael@0 | 1805 | #undef DO |
michael@0 | 1806 | for (i = thistimei; i < thistimelim; ++i) |
michael@0 | 1807 | if (pass == 1) |
michael@0 | 1808 | puttzcode((long) ats[i], fp); |
michael@0 | 1809 | else puttzcode64(ats[i], fp); |
michael@0 | 1810 | for (i = thistimei; i < thistimelim; ++i) { |
michael@0 | 1811 | unsigned char uc; |
michael@0 | 1812 | |
michael@0 | 1813 | uc = typemap[types[i]]; |
michael@0 | 1814 | (void) fwrite((void *) &uc, |
michael@0 | 1815 | (size_t) sizeof uc, |
michael@0 | 1816 | (size_t) 1, |
michael@0 | 1817 | fp); |
michael@0 | 1818 | } |
michael@0 | 1819 | for (i = 0; i < typecnt; ++i) |
michael@0 | 1820 | if (writetype[i]) { |
michael@0 | 1821 | #ifdef ICU |
michael@0 | 1822 | puttzcode((long) rawoffs[i], fp); |
michael@0 | 1823 | puttzcode((long) dstoffs[i], fp); |
michael@0 | 1824 | #else |
michael@0 | 1825 | puttzcode(gmtoffs[i], fp); |
michael@0 | 1826 | #endif |
michael@0 | 1827 | (void) putc(isdsts[i], fp); |
michael@0 | 1828 | (void) putc((unsigned char) indmap[abbrinds[i]], fp); |
michael@0 | 1829 | } |
michael@0 | 1830 | if (thischarcnt != 0) |
michael@0 | 1831 | (void) fwrite((void *) thischars, |
michael@0 | 1832 | (size_t) sizeof thischars[0], |
michael@0 | 1833 | (size_t) thischarcnt, fp); |
michael@0 | 1834 | for (i = thisleapi; i < thisleaplim; ++i) { |
michael@0 | 1835 | register zic_t todo; |
michael@0 | 1836 | |
michael@0 | 1837 | if (roll[i]) { |
michael@0 | 1838 | if (timecnt == 0 || trans[i] < ats[0]) { |
michael@0 | 1839 | j = 0; |
michael@0 | 1840 | while (isdsts[j]) |
michael@0 | 1841 | if (++j >= typecnt) { |
michael@0 | 1842 | j = 0; |
michael@0 | 1843 | break; |
michael@0 | 1844 | } |
michael@0 | 1845 | } else { |
michael@0 | 1846 | j = 1; |
michael@0 | 1847 | while (j < timecnt && |
michael@0 | 1848 | trans[i] >= ats[j]) |
michael@0 | 1849 | ++j; |
michael@0 | 1850 | j = types[j - 1]; |
michael@0 | 1851 | } |
michael@0 | 1852 | todo = tadd(trans[i], -gmtoffs[j]); |
michael@0 | 1853 | } else todo = trans[i]; |
michael@0 | 1854 | if (pass == 1) |
michael@0 | 1855 | puttzcode((long) todo, fp); |
michael@0 | 1856 | else puttzcode64(todo, fp); |
michael@0 | 1857 | puttzcode(corr[i], fp); |
michael@0 | 1858 | } |
michael@0 | 1859 | for (i = 0; i < typecnt; ++i) |
michael@0 | 1860 | if (writetype[i]) |
michael@0 | 1861 | (void) putc(ttisstds[i], fp); |
michael@0 | 1862 | for (i = 0; i < typecnt; ++i) |
michael@0 | 1863 | if (writetype[i]) |
michael@0 | 1864 | (void) putc(ttisgmts[i], fp); |
michael@0 | 1865 | } |
michael@0 | 1866 | (void) fprintf(fp, "\n%s\n", string); |
michael@0 | 1867 | if (ferror(fp) || fclose(fp)) { |
michael@0 | 1868 | (void) fprintf(stderr, _("%s: Error writing %s\n"), |
michael@0 | 1869 | progname, fullname); |
michael@0 | 1870 | exit(EXIT_FAILURE); |
michael@0 | 1871 | } |
michael@0 | 1872 | } |
michael@0 | 1873 | |
michael@0 | 1874 | static void |
michael@0 | 1875 | doabbr(abbr, format, letters, isdst, doquotes) |
michael@0 | 1876 | char * const abbr; |
michael@0 | 1877 | const char * const format; |
michael@0 | 1878 | const char * const letters; |
michael@0 | 1879 | const int isdst; |
michael@0 | 1880 | const int doquotes; |
michael@0 | 1881 | { |
michael@0 | 1882 | register char * cp; |
michael@0 | 1883 | register char * slashp; |
michael@0 | 1884 | register int len; |
michael@0 | 1885 | |
michael@0 | 1886 | slashp = strchr(format, '/'); |
michael@0 | 1887 | if (slashp == NULL) { |
michael@0 | 1888 | if (letters == NULL) |
michael@0 | 1889 | (void) strcpy(abbr, format); |
michael@0 | 1890 | else (void) sprintf(abbr, format, letters); |
michael@0 | 1891 | } else if (isdst) { |
michael@0 | 1892 | (void) strcpy(abbr, slashp + 1); |
michael@0 | 1893 | } else { |
michael@0 | 1894 | if (slashp > format) |
michael@0 | 1895 | (void) strncpy(abbr, format, |
michael@0 | 1896 | (unsigned) (slashp - format)); |
michael@0 | 1897 | abbr[slashp - format] = '\0'; |
michael@0 | 1898 | } |
michael@0 | 1899 | if (!doquotes) |
michael@0 | 1900 | return; |
michael@0 | 1901 | for (cp = abbr; *cp != '\0'; ++cp) |
michael@0 | 1902 | if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL && |
michael@0 | 1903 | strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL) |
michael@0 | 1904 | break; |
michael@0 | 1905 | len = strlen(abbr); |
michael@0 | 1906 | if (len > 0 && *cp == '\0') |
michael@0 | 1907 | return; |
michael@0 | 1908 | abbr[len + 2] = '\0'; |
michael@0 | 1909 | abbr[len + 1] = '>'; |
michael@0 | 1910 | for ( ; len > 0; --len) |
michael@0 | 1911 | abbr[len] = abbr[len - 1]; |
michael@0 | 1912 | abbr[0] = '<'; |
michael@0 | 1913 | } |
michael@0 | 1914 | |
michael@0 | 1915 | static void |
michael@0 | 1916 | updateminmax(x) |
michael@0 | 1917 | const int x; |
michael@0 | 1918 | { |
michael@0 | 1919 | if (min_year > x) |
michael@0 | 1920 | min_year = x; |
michael@0 | 1921 | if (max_year < x) |
michael@0 | 1922 | max_year = x; |
michael@0 | 1923 | } |
michael@0 | 1924 | |
michael@0 | 1925 | static int |
michael@0 | 1926 | stringoffset(result, offset) |
michael@0 | 1927 | char * result; |
michael@0 | 1928 | long offset; |
michael@0 | 1929 | { |
michael@0 | 1930 | register int hours; |
michael@0 | 1931 | register int minutes; |
michael@0 | 1932 | register int seconds; |
michael@0 | 1933 | |
michael@0 | 1934 | result[0] = '\0'; |
michael@0 | 1935 | if (offset < 0) { |
michael@0 | 1936 | (void) strcpy(result, "-"); |
michael@0 | 1937 | offset = -offset; |
michael@0 | 1938 | } |
michael@0 | 1939 | seconds = offset % SECSPERMIN; |
michael@0 | 1940 | offset /= SECSPERMIN; |
michael@0 | 1941 | minutes = offset % MINSPERHOUR; |
michael@0 | 1942 | offset /= MINSPERHOUR; |
michael@0 | 1943 | hours = offset; |
michael@0 | 1944 | if (hours >= HOURSPERDAY) { |
michael@0 | 1945 | result[0] = '\0'; |
michael@0 | 1946 | return -1; |
michael@0 | 1947 | } |
michael@0 | 1948 | (void) sprintf(end(result), "%d", hours); |
michael@0 | 1949 | if (minutes != 0 || seconds != 0) { |
michael@0 | 1950 | (void) sprintf(end(result), ":%02d", minutes); |
michael@0 | 1951 | if (seconds != 0) |
michael@0 | 1952 | (void) sprintf(end(result), ":%02d", seconds); |
michael@0 | 1953 | } |
michael@0 | 1954 | return 0; |
michael@0 | 1955 | } |
michael@0 | 1956 | |
michael@0 | 1957 | static int |
michael@0 | 1958 | stringrule(result, rp, dstoff, gmtoff) |
michael@0 | 1959 | char * result; |
michael@0 | 1960 | const struct rule * const rp; |
michael@0 | 1961 | const long dstoff; |
michael@0 | 1962 | const long gmtoff; |
michael@0 | 1963 | { |
michael@0 | 1964 | register long tod; |
michael@0 | 1965 | |
michael@0 | 1966 | result = end(result); |
michael@0 | 1967 | if (rp->r_dycode == DC_DOM) { |
michael@0 | 1968 | register int month, total; |
michael@0 | 1969 | |
michael@0 | 1970 | if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY) |
michael@0 | 1971 | return -1; |
michael@0 | 1972 | total = 0; |
michael@0 | 1973 | for (month = 0; month < rp->r_month; ++month) |
michael@0 | 1974 | total += len_months[0][month]; |
michael@0 | 1975 | (void) sprintf(result, "J%d", total + rp->r_dayofmonth); |
michael@0 | 1976 | } else { |
michael@0 | 1977 | register int week; |
michael@0 | 1978 | |
michael@0 | 1979 | if (rp->r_dycode == DC_DOWGEQ) { |
michael@0 | 1980 | week = 1 + rp->r_dayofmonth / DAYSPERWEEK; |
michael@0 | 1981 | if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth) |
michael@0 | 1982 | return -1; |
michael@0 | 1983 | } else if (rp->r_dycode == DC_DOWLEQ) { |
michael@0 | 1984 | if (rp->r_dayofmonth == len_months[1][rp->r_month]) |
michael@0 | 1985 | week = 5; |
michael@0 | 1986 | else { |
michael@0 | 1987 | week = 1 + rp->r_dayofmonth / DAYSPERWEEK; |
michael@0 | 1988 | if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth) |
michael@0 | 1989 | return -1; |
michael@0 | 1990 | } |
michael@0 | 1991 | } else return -1; /* "cannot happen" */ |
michael@0 | 1992 | (void) sprintf(result, "M%d.%d.%d", |
michael@0 | 1993 | rp->r_month + 1, week, rp->r_wday); |
michael@0 | 1994 | } |
michael@0 | 1995 | tod = rp->r_tod; |
michael@0 | 1996 | if (rp->r_todisgmt) |
michael@0 | 1997 | tod += gmtoff; |
michael@0 | 1998 | if (rp->r_todisstd && rp->r_stdoff == 0) |
michael@0 | 1999 | tod += dstoff; |
michael@0 | 2000 | if (tod < 0) { |
michael@0 | 2001 | result[0] = '\0'; |
michael@0 | 2002 | return -1; |
michael@0 | 2003 | } |
michael@0 | 2004 | if (tod != 2 * SECSPERMIN * MINSPERHOUR) { |
michael@0 | 2005 | (void) strcat(result, "/"); |
michael@0 | 2006 | if (stringoffset(end(result), tod) != 0) |
michael@0 | 2007 | return -1; |
michael@0 | 2008 | } |
michael@0 | 2009 | return 0; |
michael@0 | 2010 | } |
michael@0 | 2011 | |
michael@0 | 2012 | static void |
michael@0 | 2013 | stringzone(result, zpfirst, zonecount) |
michael@0 | 2014 | char * result; |
michael@0 | 2015 | const struct zone * const zpfirst; |
michael@0 | 2016 | const int zonecount; |
michael@0 | 2017 | { |
michael@0 | 2018 | register const struct zone * zp; |
michael@0 | 2019 | register struct rule * rp; |
michael@0 | 2020 | register struct rule * stdrp; |
michael@0 | 2021 | register struct rule * dstrp; |
michael@0 | 2022 | register int i; |
michael@0 | 2023 | register const char * abbrvar; |
michael@0 | 2024 | |
michael@0 | 2025 | result[0] = '\0'; |
michael@0 | 2026 | zp = zpfirst + zonecount - 1; |
michael@0 | 2027 | stdrp = dstrp = NULL; |
michael@0 | 2028 | for (i = 0; i < zp->z_nrules; ++i) { |
michael@0 | 2029 | rp = &zp->z_rules[i]; |
michael@0 | 2030 | if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX) |
michael@0 | 2031 | continue; |
michael@0 | 2032 | if (rp->r_yrtype != NULL) |
michael@0 | 2033 | continue; |
michael@0 | 2034 | if (rp->r_stdoff == 0) { |
michael@0 | 2035 | if (stdrp == NULL) |
michael@0 | 2036 | stdrp = rp; |
michael@0 | 2037 | else return; |
michael@0 | 2038 | } else { |
michael@0 | 2039 | if (dstrp == NULL) |
michael@0 | 2040 | dstrp = rp; |
michael@0 | 2041 | else return; |
michael@0 | 2042 | } |
michael@0 | 2043 | } |
michael@0 | 2044 | if (stdrp == NULL && dstrp == NULL) { |
michael@0 | 2045 | /* |
michael@0 | 2046 | ** There are no rules running through "max". |
michael@0 | 2047 | ** Let's find the latest rule. |
michael@0 | 2048 | */ |
michael@0 | 2049 | for (i = 0; i < zp->z_nrules; ++i) { |
michael@0 | 2050 | rp = &zp->z_rules[i]; |
michael@0 | 2051 | if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear || |
michael@0 | 2052 | (rp->r_hiyear == stdrp->r_hiyear && |
michael@0 | 2053 | rp->r_month > stdrp->r_month)) |
michael@0 | 2054 | stdrp = rp; |
michael@0 | 2055 | } |
michael@0 | 2056 | if (stdrp != NULL && stdrp->r_stdoff != 0) |
michael@0 | 2057 | return; /* We end up in DST (a POSIX no-no). */ |
michael@0 | 2058 | /* |
michael@0 | 2059 | ** Horrid special case: if year is 2037, |
michael@0 | 2060 | ** presume this is a zone handled on a year-by-year basis; |
michael@0 | 2061 | ** do not try to apply a rule to the zone. |
michael@0 | 2062 | */ |
michael@0 | 2063 | if (stdrp != NULL && stdrp->r_hiyear == 2037) |
michael@0 | 2064 | return; |
michael@0 | 2065 | } |
michael@0 | 2066 | if (stdrp == NULL && zp->z_nrules != 0) |
michael@0 | 2067 | return; |
michael@0 | 2068 | abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; |
michael@0 | 2069 | doabbr(result, zp->z_format, abbrvar, FALSE, TRUE); |
michael@0 | 2070 | if (stringoffset(end(result), -zp->z_gmtoff) != 0) { |
michael@0 | 2071 | result[0] = '\0'; |
michael@0 | 2072 | return; |
michael@0 | 2073 | } |
michael@0 | 2074 | if (dstrp == NULL) |
michael@0 | 2075 | return; |
michael@0 | 2076 | doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE); |
michael@0 | 2077 | if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) |
michael@0 | 2078 | if (stringoffset(end(result), |
michael@0 | 2079 | -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) { |
michael@0 | 2080 | result[0] = '\0'; |
michael@0 | 2081 | return; |
michael@0 | 2082 | } |
michael@0 | 2083 | (void) strcat(result, ","); |
michael@0 | 2084 | if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) { |
michael@0 | 2085 | result[0] = '\0'; |
michael@0 | 2086 | return; |
michael@0 | 2087 | } |
michael@0 | 2088 | (void) strcat(result, ","); |
michael@0 | 2089 | if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) { |
michael@0 | 2090 | result[0] = '\0'; |
michael@0 | 2091 | return; |
michael@0 | 2092 | } |
michael@0 | 2093 | } |
michael@0 | 2094 | |
michael@0 | 2095 | static void |
michael@0 | 2096 | outzone(zpfirst, zonecount) |
michael@0 | 2097 | const struct zone * const zpfirst; |
michael@0 | 2098 | const int zonecount; |
michael@0 | 2099 | { |
michael@0 | 2100 | register const struct zone * zp; |
michael@0 | 2101 | register struct rule * rp; |
michael@0 | 2102 | register int i, j; |
michael@0 | 2103 | register int usestart, useuntil; |
michael@0 | 2104 | register zic_t starttime, untiltime; |
michael@0 | 2105 | register long gmtoff; |
michael@0 | 2106 | register long stdoff; |
michael@0 | 2107 | register int year; |
michael@0 | 2108 | register long startoff; |
michael@0 | 2109 | register int startttisstd; |
michael@0 | 2110 | register int startttisgmt; |
michael@0 | 2111 | register int type; |
michael@0 | 2112 | register char * startbuf; |
michael@0 | 2113 | register char * ab; |
michael@0 | 2114 | register char * envvar; |
michael@0 | 2115 | register int max_abbr_len; |
michael@0 | 2116 | register int max_envvar_len; |
michael@0 | 2117 | #ifdef ICU |
michael@0 | 2118 | int finalRuleYear, finalRuleIndex; |
michael@0 | 2119 | const struct rule* finalRule1; |
michael@0 | 2120 | const struct rule* finalRule2; |
michael@0 | 2121 | #endif |
michael@0 | 2122 | |
michael@0 | 2123 | max_abbr_len = 2 + max_format_len + max_abbrvar_len; |
michael@0 | 2124 | max_envvar_len = 2 * max_abbr_len + 5 * 9; |
michael@0 | 2125 | startbuf = emalloc(max_abbr_len + 1); |
michael@0 | 2126 | ab = emalloc(max_abbr_len + 1); |
michael@0 | 2127 | envvar = emalloc(max_envvar_len + 1); |
michael@0 | 2128 | INITIALIZE(untiltime); |
michael@0 | 2129 | INITIALIZE(starttime); |
michael@0 | 2130 | /* |
michael@0 | 2131 | ** Now. . .finally. . .generate some useful data! |
michael@0 | 2132 | */ |
michael@0 | 2133 | timecnt = 0; |
michael@0 | 2134 | typecnt = 0; |
michael@0 | 2135 | charcnt = 0; |
michael@0 | 2136 | /* |
michael@0 | 2137 | ** Thanks to Earl Chew |
michael@0 | 2138 | ** for noting the need to unconditionally initialize startttisstd. |
michael@0 | 2139 | */ |
michael@0 | 2140 | startttisstd = FALSE; |
michael@0 | 2141 | startttisgmt = FALSE; |
michael@0 | 2142 | min_year = max_year = EPOCH_YEAR; |
michael@0 | 2143 | if (leapseen) { |
michael@0 | 2144 | updateminmax(leapminyear); |
michael@0 | 2145 | updateminmax(leapmaxyear); |
michael@0 | 2146 | } |
michael@0 | 2147 | for (i = 0; i < zonecount; ++i) { |
michael@0 | 2148 | zp = &zpfirst[i]; |
michael@0 | 2149 | if (i < zonecount - 1) |
michael@0 | 2150 | updateminmax(zp->z_untilrule.r_loyear); |
michael@0 | 2151 | for (j = 0; j < zp->z_nrules; ++j) { |
michael@0 | 2152 | rp = &zp->z_rules[j]; |
michael@0 | 2153 | if (rp->r_lowasnum) |
michael@0 | 2154 | updateminmax(rp->r_loyear); |
michael@0 | 2155 | if (rp->r_hiwasnum) |
michael@0 | 2156 | updateminmax(rp->r_hiyear); |
michael@0 | 2157 | } |
michael@0 | 2158 | } |
michael@0 | 2159 | /* |
michael@0 | 2160 | ** Generate lots of data if a rule can't cover all future times. |
michael@0 | 2161 | */ |
michael@0 | 2162 | stringzone(envvar, zpfirst, zonecount); |
michael@0 | 2163 | if (noise && envvar[0] == '\0') { |
michael@0 | 2164 | register char * wp; |
michael@0 | 2165 | |
michael@0 | 2166 | wp = ecpyalloc(_("no POSIX environment variable for zone")); |
michael@0 | 2167 | wp = ecatalloc(wp, " "); |
michael@0 | 2168 | wp = ecatalloc(wp, zpfirst->z_name); |
michael@0 | 2169 | warning(wp); |
michael@0 | 2170 | ifree(wp); |
michael@0 | 2171 | } |
michael@0 | 2172 | if (envvar[0] == '\0') { |
michael@0 | 2173 | if (min_year >= INT_MIN + YEARSPERREPEAT) |
michael@0 | 2174 | min_year -= YEARSPERREPEAT; |
michael@0 | 2175 | else min_year = INT_MIN; |
michael@0 | 2176 | if (max_year <= INT_MAX - YEARSPERREPEAT) |
michael@0 | 2177 | max_year += YEARSPERREPEAT; |
michael@0 | 2178 | else max_year = INT_MAX; |
michael@0 | 2179 | } |
michael@0 | 2180 | /* |
michael@0 | 2181 | ** For the benefit of older systems, |
michael@0 | 2182 | ** generate data from 1900 through 2037. |
michael@0 | 2183 | */ |
michael@0 | 2184 | if (min_year > 1900) |
michael@0 | 2185 | min_year = 1900; |
michael@0 | 2186 | if (max_year < 2037) |
michael@0 | 2187 | max_year = 2037; |
michael@0 | 2188 | for (i = 0; i < zonecount; ++i) { |
michael@0 | 2189 | /* |
michael@0 | 2190 | ** A guess that may well be corrected later. |
michael@0 | 2191 | */ |
michael@0 | 2192 | stdoff = 0; |
michael@0 | 2193 | zp = &zpfirst[i]; |
michael@0 | 2194 | usestart = i > 0 && (zp - 1)->z_untiltime > min_time; |
michael@0 | 2195 | useuntil = i < (zonecount - 1); |
michael@0 | 2196 | if (useuntil && zp->z_untiltime <= min_time) |
michael@0 | 2197 | continue; |
michael@0 | 2198 | gmtoff = zp->z_gmtoff; |
michael@0 | 2199 | eat(zp->z_filename, zp->z_linenum); |
michael@0 | 2200 | *startbuf = '\0'; |
michael@0 | 2201 | startoff = zp->z_gmtoff; |
michael@0 | 2202 | #ifdef ICU |
michael@0 | 2203 | finalRuleYear = finalRuleIndex = -1; |
michael@0 | 2204 | finalRule1 = finalRule2 = NULL; |
michael@0 | 2205 | if (i == (zonecount - 1)) { /* !useuntil */ |
michael@0 | 2206 | /* Look for exactly 2 rules that end at 'max' and |
michael@0 | 2207 | * note them. Determine max(r_loyear) for the 2 of |
michael@0 | 2208 | * them. */ |
michael@0 | 2209 | for (j=0; j<zp->z_nrules; ++j) { |
michael@0 | 2210 | rp = &zp->z_rules[j]; |
michael@0 | 2211 | if (rp->r_hiyear == INT_MAX) { |
michael@0 | 2212 | if (rp->r_loyear > finalRuleYear) { |
michael@0 | 2213 | finalRuleYear = rp->r_loyear; |
michael@0 | 2214 | } |
michael@0 | 2215 | if (finalRule1 == NULL) { |
michael@0 | 2216 | finalRule1 = rp; |
michael@0 | 2217 | } else if (finalRule2 == NULL) { |
michael@0 | 2218 | finalRule2 = rp; |
michael@0 | 2219 | } else { |
michael@0 | 2220 | error("more than two max rules found (ICU)"); |
michael@0 | 2221 | exit(EXIT_FAILURE); |
michael@0 | 2222 | } |
michael@0 | 2223 | } else if (rp->r_hiyear >= finalRuleYear) { |
michael@0 | 2224 | /* There might be an overriding non-max rule |
michael@0 | 2225 | * to be applied to a specific year after one of |
michael@0 | 2226 | * max rule's start year. For example, |
michael@0 | 2227 | * |
michael@0 | 2228 | * Rule Foo 2010 max ... |
michael@0 | 2229 | * Rule Foo 2015 only ... |
michael@0 | 2230 | * |
michael@0 | 2231 | * In this case, we need to change the start year of |
michael@0 | 2232 | * the final (max) rules to the next year. */ |
michael@0 | 2233 | finalRuleYear = rp->r_hiyear + 1; |
michael@0 | 2234 | |
michael@0 | 2235 | /* When above adjustment is done, max_year might need |
michael@0 | 2236 | * to be adjusted, so the final rule will be properly |
michael@0 | 2237 | * evaluated and emitted by the later code block. |
michael@0 | 2238 | * |
michael@0 | 2239 | * Note: This may push the start year of the final |
michael@0 | 2240 | * rules ahead by 1 year unnecessarily. For example, |
michael@0 | 2241 | * If there are two rules, non-max rule and max rule |
michael@0 | 2242 | * starting in the same year, such as |
michael@0 | 2243 | * |
michael@0 | 2244 | * Rule Foo 2010 only .... |
michael@0 | 2245 | * Rule Foo 2010 max .... |
michael@0 | 2246 | * |
michael@0 | 2247 | * In this case, the final (max) rule actually starts |
michael@0 | 2248 | * in 2010, instead of 2010. We could make this tool |
michael@0 | 2249 | * more intelligent to detect such situation. But pushing |
michael@0 | 2250 | * final rule start year to 1 year ahead (in the worst case) |
michael@0 | 2251 | * will just populate a few extra transitions, and it still |
michael@0 | 2252 | * works fine. So for now, we're not trying to put additional |
michael@0 | 2253 | * logic to optimize the case. |
michael@0 | 2254 | */ |
michael@0 | 2255 | if (max_year < finalRuleYear) { |
michael@0 | 2256 | max_year = finalRuleYear; |
michael@0 | 2257 | } |
michael@0 | 2258 | } |
michael@0 | 2259 | } |
michael@0 | 2260 | if (finalRule1 != NULL) { |
michael@0 | 2261 | if (finalRule2 == NULL) { |
michael@0 | 2262 | warning("only one max rule found (ICU)"); |
michael@0 | 2263 | finalRuleYear = finalRuleIndex = -1; |
michael@0 | 2264 | finalRule1 = NULL; |
michael@0 | 2265 | } else { |
michael@0 | 2266 | if (finalRule1->r_stdoff == finalRule2->r_stdoff) { |
michael@0 | 2267 | /* America/Resolute in 2009a uses a pair of rules |
michael@0 | 2268 | * which does not change the offset. ICU ignores |
michael@0 | 2269 | * such rules without actual time transitions. */ |
michael@0 | 2270 | finalRuleYear = finalRuleIndex = -1; |
michael@0 | 2271 | finalRule1 = finalRule2 = NULL; |
michael@0 | 2272 | } else { |
michael@0 | 2273 | /* Swap if necessary so finalRule1 occurs before |
michael@0 | 2274 | * finalRule2 */ |
michael@0 | 2275 | if (finalRule1->r_month > finalRule2->r_month) { |
michael@0 | 2276 | const struct rule* t = finalRule1; |
michael@0 | 2277 | finalRule1 = finalRule2; |
michael@0 | 2278 | finalRule2 = t; |
michael@0 | 2279 | } |
michael@0 | 2280 | /* Add final rule to our list */ |
michael@0 | 2281 | finalRuleIndex = add_icu_final_rules(finalRule1, finalRule2); |
michael@0 | 2282 | } |
michael@0 | 2283 | } |
michael@0 | 2284 | } |
michael@0 | 2285 | } |
michael@0 | 2286 | #endif |
michael@0 | 2287 | |
michael@0 | 2288 | if (zp->z_nrules == 0) { |
michael@0 | 2289 | stdoff = zp->z_stdoff; |
michael@0 | 2290 | doabbr(startbuf, zp->z_format, |
michael@0 | 2291 | (char *) NULL, stdoff != 0, FALSE); |
michael@0 | 2292 | type = addtype(oadd(zp->z_gmtoff, stdoff), |
michael@0 | 2293 | #ifdef ICU |
michael@0 | 2294 | zp->z_gmtoff, stdoff, |
michael@0 | 2295 | #endif |
michael@0 | 2296 | startbuf, stdoff != 0, startttisstd, |
michael@0 | 2297 | startttisgmt); |
michael@0 | 2298 | if (usestart) { |
michael@0 | 2299 | addtt(starttime, type); |
michael@0 | 2300 | usestart = FALSE; |
michael@0 | 2301 | } else if (stdoff != 0) |
michael@0 | 2302 | addtt(min_time, type); |
michael@0 | 2303 | } else for (year = min_year; year <= max_year; ++year) { |
michael@0 | 2304 | if (useuntil && year > zp->z_untilrule.r_hiyear) |
michael@0 | 2305 | break; |
michael@0 | 2306 | /* |
michael@0 | 2307 | ** Mark which rules to do in the current year. |
michael@0 | 2308 | ** For those to do, calculate rpytime(rp, year); |
michael@0 | 2309 | */ |
michael@0 | 2310 | for (j = 0; j < zp->z_nrules; ++j) { |
michael@0 | 2311 | rp = &zp->z_rules[j]; |
michael@0 | 2312 | eats(zp->z_filename, zp->z_linenum, |
michael@0 | 2313 | rp->r_filename, rp->r_linenum); |
michael@0 | 2314 | rp->r_todo = year >= rp->r_loyear && |
michael@0 | 2315 | year <= rp->r_hiyear && |
michael@0 | 2316 | yearistype(year, rp->r_yrtype); |
michael@0 | 2317 | if (rp->r_todo) |
michael@0 | 2318 | rp->r_temp = rpytime(rp, year); |
michael@0 | 2319 | } |
michael@0 | 2320 | for ( ; ; ) { |
michael@0 | 2321 | register int k; |
michael@0 | 2322 | register zic_t jtime, ktime; |
michael@0 | 2323 | register long offset; |
michael@0 | 2324 | |
michael@0 | 2325 | INITIALIZE(ktime); |
michael@0 | 2326 | if (useuntil) { |
michael@0 | 2327 | /* |
michael@0 | 2328 | ** Turn untiltime into UTC |
michael@0 | 2329 | ** assuming the current gmtoff and |
michael@0 | 2330 | ** stdoff values. |
michael@0 | 2331 | */ |
michael@0 | 2332 | untiltime = zp->z_untiltime; |
michael@0 | 2333 | if (!zp->z_untilrule.r_todisgmt) |
michael@0 | 2334 | untiltime = tadd(untiltime, |
michael@0 | 2335 | -gmtoff); |
michael@0 | 2336 | if (!zp->z_untilrule.r_todisstd) |
michael@0 | 2337 | untiltime = tadd(untiltime, |
michael@0 | 2338 | -stdoff); |
michael@0 | 2339 | } |
michael@0 | 2340 | /* |
michael@0 | 2341 | ** Find the rule (of those to do, if any) |
michael@0 | 2342 | ** that takes effect earliest in the year. |
michael@0 | 2343 | */ |
michael@0 | 2344 | k = -1; |
michael@0 | 2345 | for (j = 0; j < zp->z_nrules; ++j) { |
michael@0 | 2346 | rp = &zp->z_rules[j]; |
michael@0 | 2347 | if (!rp->r_todo) |
michael@0 | 2348 | continue; |
michael@0 | 2349 | eats(zp->z_filename, zp->z_linenum, |
michael@0 | 2350 | rp->r_filename, rp->r_linenum); |
michael@0 | 2351 | offset = rp->r_todisgmt ? 0 : gmtoff; |
michael@0 | 2352 | if (!rp->r_todisstd) |
michael@0 | 2353 | offset = oadd(offset, stdoff); |
michael@0 | 2354 | jtime = rp->r_temp; |
michael@0 | 2355 | if (jtime == min_time || |
michael@0 | 2356 | jtime == max_time) |
michael@0 | 2357 | continue; |
michael@0 | 2358 | jtime = tadd(jtime, -offset); |
michael@0 | 2359 | if (k < 0 || jtime < ktime) { |
michael@0 | 2360 | k = j; |
michael@0 | 2361 | ktime = jtime; |
michael@0 | 2362 | } |
michael@0 | 2363 | } |
michael@0 | 2364 | if (k < 0) |
michael@0 | 2365 | break; /* go on to next year */ |
michael@0 | 2366 | rp = &zp->z_rules[k]; |
michael@0 | 2367 | rp->r_todo = FALSE; |
michael@0 | 2368 | if (useuntil && ktime >= untiltime) |
michael@0 | 2369 | break; |
michael@0 | 2370 | stdoff = rp->r_stdoff; |
michael@0 | 2371 | if (usestart && ktime == starttime) |
michael@0 | 2372 | usestart = FALSE; |
michael@0 | 2373 | if (usestart) { |
michael@0 | 2374 | if (ktime < starttime) { |
michael@0 | 2375 | startoff = oadd(zp->z_gmtoff, |
michael@0 | 2376 | stdoff); |
michael@0 | 2377 | doabbr(startbuf, zp->z_format, |
michael@0 | 2378 | rp->r_abbrvar, |
michael@0 | 2379 | rp->r_stdoff != 0, |
michael@0 | 2380 | FALSE); |
michael@0 | 2381 | continue; |
michael@0 | 2382 | } |
michael@0 | 2383 | if (*startbuf == '\0' && |
michael@0 | 2384 | startoff == oadd(zp->z_gmtoff, |
michael@0 | 2385 | stdoff)) { |
michael@0 | 2386 | doabbr(startbuf, |
michael@0 | 2387 | zp->z_format, |
michael@0 | 2388 | rp->r_abbrvar, |
michael@0 | 2389 | rp->r_stdoff != |
michael@0 | 2390 | 0, |
michael@0 | 2391 | FALSE); |
michael@0 | 2392 | } |
michael@0 | 2393 | } |
michael@0 | 2394 | #ifdef ICU |
michael@0 | 2395 | if (year >= finalRuleYear && rp == finalRule1) { |
michael@0 | 2396 | /* We want to shift final year 1 year after |
michael@0 | 2397 | * the actual final rule takes effect (year + 1), |
michael@0 | 2398 | * because the previous type is valid until the first |
michael@0 | 2399 | * transition defined by the final rule. Otherwise |
michael@0 | 2400 | * we may see unexpected offset shift at the |
michael@0 | 2401 | * begining of the year when the final rule takes |
michael@0 | 2402 | * effect. |
michael@0 | 2403 | * |
michael@0 | 2404 | * Note: This may results some 64bit second transitions |
michael@0 | 2405 | * at the very end (year 2038). ICU 4.2 or older releases |
michael@0 | 2406 | * cannot handle 64bit second transitions and they are |
michael@0 | 2407 | * dropped from zoneinfo.txt. */ |
michael@0 | 2408 | emit_icu_zone(icuFile, |
michael@0 | 2409 | zpfirst->z_name, zp->z_gmtoff, |
michael@0 | 2410 | rp, finalRuleIndex, year + 1); |
michael@0 | 2411 | /* only emit this for the first year */ |
michael@0 | 2412 | finalRule1 = NULL; |
michael@0 | 2413 | } |
michael@0 | 2414 | #endif |
michael@0 | 2415 | eats(zp->z_filename, zp->z_linenum, |
michael@0 | 2416 | rp->r_filename, rp->r_linenum); |
michael@0 | 2417 | doabbr(ab, zp->z_format, rp->r_abbrvar, |
michael@0 | 2418 | rp->r_stdoff != 0, FALSE); |
michael@0 | 2419 | offset = oadd(zp->z_gmtoff, rp->r_stdoff); |
michael@0 | 2420 | #ifdef ICU |
michael@0 | 2421 | type = addtype(offset, zp->z_gmtoff, rp->r_stdoff, |
michael@0 | 2422 | ab, rp->r_stdoff != 0, |
michael@0 | 2423 | rp->r_todisstd, rp->r_todisgmt); |
michael@0 | 2424 | #else |
michael@0 | 2425 | type = addtype(offset, ab, rp->r_stdoff != 0, |
michael@0 | 2426 | rp->r_todisstd, rp->r_todisgmt); |
michael@0 | 2427 | #endif |
michael@0 | 2428 | addtt(ktime, type); |
michael@0 | 2429 | } |
michael@0 | 2430 | } |
michael@0 | 2431 | if (usestart) { |
michael@0 | 2432 | if (*startbuf == '\0' && |
michael@0 | 2433 | zp->z_format != NULL && |
michael@0 | 2434 | strchr(zp->z_format, '%') == NULL && |
michael@0 | 2435 | strchr(zp->z_format, '/') == NULL) |
michael@0 | 2436 | (void) strcpy(startbuf, zp->z_format); |
michael@0 | 2437 | eat(zp->z_filename, zp->z_linenum); |
michael@0 | 2438 | if (*startbuf == '\0') |
michael@0 | 2439 | error(_("can't determine time zone abbreviation to use just after until time")); |
michael@0 | 2440 | else addtt(starttime, |
michael@0 | 2441 | #ifdef ICU |
michael@0 | 2442 | addtype(startoff, |
michael@0 | 2443 | zp->z_gmtoff, startoff - zp->z_gmtoff, |
michael@0 | 2444 | startbuf, |
michael@0 | 2445 | startoff != zp->z_gmtoff, |
michael@0 | 2446 | startttisstd, |
michael@0 | 2447 | startttisgmt)); |
michael@0 | 2448 | #else |
michael@0 | 2449 | addtype(startoff, startbuf, |
michael@0 | 2450 | startoff != zp->z_gmtoff, |
michael@0 | 2451 | startttisstd, |
michael@0 | 2452 | startttisgmt)); |
michael@0 | 2453 | #endif |
michael@0 | 2454 | } |
michael@0 | 2455 | /* |
michael@0 | 2456 | ** Now we may get to set starttime for the next zone line. |
michael@0 | 2457 | */ |
michael@0 | 2458 | if (useuntil) { |
michael@0 | 2459 | startttisstd = zp->z_untilrule.r_todisstd; |
michael@0 | 2460 | startttisgmt = zp->z_untilrule.r_todisgmt; |
michael@0 | 2461 | starttime = zp->z_untiltime; |
michael@0 | 2462 | if (!startttisstd) |
michael@0 | 2463 | starttime = tadd(starttime, -stdoff); |
michael@0 | 2464 | if (!startttisgmt) |
michael@0 | 2465 | starttime = tadd(starttime, -gmtoff); |
michael@0 | 2466 | } |
michael@0 | 2467 | } |
michael@0 | 2468 | writezone(zpfirst->z_name, envvar); |
michael@0 | 2469 | ifree(startbuf); |
michael@0 | 2470 | ifree(ab); |
michael@0 | 2471 | ifree(envvar); |
michael@0 | 2472 | } |
michael@0 | 2473 | |
michael@0 | 2474 | static void |
michael@0 | 2475 | addtt(starttime, type) |
michael@0 | 2476 | const zic_t starttime; |
michael@0 | 2477 | int type; |
michael@0 | 2478 | { |
michael@0 | 2479 | if (starttime <= min_time || |
michael@0 | 2480 | (timecnt == 1 && attypes[0].at < min_time)) { |
michael@0 | 2481 | gmtoffs[0] = gmtoffs[type]; |
michael@0 | 2482 | #ifdef ICU |
michael@0 | 2483 | rawoffs[0] = rawoffs[type]; |
michael@0 | 2484 | dstoffs[0] = dstoffs[type]; |
michael@0 | 2485 | #endif |
michael@0 | 2486 | isdsts[0] = isdsts[type]; |
michael@0 | 2487 | ttisstds[0] = ttisstds[type]; |
michael@0 | 2488 | ttisgmts[0] = ttisgmts[type]; |
michael@0 | 2489 | if (abbrinds[type] != 0) |
michael@0 | 2490 | (void) strcpy(chars, &chars[abbrinds[type]]); |
michael@0 | 2491 | abbrinds[0] = 0; |
michael@0 | 2492 | charcnt = strlen(chars) + 1; |
michael@0 | 2493 | typecnt = 1; |
michael@0 | 2494 | timecnt = 0; |
michael@0 | 2495 | type = 0; |
michael@0 | 2496 | } |
michael@0 | 2497 | if (timecnt >= TZ_MAX_TIMES) { |
michael@0 | 2498 | error(_("too many transitions?!")); |
michael@0 | 2499 | exit(EXIT_FAILURE); |
michael@0 | 2500 | } |
michael@0 | 2501 | attypes[timecnt].at = starttime; |
michael@0 | 2502 | attypes[timecnt].type = type; |
michael@0 | 2503 | ++timecnt; |
michael@0 | 2504 | } |
michael@0 | 2505 | |
michael@0 | 2506 | static int |
michael@0 | 2507 | #ifdef ICU |
michael@0 | 2508 | addtype(gmtoff, rawoff, dstoff, abbr, isdst, ttisstd, ttisgmt) |
michael@0 | 2509 | const long gmtoff; |
michael@0 | 2510 | const long rawoff; |
michael@0 | 2511 | const long dstoff; |
michael@0 | 2512 | #else |
michael@0 | 2513 | addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt) |
michael@0 | 2514 | const long gmtoff; |
michael@0 | 2515 | #endif |
michael@0 | 2516 | const char * const abbr; |
michael@0 | 2517 | const int isdst; |
michael@0 | 2518 | const int ttisstd; |
michael@0 | 2519 | const int ttisgmt; |
michael@0 | 2520 | { |
michael@0 | 2521 | register int i, j; |
michael@0 | 2522 | |
michael@0 | 2523 | if (isdst != TRUE && isdst != FALSE) { |
michael@0 | 2524 | error(_("internal error - addtype called with bad isdst")); |
michael@0 | 2525 | exit(EXIT_FAILURE); |
michael@0 | 2526 | } |
michael@0 | 2527 | if (ttisstd != TRUE && ttisstd != FALSE) { |
michael@0 | 2528 | error(_("internal error - addtype called with bad ttisstd")); |
michael@0 | 2529 | exit(EXIT_FAILURE); |
michael@0 | 2530 | } |
michael@0 | 2531 | if (ttisgmt != TRUE && ttisgmt != FALSE) { |
michael@0 | 2532 | error(_("internal error - addtype called with bad ttisgmt")); |
michael@0 | 2533 | exit(EXIT_FAILURE); |
michael@0 | 2534 | } |
michael@0 | 2535 | #ifdef ICU |
michael@0 | 2536 | if (isdst != (dstoff != 0)) { |
michael@0 | 2537 | error(_("internal error - addtype called with bad isdst/dstoff")); |
michael@0 | 2538 | (void) exit(EXIT_FAILURE); |
michael@0 | 2539 | } |
michael@0 | 2540 | if (gmtoff != (rawoff + dstoff)) { |
michael@0 | 2541 | error(_("internal error - addtype called with bad gmt/raw/dstoff")); |
michael@0 | 2542 | (void) exit(EXIT_FAILURE); |
michael@0 | 2543 | } |
michael@0 | 2544 | #endif |
michael@0 | 2545 | /* |
michael@0 | 2546 | ** See if there's already an entry for this zone type. |
michael@0 | 2547 | ** If so, just return its index. |
michael@0 | 2548 | */ |
michael@0 | 2549 | for (i = 0; i < typecnt; ++i) { |
michael@0 | 2550 | if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && |
michael@0 | 2551 | #ifdef ICU |
michael@0 | 2552 | rawoff == rawoffs[i] && dstoff == dstoffs[i] && |
michael@0 | 2553 | #endif |
michael@0 | 2554 | strcmp(abbr, &chars[abbrinds[i]]) == 0 && |
michael@0 | 2555 | ttisstd == ttisstds[i] && |
michael@0 | 2556 | ttisgmt == ttisgmts[i]) |
michael@0 | 2557 | return i; |
michael@0 | 2558 | } |
michael@0 | 2559 | /* |
michael@0 | 2560 | ** There isn't one; add a new one, unless there are already too |
michael@0 | 2561 | ** many. |
michael@0 | 2562 | */ |
michael@0 | 2563 | if (typecnt >= TZ_MAX_TYPES) { |
michael@0 | 2564 | error(_("too many local time types")); |
michael@0 | 2565 | exit(EXIT_FAILURE); |
michael@0 | 2566 | } |
michael@0 | 2567 | if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { |
michael@0 | 2568 | error(_("UTC offset out of range")); |
michael@0 | 2569 | exit(EXIT_FAILURE); |
michael@0 | 2570 | } |
michael@0 | 2571 | gmtoffs[i] = gmtoff; |
michael@0 | 2572 | #ifdef ICU |
michael@0 | 2573 | rawoffs[i] = rawoff; |
michael@0 | 2574 | dstoffs[i] = dstoff; |
michael@0 | 2575 | #endif |
michael@0 | 2576 | isdsts[i] = isdst; |
michael@0 | 2577 | ttisstds[i] = ttisstd; |
michael@0 | 2578 | ttisgmts[i] = ttisgmt; |
michael@0 | 2579 | |
michael@0 | 2580 | for (j = 0; j < charcnt; ++j) |
michael@0 | 2581 | if (strcmp(&chars[j], abbr) == 0) |
michael@0 | 2582 | break; |
michael@0 | 2583 | if (j == charcnt) |
michael@0 | 2584 | newabbr(abbr); |
michael@0 | 2585 | abbrinds[i] = j; |
michael@0 | 2586 | ++typecnt; |
michael@0 | 2587 | return i; |
michael@0 | 2588 | } |
michael@0 | 2589 | |
michael@0 | 2590 | static void |
michael@0 | 2591 | leapadd(t, positive, rolling, count) |
michael@0 | 2592 | const zic_t t; |
michael@0 | 2593 | const int positive; |
michael@0 | 2594 | const int rolling; |
michael@0 | 2595 | int count; |
michael@0 | 2596 | { |
michael@0 | 2597 | register int i, j; |
michael@0 | 2598 | |
michael@0 | 2599 | if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { |
michael@0 | 2600 | error(_("too many leap seconds")); |
michael@0 | 2601 | exit(EXIT_FAILURE); |
michael@0 | 2602 | } |
michael@0 | 2603 | for (i = 0; i < leapcnt; ++i) |
michael@0 | 2604 | if (t <= trans[i]) { |
michael@0 | 2605 | if (t == trans[i]) { |
michael@0 | 2606 | error(_("repeated leap second moment")); |
michael@0 | 2607 | exit(EXIT_FAILURE); |
michael@0 | 2608 | } |
michael@0 | 2609 | break; |
michael@0 | 2610 | } |
michael@0 | 2611 | do { |
michael@0 | 2612 | for (j = leapcnt; j > i; --j) { |
michael@0 | 2613 | trans[j] = trans[j - 1]; |
michael@0 | 2614 | corr[j] = corr[j - 1]; |
michael@0 | 2615 | roll[j] = roll[j - 1]; |
michael@0 | 2616 | } |
michael@0 | 2617 | trans[i] = t; |
michael@0 | 2618 | corr[i] = positive ? 1L : eitol(-count); |
michael@0 | 2619 | roll[i] = rolling; |
michael@0 | 2620 | ++leapcnt; |
michael@0 | 2621 | } while (positive && --count != 0); |
michael@0 | 2622 | } |
michael@0 | 2623 | |
michael@0 | 2624 | static void |
michael@0 | 2625 | adjleap(void) |
michael@0 | 2626 | { |
michael@0 | 2627 | register int i; |
michael@0 | 2628 | register long last = 0; |
michael@0 | 2629 | |
michael@0 | 2630 | /* |
michael@0 | 2631 | ** propagate leap seconds forward |
michael@0 | 2632 | */ |
michael@0 | 2633 | for (i = 0; i < leapcnt; ++i) { |
michael@0 | 2634 | trans[i] = tadd(trans[i], last); |
michael@0 | 2635 | last = corr[i] += last; |
michael@0 | 2636 | } |
michael@0 | 2637 | } |
michael@0 | 2638 | |
michael@0 | 2639 | static int |
michael@0 | 2640 | yearistype(year, type) |
michael@0 | 2641 | const int year; |
michael@0 | 2642 | const char * const type; |
michael@0 | 2643 | { |
michael@0 | 2644 | static char * buf; |
michael@0 | 2645 | int result; |
michael@0 | 2646 | |
michael@0 | 2647 | if (type == NULL || *type == '\0') |
michael@0 | 2648 | return TRUE; |
michael@0 | 2649 | buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type))); |
michael@0 | 2650 | (void) sprintf(buf, "%s %d %s", yitcommand, year, type); |
michael@0 | 2651 | result = system(buf); |
michael@0 | 2652 | if (WIFEXITED(result)) switch (WEXITSTATUS(result)) { |
michael@0 | 2653 | case 0: |
michael@0 | 2654 | return TRUE; |
michael@0 | 2655 | case 1: |
michael@0 | 2656 | return FALSE; |
michael@0 | 2657 | } |
michael@0 | 2658 | error(_("Wild result from command execution")); |
michael@0 | 2659 | (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"), |
michael@0 | 2660 | progname, buf, result); |
michael@0 | 2661 | for ( ; ; ) |
michael@0 | 2662 | exit(EXIT_FAILURE); |
michael@0 | 2663 | } |
michael@0 | 2664 | |
michael@0 | 2665 | static int |
michael@0 | 2666 | lowerit(a) |
michael@0 | 2667 | int a; |
michael@0 | 2668 | { |
michael@0 | 2669 | a = (unsigned char) a; |
michael@0 | 2670 | return (isascii(a) && isupper(a)) ? tolower(a) : a; |
michael@0 | 2671 | } |
michael@0 | 2672 | |
michael@0 | 2673 | static int |
michael@0 | 2674 | ciequal(ap, bp) /* case-insensitive equality */ |
michael@0 | 2675 | register const char * ap; |
michael@0 | 2676 | register const char * bp; |
michael@0 | 2677 | { |
michael@0 | 2678 | while (lowerit(*ap) == lowerit(*bp++)) |
michael@0 | 2679 | if (*ap++ == '\0') |
michael@0 | 2680 | return TRUE; |
michael@0 | 2681 | return FALSE; |
michael@0 | 2682 | } |
michael@0 | 2683 | |
michael@0 | 2684 | static int |
michael@0 | 2685 | itsabbr(abbr, word) |
michael@0 | 2686 | register const char * abbr; |
michael@0 | 2687 | register const char * word; |
michael@0 | 2688 | { |
michael@0 | 2689 | if (lowerit(*abbr) != lowerit(*word)) |
michael@0 | 2690 | return FALSE; |
michael@0 | 2691 | ++word; |
michael@0 | 2692 | while (*++abbr != '\0') |
michael@0 | 2693 | do { |
michael@0 | 2694 | if (*word == '\0') |
michael@0 | 2695 | return FALSE; |
michael@0 | 2696 | } while (lowerit(*word++) != lowerit(*abbr)); |
michael@0 | 2697 | return TRUE; |
michael@0 | 2698 | } |
michael@0 | 2699 | |
michael@0 | 2700 | static const struct lookup * |
michael@0 | 2701 | byword(word, table) |
michael@0 | 2702 | register const char * const word; |
michael@0 | 2703 | register const struct lookup * const table; |
michael@0 | 2704 | { |
michael@0 | 2705 | register const struct lookup * foundlp; |
michael@0 | 2706 | register const struct lookup * lp; |
michael@0 | 2707 | |
michael@0 | 2708 | if (word == NULL || table == NULL) |
michael@0 | 2709 | return NULL; |
michael@0 | 2710 | /* |
michael@0 | 2711 | ** Look for exact match. |
michael@0 | 2712 | */ |
michael@0 | 2713 | for (lp = table; lp->l_word != NULL; ++lp) |
michael@0 | 2714 | if (ciequal(word, lp->l_word)) |
michael@0 | 2715 | return lp; |
michael@0 | 2716 | /* |
michael@0 | 2717 | ** Look for inexact match. |
michael@0 | 2718 | */ |
michael@0 | 2719 | foundlp = NULL; |
michael@0 | 2720 | for (lp = table; lp->l_word != NULL; ++lp) |
michael@0 | 2721 | if (itsabbr(word, lp->l_word)) { |
michael@0 | 2722 | if (foundlp == NULL) |
michael@0 | 2723 | foundlp = lp; |
michael@0 | 2724 | else return NULL; /* multiple inexact matches */ |
michael@0 | 2725 | } |
michael@0 | 2726 | return foundlp; |
michael@0 | 2727 | } |
michael@0 | 2728 | |
michael@0 | 2729 | static char ** |
michael@0 | 2730 | getfields(cp) |
michael@0 | 2731 | register char * cp; |
michael@0 | 2732 | { |
michael@0 | 2733 | register char * dp; |
michael@0 | 2734 | register char ** array; |
michael@0 | 2735 | register int nsubs; |
michael@0 | 2736 | |
michael@0 | 2737 | if (cp == NULL) |
michael@0 | 2738 | return NULL; |
michael@0 | 2739 | array = (char **) (void *) |
michael@0 | 2740 | emalloc((int) ((strlen(cp) + 1) * sizeof *array)); |
michael@0 | 2741 | nsubs = 0; |
michael@0 | 2742 | for ( ; ; ) { |
michael@0 | 2743 | while (isascii((unsigned char) *cp) && |
michael@0 | 2744 | isspace((unsigned char) *cp)) |
michael@0 | 2745 | ++cp; |
michael@0 | 2746 | if (*cp == '\0' || *cp == '#') |
michael@0 | 2747 | break; |
michael@0 | 2748 | array[nsubs++] = dp = cp; |
michael@0 | 2749 | do { |
michael@0 | 2750 | if ((*dp = *cp++) != '"') |
michael@0 | 2751 | ++dp; |
michael@0 | 2752 | else while ((*dp = *cp++) != '"') |
michael@0 | 2753 | if (*dp != '\0') |
michael@0 | 2754 | ++dp; |
michael@0 | 2755 | else { |
michael@0 | 2756 | error(_( |
michael@0 | 2757 | "Odd number of quotation marks" |
michael@0 | 2758 | )); |
michael@0 | 2759 | exit(1); |
michael@0 | 2760 | } |
michael@0 | 2761 | } while (*cp != '\0' && *cp != '#' && |
michael@0 | 2762 | (!isascii(*cp) || !isspace((unsigned char) *cp))); |
michael@0 | 2763 | if (isascii(*cp) && isspace((unsigned char) *cp)) |
michael@0 | 2764 | ++cp; |
michael@0 | 2765 | *dp = '\0'; |
michael@0 | 2766 | } |
michael@0 | 2767 | array[nsubs] = NULL; |
michael@0 | 2768 | return array; |
michael@0 | 2769 | } |
michael@0 | 2770 | |
michael@0 | 2771 | static long |
michael@0 | 2772 | oadd(t1, t2) |
michael@0 | 2773 | const long t1; |
michael@0 | 2774 | const long t2; |
michael@0 | 2775 | { |
michael@0 | 2776 | register long t; |
michael@0 | 2777 | |
michael@0 | 2778 | t = t1 + t2; |
michael@0 | 2779 | if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { |
michael@0 | 2780 | error(_("time overflow")); |
michael@0 | 2781 | exit(EXIT_FAILURE); |
michael@0 | 2782 | } |
michael@0 | 2783 | return t; |
michael@0 | 2784 | } |
michael@0 | 2785 | |
michael@0 | 2786 | static zic_t |
michael@0 | 2787 | tadd(t1, t2) |
michael@0 | 2788 | const zic_t t1; |
michael@0 | 2789 | const long t2; |
michael@0 | 2790 | { |
michael@0 | 2791 | register zic_t t; |
michael@0 | 2792 | |
michael@0 | 2793 | if (t1 == max_time && t2 > 0) |
michael@0 | 2794 | return max_time; |
michael@0 | 2795 | if (t1 == min_time && t2 < 0) |
michael@0 | 2796 | return min_time; |
michael@0 | 2797 | t = t1 + t2; |
michael@0 | 2798 | if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { |
michael@0 | 2799 | error(_("time overflow")); |
michael@0 | 2800 | exit(EXIT_FAILURE); |
michael@0 | 2801 | } |
michael@0 | 2802 | return t; |
michael@0 | 2803 | } |
michael@0 | 2804 | |
michael@0 | 2805 | /* |
michael@0 | 2806 | ** Given a rule, and a year, compute the date - in seconds since January 1, |
michael@0 | 2807 | ** 1970, 00:00 LOCAL time - in that year that the rule refers to. |
michael@0 | 2808 | */ |
michael@0 | 2809 | |
michael@0 | 2810 | static zic_t |
michael@0 | 2811 | rpytime(rp, wantedy) |
michael@0 | 2812 | register const struct rule * const rp; |
michael@0 | 2813 | register const int wantedy; |
michael@0 | 2814 | { |
michael@0 | 2815 | register int y, m, i; |
michael@0 | 2816 | register long dayoff; /* with a nod to Margaret O. */ |
michael@0 | 2817 | register zic_t t; |
michael@0 | 2818 | |
michael@0 | 2819 | if (wantedy == INT_MIN) |
michael@0 | 2820 | return min_time; |
michael@0 | 2821 | if (wantedy == INT_MAX) |
michael@0 | 2822 | return max_time; |
michael@0 | 2823 | dayoff = 0; |
michael@0 | 2824 | m = TM_JANUARY; |
michael@0 | 2825 | y = EPOCH_YEAR; |
michael@0 | 2826 | while (wantedy != y) { |
michael@0 | 2827 | if (wantedy > y) { |
michael@0 | 2828 | i = len_years[isleap(y)]; |
michael@0 | 2829 | ++y; |
michael@0 | 2830 | } else { |
michael@0 | 2831 | --y; |
michael@0 | 2832 | i = -len_years[isleap(y)]; |
michael@0 | 2833 | } |
michael@0 | 2834 | dayoff = oadd(dayoff, eitol(i)); |
michael@0 | 2835 | } |
michael@0 | 2836 | while (m != rp->r_month) { |
michael@0 | 2837 | i = len_months[isleap(y)][m]; |
michael@0 | 2838 | dayoff = oadd(dayoff, eitol(i)); |
michael@0 | 2839 | ++m; |
michael@0 | 2840 | } |
michael@0 | 2841 | i = rp->r_dayofmonth; |
michael@0 | 2842 | if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { |
michael@0 | 2843 | if (rp->r_dycode == DC_DOWLEQ) |
michael@0 | 2844 | --i; |
michael@0 | 2845 | else { |
michael@0 | 2846 | error(_("use of 2/29 in non leap-year")); |
michael@0 | 2847 | exit(EXIT_FAILURE); |
michael@0 | 2848 | } |
michael@0 | 2849 | } |
michael@0 | 2850 | --i; |
michael@0 | 2851 | dayoff = oadd(dayoff, eitol(i)); |
michael@0 | 2852 | if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { |
michael@0 | 2853 | register long wday; |
michael@0 | 2854 | |
michael@0 | 2855 | #define LDAYSPERWEEK ((long) DAYSPERWEEK) |
michael@0 | 2856 | wday = eitol(EPOCH_WDAY); |
michael@0 | 2857 | /* |
michael@0 | 2858 | ** Don't trust mod of negative numbers. |
michael@0 | 2859 | */ |
michael@0 | 2860 | if (dayoff >= 0) |
michael@0 | 2861 | wday = (wday + dayoff) % LDAYSPERWEEK; |
michael@0 | 2862 | else { |
michael@0 | 2863 | wday -= ((-dayoff) % LDAYSPERWEEK); |
michael@0 | 2864 | if (wday < 0) |
michael@0 | 2865 | wday += LDAYSPERWEEK; |
michael@0 | 2866 | } |
michael@0 | 2867 | while (wday != eitol(rp->r_wday)) |
michael@0 | 2868 | if (rp->r_dycode == DC_DOWGEQ) { |
michael@0 | 2869 | dayoff = oadd(dayoff, (long) 1); |
michael@0 | 2870 | if (++wday >= LDAYSPERWEEK) |
michael@0 | 2871 | wday = 0; |
michael@0 | 2872 | ++i; |
michael@0 | 2873 | } else { |
michael@0 | 2874 | dayoff = oadd(dayoff, (long) -1); |
michael@0 | 2875 | if (--wday < 0) |
michael@0 | 2876 | wday = LDAYSPERWEEK - 1; |
michael@0 | 2877 | --i; |
michael@0 | 2878 | } |
michael@0 | 2879 | if (i < 0 || i >= len_months[isleap(y)][m]) { |
michael@0 | 2880 | if (noise) |
michael@0 | 2881 | warning(_("rule goes past start/end of month--\ |
michael@0 | 2882 | will not work with pre-2004 versions of zic")); |
michael@0 | 2883 | } |
michael@0 | 2884 | } |
michael@0 | 2885 | if (dayoff < min_time / SECSPERDAY) |
michael@0 | 2886 | return min_time; |
michael@0 | 2887 | if (dayoff > max_time / SECSPERDAY) |
michael@0 | 2888 | return max_time; |
michael@0 | 2889 | t = (zic_t) dayoff * SECSPERDAY; |
michael@0 | 2890 | return tadd(t, rp->r_tod); |
michael@0 | 2891 | } |
michael@0 | 2892 | |
michael@0 | 2893 | static void |
michael@0 | 2894 | newabbr(string) |
michael@0 | 2895 | const char * const string; |
michael@0 | 2896 | { |
michael@0 | 2897 | register int i; |
michael@0 | 2898 | |
michael@0 | 2899 | if (strcmp(string, GRANDPARENTED) != 0) { |
michael@0 | 2900 | register const char * cp; |
michael@0 | 2901 | register char * wp; |
michael@0 | 2902 | |
michael@0 | 2903 | /* |
michael@0 | 2904 | ** Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics |
michael@0 | 2905 | ** optionally followed by a + or - and a number from 1 to 14. |
michael@0 | 2906 | */ |
michael@0 | 2907 | cp = string; |
michael@0 | 2908 | wp = NULL; |
michael@0 | 2909 | while (isascii((unsigned char) *cp) && |
michael@0 | 2910 | isalpha((unsigned char) *cp)) |
michael@0 | 2911 | ++cp; |
michael@0 | 2912 | if (cp - string == 0) |
michael@0 | 2913 | wp = _("time zone abbreviation lacks alphabetic at start"); |
michael@0 | 2914 | if (noise && cp - string > 3) |
michael@0 | 2915 | wp = _("time zone abbreviation has more than 3 alphabetics"); |
michael@0 | 2916 | if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) |
michael@0 | 2917 | wp = _("time zone abbreviation has too many alphabetics"); |
michael@0 | 2918 | if (wp == NULL && (*cp == '+' || *cp == '-')) { |
michael@0 | 2919 | ++cp; |
michael@0 | 2920 | if (isascii((unsigned char) *cp) && |
michael@0 | 2921 | isdigit((unsigned char) *cp)) |
michael@0 | 2922 | if (*cp++ == '1' && |
michael@0 | 2923 | *cp >= '0' && *cp <= '4') |
michael@0 | 2924 | ++cp; |
michael@0 | 2925 | } |
michael@0 | 2926 | if (*cp != '\0') |
michael@0 | 2927 | wp = _("time zone abbreviation differs from POSIX standard"); |
michael@0 | 2928 | if (wp != NULL) { |
michael@0 | 2929 | wp = ecpyalloc(wp); |
michael@0 | 2930 | wp = ecatalloc(wp, " ("); |
michael@0 | 2931 | wp = ecatalloc(wp, string); |
michael@0 | 2932 | wp = ecatalloc(wp, ")"); |
michael@0 | 2933 | warning(wp); |
michael@0 | 2934 | ifree(wp); |
michael@0 | 2935 | } |
michael@0 | 2936 | } |
michael@0 | 2937 | i = strlen(string) + 1; |
michael@0 | 2938 | if (charcnt + i > TZ_MAX_CHARS) { |
michael@0 | 2939 | error(_("too many, or too long, time zone abbreviations")); |
michael@0 | 2940 | exit(EXIT_FAILURE); |
michael@0 | 2941 | } |
michael@0 | 2942 | (void) strcpy(&chars[charcnt], string); |
michael@0 | 2943 | charcnt += eitol(i); |
michael@0 | 2944 | } |
michael@0 | 2945 | |
michael@0 | 2946 | static int |
michael@0 | 2947 | mkdirs(argname) |
michael@0 | 2948 | char * argname; |
michael@0 | 2949 | { |
michael@0 | 2950 | register char * name; |
michael@0 | 2951 | register char * cp; |
michael@0 | 2952 | |
michael@0 | 2953 | if (argname == NULL || *argname == '\0') |
michael@0 | 2954 | return 0; |
michael@0 | 2955 | cp = name = ecpyalloc(argname); |
michael@0 | 2956 | while ((cp = strchr(cp + 1, '/')) != 0) { |
michael@0 | 2957 | *cp = '\0'; |
michael@0 | 2958 | #ifndef unix |
michael@0 | 2959 | /* |
michael@0 | 2960 | ** DOS drive specifier? |
michael@0 | 2961 | */ |
michael@0 | 2962 | if (isalpha((unsigned char) name[0]) && |
michael@0 | 2963 | name[1] == ':' && name[2] == '\0') { |
michael@0 | 2964 | *cp = '/'; |
michael@0 | 2965 | continue; |
michael@0 | 2966 | } |
michael@0 | 2967 | #endif /* !defined unix */ |
michael@0 | 2968 | if (!itsdir(name)) { |
michael@0 | 2969 | /* |
michael@0 | 2970 | ** It doesn't seem to exist, so we try to create it. |
michael@0 | 2971 | ** Creation may fail because of the directory being |
michael@0 | 2972 | ** created by some other multiprocessor, so we get |
michael@0 | 2973 | ** to do extra checking. |
michael@0 | 2974 | */ |
michael@0 | 2975 | if (mkdir(name, MKDIR_UMASK) != 0) { |
michael@0 | 2976 | const char *e = strerror(errno); |
michael@0 | 2977 | |
michael@0 | 2978 | if (errno != EEXIST || !itsdir(name)) { |
michael@0 | 2979 | (void) fprintf(stderr, |
michael@0 | 2980 | _("%s: Can't create directory %s: %s\n"), |
michael@0 | 2981 | progname, name, e); |
michael@0 | 2982 | ifree(name); |
michael@0 | 2983 | return -1; |
michael@0 | 2984 | } |
michael@0 | 2985 | } |
michael@0 | 2986 | } |
michael@0 | 2987 | *cp = '/'; |
michael@0 | 2988 | } |
michael@0 | 2989 | ifree(name); |
michael@0 | 2990 | return 0; |
michael@0 | 2991 | } |
michael@0 | 2992 | |
michael@0 | 2993 | static long |
michael@0 | 2994 | eitol(i) |
michael@0 | 2995 | const int i; |
michael@0 | 2996 | { |
michael@0 | 2997 | long l; |
michael@0 | 2998 | |
michael@0 | 2999 | l = i; |
michael@0 | 3000 | if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) { |
michael@0 | 3001 | (void) fprintf(stderr, |
michael@0 | 3002 | _("%s: %d did not sign extend correctly\n"), |
michael@0 | 3003 | progname, i); |
michael@0 | 3004 | exit(EXIT_FAILURE); |
michael@0 | 3005 | } |
michael@0 | 3006 | return l; |
michael@0 | 3007 | } |
michael@0 | 3008 | |
michael@0 | 3009 | /* |
michael@0 | 3010 | ** UNIX was a registered trademark of The Open Group in 2003. |
michael@0 | 3011 | */ |