#define linkat(fromdir, from, todir, to, flag) \
(itssymlink(from) ? (errno = ENOTSUP, -1) : link(from, to))
#endif
+/* Port to native MS-Windows and to ancient UNIX. */
+#if !defined S_ISDIR && defined S_IFDIR && defined S_IFMT
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
/* The maximum ptrdiff_t value, for pre-C99 platforms. */
#ifndef PTRDIFF_MAX
bool r_todisstd; /* above is standard time if 1 or wall clock
* time if 0 */
bool r_todisgmt; /* above is GMT if 1 or local time if 0 */
- zic_t r_stdoff; /* offset from standard time */
+ bool r_isdst; /* is this daylight saving time? */
+ zic_t r_stdoff; /* offset from default time (which is usually
+ * standard time) */
const char *r_abbrvar; /* variable part of abbreviation */
bool r_todo; /* a rule to do (used in outzone) */
const char *z_name;
zic_t z_gmtoff;
- const char *z_rule;
+ char *z_rule;
const char *z_format;
char z_format_specifier;
+ bool z_isdst;
zic_t z_stdoff;
struct rule *z_rules;
static char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring,
bool);
+static zic_t getstdoff(char *, bool *);
static void infile(const char *filename);
static void inleap(char **fields, int nfields);
static void inlink(char **fields, int nfields);
fprintf(stream,
_("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -P ] \\\n"
"\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
- "\t[ -L leapseconds ] [ filename ... ]\n\n"
+ "\t[ -t localtime-link ] [ -L leapseconds ] [ filename ... ]\n\n"
"Report bugs to %s.\n"),
progname, progname, PACKAGE_BUGREPORT);
if (status == EXIT_SUCCESS)
static const char *lcltime;
static const char *directory;
static const char *leapsec;
+static const char *tzdefault;
static const char *yitcommand;
int
{
usage(stdout, EXIT_SUCCESS);
}
- while ((c = getopt(argc, argv, "d:l:p:L:vPsy:")) != EOF && c != -1)
+ while ((c = getopt(argc, argv, "d:l:L:p:Pst:vy:")) != EOF && c != -1)
switch (c)
{
default:
return EXIT_FAILURE;
}
break;
+ case 't':
+ if (tzdefault != NULL)
+ {
+ fprintf(stderr,
+ _("%s: More than one -t option"
+ " specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ tzdefault = optarg;
+ break;
case 'y':
if (yitcommand == NULL)
{
usage(stderr, EXIT_FAILURE); /* usage message by request */
if (directory == NULL)
directory = "data";
+ if (tzdefault == NULL)
+ tzdefault = TZDEFAULT;
if (yitcommand == NULL)
yitcommand = "yearistype";
if (lcltime != NULL)
{
eat(_("command line"), 1);
- dolink(lcltime, TZDEFAULT, true);
+ dolink(lcltime, tzdefault, true);
}
if (psxrules != NULL)
{
char const *contents = absolute ? fromfield : linkalloc;
int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
- if (symlink_errno == ENOENT && !todirs_made)
+ if (!todirs_made
+ && (symlink_errno == ENOENT || symlink_errno == ENOTSUP))
{
mkdirs(tofield, true);
- symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
+ if (symlink_errno == ENOENT)
+ symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
}
free(linkalloc);
if (symlink_errno == 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);
+ zp->z_stdoff = getstdoff(zp->z_rule, &zp->z_isdst);
/*
* Note, though, that if there's no rule, a '%s' in the format is
{
/* PG: make hh be int not zic_t to avoid sscanf portability issues */
int hh;
- int mm,
- ss,
- sign;
- char xs;
+ int sign,
+ mm = 0,
+ ss = 0;
+ char hhx,
+ mmx,
+ ssx,
+ xr = '0',
+ xs;
+ int tenths = 0;
+ bool ok = true;
if (string == NULL || *string == '\0')
return 0;
}
else
sign = 1;
- if (sscanf(string, "%d%c", &hh, &xs) == 1)
- mm = ss = 0;
- else if (sscanf(string, "%d:%d%c", &hh, &mm, &xs) == 2)
- ss = 0;
- else if (sscanf(string, "%d:%d:%d%c", &hh, &mm, &ss, &xs)
- != 3)
+ switch (sscanf(string,
+ "%d%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+ &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs))
+ {
+ default:
+ ok = false;
+ break;
+ case 8:
+ ok = '0' <= xr && xr <= '9';
+ /* fallthrough */
+ case 7:
+ ok &= ssx == '.';
+ if (ok && noise)
+ warning(_("fractional seconds rejected by"
+ " pre-2018 versions of zic"));
+ /* fallthrough */
+ case 5:
+ ok &= mmx == ':';
+ /* fallthrough */
+ case 3:
+ ok &= hhx == ':';
+ /* fallthrough */
+ case 1:
+ break;
+ }
+ if (!ok)
{
error("%s", errstring);
return 0;
return 0;
}
#endif
+ ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
if (noise && (hh > HOURSPERDAY ||
(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
sign * (mm * SECSPERMIN + ss));
}
+static zic_t
+getstdoff(char *field, bool *isdst)
+{
+ int dst = -1;
+ zic_t stdoff;
+ size_t fieldlen = strlen(field);
+
+ if (fieldlen != 0)
+ {
+ char *ep = field + fieldlen - 1;
+
+ switch (*ep)
+ {
+ case 'd':
+ dst = 1;
+ *ep = '\0';
+ break;
+ case 's':
+ dst = 0;
+ *ep = '\0';
+ break;
+ }
+ }
+ stdoff = gethms(field, _("invalid saved time"), true);
+ *isdst = dst < 0 ? stdoff != 0 : dst;
+ return stdoff;
+}
+
static void
inrule(char **fields, int nfields)
{
}
r.r_filename = filename;
r.r_linenum = linenum;
- r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
+ r.r_stdoff = getstdoff(fields[RF_STDOFF], &r.r_isdst);
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]);
error(_("wrong number of fields on Zone line"));
return false;
}
- if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL)
+ if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0)
{
error(
_("\"Zone %s\" line and -l option are mutually exclusive"),
- TZDEFAULT);
+ tzdefault);
return false;
}
if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL)
}
#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
tzh = tzh0;
- strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+ memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
tzh.tzh_version[0] = version;
convert(thistypecnt, tzh.tzh_ttisgmtcnt);
convert(thistypecnt, tzh.tzh_ttisstdcnt);
offset /= MINSPERHOUR;
if (100 <= offset)
{
- error(_("%%z UTC offset magnitude exceeds 99:59:59"));
+ error(_("%%z UT offset magnitude exceeds 99:59:59"));
return "%z";
}
else
static size_t
doabbr(char *abbr, struct zone const *zp, char const *letters,
- zic_t stdoff, bool doquotes)
+ bool isdst, zic_t stdoff, bool doquotes)
{
char *cp;
char *slashp;
letters = "%s";
sprintf(abbr, format, letters);
}
- else if (stdoff != 0)
+ else if (isdst)
{
strcpy(abbr, slashp + 1);
}
}
if (rp->r_todisgmt)
tod += gmtoff;
- if (rp->r_todisstd && rp->r_stdoff == 0)
+ if (rp->r_todisstd && !rp->r_isdst)
tod += dstoff;
if (tod != 2 * SECSPERMIN * MINSPERHOUR)
{
continue;
if (rp->r_yrtype != NULL)
continue;
- if (rp->r_stdoff == 0)
+ if (!rp->r_isdst)
{
if (stdrp == NULL)
stdrp = rp;
for (i = 0; i < zp->z_nrules; ++i)
{
rp = &zp->z_rules[i];
- if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
+ if (!rp->r_isdst && rule_cmp(stdabbrrp, rp) < 0)
stdabbrrp = rp;
if (rule_cmp(stdrp, rp) < 0)
stdrp = rp;
if (stdrp != NULL && stdrp->r_hiyear == 2037)
return YEAR_BY_YEAR_ZONE;
- if (stdrp != NULL && stdrp->r_stdoff != 0)
+ if (stdrp != NULL && stdrp->r_isdst)
{
/* Perpetual DST. */
dstr.r_month = TM_JANUARY;
dstr.r_dayofmonth = 1;
dstr.r_tod = 0;
dstr.r_todisstd = dstr.r_todisgmt = false;
+ dstr.r_isdst = stdrp->r_isdst;
dstr.r_stdoff = stdrp->r_stdoff;
dstr.r_abbrvar = stdrp->r_abbrvar;
stdr.r_month = TM_DECEMBER;
stdr.r_dayofmonth = 31;
stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
stdr.r_todisstd = stdr.r_todisgmt = false;
+ stdr.r_isdst = false;
stdr.r_stdoff = 0;
stdr.r_abbrvar
= (stdabbrrp ? stdabbrrp->r_abbrvar : "");
stdrp = &stdr;
}
}
- if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
+ if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_isdst))
return -1;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
- len = doabbr(result, zp, abbrvar, 0, true);
+ len = doabbr(result, zp, abbrvar, false, 0, true);
offsetlen = stringoffset(result + len, -zp->z_gmtoff);
if (!offsetlen)
{
len += offsetlen;
if (dstrp == NULL)
return compat;
- len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true);
+ len += doabbr(result + len, zp, dstrp->r_abbrvar,
+ dstrp->r_isdst, dstrp->r_stdoff, true);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
{
offsetlen = stringoffset(result + len,
if (zp->z_nrules == 0)
{
stdoff = zp->z_stdoff;
- doabbr(startbuf, zp, NULL, stdoff, false);
+ doabbr(startbuf, zp, NULL, zp->z_isdst, stdoff, false);
type = addtype(oadd(zp->z_gmtoff, stdoff),
- startbuf, stdoff != 0, startttisstd,
+ startbuf, zp->z_isdst, startttisstd,
startttisgmt);
if (usestart)
{
stdoff);
doabbr(startbuf, zp,
rp->r_abbrvar,
+ rp->r_isdst,
rp->r_stdoff,
false);
continue;
doabbr(startbuf,
zp,
rp->r_abbrvar,
+ rp->r_isdst,
rp->r_stdoff,
false);
}
eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum);
doabbr(ab, zp, rp->r_abbrvar,
- rp->r_stdoff, false);
+ rp->r_isdst, rp->r_stdoff, false);
offset = oadd(zp->z_gmtoff, rp->r_stdoff);
- type = addtype(offset, ab, rp->r_stdoff != 0,
+ type = addtype(offset, ab, rp->r_isdst,
rp->r_todisstd, rp->r_todisgmt);
if (rp->r_hiyear == ZIC_MAX
&& !(0 <= lastatmax