Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/intl/icu/source/tools/tzcode/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 76 kB image not shown  

Quelle  zic.c   Sprache: C

 
/*
** This file is in the public domain, so clarified as of
** 2006-07-17 by Arthur David Olson.
*/


/* Enable extensions and modifications for ICU. */
#define ICU

/* Continue executing after link failure. Even if ICU is undefined
 * (for vanilla zic behavior), ICU_LINKS should be defined, since zic
 * appears to fail on the 2003 data the first time through during the
 * linking phase. Running zic twice, with ICU_LINKS defined, causes
 * links to be handled correctly. */

#define ICU_LINKS

#define LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH

#ifdef ICU
/* These constants are embedded in dynamically generated header
 * version.h in the standard tzcode distribution. */

static char const PKGVERSION[]="N/A";
static char const TZVERSION[]="N/A";
static char const REPORT_BUGS_TO[]="N/A";
#else
#include "version.h"
#endif
#include "private.h"
#include "locale.h"
#include "tzfile.h"

#include <stdarg.h>
#include <stdbool.h>

#define ZIC_VERSION_PRE_2013 '2'
#define ZIC_VERSION '3'

typedef int_fast64_t zic_t;
#define ZIC_MIN INT_FAST64_MIN
#define ZIC_MAX INT_FAST64_MAX
#define SCNdZIC SCNdFAST64

#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */

#if HAVE_SYS_STAT_H
#include "sys/stat.h"
#endif
#ifdef S_IRUSR
#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
#else
#define MKDIR_UMASK 0755
#endif

#ifdef ICU
#include "tz2icu.h"
#endif

/*
** On some ancient hosts, predicates like `isspace(C)' are defined
** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
** which says they are defined only if C == ((unsigned char) C) || C == EOF.
** Neither the C Standard nor Posix require that `isascii' exist.
** For portability, we check both ancient and modern requirements.
** If isascii is not defined, the isascii check succeeds trivially.
*/

#include "ctype.h"
#ifndef isascii
#define isascii(x) 1
#endif

#define end(cp) (strchr((cp), '\0'))

struct rule {
 const char * r_filename;
 int  r_linenum;
 const char * r_name;

 zic_t  r_loyear; /* for example, 1986 */
 zic_t  r_hiyear; /* for example, 1986 */
 const char * r_yrtype;
 int  r_lowasnum;
 int  r_hiwasnum;

 int  r_month; /* 0..11 */

 int  r_dycode; /* see below */
 int  r_dayofmonth;
 int  r_wday;

 zic_t  r_tod;  /* time from midnight */
 int  r_todisstd; /* above is standard time if true */
     /* or wall clock time if false */
 int  r_todisgmt; /* above is GMT if true */
     /* or local time if false */
 zic_t  r_stdoff; /* offset from standard time */
 const char * r_abbrvar; /* variable part of abbreviation */

 int  r_todo;  /* a rule to do (used in outzone) */
 zic_t  r_temp;  /* used in outzone */
};

/*
** r_dycode r_dayofmonth r_wday
*/


#define DC_DOM  0 /* 1..31 */ /* unused */
#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */

struct zone {
 const char * z_filename;
 int  z_linenum;

 const char * z_name;
 zic_t  z_gmtoff;
 const char * z_rule;
 const char * z_format;

 zic_t  z_stdoff;

 struct rule * z_rules;
 int  z_nrules;

 struct rule z_untilrule;
 zic_t  z_untiltime;
};

extern int getopt(int argc, char * const argv[],
   const char * options);
extern int link(const char * fromname, const char * toname);
extern char * optarg;
extern int optind;

#if ! HAVE_LINK
define link(from, to) (-1)
#endif
#if ! HAVE_SYMLINK
define symlink(from, to) (-1)
#endif

static void addtt(zic_t starttime, int type);
#ifdef ICU
static int addtype(const zic_t gmtoff, const zic_t rawoff, const zic_t dstoff,
    char *const abbr, int isdst,
    int ttisstd, int ttisgmt);
#else
static int addtype(zic_t gmtoff, const char * abbr, int isdst,
    int ttisstd, int ttisgmt);
#endif
static void leapadd(zic_t t, int positive, int rolling, int count);
static void adjleap(void);
static void associate(void);
static void dolink(const char * fromfield, const char * tofield);
static char ** getfields(char * buf);
static zic_t gethms(const char * string, const char * errstrng,
         int signable);
static void infile(const char * filename);
static void inleap(char ** fields, int nfields);
static void inlink(char ** fields, int nfields);
static void inrule(char ** fields, int nfields);
static int inzcont(char ** fields, int nfields);
static int inzone(char ** fields, int nfields);
static int inzsub(char ** fields, int nfields, int iscont);
static int itsdir(const char * name);
static int lowerit(int c);
static int mkdirs(char * filename);
static void newabbr(const char * abbr);
static zic_t oadd(zic_t t1, zic_t t2);
static void outzone(const struct zone * zp, int ntzones);
static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static void rulesub(struct rule * rp,
   const char * loyearp, const char * hiyearp,
   const char * typep, const char * monthp,
   const char * dayp, const char * timep);
static zic_t tadd(zic_t t1, zic_t t2);
static int yearistype(int year, const char * type);
#ifdef ICU
static void emit_icu_zone(FILE* f, const char* zoneName, int zoneOffset,
     const struct rule* rule,
     int ruleIndex, int startYear);
static void emit_icu_link(FILE* f, const char* from, const char* to);
static void emit_icu_rule(FILE* f, const struct rule* r, int ruleIndex);
static int add_icu_final_rules(const struct rule* r1, const struct rule* r2);
#endif

static int  charcnt;
static int  errors;
static const char * filename;
static int  leapcnt;
static int  leapseen;
static zic_t  leapminyear;
static zic_t  leapmaxyear;
static int  linenum;
static int  max_abbrvar_len;
static int  max_format_len;
static zic_t  max_year;
static zic_t  min_year;
static int  noise;
static const char * rfilename;
static int  rlinenum;
static const char * progname;
static int  timecnt;
static int  timecnt_alloc;
static int  typecnt;

/*
** Line codes.
*/


#define LC_RULE  0
#define LC_ZONE  1
#define LC_LINK  2
#define LC_LEAP  3

/*
** Which fields are which on a Zone line.
*/


#define ZF_NAME  1
#define ZF_GMTOFF 2
#define ZF_RULE  3
#define ZF_FORMAT 4
#define ZF_TILYEAR 5
#define ZF_TILMONTH 6
#define ZF_TILDAY 7
#define ZF_TILTIME 8
#define ZONE_MINFIELDS 5
#define ZONE_MAXFIELDS 9

/*
** Which fields are which on a Zone continuation line.
*/


#define ZFC_GMTOFF 0
#define ZFC_RULE 1
#define ZFC_FORMAT 2
#define ZFC_TILYEAR 3
#define ZFC_TILMONTH 4
#define ZFC_TILDAY 5
#define ZFC_TILTIME 6
#define ZONEC_MINFIELDS 3
#define ZONEC_MAXFIELDS 7

/*
** Which files are which on a Rule line.
*/


#define RF_NAME  1
#define RF_LOYEAR 2
#define RF_HIYEAR 3
#define RF_COMMAND 4
#define RF_MONTH 5
#define RF_DAY  6
#define RF_TOD  7
#define RF_STDOFF 8
#define RF_ABBRVAR 9
#define RULE_FIELDS 10

/*
** Which fields are which on a Link line.
*/


#define LF_FROM  1
#define LF_TO  2
#define LINK_FIELDS 3

/*
** Which fields are which on a Leap line.
*/


#define LP_YEAR  1
#define LP_MONTH 2
#define LP_DAY  3
#define LP_TIME  4
#define LP_CORR  5
#define LP_ROLL  6
#define LEAP_FIELDS 7

/*
** Year synonyms.
*/


#define YR_MINIMUM 0
#define YR_MAXIMUM 1
#define YR_ONLY  2

static struct rule * rules;
static int  nrules; /* number of rules */
static int  nrules_alloc;

static struct zone * zones;
static int  nzones; /* number of zones */
static int  nzones_alloc;

struct link {
 const char * l_filename;
 int  l_linenum;
 const char * l_from;
 const char * l_to;
};

static struct link * links;
static int  nlinks;
static int  nlinks_alloc;

struct lookup {
 const char * l_word;
 const int l_value;
};

#ifdef ICU
/* Indices into rules[] for final rules.  They will occur in pairs,
 * with finalRules[i] occurring before finalRules[i+1] in the year.
 * Each zone need only store a start year, a standard offset, and an
 * index into finalRules[].  FinalRules[] are aliases into rules[]. */

static const struct rule ** finalRules = NULL;
static int     finalRulesCount = 0;
#endif

static struct lookup const * byword(const char * string,
     const struct lookup * lp);

static struct lookup const line_codes[] = {
 { "Rule", LC_RULE },
 { "Zone", LC_ZONE },
 { "Link", LC_LINK },
 { "Leap", LC_LEAP },
 { NULL,  0}
};

static struct lookup const mon_names[] = {
 { "January", TM_JANUARY },
 { "February", TM_FEBRUARY },
 { "March", TM_MARCH },
 { "April", TM_APRIL },
 { "May", TM_MAY },
 { "June", TM_JUNE },
 { "July", TM_JULY },
 { "August", TM_AUGUST },
 { "September", TM_SEPTEMBER },
 { "October", TM_OCTOBER },
 { "November", TM_NOVEMBER },
 { "December", TM_DECEMBER },
 { NULL,  0 }
};

static struct lookup const wday_names[] = {
 { "Sunday", TM_SUNDAY },
 { "Monday", TM_MONDAY },
 { "Tuesday", TM_TUESDAY },
 { "Wednesday", TM_WEDNESDAY },
 { "Thursday", TM_THURSDAY },
 { "Friday", TM_FRIDAY },
 { "Saturday", TM_SATURDAY },
 { NULL,  0 }
};

static struct lookup const lasts[] = {
 { "last-Sunday", TM_SUNDAY },
 { "last-Monday", TM_MONDAY },
 { "last-Tuesday", TM_TUESDAY },
 { "last-Wednesday", TM_WEDNESDAY },
 { "last-Thursday", TM_THURSDAY },
 { "last-Friday", TM_FRIDAY },
 { "last-Saturday", TM_SATURDAY },
 { NULL,   0 }
};

static struct lookup const begin_years[] = {
 { "minimum", YR_MINIMUM },
 { "maximum", YR_MAXIMUM },
 { NULL,  0 }
};

static struct lookup const end_years[] = {
 { "minimum", YR_MINIMUM },
 { "maximum", YR_MAXIMUM },
 { "only", YR_ONLY },
 { NULL,  0 }
};

static struct lookup const leap_types[] = {
 { "Rolling"true },
 { "Stationary"false },
 { NULL,  0 }
};

static const int len_months[2][MONSPERYEAR] = {
 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

static const int len_years[2] = {
 DAYSPERNYEAR, DAYSPERLYEAR
};

static struct attype {
 zic_t  at;
 unsigned char type;
} *   attypes;
static zic_t  gmtoffs[TZ_MAX_TYPES];
#ifdef ICU
/* gmtoffs[i] = rawoffs[i] + dstoffs[i] */
static zic_t  rawoffs[TZ_MAX_TYPES];
static zic_t  dstoffs[TZ_MAX_TYPES];
#endif
static char  isdsts[TZ_MAX_TYPES];
static unsigned char abbrinds[TZ_MAX_TYPES];
static char  ttisstds[TZ_MAX_TYPES];
static char  ttisgmts[TZ_MAX_TYPES];
static char  chars[TZ_MAX_CHARS];
static zic_t  trans[TZ_MAX_LEAPS];
static zic_t  corr[TZ_MAX_LEAPS];
static char  roll[TZ_MAX_LEAPS];

/*
** Memory allocation.
*/


static _Noreturn void
memory_exhausted(const char *msg)
{
 fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
 exit(EXIT_FAILURE);
}

static ATTRIBUTE_PURE size_t
size_product(size_t nitems, size_t itemsize)
{
 if (SIZE_MAX / itemsize < nitems)
  memory_exhausted("size overflow");
 return nitems * itemsize;
}

static ATTRIBUTE_PURE void *
memcheck(void *const ptr)
{
 if (ptr == NULL)
  memory_exhausted(strerror(errno));
 return ptr;
}

#define emalloc(size)  memcheck(malloc(size))
#define erealloc(ptr, size) memcheck(realloc(ptr, size))
#define ecpyalloc(ptr)  memcheck(icpyalloc(ptr))
#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp)))

static void *
growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
{
 if (nitems < *nitems_alloc)
  return ptr;
 else {
  int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
  if ((amax - 1) / 3 * 2 < *nitems_alloc)
   memory_exhausted("int overflow");
  *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1;
  return erealloc(ptr, size_product(*nitems_alloc, itemsize));
 }
}

/*
** Error handling.
*/


static void
eats(const char *const name, const int num, const char *const rname,
     const int rnum)
{
 filename = name;
 linenum = num;
 rfilename = rname;
 rlinenum = rnum;
}

static void
eat(const char *const name, const int num)
{
 eats(name, num, NULL, -1);
}

static void ATTRIBUTE_FORMAT((printf, 1, 0))
verror(const char *const string, va_list args)
{
 /*
** Match the format of "cc" to allow sh users to
** zic ... 2>&1 | error -t "*" -v
** on BSD systems.
*/

 fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
 vfprintf(stderr, string, args);
 if (rfilename != NULL)
  (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
   rfilename, rlinenum);
 (void) fprintf(stderr, "\n");
 ++errors;
}

static void ATTRIBUTE_FORMAT((printf, 1, 2))
error(const char *const string, ...)
{
 va_list args;
 va_start(args, string);
 verror(string, args);
 va_end(args);
}

static void ATTRIBUTE_FORMAT((printf, 1, 2))
warning(const char *const string, ...)
{
 va_list args;
 fprintf(stderr, _("warning: "));
 va_start(args, string);
 verror(string, args);
 va_end(args);
 --errors;
}

static _Noreturn void
usage(FILE *stream, int status)
{
 (void) fprintf(stream, _("%s: usage is %s \
[ --version ] [ --help ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\
\n\
Report bugs to %s.\n"),
         progname, progname, REPORT_BUGS_TO);
 exit(status);
}

#ifdef ICU
/* File into which we will write supplemental ICU data. */
static FILE * icuFile;

static void
emit_icu_zone(FILE* f, const char* zoneName, int zoneOffset,
     const struct rule* rule,
     int ruleIndex, int startYear) {
 /* machine-readable section */
 fprintf(f, "zone %s %d %d %s", zoneName, zoneOffset, startYear, rule->r_name);

 /* human-readable section */
 fprintf(f, " # zone %s, offset %d, year >= %d, rule %s (%d)\n",
   zoneName, zoneOffset, startYear,
   rule->r_name, ruleIndex);
}

static void
emit_icu_link(FILE* f, const char* from, const char* to) {
 /* machine-readable section */
 fprintf(f, "link %s %s\n", from, to);
}

static const char* DYCODE[] = {"DOM""DOWGEQ""DOWLEQ"};

static void
emit_icu_rule(FILE* f, const struct rule* r, int ruleIndex) {
 if (r->r_yrtype != NULL) {
  warning("year types not supported by ICU");
  fprintf(stderr, "rule %s, file %s, line %d\n",
    r->r_name, r->r_filename, r->r_linenum);
    }

 /* machine-readable section */
 fprintf(f, "rule %s %s %d %d %d %lld %d %d %lld",
   r->r_name, DYCODE[r->r_dycode],
   r->r_month, r->r_dayofmonth,
   (r->r_dycode == DC_DOM ? -1 : r->r_wday),
   r->r_tod, r->r_todisstd, r->r_todisgmt, r->r_stdoff
   );

 /* human-readable section */
 fprintf(f, " # %d: %s, file %s, line %d",
   ruleIndex, r->r_name, r->r_filename, r->r_linenum);
 fprintf(f, ", mode %s", DYCODE[r->r_dycode]);
 fprintf(f, ", %s, dom %d", mon_names[r->r_month].l_word, r->r_dayofmonth);
 if (r->r_dycode != DC_DOM) {
  fprintf(f, ", %s", wday_names[r->r_wday].l_word);
 }
 fprintf(f, ", time %lld", r->r_tod);
 fprintf(f, ", isstd %d", r->r_todisstd);
 fprintf(f, ", isgmt %d", r->r_todisgmt);
 fprintf(f, ", offset %lld", r->r_stdoff);
 fprintf(f, "\n");
}

static int
add_icu_final_rules(const struct rule* r1, const struct rule* r2) {
 int i;

 for (i=0; i<finalRulesCount; ++i) { /* i+=2 should work too */
  if (r1==finalRules[i]) return i; /* [sic] pointer comparison */
 }

 finalRules = (const struct rule**) (void*) erealloc((char *) finalRules,
    (finalRulesCount + 2) * sizeof(*finalRules));
 finalRules[finalRulesCount++] = r1;
 finalRules[finalRulesCount++] = r2;
 return finalRulesCount - 2;
}
#endif

static const char * psxrules;
static const char * lcltime;
static const char * directory;
static const char * leapsec;
static const char * yitcommand;

int
main(int argc, char **argv)
{
 register int i;
 register int j;
 register int c;

#ifdef S_IWGRP
 (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
#endif
#if HAVE_GETTEXT
 (void) setlocale(LC_ALL, "");
#ifdef TZ_DOMAINDIR
 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
#endif /* defined TEXTDOMAINDIR */
 (void) textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */
 progname = argv[0];
 if (TYPE_BIT(zic_t) < 64) {
  (void) fprintf(stderr, "%s: %s\n", progname,
   _("wild compilation-time specification of zic_t"));
  exit(EXIT_FAILURE);
 }
 for (i = 1; i < argc; ++i)
  if (strcmp(argv[i], "--version") == 0) {
   (void) printf("zic %s%s\n", PKGVERSION, TZVERSION);
   exit(EXIT_SUCCESS);
  } else if (strcmp(argv[i], "--help") == 0) {
   usage(stdout, EXIT_SUCCESS);
  }
 while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1)
  switch (c) {
   default:
    usage(stderr, EXIT_FAILURE);
   case 'd':
    if (directory == NULL)
     directory = optarg;
    else {
     (void) fprintf(stderr,
_("%s: More than one -d option specified\n"),
      progname);
     exit(EXIT_FAILURE);
    }
    break;
   case 'l':
    if (lcltime == NULL)
     lcltime = optarg;
    else {
     (void) fprintf(stderr,
_("%s: More than one -l option specified\n"),
      progname);
     exit(EXIT_FAILURE);
    }
    break;
   case 'p':
    if (psxrules == NULL)
     psxrules = optarg;
    else {
     (void) fprintf(stderr,
_("%s: More than one -p option specified\n"),
      progname);
     exit(EXIT_FAILURE);
    }
    break;
   case 'y':
    if (yitcommand == NULL)
     yitcommand = optarg;
    else {
     (void) fprintf(stderr,
_("%s: More than one -y option specified\n"),
      progname);
     exit(EXIT_FAILURE);
    }
    break;
   case 'L':
    if (leapsec == NULL)
     leapsec = optarg;
    else {
     (void) fprintf(stderr,
_("%s: More than one -L option specified\n"),
      progname);
     exit(EXIT_FAILURE);
    }
    break;
   case 'v':
    noise = true;
    break;
   case 's':
    (void) printf("%s: -s ignored\n", progname);
    break;
  }
 if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
  usage(stderr, EXIT_FAILURE); /* usage message by request */
 if (directory == NULL)
  directory = TZDIR;
 if (yitcommand == NULL)
  yitcommand = "yearistype";

 if (optind < argc && leapsec != NULL) {
  infile(leapsec);
  adjleap();
 }

#ifdef ICU
 if ((icuFile = fopen(ICU_ZONE_FILE, "w")) == NULL) {
  const char *e = strerror(errno);
  (void) fprintf(stderr, _("%s: Can't open %s: %s\n"),
      progname, ICU_ZONE_FILE, e);
  (voidexit(EXIT_FAILURE);
 }
#endif
 for (i = optind; i < argc; ++i)
  infile(argv[i]);
 if (errors)
  exit(EXIT_FAILURE);
 associate();
 for (i = 0; i < nzones; i = j) {
  /*
** Find the next non-continuation zone entry.
*/

  for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
   continue;
  outzone(&zones[i], j - i);
 }
 /*
** Make links.
*/

 for (i = 0; i < nlinks; ++i) {
  eat(links[i].l_filename, links[i].l_linenum);
  dolink(links[i].l_from, links[i].l_to);
#ifdef ICU
  emit_icu_link(icuFile, links[i].l_from, links[i].l_to);
#endif
  if (noise)
   for (j = 0; j < nlinks; ++j)
    if (strcmp(links[i].l_to,
     links[j].l_from) == 0)
      warning(_("link to link"));
 }
 if (lcltime != NULL) {
  eat("command line", 1);
  dolink(lcltime, TZDEFAULT);
 }
 if (psxrules != NULL) {
  eat("command line", 1);
  dolink(psxrules, TZDEFRULES);
 }
#ifdef ICU
 for (i=0; i<finalRulesCount; ++i) {
  emit_icu_rule(icuFile, finalRules[i], i);
 }
#endif /*ICU*/
 return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

static void
dolink(const char *const fromfield, const char *const tofield)
{
 register char * fromname;
 register char * toname;

 if (fromfield[0] == '/')
  fromname = ecpyalloc(fromfield);
 else {
  fromname = ecpyalloc(directory);
  fromname = ecatalloc(fromname, "/");
  fromname = ecatalloc(fromname, fromfield);
 }
 if (tofield[0] == '/')
  toname = ecpyalloc(tofield);
 else {
  toname = ecpyalloc(directory);
  toname = ecatalloc(toname, "/");
  toname = ecatalloc(toname, tofield);
 }
 /*
** We get to be careful here since
** there's a fair chance of root running us.
*/

 if (!itsdir(toname))
  (void) remove(toname);
 if (link(fromname, toname) != 0
     && access(fromname, F_OK) == 0 && !itsdir(fromname)) {
  int result;

  if (mkdirs(toname) != 0)
   exit(EXIT_FAILURE);

  result = link(fromname, toname);
  if (result != 0) {
    const char *s = fromfield;
    const char *t;
    register char * symlinkcontents = NULL;

    do
      t = s;
    while ((s = strchr(s, '/'))
           && ! strncmp (fromfield, tofield,
           ++s - fromfield));

    for (s = tofield + (t - fromfield);
         (s = strchr(s, '/'));
         s++)
     symlinkcontents =
      ecatalloc(symlinkcontents,
      "../");
    symlinkcontents = ecatalloc(symlinkcontents, t);
    result = symlink(symlinkcontents, toname);
    if (result == 0)
warning(_("hard link failed, symbolic link used"));
    free(symlinkcontents);
  }
  if (result != 0) {
   FILE *fp, *tp;
   int c;
   fp = fopen(fromname, "rb");
   if (!fp) {
    const char *e = strerror(errno);
    (void) fprintf(stderr,
            _("%s: Can't read %s: %s\n"),
            progname, fromname, e);
    exit(EXIT_FAILURE);
   }
   tp = fopen(toname, "wb");
   if (!tp) {
    const char *e = strerror(errno);
    (void) fprintf(stderr,
            _("%s: Can't create %s: %s\n"),
            progname, toname, e);
    exit(EXIT_FAILURE);
   }
   while ((c = getc(fp)) != EOF)
    putc(c, tp);
   if (ferror(fp) || fclose(fp)) {
    (void) fprintf(stderr,
            _("%s: Error reading %s\n"),
            progname, fromname);
    exit(EXIT_FAILURE);
   }
   if (ferror(tp) || fclose(tp)) {
    (void) fprintf(stderr,
            _("%s: Error writing %s\n"),
            progname, toname);
    exit(EXIT_FAILURE);
   }
   warning(_("link failed, copy used"));
#ifndef ICU_LINKS
   exit(EXIT_FAILURE);
#endif
  }
 }
 free(fromname);
 free(toname);
}

#define TIME_T_BITS_IN_FILE 64

static const zic_t min_time = (zic_t) -1 << (TIME_T_BITS_IN_FILE - 1);
static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1));

static int
itsdir(const char *const name)
{
 register char * myname;
 register int accres;

 myname = ecpyalloc(name);
 myname = ecatalloc(myname, "/.");
 accres = access(myname, F_OK);
 free(myname);
 return accres == 0;
}

/*
** Associate sets of rules with zones.
*/


/*
** Sort by rule name.
*/


static int
rcomp(const void *cp1, const void *cp2)
{
 return strcmp(((const struct rule *) cp1)->r_name,
  ((const struct rule *) cp2)->r_name);
}

static void
associate(void)
{
 register struct zone * zp;
 register struct rule * rp;
 register int  base, out;
 register int  i, j;

 if (nrules != 0) {
  (void) qsort(rules, nrules, sizeof *rules, rcomp);
  for (i = 0; i < nrules - 1; ++i) {
   if (strcmp(rules[i].r_name,
    rules[i + 1].r_name) != 0)
     continue;
   if (strcmp(rules[i].r_filename,
    rules[i + 1].r_filename) == 0)
     continue;
   eat(rules[i].r_filename, rules[i].r_linenum);
   warning(_("same rule name in multiple files"));
   eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
   warning(_("same rule name in multiple files"));
   for (j = i + 2; j < nrules; ++j) {
    if (strcmp(rules[i].r_name,
     rules[j].r_name) != 0)
      break;
    if (strcmp(rules[i].r_filename,
     rules[j].r_filename) == 0)
      continue;
    if (strcmp(rules[i + 1].r_filename,
     rules[j].r_filename) == 0)
      continue;
    break;
   }
   i = j - 1;
  }
 }
 for (i = 0; i < nzones; ++i) {
  zp = &zones[i];
  zp->z_rules = NULL;
  zp->z_nrules = 0;
 }
 for (base = 0; base < nrules; base = out) {
  rp = &rules[base];
  for (out = base + 1; out < nrules; ++out)
   if (strcmp(rp->r_name, rules[out].r_name) != 0)
    break;
  for (i = 0; i < nzones; ++i) {
   zp = &zones[i];
   if (strcmp(zp->z_rule, rp->r_name) != 0)
    continue;
   zp->z_rules = rp;
   zp->z_nrules = out - base;
  }
 }
 for (i = 0; i < nzones; ++i) {
  zp = &zones[i];
  if (zp->z_nrules == 0) {
   /*
** Maybe we have a local standard time offset.
*/

   eat(zp->z_filename, zp->z_linenum);
   zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
    true);
   /*
** Note, though, that if there's no rule,
** a '%s' in the format is a bad thing.
*/

   if (strchr(zp->z_format, '%') != 0)
    error("%s", _("%s in ruleless zone"));
  }
 }
 if (errors)
  exit(EXIT_FAILURE);
}

static void
infile(const char *name)
{
 register FILE *   fp;
 register char **  fields;
 register char *   cp;
 register const struct lookup * lp;
 register int   nfields;
 register int   wantcont;
 register int   num;
 char    buf[BUFSIZ];

 if (strcmp(name, "-") == 0) {
  name = _("standard input");
  fp = stdin;
 } else if ((fp = fopen(name, "r")) == NULL) {
  const char *e = strerror(errno);

  (void) fprintf(stderr, _("%s: Can't open %s: %s\n"),
   progname, name, e);
  exit(EXIT_FAILURE);
 }
 wantcont = false;
 for (num = 1; ; ++num) {
  eat(name, num);
  if (fgets(buf, sizeof buf, fp) != buf)
   break;
  cp = strchr(buf, '\n');
  if (cp == NULL) {
   error(_("line too long"));
   exit(EXIT_FAILURE);
  }
  *cp = '\0';
  fields = getfields(buf);
  nfields = 0;
  while (fields[nfields] != NULL) {
   static char nada;

   if (strcmp(fields[nfields], "-") == 0)
    fields[nfields] = &nada;
   ++nfields;
  }
  if (nfields == 0) {
   /* nothing to do */
  } else if (wantcont) {
   wantcont = inzcont(fields, nfields);
  } else {
   lp = byword(fields[0], line_codes);
   if (lp == NULL)
    error(_("input line of unknown type"));
   else switch ((int) (lp->l_value)) {
    case LC_RULE:
     inrule(fields, nfields);
     wantcont = false;
     break;
    case LC_ZONE:
     wantcont = inzone(fields, nfields);
     break;
    case LC_LINK:
     inlink(fields, nfields);
     wantcont = false;
     break;
    case LC_LEAP:
     if (name != leapsec)
      (void) fprintf(stderr,
_("%s: Leap line in non leap seconds file %s\n"),
       progname, name);
     else inleap(fields, nfields);
     wantcont = false;
     break;
    default/* "cannot happen" */
     (void) fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"),
      progname, lp->l_value);
     exit(EXIT_FAILURE);
   }
  }
  free(fields);
 }
 if (ferror(fp)) {
  (void) fprintf(stderr, _("%s: Error reading %s\n"),
   progname, filename);
  exit(EXIT_FAILURE);
 }
 if (fp != stdin && fclose(fp)) {
  const char *e = strerror(errno);

  (void) fprintf(stderr, _("%s: Error closing %s: %s\n"),
   progname, filename, e);
  exit(EXIT_FAILURE);
 }
 if (wantcont)
  error(_("expected continuation line not found"));
}

/*
** Convert a string of one of the forms
** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
** into a number of seconds.
** A null string maps to zero.
** Call error with errstring and return zero on errors.
*/


static zic_t
gethms(const char *string, const char *const errstring, const int signable)
{
 zic_t hh;
 int mm, ss, sign;

 if (string == NULL || *string == '\0')
  return 0;
 if (!signable)
  sign = 1;
 else if (*string == '-') {
  sign = -1;
  ++string;
 } else sign = 1;
 if (sscanf(string, scheck(string, "%"SCNdZIC), &hh) == 1)
  mm = ss = 0;
 else if (sscanf(string, scheck(string, "%"SCNdZIC":%d"), &hh, &mm) == 2)
  ss = 0;
 else if (sscanf(string, scheck(string, "%"SCNdZIC":%d:%d"),
  &hh, &mm, &ss) != 3) {
   error("%s", errstring);
   return 0;
 }
 if (hh < 0 ||
  mm < 0 || mm >= MINSPERHOUR ||
  ss < 0 || ss > SECSPERMIN) {
   error("%s", errstring);
   return 0;
 }
 if (ZIC_MAX / SECSPERHOUR < hh) {
  error(_("time overflow"));
  return 0;
 }
 if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
  warning(_("24:00 not handled by pre-1998 versions of zic"));
 if (noise && (hh > HOURSPERDAY ||
  (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
 return oadd(sign * hh * SECSPERHOUR,
      sign * (mm * SECSPERMIN + ss));
}

static void
inrule(register char **const fields, const int nfields)
{
 static struct rule r;

 if (nfields != RULE_FIELDS) {
  error(_("wrong number of fields on Rule line"));
  return;
 }
 if (*fields[RF_NAME] == '\0') {
  error(_("nameless rule"));
  return;
 }
 r.r_filename = filename;
 r.r_linenum = linenum;
 r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
 rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
  fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
 r.r_name = ecpyalloc(fields[RF_NAME]);
 r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
 if (max_abbrvar_len < strlen(r.r_abbrvar))
  max_abbrvar_len = strlen(r.r_abbrvar);
 rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
 rules[nrules++] = r;
}

static int
inzone(register char **const fields, const int nfields)
{
 register int i;

 if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
  error(_("wrong number of fields on Zone line"));
  return false;
 }
 if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
  error(
_("\"Zone %s\" line and -l option are mutually exclusive"),
   TZDEFAULT);
  return false;
 }
 if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
  error(
_("\"Zone %s\" line and -p option are mutually exclusive"),
   TZDEFRULES);
  return false;
 }
 for (i = 0; i < nzones; ++i)
  if (zones[i].z_name != NULL &&
   strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
    error(
_("duplicate zone name %s (file \"%s\", line %d)"),
     fields[ZF_NAME],
     zones[i].z_filename,
     zones[i].z_linenum);
    return false;
  }
 return inzsub(fields, nfields, false);
}

static int
inzcont(register char **const fields, const int nfields)
{
 if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
  error(_("wrong number of fields on Zone continuation line"));
  return false;
 }
 return inzsub(fields, nfields, true);
}

static int
inzsub(register char **const fields, const int nfields, const int iscont)
{
 register char *  cp;
 static struct zone z;
 register int  i_gmtoff, i_rule, i_format;
 register int  i_untilyear, i_untilmonth;
 register int  i_untilday, i_untiltime;
 register int  hasuntil;

 if (iscont) {
  i_gmtoff = ZFC_GMTOFF;
  i_rule = ZFC_RULE;
  i_format = ZFC_FORMAT;
  i_untilyear = ZFC_TILYEAR;
  i_untilmonth = ZFC_TILMONTH;
  i_untilday = ZFC_TILDAY;
  i_untiltime = ZFC_TILTIME;
  z.z_name = NULL;
 } else {
  i_gmtoff = ZF_GMTOFF;
  i_rule = ZF_RULE;
  i_format = ZF_FORMAT;
  i_untilyear = ZF_TILYEAR;
  i_untilmonth = ZF_TILMONTH;
  i_untilday = ZF_TILDAY;
  i_untiltime = ZF_TILTIME;
  z.z_name = ecpyalloc(fields[ZF_NAME]);
 }
 z.z_filename = filename;
 z.z_linenum = linenum;
 z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true);
 if ((cp = strchr(fields[i_format], '%')) != 0) {
  if (*++cp != 's' || strchr(cp, '%') != 0) {
   error(_("invalid abbreviation format"));
   return false;
  }
 }
 z.z_rule = ecpyalloc(fields[i_rule]);
 z.z_format = ecpyalloc(fields[i_format]);
 if (max_format_len < strlen(z.z_format))
  max_format_len = strlen(z.z_format);
 hasuntil = nfields > i_untilyear;
 if (hasuntil) {
  z.z_untilrule.r_filename = filename;
  z.z_untilrule.r_linenum = linenum;
  rulesub(&z.z_untilrule,
   fields[i_untilyear],
   "only",
   "",
   (nfields > i_untilmonth) ?
   fields[i_untilmonth] : "Jan",
   (nfields > i_untilday) ? fields[i_untilday] : "1",
   (nfields > i_untiltime) ? fields[i_untiltime] : "0");
  z.z_untiltime = rpytime(&z.z_untilrule,
   z.z_untilrule.r_loyear);
  if (iscont && nzones > 0 &&
   z.z_untiltime > min_time &&
   z.z_untiltime < max_time &&
   zones[nzones - 1].z_untiltime > min_time &&
   zones[nzones - 1].z_untiltime < max_time &&
   zones[nzones - 1].z_untiltime >= z.z_untiltime) {
    error(_(
"Zone continuation line end time is not after end time of previous line"
     ));
    return false;
  }
 }
 zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
 zones[nzones++] = z;
 /*
** If there was an UNTIL field on this line,
** there's more information about the zone on the next line.
*/

 return hasuntil;
}

static void
inleap(register char ** const fields, const int nfields)
{
 register const char *  cp;
 register const struct lookup * lp;
 register int   i, j;
 zic_t    year;
 int    month, day;
 zic_t    dayoff, tod;
 zic_t    t;

 if (nfields != LEAP_FIELDS) {
  error(_("wrong number of fields on Leap line"));
  return;
 }
 dayoff = 0;
 cp = fields[LP_YEAR];
 if (sscanf(cp, scheck(cp, "%"SCNdZIC), &year) != 1) {
  /*
** Leapin' Lizards!
*/

  error(_("invalid leaping year"));
  return;
 }
 if (!leapseen || leapmaxyear < year)
  leapmaxyear = year;
 if (!leapseen || leapminyear > year)
  leapminyear = year;
 leapseen = true;
 j = EPOCH_YEAR;
 while (j != year) {
  if (year > j) {
   i = len_years[isleap(j)];
   ++j;
  } else {
   --j;
   i = -len_years[isleap(j)];
  }
  dayoff = oadd(dayoff, i);
 }
 if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
  error(_("invalid month name"));
  return;
 }
 month = lp->l_value;
 j = TM_JANUARY;
 while (j != month) {
  i = len_months[isleap(year)][j];
  dayoff = oadd(dayoff, i);
  ++j;
 }
 cp = fields[LP_DAY];
 if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
  day <= 0 || day > len_months[isleap(year)][month]) {
   error(_("invalid day of month"));
   return;
 }
 dayoff = oadd(dayoff, day - 1);
 if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
  error(_("time before zero"));
  return;
 }
 if (dayoff < min_time / SECSPERDAY) {
  error(_("time too small"));
  return;
 }
 if (dayoff > max_time / SECSPERDAY) {
  error(_("time too large"));
  return;
 }
 t = (zic_t) dayoff * SECSPERDAY;
 tod = gethms(fields[LP_TIME], _("invalid time of day"), false);
 cp = fields[LP_CORR];
 {
  register int positive;
  int  count;

  if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
   positive = false;
   count = 1;
  } else if (strcmp(cp, "--") == 0) {
   positive = false;
   count = 2;
  } else if (strcmp(cp, "+") == 0) {
   positive = true;
   count = 1;
  } else if (strcmp(cp, "++") == 0) {
   positive = true;
   count = 2;
  } else {
   error(_("illegal CORRECTION field on Leap line"));
   return;
  }
  if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
   error(_(
    "illegal Rolling/Stationary field on Leap line"
    ));
   return;
  }
  leapadd(tadd(t, tod), positive, lp->l_value, count);
 }
}

static void
inlink(register char **const fields, const int nfields)
{
 struct link l;

 if (nfields != LINK_FIELDS) {
  error(_("wrong number of fields on Link line"));
  return;
 }
 if (*fields[LF_FROM] == '\0') {
  error(_("blank FROM field on Link line"));
  return;
 }
 if (*fields[LF_TO] == '\0') {
  error(_("blank TO field on Link line"));
  return;
 }
 l.l_filename = filename;
 l.l_linenum = linenum;
 l.l_from = ecpyalloc(fields[LF_FROM]);
 l.l_to = ecpyalloc(fields[LF_TO]);
 links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
 links[nlinks++] = l;
}

static void
rulesub(register struct rule *const rp,
 const char *const loyearp,
 const char *const hiyearp,
 const char *const typep,
 const char *const monthp,
 const char *const dayp,
 const char *const timep)
{
 register const struct lookup * lp;
 register const char *  cp;
 register char *   dp;
 register char *   ep;

 if ((lp = byword(monthp, mon_names)) == NULL) {
  error(_("invalid month name"));
  return;
 }
 rp->r_month = lp->l_value;
 rp->r_todisstd = false;
 rp->r_todisgmt = false;
 dp = ecpyalloc(timep);
 if (*dp != '\0') {
  ep = dp + strlen(dp) - 1;
  switch (lowerit(*ep)) {
   case 's'/* Standard */
    rp->r_todisstd = true;
    rp->r_todisgmt = false;
    *ep = '\0';
    break;
   case 'w'/* Wall */
    rp->r_todisstd = false;
    rp->r_todisgmt = false;
    *ep = '\0';
    break;
   case 'g'/* Greenwich */
   case 'u'/* Universal */
   case 'z'/* Zulu */
    rp->r_todisstd = true;
    rp->r_todisgmt = true;
    *ep = '\0';
    break;
  }
 }
 rp->r_tod = gethms(dp, _("invalid time of day"), false);
 free(dp);
 /*
** Year work.
*/

 cp = loyearp;
 lp = byword(cp, begin_years);
 rp->r_lowasnum = lp == NULL;
 if (!rp->r_lowasnum) switch ((int) lp->l_value) {
  case YR_MINIMUM:
   rp->r_loyear = ZIC_MIN;
   break;
  case YR_MAXIMUM:
   rp->r_loyear = ZIC_MAX;
   break;
  default/* "cannot happen" */
   (void) fprintf(stderr,
    _("%s: panic: Invalid l_value %d\n"),
    progname, lp->l_value);
   exit(EXIT_FAILURE);
 } else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_loyear) != 1) {
  error(_("invalid starting year"));
  return;
 }
 cp = hiyearp;
 lp = byword(cp, end_years);
 rp->r_hiwasnum = lp == NULL;
 if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
  case YR_MINIMUM:
   rp->r_hiyear = ZIC_MIN;
   break;
  case YR_MAXIMUM:
   rp->r_hiyear = ZIC_MAX;
   break;
  case YR_ONLY:
   rp->r_hiyear = rp->r_loyear;
   break;
  default/* "cannot happen" */
   (void) fprintf(stderr,
    _("%s: panic: Invalid l_value %d\n"),
    progname, lp->l_value);
   exit(EXIT_FAILURE);
 } else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_hiyear) != 1) {
  error(_("invalid ending year"));
  return;
 }
 if (rp->r_loyear > rp->r_hiyear) {
  error(_("starting year greater than ending year"));
  return;
 }
 if (*typep == '\0')
  rp->r_yrtype = NULL;
 else {
  if (rp->r_loyear == rp->r_hiyear) {
   error(_("typed single year"));
   return;
  }
  rp->r_yrtype = ecpyalloc(typep);
 }
 /*
** Day work.
** Accept things such as:
** 1
** last-Sunday
** Sun<=20
** Sun>=7
*/

 dp = ecpyalloc(dayp);
 if ((lp = byword(dp, lasts)) != NULL) {
  rp->r_dycode = DC_DOWLEQ;
  rp->r_wday = lp->l_value;
  rp->r_dayofmonth = len_months[1][rp->r_month];
 } else {
  if ((ep = strchr(dp, '<')) != 0)
   rp->r_dycode = DC_DOWLEQ;
  else if ((ep = strchr(dp, '>')) != 0)
   rp->r_dycode = DC_DOWGEQ;
  else {
   ep = dp;
   rp->r_dycode = DC_DOM;
  }
  if (rp->r_dycode != DC_DOM) {
   *ep++ = 0;
   if (*ep++ != '=') {
    error(_("invalid day of month"));
    free(dp);
    return;
   }
   if ((lp = byword(dp, wday_names)) == NULL) {
    error(_("invalid weekday name"));
    free(dp);
    return;
   }
   rp->r_wday = lp->l_value;
  }
  if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
   rp->r_dayofmonth <= 0 ||
   (rp->r_dayofmonth > len_months[1][rp->r_month])) {
    error(_("invalid day of month"));
    free(dp);
    return;
  }
 }
 free(dp);
}

static void
convert(const int_fast32_t val, char *const buf)
{
 register int i;
 register int shift;
 unsigned char *const b = (unsigned char *) buf;

 for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
  b[i] = val >> shift;
}

static void
convert64(const zic_t val, char *const buf)
{
 register int i;
 register int shift;
 unsigned char *const b = (unsigned char *) buf;

 for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
  b[i] = val >> shift;
}

static void
puttzcode(const int_fast32_t val, FILE *const fp)
{
 char buf[4];

 convert(val, buf);
 (void) fwrite(buf, sizeof buf, 1, fp);
}

static void
puttzcode64(const zic_t val, FILE *const fp)
{
 char buf[8];

 convert64(val, buf);
 (void) fwrite(buf, sizeof buf, 1, fp);
}

static int
atcomp(const void *avp, const void *bvp)
{
 const zic_t a = ((const struct attype *) avp)->at;
 const zic_t b = ((const struct attype *) bvp)->at;

 return (a < b) ? -1 : (a > b);
}

static int
is32(const zic_t x)
{
 return INT32_MIN <= x && x <= INT32_MAX;
}

static void
writezone(const char *const name, const char *const string, char version)
{
 register FILE *   fp;
 register int   i, j;
 register int   leapcnt32, leapi32;
 register int   timecnt32, timei32;
 register int   pass;
 static char *   fullname;
 static const struct tzhead tzh0;
 static struct tzhead  tzh;
 zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
 void *typesptr = ats + timecnt;
 unsigned char *types = typesptr;

 /*
** Sort.
*/

 if (timecnt > 1)
  (void) qsort(attypes, timecnt, sizeof *attypes, atcomp);
 /*
** Optimize.
*/

 {
  int fromi;
  int toi;

  toi = 0;
  fromi = 0;
  while (fromi < timecnt && attypes[fromi].at < min_time)
   ++fromi;
  /*
** Remember that type 0 is reserved.
*/

  if (isdsts[1] == 0)
   while (fromi < timecnt && attypes[fromi].type == 1)
    ++fromi; /* handled by default rule */
  for ( ; fromi < timecnt; ++fromi) {
   if (toi != 0 && ((attypes[fromi].at +
    gmtoffs[attypes[toi - 1].type]) <=
    (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
    : attypes[toi - 2].type]))) {
     attypes[toi - 1].type =
      attypes[fromi].type;
     continue;
   }
   if (toi == 0 ||
    attypes[toi - 1].type != attypes[fromi].type)
     attypes[toi++] = attypes[fromi];
  }
  timecnt = toi;
 }
 /*
** Transfer.
*/

 for (i = 0; i < timecnt; ++i) {
  ats[i] = attypes[i].at;
  types[i] = attypes[i].type;
 }
 /*
** Correct for leap seconds.
*/

 for (i = 0; i < timecnt; ++i) {
  j = leapcnt;
  while (--j >= 0)
   if (ats[i] > trans[j] - corr[j]) {
    ats[i] = tadd(ats[i], corr[j]);
    break;
   }
 }
 /*
** Figure out 32-bit-limited starts and counts.
*/

 timecnt32 = timecnt;
 timei32 = 0;
 leapcnt32 = leapcnt;
 leapi32 = 0;
 while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
  --timecnt32;
 while (timecnt32 > 0 && !is32(ats[timei32])) {
  --timecnt32;
  ++timei32;
 }
 while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
  --leapcnt32;
 while (leapcnt32 > 0 && !is32(trans[leapi32])) {
  --leapcnt32;
  ++leapi32;
 }
 fullname = erealloc(fullname,
       strlen(directory) + 1 + strlen(name) + 1);
 (void) sprintf(fullname, "%s/%s", directory, name);
 /*
** Remove old file, if any, to snap links.
*/

 if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
  const char *e = strerror(errno);

  (void) fprintf(stderr, _("%s: Can't remove %s: %s\n"),
   progname, fullname, e);
  exit(EXIT_FAILURE);
 }
 if ((fp = fopen(fullname, "wb")) == NULL) {
  if (mkdirs(fullname) != 0)
   exit(EXIT_FAILURE);
  if ((fp = fopen(fullname, "wb")) == NULL) {
   const char *e = strerror(errno);

   (void) fprintf(stderr, _("%s: Can't create %s: %s\n"),
    progname, fullname, e);
   exit(EXIT_FAILURE);
  }
 }
 for (pass = 1; pass <= 2; ++pass) {
  register int thistimei, thistimecnt;
  register int thisleapi, thisleapcnt;
  register int thistimelim, thisleaplim;
  int  writetype[TZ_MAX_TYPES];
  int  typemap[TZ_MAX_TYPES];
  register int thistypecnt;
  char  thischars[TZ_MAX_CHARS];
  char  thischarcnt;
  int   indmap[TZ_MAX_CHARS];

  if (pass == 1) {
   thistimei = timei32;
   thistimecnt = timecnt32;
   thisleapi = leapi32;
   thisleapcnt = leapcnt32;
  } else {
   thistimei = 0;
   thistimecnt = timecnt;
   thisleapi = 0;
   thisleapcnt = leapcnt;
  }
  thistimelim = thistimei + thistimecnt;
  thisleaplim = thisleapi + thisleapcnt;
  /*
** Remember that type 0 is reserved.
*/

  writetype[0] = false;
  for (i = 1; i < typecnt; ++i)
   writetype[i] = thistimecnt == timecnt;
  if (thistimecnt == 0) {
   /*
** No transition times fall in the current
** (32- or 64-bit) window.
*/

   if (typecnt != 0)
    writetype[typecnt - 1] = true;
  } else {
   for (i = thistimei - 1; i < thistimelim; ++i)
    if (i >= 0)
     writetype[types[i]] = true;
   /*
** For America/Godthab and Antarctica/Palmer
*/

   /*
** Remember that type 0 is reserved.
*/

   if (thistimei == 0)
    writetype[1] = true;
  }
#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
  /*
** For some pre-2011 systems: if the last-to-be-written
** standard (or daylight) type has an offset different from the
** most recently used offset,
** append an (unused) copy of the most recently used type
** (to help get global "altzone" and "timezone" variables
** set correctly).
*/

  {
   register int mrudst, mrustd, hidst, histd, type;

   hidst = histd = mrudst = mrustd = -1;
   for (i = thistimei; i < thistimelim; ++i)
    if (isdsts[types[i]])
     mrudst = types[i];
    else mrustd = types[i];
   for (i = 0; i < typecnt; ++i)
    if (writetype[i]) {
     if (isdsts[i])
      hidst = i;
     else histd = i;
    }
   if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
    gmtoffs[hidst] != gmtoffs[mrudst]) {
     isdsts[mrudst] = -1;
     type = addtype(gmtoffs[mrudst],
#ifdef ICU
      rawoffs[mrudst], dstoffs[mrudst],
#endif
      &chars[abbrinds[mrudst]],
      true,
      ttisstds[mrudst],
      ttisgmts[mrudst]);
     isdsts[mrudst] = true;
     writetype[type] = true;
   }
   if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
    gmtoffs[histd] != gmtoffs[mrustd]) {
     isdsts[mrustd] = -1;
     type = addtype(gmtoffs[mrustd],
#ifdef ICU
      rawoffs[mrudst], dstoffs[mrudst],
#endif
      &chars[abbrinds[mrustd]],
      false,
      ttisstds[mrustd],
      ttisgmts[mrustd]);
     isdsts[mrustd] = false;
     writetype[type] = true;
   }
  }
#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
  thistypecnt = 0;
  /*
** Potentially, set type 0 to that of lowest-valued time.
*/

  if (thistimei > 0) {
   for (i = 1; i < typecnt; ++i)
    if (writetype[i] && !isdsts[i])
     break;
   if (i != types[thistimei - 1]) {
    i = types[thistimei - 1];
    gmtoffs[0] = gmtoffs[i];
    isdsts[0] = isdsts[i];
    ttisstds[0] = ttisstds[i];
    ttisgmts[0] = ttisgmts[i];
    abbrinds[0] = abbrinds[i];
    writetype[0] = true;
    writetype[i] = false;
   }
  }
  for (i = 0; i < typecnt; ++i)
   typemap[i] = writetype[i] ?  thistypecnt++ : 0;
  for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
   indmap[i] = -1;
  thischarcnt = 0;
  for (i = 0; i < typecnt; ++i) {
   register char * thisabbr;

   if (!writetype[i])
    continue;
   if (indmap[abbrinds[i]] >= 0)
    continue;
   thisabbr = &chars[abbrinds[i]];
   for (j = 0; j < thischarcnt; ++j)
    if (strcmp(&thischars[j], thisabbr) == 0)
     break;
   if (j == thischarcnt) {
    (void) strcpy(&thischars[(int) thischarcnt],
     thisabbr);
    thischarcnt += strlen(thisabbr) + 1;
   }
   indmap[abbrinds[i]] = j;
  }
#define DO(field) ((void) fwrite(tzh.field, sizeof tzh.field, 1, fp))
  tzh = tzh0;
#ifdef ICU
  * (ICUZoneinfoVersion*) &tzh.tzh_reserved = TZ_ICU_VERSION;
  (void) strncpy(tzh.tzh_magic, TZ_ICU_MAGIC, sizeof tzh.tzh_magic);
#else
  (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
#endif
  tzh.tzh_version[0] = version;
  convert(thistypecnt, tzh.tzh_ttisgmtcnt);
  convert(thistypecnt, tzh.tzh_ttisstdcnt);
  convert(thisleapcnt, tzh.tzh_leapcnt);
  convert(thistimecnt, tzh.tzh_timecnt);
  convert(thistypecnt, tzh.tzh_typecnt);
  convert(thischarcnt, tzh.tzh_charcnt);
  DO(tzh_magic);
  DO(tzh_version);
  DO(tzh_reserved);
  DO(tzh_ttisgmtcnt);
  DO(tzh_ttisstdcnt);
  DO(tzh_leapcnt);
  DO(tzh_timecnt);
  DO(tzh_typecnt);
  DO(tzh_charcnt);
#undef DO
  for (i = thistimei; i < thistimelim; ++i)
   if (pass == 1)
    puttzcode(ats[i], fp);
   else puttzcode64(ats[i], fp);
  for (i = thistimei; i < thistimelim; ++i) {
   unsigned char uc;

   uc = typemap[types[i]];
   (void) fwrite(&uc, sizeof uc, 1, fp);
  }
  for (i = 0; i < typecnt; ++i)
   if (writetype[i]) {
#ifdef ICU
    puttzcode(rawoffs[i], fp);
    puttzcode(dstoffs[i], fp);
#else
    puttzcode(gmtoffs[i], fp);
#endif
    (void) putc(isdsts[i], fp);
    (void) putc((unsigned char) indmap[abbrinds[i]], fp);
   }
  if (thischarcnt != 0)
   (void) fwrite(thischars, sizeof thischars[0],
          thischarcnt, fp);
  for (i = thisleapi; i < thisleaplim; ++i) {
   register zic_t todo;

   if (roll[i]) {
    if (timecnt == 0 || trans[i] < ats[0]) {
     j = 0;
     while (isdsts[j])
      if (++j >= typecnt) {
       j = 0;
       break;
      }
    } else {
     j = 1;
     while (j < timecnt &&
      trans[i] >= ats[j])
       ++j;
     j = types[j - 1];
    }
    todo = tadd(trans[i], -gmtoffs[j]);
   } else todo = trans[i];
   if (pass == 1)
    puttzcode(todo, fp);
   else puttzcode64(todo, fp);
   puttzcode(corr[i], fp);
  }
  for (i = 0; i < typecnt; ++i)
   if (writetype[i])
    (void) putc(ttisstds[i], fp);
  for (i = 0; i < typecnt; ++i)
   if (writetype[i])
    (void) putc(ttisgmts[i], fp);
 }
 (void) fprintf(fp, "\n%s\n", string);
 if (ferror(fp) || fclose(fp)) {
  (void) fprintf(stderr, _("%s: Error writing %s\n"),
   progname, fullname);
  exit(EXIT_FAILURE);
 }
 free(ats);
}

static void
doabbr(char *const abbr, const char *const format, const char *const letters,
       const int isdst, const int doquotes)
{
 register char * cp;
 register char * slashp;
 register int len;

 slashp = strchr(format, '/');
 if (slashp == NULL) {
  if (letters == NULL)
   (void) strcpy(abbr, format);
  else (void) sprintf(abbr, format, letters);
 } else if (isdst) {
  (void) strcpy(abbr, slashp + 1);
 } else {
  if (slashp > format)
   (void) strncpy(abbr, format, slashp - format);
  abbr[slashp - format] = '\0';
 }
 if (!doquotes)
  return;
 for (cp = abbr; *cp != '\0'; ++cp)
  if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
   strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
    break;
 len = strlen(abbr);
 if (len > 0 && *cp == '\0')
  return;
 abbr[len + 2] = '\0';
 abbr[len + 1] = '>';
 for ( ; len > 0; --len)
  abbr[len] = abbr[len - 1];
 abbr[0] = '<';
}

static void
updateminmax(const zic_t x)
{
 if (min_year > x)
  min_year = x;
 if (max_year < x)
  max_year = x;
}

static int
stringoffset(char *result, zic_t offset)
{
 register int hours;
 register int minutes;
 register int seconds;

 result[0] = '\0';
 if (offset < 0) {
  (void) strcpy(result, "-");
  offset = -offset;
 }
 seconds = offset % SECSPERMIN;
 offset /= SECSPERMIN;
 minutes = offset % MINSPERHOUR;
 offset /= MINSPERHOUR;
 hours = offset;
 if (hours >= HOURSPERDAY * DAYSPERWEEK) {
  result[0] = '\0';
  return -1;
 }
 (void) sprintf(end(result), "%d", hours);
 if (minutes != 0 || seconds != 0) {
  (void) sprintf(end(result), ":%02d", minutes);
  if (seconds != 0)
   (void) sprintf(end(result), ":%02d", seconds);
 }
 return 0;
}

static int
stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
    const zic_t gmtoff)
{
 register zic_t tod = rp->r_tod;
 register int compat = 0;

 result = end(result);
 if (rp->r_dycode == DC_DOM) {
  register int month, total;

  if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
   return -1;
  total = 0;
  for (month = 0; month < rp->r_month; ++month)
   total += len_months[0][month];
  /* Omit the "J" in Jan and Feb, as that's shorter.  */
  if (rp->r_month <= 1)
    (void) sprintf(result, "%d", total + rp->r_dayofmonth - 1);
  else
    (void) sprintf(result, "J%d", total + rp->r_dayofmonth);
 } else {
  register int week;
  register int wday = rp->r_wday;
  register int wdayoff;

  if (rp->r_dycode == DC_DOWGEQ) {
   wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
   if (wdayoff)
    compat = 2013;
   wday -= wdayoff;
   tod += wdayoff * SECSPERDAY;
   week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
  } else if (rp->r_dycode == DC_DOWLEQ) {
   if (rp->r_dayofmonth == len_months[1][rp->r_month])
    week = 5;
   else {
    wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
    if (wdayoff)
     compat = 2013;
    wday -= wdayoff;
    tod += wdayoff * SECSPERDAY;
    week = rp->r_dayofmonth / DAYSPERWEEK;
   }
  } else return -1; /* "cannot happen" */
  if (wday < 0)
   wday += DAYSPERWEEK;
  (void) sprintf(result, "M%d.%d.%d",
   rp->r_month + 1, week, wday);
 }
 if (rp->r_todisgmt)
  tod += gmtoff;
 if (rp->r_todisstd && rp->r_stdoff == 0)
  tod += dstoff;
 if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
  (void) strcat(result, "/");
  if (stringoffset(end(result), tod) != 0)
   return -1;
  if (tod < 0) {
   if (compat < 2013)
    compat = 2013;
  } else if (SECSPERDAY <= tod) {
   if (compat < 1994)
    compat = 1994;
  }
 }
 return compat;
}

static int
rule_cmp(struct rule const *a, struct rule const *b)
{
 if (!a)
  return -!!b;
 if (!b)
  return 1;
 if (a->r_hiyear != b->r_hiyear)
  return a->r_hiyear < b->r_hiyear ? -1 : 1;
 if (a->r_month - b->r_month != 0)
  return a->r_month - b->r_month;
 return a->r_dayofmonth - b->r_dayofmonth;
}

enum { YEAR_BY_YEAR_ZONE = 1 };

static int
stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
{
 register const struct zone * zp;
 register struct rule *  rp;
 register struct rule *  stdrp;
 register struct rule *  dstrp;
 register int   i;
 register const char *  abbrvar;
 register int   compat = 0;
 register int   c;
 struct rule   stdr, dstr;

 result[0] = '\0';
 zp = zpfirst + zonecount - 1;
 stdrp = dstrp = NULL;
 for (i = 0; i < zp->z_nrules; ++i) {
  rp = &zp->z_rules[i];
  if (rp->r_hiwasnum || rp->r_hiyear != ZIC_MAX)
   continue;
  if (rp->r_yrtype != NULL)
   continue;
  if (rp->r_stdoff == 0) {
   if (stdrp == NULL)
    stdrp = rp;
   else return -1;
  } else {
   if (dstrp == NULL)
    dstrp = rp;
   else return -1;
  }
 }
 if (stdrp == NULL && dstrp == NULL) {
  /*
** There are no rules running through "max".
** Find the latest std rule in stdabbrrp
** and latest rule of any type in stdrp.
*/

  register struct rule *stdabbrrp = NULL;
  for (i = 0; i < zp->z_nrules; ++i) {
   rp = &zp->z_rules[i];
   if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
    stdabbrrp = rp;
   if (rule_cmp(stdrp, rp) < 0)
    stdrp = rp;
  }
  /*
** Horrid special case: if year is 2037,
** presume this is a zone handled on a year-by-year basis;
** do not try to apply a rule to the zone.
*/

  if (stdrp != NULL && stdrp->r_hiyear == 2037)
   return YEAR_BY_YEAR_ZONE;

  if (stdrp != NULL && stdrp->r_stdoff != 0) {
   /* Perpetual DST.  */
   dstr.r_month = TM_JANUARY;
   dstr.r_dycode = DC_DOM;
   dstr.r_dayofmonth = 1;
   dstr.r_tod = 0;
   dstr.r_todisstd = dstr.r_todisgmt = false;
   dstr.r_stdoff = stdrp->r_stdoff;
   dstr.r_abbrvar = stdrp->r_abbrvar;
   stdr.r_month = TM_DECEMBER;
   stdr.r_dycode = DC_DOM;
   stdr.r_dayofmonth = 31;
   stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
   stdr.r_todisstd = stdr.r_todisgmt = false;
   stdr.r_stdoff = 0;
   stdr.r_abbrvar
     = (stdabbrrp ? stdabbrrp->r_abbrvar : "");
   dstrp = &dstr;
   stdrp = &stdr;
  }
 }
 if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
  return -1;
 abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
 doabbr(result, zp->z_format, abbrvar, falsetrue);
 if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
  result[0] = '\0';
  return -1;
 }
 if (dstrp == NULL)
  return compat;
 doabbr(end(result), zp->z_format, dstrp->r_abbrvar, truetrue);
 if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
  if (stringoffset(end(result),
   -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
    result[0] = '\0';
    return -1;
  }
 (void) strcat(result, ",");
 c = stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
 if (c < 0) {
  result[0] = '\0';
  return -1;
 }
 if (compat < c)
  compat = c;
 (void) strcat(result, ",");
 c = stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff);
 if (c < 0) {
  result[0] = '\0';
  return -1;
 }
 if (compat < c)
  compat = c;
 return compat;
}

static void
outzone(const struct zone * const zpfirst, const int zonecount)
{
 register const struct zone * zp;
 register struct rule *  rp;
 register int   i, j;
 register int   usestart, useuntil;
 register zic_t   starttime, untiltime;
 register zic_t   gmtoff;
 register zic_t   stdoff;
 register zic_t   year;
 register zic_t   startoff;
 register int   startttisstd;
 register int   startttisgmt;
 register int   type;
 register char *   startbuf;
 register char *   ab;
 register char *   envvar;
 register int   max_abbr_len;
 register int   max_envvar_len;
 register int   prodstic; /* all rules are min to max */
 register int   compat;
 register int   do_extend;
 register char   version;
#ifdef ICU
 int      finalRuleYear, finalRuleIndex;
 const struct rule*  finalRule1;
 const struct rule*  finalRule2;
#endif

 max_abbr_len = 2 + max_format_len + max_abbrvar_len;
 max_envvar_len = 2 * max_abbr_len + 5 * 9;
 startbuf = emalloc(max_abbr_len + 1);
 ab = emalloc(max_abbr_len + 1);
 envvar = emalloc(max_envvar_len + 1);
 INITIALIZE(untiltime);
 INITIALIZE(starttime);
 /*
** Now. . .finally. . .generate some useful data!
*/

 timecnt = 0;
 typecnt = 0;
 charcnt = 0;
 prodstic = zonecount == 1;
 /*
** Thanks to Earl Chew
** for noting the need to unconditionally initialize startttisstd.
*/

 startttisstd = false;
 startttisgmt = false;
 min_year = max_year = EPOCH_YEAR;
 if (leapseen) {
  updateminmax(leapminyear);
  updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
 }
 /*
** Reserve type 0.
*/

 gmtoffs[0] = isdsts[0] = ttisstds[0] = ttisgmts[0] = abbrinds[0] = -1;
 typecnt = 1;
 for (i = 0; i < zonecount; ++i) {
  zp = &zpfirst[i];
  if (i < zonecount - 1)
   updateminmax(zp->z_untilrule.r_loyear);
  for (j = 0; j < zp->z_nrules; ++j) {
   rp = &zp->z_rules[j];
   if (rp->r_lowasnum)
    updateminmax(rp->r_loyear);
   if (rp->r_hiwasnum)
    updateminmax(rp->r_hiyear);
   if (rp->r_lowasnum || rp->r_hiwasnum)
    prodstic = false;
  }
 }
 /*
** Generate lots of data if a rule can't cover all future times.
*/

 compat = stringzone(envvar, zpfirst, zonecount);
 version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION;
 do_extend = compat < 0 || compat == YEAR_BY_YEAR_ZONE;
#ifdef ICU
 do_extend = 0;
#endif
 if (noise) {
  if (!*envvar)
   warning("%s %s",
    _("no POSIX environment variable for zone"),
    zpfirst->z_name);
  else if (compat != 0 && compat != YEAR_BY_YEAR_ZONE) {
   /* Circa-COMPAT clients, and earlier clients, might
   not work for this zone when given dates before
   1970 or after 2038.  */

   warning(_("%s: pre-%d clients may mishandle"
      " distant timestamps"),
    zpfirst->z_name, compat);
  }
 }
 if (do_extend) {
  /*
** Search through a couple of extra years past the obvious
** 400, to avoid edge cases.  For example, suppose a non-POSIX
** rule applies from 2012 onwards and has transitions in March
** and September, plus some one-off transitions in November
** 2013.  If zic looked only at the last 400 years, it would
** set max_year=2413, with the intent that the 400 years 2014
** through 2413 will be repeated.  The last transition listed
** in the tzfile would be in 2413-09, less than 400 years
** after the last one-off transition in 2013-11.  Two years
** might be overkill, but with the kind of edge cases
** available we're not sure that one year would suffice.
*/

  enum { years_of_observations = YEARSPERREPEAT + 2 };

  if (min_year >= ZIC_MIN + years_of_observations)
   min_year -= years_of_observations;
  else min_year = ZIC_MIN;
  if (max_year <= ZIC_MAX - years_of_observations)
   max_year += years_of_observations;
  else max_year = ZIC_MAX;
  /*
** Regardless of any of the above,
** for a "proDSTic" zone which specifies that its rules
** always have and always will be in effect,
** we only need one cycle to define the zone.
*/

  if (prodstic) {
   min_year = 1900;
   max_year = min_year + years_of_observations;
  }
 }
 /*
** For the benefit of older systems,
** generate data from 1900 through 2037.
*/

 if (min_year > 1900)
  min_year = 1900;
 if (max_year < 2037)
  max_year = 2037;
 for (i = 0; i < zonecount; ++i) {
  /*
** A guess that may well be corrected later.
*/

  stdoff = 0;
  zp = &zpfirst[i];
  usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
  useuntil = i < (zonecount - 1);
  if (useuntil && zp->z_untiltime <= min_time)
   continue;
  gmtoff = zp->z_gmtoff;
  eat(zp->z_filename, zp->z_linenum);
  *startbuf = '\0';
  startoff = zp->z_gmtoff;
#ifdef ICU
  finalRuleYear = finalRuleIndex = -1;
  finalRule1 = finalRule2 = NULL;
  if (i == (zonecount - 1)) { /* !useuntil */
   /* Look for exactly 2 rules that end at 'max' and
 * note them. Determine max(r_loyear) for the 2 of
 * them. */

   for (j=0; j<zp->z_nrules; ++j) {
    rp = &zp->z_rules[j];
    if (rp->r_hiyear == ZIC_MAX) {
     if (rp->r_loyear > finalRuleYear) {
      finalRuleYear = rp->r_loyear;
     }
     if (finalRule1 == NULL) {
      finalRule1 = rp;
     } else if (finalRule2 == NULL) {
      finalRule2 = rp;
     } else {
      error("more than two max rules found (ICU)");
      exit(EXIT_FAILURE);
     }
    } else if (rp->r_hiyear >= finalRuleYear) {
     /* There might be an overriding non-max rule
 * to be applied to a specific year after one of
 * max rule's start year. For example,
 *
 * Rule Foo 2010 max ...
 * Rule Foo 2015 only ...
 *
 * In this case, we need to change the start year of
 * the final (max) rules to the next year. */

     finalRuleYear = rp->r_hiyear + 1;

     /* When above adjustment is done, max_year might need
 * to be adjusted, so the final rule will be properly
 * evaluated and emitted by the later code block.
 *
 * Note: This may push the start year of the final
 * rules ahead by 1 year unnecessarily. For example,
 * If there are two rules, non-max rule and max rule
 * starting in the same year, such as
 *
 * Rule Foo 2010 only ....
 * Rule Foo 2010 max ....
 *
 * In this case, the final (max) rule actually starts
 * in 2010, instead of 2010. We could make this tool
 * more intelligent to detect such situation. But pushing
 * final rule start year to 1 year ahead (in the worst case)
 * will just populate a few extra transitions, and it still
 * works fine. So for now, we're not trying to put additional
 * logic to optimize the case.
 */

     if (max_year < finalRuleYear) {
      max_year = finalRuleYear;
     }
    }
   }
   if (finalRule1 != NULL) {
    if (finalRule2 == NULL) {
     warning("only one max rule found (ICU)");
     finalRuleYear = finalRuleIndex = -1;
     finalRule1 = NULL;
    } else {
     if (finalRule1->r_stdoff == finalRule2->r_stdoff) {
      /* America/Resolute in 2009a uses a pair of rules
 * which does not change the offset.  ICU ignores
 * such rules without actual time transitions. */

      finalRuleYear = finalRuleIndex = -1;
      finalRule1 = finalRule2 = NULL; 
     } else {
      /* Swap if necessary so finalRule1 occurs before
 * finalRule2 */

      if (finalRule1->r_month > finalRule2->r_month) {
       const struct rule* t = finalRule1;
       finalRule1 = finalRule2;
       finalRule2 = t;
      }
      /* Add final rule to our list */
      finalRuleIndex = add_icu_final_rules(finalRule1, finalRule2);
     }
    }
   }
  }
#endif

  if (zp->z_nrules == 0) {
   stdoff = zp->z_stdoff;
   doabbr(startbuf, zp->z_format,
          NULL, stdoff != 0, false);
   type = addtype(oadd(zp->z_gmtoff, stdoff),
#ifdef ICU
    zp->z_gmtoff, stdoff,
#endif
    startbuf, stdoff != 0, startttisstd,
    startttisgmt);
   if (usestart) {
    addtt(starttime, type);
    usestart = false;
   } else if (stdoff != 0)
    addtt(min_time, type);
  } else for (year = min_year; year <= max_year; ++year) {
   if (useuntil && year > zp->z_untilrule.r_hiyear)
    break;
   /*
** Mark which rules to do in the current year.
** For those to do, calculate rpytime(rp, year);
*/

   for (j = 0; j < zp->z_nrules; ++j) {
    rp = &zp->z_rules[j];
    eats(zp->z_filename, zp->z_linenum,
     rp->r_filename, rp->r_linenum);
    rp->r_todo = year >= rp->r_loyear &&
      year <= rp->r_hiyear &&
      yearistype(year, rp->r_yrtype);
    if (rp->r_todo)
     rp->r_temp = rpytime(rp, year);
   }
   for ( ; ; ) {
    register int k;
    register zic_t jtime, ktime;
    register zic_t offset;

    INITIALIZE(ktime);
    if (useuntil) {
     /*
** Turn untiltime into UT
** assuming the current gmtoff and
** stdoff values.
*/

     untiltime = zp->z_untiltime;
     if (!zp->z_untilrule.r_todisgmt)
      untiltime = tadd(untiltime,
       -gmtoff);
     if (!zp->z_untilrule.r_todisstd)
      untiltime = tadd(untiltime,
       -stdoff);
    }
    /*
** Find the rule (of those to do, if any)
** that takes effect earliest in the year.
*/

    k = -1;
    for (j = 0; j < zp->z_nrules; ++j) {
     rp = &zp->z_rules[j];
     if (!rp->r_todo)
      continue;
     eats(zp->z_filename, zp->z_linenum,
      rp->r_filename, rp->r_linenum);
     offset = rp->r_todisgmt ? 0 : gmtoff;
     if (!rp->r_todisstd)
      offset = oadd(offset, stdoff);
     jtime = rp->r_temp;
     if (jtime == min_time ||
      jtime == max_time)
       continue;
     jtime = tadd(jtime, -offset);
     if (k < 0 || jtime < ktime) {
      k = j;
      ktime = jtime;
     }
    }
    if (k < 0)
     break/* go on to next year */
    rp = &zp->z_rules[k];
    rp->r_todo = false;
    if (useuntil && ktime >= untiltime)
     break;
    stdoff = rp->r_stdoff;
    if (usestart && ktime == starttime)
     usestart = false;
    if (usestart) {
     if (ktime < starttime) {
      startoff = oadd(zp->z_gmtoff,
       stdoff);
      doabbr(startbuf, zp->z_format,
       rp->r_abbrvar,
       rp->r_stdoff != 0,
       false);
      continue;
     }
     if (*startbuf == '\0' &&
      startoff == oadd(zp->z_gmtoff,
      stdoff)) {
       doabbr(startbuf,
        zp->z_format,
        rp->r_abbrvar,
        rp->r_stdoff !=
        0,
        false);
     }
    }
#ifdef ICU
    if (year >= finalRuleYear && rp == finalRule1) {
     /* We want to shift final year 1 year after
 * the actual final rule takes effect (year + 1),
 * because the previous type is valid until the first
 * transition defined by the final rule.  Otherwise
 * we may see unexpected offset shift at the
 * beginning of the year when the final rule takes
 * effect.
 *
 * Note: This may results some 64bit second transitions
 * at the very end (year 2038). ICU 4.2 or older releases
 * cannot handle 64bit second transitions and they are
 * dropped from zoneinfo.txt. */

     emit_icu_zone(icuFile,
       zpfirst->z_name, zp->z_gmtoff,
       rp, finalRuleIndex, year + 1);
     /* only emit this for the first year */
     finalRule1 = NULL;
    }
#endif
    eats(zp->z_filename, zp->z_linenum,
     rp->r_filename, rp->r_linenum);
    doabbr(ab, zp->z_format, rp->r_abbrvar,
     rp->r_stdoff != 0, false);
    offset = oadd(zp->z_gmtoff, rp->r_stdoff);
#ifdef ICU
    type = addtype(offset, zp->z_gmtoff, rp->r_stdoff,
     ab, rp->r_stdoff != 0,
     rp->r_todisstd, rp->r_todisgmt);
#else
    type = addtype(offset, ab, rp->r_stdoff != 0,
     rp->r_todisstd, rp->r_todisgmt);
#endif
    addtt(ktime, type);
   }
  }
  if (usestart) {
   if (*startbuf == '\0' &&
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=92 G=93

¤ Dauer der Verarbeitung: 0.47 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.