static char elsieid
[] = "@(#)zic.c 4.12";
#endif /* !defined NOID */
#endif /* !defined lint */
#endif /* !defined TRUE */
int r_loyear
; /* for example, 1986 */
int r_hiyear
; /* for example, 1986 */
int r_dycode
; /* see below */
long r_tod
; /* time from midnight */
int r_todisstd
; /* above is standard time if TRUE */
/* or wall clock time if FALSE */
long r_stdoff
; /* offset from standard time */
const char * r_abbrvar
; /* variable part of abbreviation */
int r_todo
; /* a rule to do (used in outzone) */
time_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) */
extern int emkdir
P((const char * name
, int mode
));
extern int getopt
P((int argc
, char * argv
[], const char * options
));
extern char * icatalloc
P((char * old
, const char * new));
extern char * icpyalloc
P((const char * string
));
extern void ifree
P((char * p
));
extern char * imalloc
P((int n
));
extern char * irealloc
P((char * old
, int n
));
extern int link
P((const char * fromname
, const char * toname
));
extern void perror
P((const char * string
));
extern char * scheck
P((const char * string
, const char * format
));
static void addtt
P((time_t starttime
, int type
));
static int addtype
P((long gmtoff
, const char * abbr
, int isdst
,
static void addleap
P((time_t t
, int positive
, int rolling
));
static void adjleap
P((void));
static void associate
P((void));
static int ciequal
P((const char * ap
, const char * bp
));
static void convert
P((long val
, char * buf
));
static void dolink
P((const char * fromfile
, const char * tofile
));
static void eat
P((const char * name
, int num
));
static void eats
P((const char * name
, int num
,
const char * rname
, int rnum
));
static long eitol
P((int i
));
static void error
P((const char * message
));
static char ** getfields
P((char * buf
));
static long gethms
P((const char * string
, const char * errstrng
,
static void infile
P((const char * filename
));
static void inleap
P((char ** fields
, int nfields
));
static void inlink
P((char ** fields
, int nfields
));
static void inrule
P((char ** fields
, int nfields
));
static int inzcont
P((char ** fields
, int nfields
));
static int inzone
P((char ** fields
, int nfields
));
static int inzsub
P((char ** fields
, int nfields
, int iscont
));
static int itsabbr
P((const char * abbr
, const char * word
));
static int itsdir
P((const char * name
));
static int lowerit
P((int c
));
static char * memcheck
P((char * tocheck
));
static int mkdirs
P((char * filename
));
static void newabbr
P((const char * abbr
));
static long oadd
P((long t1
, long t2
));
static void outzone
P((const struct zone
* zp
, int ntzones
));
static void puttzcode
P((long code
, FILE * fp
));
static int rcomp
P((const genericptr_t leftp
, const genericptr_t rightp
));
static time_t rpytime
P((const struct rule
* rp
, int wantedy
));
static void rulesub
P((struct rule
* rp
,
char * loyearp
, char * hiyearp
,
char * typep
, char * monthp
,
char * dayp
, char * timep
));
static void setboundaries
P((void));
static time_t tadd
P((time_t t1
, long t2
));
static void usage
P((void));
static void writezone
P((const char * name
));
static int yearistype
P((int year
, const char * type
));
static const char * filename
;
static const char * rfilename
;
static const char * progname
;
** Which fields are which on a Zone line.
** Which fields are which on a Zone continuation line.
#define ZONEC_MINFIELDS 3
#define ZONEC_MAXFIELDS 7
** Which files are which on a Rule line.
** Which fields are which on a Link line.
** Which fields are which on a Leap line.
static struct rule
* rules
;
static int nrules
; /* number of rules */
static struct zone
* zones
;
static int nzones
; /* number of zones */
static struct link
* links
;
static struct lookup
const * byword
P((const char * string
,
const struct lookup
* lp
));
static struct lookup
const line_codes
[] = {
static struct lookup
const mon_names
[] = {
"September", TM_SEPTEMBER
,
static struct lookup
const wday_names
[] = {
"Wednesday", TM_WEDNESDAY
,
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
,
static struct lookup
const begin_years
[] = {
static struct lookup
const end_years
[] = {
static struct lookup
const leap_types
[] = {
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 time_t ats
[TZ_MAX_TIMES
];
static unsigned char types
[TZ_MAX_TIMES
];
static long gmtoffs
[TZ_MAX_TYPES
];
static char isdsts
[TZ_MAX_TYPES
];
static char abbrinds
[TZ_MAX_TYPES
];
static char ttisstds
[TZ_MAX_TYPES
];
static char chars
[TZ_MAX_CHARS
];
static time_t trans
[TZ_MAX_LEAPS
];
static long corr
[TZ_MAX_LEAPS
];
static char roll
[TZ_MAX_LEAPS
];
(void) exit(EXIT_FAILURE
);
#define emalloc(size) memcheck(imalloc(size))
#define erealloc(ptr, size) memcheck(irealloc(ptr, size))
#define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
#define ecatalloc(oldp, newp) memcheck(icatalloc(oldp, newp))
eats(name
, num
, rname
, rnum
)
const char * const rname
;
eats(name
, num
, (char *) NULL
, -1);
const char * const string
;
** Match the format of "cc" to allow sh users to
** zic ... 2>&1 | error -t "*" -v
(void) fprintf(stderr
, "\"%s\", line %d: %s",
filename
, linenum
, string
);
(void) fprintf(stderr
, " (rule from \"%s\", line %d)",
(void) fprintf(stderr
, "\n");
"%s: usage is %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n\
\t[ -L leapseconds ] [ filename ... ]\n",
(void) exit(EXIT_FAILURE
);
static const char * psxrules
= NULL
;
static const char * lcltime
= NULL
;
static const char * directory
= NULL
;
static const char * leapsec
= NULL
;
static int sflag
= FALSE
;
(void) umask(umask(022) | 022);
#endif /* defined unix */
while ((c
= getopt(argc
, argv
, "d:l:p:L:vs")) != EOF
)
"%s: More than one -d option specified\n",
(void) exit(EXIT_FAILURE
);
"%s: More than one -l option specified\n",
(void) exit(EXIT_FAILURE
);
"%s: More than one -p option specified\n",
(void) exit(EXIT_FAILURE
);
"%s: More than one -L option specified\n",
(void) exit(EXIT_FAILURE
);
if (optind
== argc
- 1 && strcmp(argv
[optind
], "=") == 0)
usage(); /* usage message by request */
if (optind
< argc
&& leapsec
!= NULL
) {
zones
= (struct zone
*) emalloc(0);
rules
= (struct rule
*) emalloc(0);
links
= (struct link
*) emalloc(0);
for (i
= optind
; i
< argc
; ++i
)
(void) exit(EXIT_FAILURE
);
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
)
outzone(&zones
[i
], j
- i
);
for (i
= 0; i
< nlinks
; ++i
)
dolink(links
[i
].l_from
, links
[i
].l_to
);
dolink(lcltime
, TZDEFAULT
);
dolink(psxrules
, TZDEFRULES
);
return (errors
== 0) ? EXIT_SUCCESS
: EXIT_FAILURE
;
const char * const fromfile
;
const char * const tofile
;
register char * fromname
;
fromname
= ecpyalloc(directory
);
fromname
= ecatalloc(fromname
, "/");
fromname
= ecatalloc(fromname
, fromfile
);
toname
= ecpyalloc(directory
);
toname
= ecatalloc(toname
, "/");
toname
= ecatalloc(toname
, tofile
);
** We get to be careful here since
** there's a fair chance of root running us.
if (link(fromname
, toname
) != 0) {
(void) fprintf(stderr
, "%s: Can't link from %s to ",
(void) exit(EXIT_FAILURE
);
for (bit
= 1; bit
> 0; bit
<<= 1)
if (bit
== 0) { /* time_t is an unsigned type */
min_year
= TM_YEAR_BASE
+ gmtime(&min_time
)->tm_year
;
max_year
= TM_YEAR_BASE
+ gmtime(&max_time
)->tm_year
;
return stat(name
, &s
) == 0 && (s
.st_mode
& S_IFMT
) == S_IFDIR
;
** Associate sets of rules with zones.
return strcmp(((struct rule
*) cp1
)->r_name
,
((struct rule
*) cp2
)->r_name
);
register struct zone
* zp
;
register struct rule
* rp
;
(void) qsort((genericptr_t
) rules
,
(qsort_size_t
) sizeof *rules
, rcomp
);
for (i
= 0; i
< nzones
; ++i
) {
for (base
= 0; base
< nrules
; base
= out
) {
for (out
= base
+ 1; out
< nrules
; ++out
)
if (strcmp(rp
->r_name
, rules
[out
].r_name
) != 0)
for (i
= 0; i
< nzones
; ++i
) {
if (strcmp(zp
->z_rule
, rp
->r_name
) != 0)
zp
->z_nrules
= out
- base
;
for (i
= 0; i
< nzones
; ++i
) {
** 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 in ruleless zone");
(void) exit(EXIT_FAILURE
);
register const struct lookup
* lp
;
if (strcmp(name
, "-") == 0) {
} else if ((fp
= fopen(name
, "r")) == NULL
) {
(void) fprintf(stderr
, "%s: Can't open ", progname
);
(void) exit(EXIT_FAILURE
);
if (fgets(buf
, (int) sizeof buf
, fp
) != buf
)
(void) exit(EXIT_FAILURE
);
while (fields
[nfields
] != NULL
) {
if (ciequal(fields
[nfields
], "-"))
wantcont
= inzcont(fields
, nfields
);
lp
= byword(fields
[0], line_codes
);
error("input line of unknown type");
else switch ((int) (lp
->l_value
)) {
wantcont
= inzone(fields
, nfields
);
"%s: Leap line in non leap seconds file %s\n",
else inleap(fields
, nfields
);
default: /* "cannot happen" */
"%s: panic: Invalid l_value %d\n",
(void) exit(EXIT_FAILURE
);
(void) fprintf(stderr
, "%s: Error reading ", progname
);
(void) exit(EXIT_FAILURE
);
if (fp
!= stdin
&& fclose(fp
)) {
(void) fprintf(stderr
, "%s: Error closing ", progname
);
(void) exit(EXIT_FAILURE
);
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.
gethms(string
, errstring
, signable
)
const char * const errstring
;
if (string
== NULL
|| *string
== '\0')
else if (*string
== '-') {
if (sscanf(string
, scheck(string
, "%d"), &hh
) == 1)
else if (sscanf(string
, scheck(string
, "%d:%d"), &hh
, &mm
) == 2)
else if (sscanf(string
, scheck(string
, "%d:%d:%d"),
if (hh
< 0 || hh
>= HOURSPERDAY
||
mm
< 0 || mm
>= MINSPERHOUR
||
ss
< 0 || ss
> SECSPERMIN
) {
(eitol(hh
* MINSPERHOUR
+ mm
) *
eitol(SECSPERMIN
) + eitol(ss
));
register char ** const fields
;
if (nfields
!= RULE_FIELDS
) {
error("wrong number of fields on Rule line");
if (*fields
[RF_NAME
] == '\0') {
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
]);
rules
= (struct rule
*) erealloc((char *) rules
,
(int) ((nrules
+ 1) * sizeof *rules
));
register char ** const fields
;
if (nfields
< ZONE_MINFIELDS
|| nfields
> ZONE_MAXFIELDS
) {
error("wrong number of fields on Zone line");
if (strcmp(fields
[ZF_NAME
], TZDEFAULT
) == 0 && lcltime
!= NULL
) {
"\"Zone %s\" line and -l option are mutually exclusive",
if (strcmp(fields
[ZF_NAME
], TZDEFRULES
) == 0 && psxrules
!= NULL
) {
"\"Zone %s\" line and -p option are mutually exclusive",
for (i
= 0; i
< nzones
; ++i
)
if (zones
[i
].z_name
!= NULL
&&
strcmp(zones
[i
].z_name
, fields
[ZF_NAME
]) == 0) {
"duplicate zone name %s (file \"%s\", line %d)",
return inzsub(fields
, nfields
, FALSE
);
register char ** const fields
;
if (nfields
< ZONEC_MINFIELDS
|| nfields
> ZONEC_MAXFIELDS
) {
error("wrong number of fields on Zone continuation line");
return inzsub(fields
, nfields
, TRUE
);
inzsub(fields
, nfields
, iscont
)
register char ** const fields
;
register int i_gmtoff
, i_rule
, i_format
;
register int i_untilyear
, i_untilmonth
;
register int i_untilday
, i_untiltime
;
i_untilyear
= ZFC_TILYEAR
;
i_untilmonth
= ZFC_TILMONTH
;
i_untiltime
= ZFC_TILTIME
;
i_untilyear
= ZF_TILYEAR
;
i_untilmonth
= ZF_TILMONTH
;
i_untiltime
= ZF_TILTIME
;
z
.z_name
= ecpyalloc(fields
[ZF_NAME
]);
z
.z_gmtoff
= gethms(fields
[i_gmtoff
], "invalid GMT offset", TRUE
);
if ((cp
= strchr(fields
[i_format
], '%')) != 0) {
if (*++cp
!= 's' || strchr(cp
, '%') != 0) {
error("invalid abbreviation format");
z
.z_rule
= ecpyalloc(fields
[i_rule
]);
z
.z_format
= ecpyalloc(fields
[i_format
]);
hasuntil
= nfields
> i_untilyear
;
z
.z_untilrule
.r_filename
= filename
;
z
.z_untilrule
.r_linenum
= linenum
;
(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
< max_time
&&
z
.z_untiltime
> min_time
&&
zones
[nzones
- 1].z_untiltime
>= z
.z_untiltime
) {
error("Zone continuation line end time is not after end time of previous line");
zones
= (struct zone
*) erealloc((char *) zones
,
(int) ((nzones
+ 1) * sizeof *zones
));
** If there was an UNTIL field on this line,
** there's more information about the zone on the next line.
register char ** const fields
;
register const char * cp
;
register const struct lookup
* lp
;
if (nfields
!= LEAP_FIELDS
) {
error("wrong number of fields on Leap line");
if (sscanf(cp
, scheck(cp
, "%d"), &year
) != 1 ||
year
< min_year
|| year
> max_year
) {
error("invalid leaping year");
i
= len_years
[isleap(j
)];
i
= -len_years
[isleap(j
)];
dayoff
= oadd(dayoff
, eitol(i
));
if ((lp
= byword(fields
[LP_MONTH
], mon_names
)) == NULL
) {
error("invalid month name");
i
= len_months
[isleap(year
)][j
];
dayoff
= oadd(dayoff
, eitol(i
));
if (sscanf(cp
, scheck(cp
, "%d"), &day
) != 1 ||
day
<= 0 || day
> len_months
[isleap(year
)][month
]) {
error("invalid day of month");
dayoff
= oadd(dayoff
, eitol(day
- 1));
if (dayoff
< 0 && !tt_signed
) {
error("time before zero");
t
= (time_t) dayoff
* SECSPERDAY
;
if (t
/ SECSPERDAY
!= dayoff
) {
tod
= gethms(fields
[LP_TIME
], "invalid time of day", FALSE
);
if (strcmp(cp
, "+") != 0 && strcmp(cp
, "") != 0) {
/* infile() turned "-" into "" */
error("illegal CORRECTION field on Leap line");
if ((lp
= byword(fields
[LP_ROLL
], leap_types
)) == NULL
) {
error("illegal Rolling/Stationary field on Leap line");
addleap(tadd(t
, tod
), *cp
== '+', lp
->l_value
);
register char ** const fields
;
if (nfields
!= LINK_FIELDS
) {
error("wrong number of fields on Link line");
if (*fields
[LF_FROM
] == '\0') {
error("blank FROM field on Link line");
if (*fields
[LF_TO
] == '\0') {
error("blank TO field on Link line");
l
.l_from
= ecpyalloc(fields
[LF_FROM
]);
l
.l_to
= ecpyalloc(fields
[LF_TO
]);
links
= (struct link
*) erealloc((char *) links
,
(int) ((nlinks
+ 1) * sizeof *links
));
rulesub(rp
, loyearp
, hiyearp
, typep
, monthp
, dayp
, timep
)
register struct rule
* const rp
;
register struct lookup
const * lp
;
if ((lp
= byword(monthp
, mon_names
)) == NULL
) {
error("invalid month name");
rp
->r_month
= lp
->l_value
;
rp
->r_tod
= gethms(timep
, "invalid time of day", FALSE
);
if ((lp
= byword(cp
, begin_years
)) != NULL
) switch ((int) lp
->l_value
) {
default: /* "cannot happen" */
"%s: panic: Invalid l_value %d\n",
(void) exit(EXIT_FAILURE
);
} else if (sscanf(cp
, scheck(cp
, "%d"), &rp
->r_loyear
) != 1 ||
rp
->r_loyear
< min_year
|| rp
->r_loyear
> max_year
) {
error("invalid starting year");
if (rp
->r_loyear
> max_year
)
if ((lp
= byword(cp
, end_years
)) != NULL
) switch ((int) lp
->l_value
) {
rp
->r_hiyear
= rp
->r_loyear
;
default: /* "cannot happen" */
"%s: panic: Invalid l_value %d\n",
(void) exit(EXIT_FAILURE
);
} else if (sscanf(cp
, scheck(cp
, "%d"), &rp
->r_hiyear
) != 1 ||
rp
->r_hiyear
< min_year
|| rp
->r_hiyear
> max_year
) {
error("invalid ending year");
if (rp
->r_hiyear
< min_year
)
if (rp
->r_hiyear
< min_year
)
if (rp
->r_loyear
< min_year
)
if (rp
->r_hiyear
> max_year
)
if (rp
->r_loyear
> rp
->r_hiyear
) {
error("starting year greater than ending year");
if (rp
->r_loyear
== rp
->r_hiyear
) {
error("typed single year");
rp
->r_yrtype
= ecpyalloc(typep
);
** Accept things such as:
if ((lp
= byword(dayp
, lasts
)) != NULL
) {
rp
->r_dycode
= DC_DOWLEQ
;
rp
->r_wday
= lp
->l_value
;
rp
->r_dayofmonth
= len_months
[1][rp
->r_month
];
if ((cp
= strchr(dayp
, '<')) != 0)
rp
->r_dycode
= DC_DOWLEQ
;
else if ((cp
= strchr(dayp
, '>')) != 0)
rp
->r_dycode
= DC_DOWGEQ
;
if (rp
->r_dycode
!= DC_DOM
) {
error("invalid day of month");
if ((lp
= byword(dayp
, wday_names
)) == NULL
) {
error("invalid weekday name");
rp
->r_wday
= lp
->l_value
;
if (sscanf(cp
, scheck(cp
, "%d"), &rp
->r_dayofmonth
) != 1 ||
(rp
->r_dayofmonth
> len_months
[1][rp
->r_month
])) {
error("invalid day of month");
for (i
= 0, shift
= 24; i
< 4; ++i
, shift
-= 8)
(void) fwrite((genericptr_t
) buf
,
(fwrite_size_t
) sizeof buf
,
static struct tzhead tzh
;
if (strlen(directory
) + 1 + strlen(name
) >= sizeof fullname
) {
"%s: File name %s/%s too long\n", progname
,
(void) exit(EXIT_FAILURE
);
(void) sprintf(fullname
, "%s/%s", directory
, name
);
if ((fp
= fopen(fullname
, "wb")) == NULL
) {
if (mkdirs(fullname
) != 0)
(void) exit(EXIT_FAILURE
);
if ((fp
= fopen(fullname
, "wb")) == NULL
) {
(void) fprintf(stderr
, "%s: Can't create ", progname
);
(void) exit(EXIT_FAILURE
);
convert(eitol(typecnt
), tzh
.tzh_ttisstdcnt
);
convert(eitol(leapcnt
), tzh
.tzh_leapcnt
);
convert(eitol(timecnt
), tzh
.tzh_timecnt
);
convert(eitol(typecnt
), tzh
.tzh_typecnt
);
convert(eitol(charcnt
), tzh
.tzh_charcnt
);
(void) fwrite((genericptr_t
) &tzh
,
(fwrite_size_t
) sizeof tzh
,
for (i
= 0; i
< timecnt
; ++i
) {
if (ats
[i
] >= trans
[j
]) {
ats
[i
] = tadd(ats
[i
], corr
[j
]);
puttzcode((long) ats
[i
], fp
);
(void) fwrite((genericptr_t
) types
,
(fwrite_size_t
) sizeof types
[0],
(fwrite_size_t
) timecnt
, fp
);
for (i
= 0; i
< typecnt
; ++i
) {
puttzcode((long) gmtoffs
[i
], fp
);
(void) putc(isdsts
[i
], fp
);
(void) putc(abbrinds
[i
], fp
);
(void) fwrite((genericptr_t
) chars
,
(fwrite_size_t
) sizeof chars
[0],
(fwrite_size_t
) charcnt
, fp
);
for (i
= 0; i
< leapcnt
; ++i
) {
if (timecnt
== 0 || trans
[i
] < ats
[0]) {
while (j
< timecnt
&& trans
[i
] >= ats
[j
])
puttzcode((long) tadd(trans
[i
], -gmtoffs
[j
]), fp
);
} else puttzcode((long) trans
[i
], fp
);
puttzcode((long) corr
[i
], fp
);
for (i
= 0; i
< typecnt
; ++i
)
(void) putc(ttisstds
[i
], fp
);
if (ferror(fp
) || fclose(fp
)) {
(void) fprintf(stderr
, "%s: Write error on ", progname
);
(void) exit(EXIT_FAILURE
);
outzone(zpfirst
, zonecount
)
const struct zone
* const zpfirst
;
register const struct zone
* zp
;
register struct rule
* rp
;
register int usestart
, useuntil
;
register time_t starttime
, untiltime
;
register int startttisstd
;
** Now. . .finally. . .generate some useful data!
** Two guesses. . .the second may well be corrected later.
gmtoff
= zpfirst
->z_gmtoff
;
#endif /* defined lint */
for (i
= 0; i
< zonecount
; ++i
) {
useuntil
= i
< (zonecount
- 1);
eat(zp
->z_filename
, zp
->z_linenum
);
type
= addtype(oadd(zp
->z_gmtoff
, zp
->z_stdoff
),
zp
->z_format
, zp
->z_stdoff
!= 0,
} else for (year
= min_year
; year
<= max_year
; ++year
) {
if (useuntil
&& year
> zp
->z_untilrule
.r_hiyear
)
** 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
) {
eats(zp
->z_filename
, zp
->z_linenum
,
rp
->r_filename
, rp
->r_linenum
);
rp
->r_todo
= year
>= rp
->r_loyear
&&
yearistype(year
, rp
->r_yrtype
);
rp
->r_temp
= rpytime(rp
, year
);
register time_t jtime
, ktime
;
** Turn untiltime into GMT
** assuming the current gmtoff and
if (!zp
->z_untilrule
.r_todisstd
)
offset
= oadd(offset
, stdoff
);
untiltime
= tadd(zp
->z_untiltime
,
** Find the rule (of those to do, if any)
** that takes effect earliest in the year.
#endif /* defined lint */
for (j
= 0; j
< zp
->z_nrules
; ++j
) {
eats(zp
->z_filename
, zp
->z_linenum
,
rp
->r_filename
, rp
->r_linenum
);
offset
= oadd(offset
, stdoff
);
jtime
= tadd(jtime
, -offset
);
if (k
< 0 || jtime
< ktime
) {
break; /* go on to next year */
if (useuntil
&& ktime
>= untiltime
)
startoff
= oadd(zp
->z_gmtoff
,
if (ktime
!= starttime
&&
addtt(starttime
, addtype(startoff
, startbuf
, startisdst
, startttisstd
));
eats(zp
->z_filename
, zp
->z_linenum
,
rp
->r_filename
, rp
->r_linenum
);
(void) sprintf(buf
, zp
->z_format
,
offset
= oadd(zp
->z_gmtoff
, rp
->r_stdoff
);
type
= addtype(offset
, buf
, rp
->r_stdoff
!= 0,
if (timecnt
!= 0 || rp
->r_stdoff
!= 0)
** Now we may get to set starttime for the next zone line.
starttime
= tadd(zp
->z_untiltime
,
-gmtoffs
[types
[timecnt
- 1]]);
startttisstd
= zp
->z_untilrule
.r_todisstd
;
writezone(zpfirst
->z_name
);
if (timecnt
!= 0 && type
== types
[timecnt
- 1])
return; /* easy enough! */
if (timecnt
>= TZ_MAX_TIMES
) {
error("too many transitions?!");
(void) exit(EXIT_FAILURE
);
ats
[timecnt
] = starttime
;
addtype(gmtoff
, abbr
, isdst
, ttisstd
)
** See if there's already an entry for this zone type.
** If so, just return its index.
for (i
= 0; i
< typecnt
; ++i
) {
if (gmtoff
== gmtoffs
[i
] && isdst
== isdsts
[i
] &&
strcmp(abbr
, &chars
[abbrinds
[i
]]) == 0 &&
** There isn't one; add a new one, unless there are already too
if (typecnt
>= TZ_MAX_TYPES
) {
error("too many local time types");
(void) exit(EXIT_FAILURE
);
for (j
= 0; j
< charcnt
; ++j
)
if (strcmp(&chars
[j
], abbr
) == 0)
addleap(t
, positive
, rolling
)
if (leapcnt
>= TZ_MAX_LEAPS
) {
error("too many leap seconds");
(void) exit(EXIT_FAILURE
);
for (i
= 0; i
< leapcnt
; ++i
)
error("repeated leap second moment");
(void) exit(EXIT_FAILURE
);
for (j
= leapcnt
; j
> i
; --j
) {
corr
[i
] = (positive
? 1L : -1L);
** propagate leap seconds forward
for (i
= 0; i
< leapcnt
; ++i
) {
trans
[i
] = tadd(trans
[i
], last
);
if (type
== NULL
|| *type
== '\0')
if (strcmp(type
, "uspres") == 0)
if (strcmp(type
, "nonpres") == 0)
(void) sprintf(buf
, "yearistype %d %s", year
, type
);
error("Wild result from command execution");
(void) fprintf(stderr
, "%s: command was '%s', result was %d\n",
(void) exit(EXIT_FAILURE
);
return (isascii(a
) && isupper(a
)) ? tolower(a
) : a
;
ciequal(ap
, bp
) /* case-insensitive equality */
register const char * ap
;
register const char * bp
;
while (lowerit(*ap
) == lowerit(*bp
++))
register const char * abbr
;
register const char * word
;
if (lowerit(*abbr
) != lowerit(*word
))
while (lowerit(*word
++) != lowerit(*abbr
));
static const struct lookup
*
register const char * const word
;
register const struct lookup
* const table
;
register const struct lookup
* foundlp
;
register const struct lookup
* lp
;
if (word
== NULL
|| table
== NULL
)
for (lp
= table
; lp
->l_word
!= NULL
; ++lp
)
if (ciequal(word
, lp
->l_word
))
** Look for inexact match.
for (lp
= table
; lp
->l_word
!= NULL
; ++lp
)
if (itsabbr(word
, lp
->l_word
))
else return NULL
; /* multiple inexact matches */
array
= (char **) emalloc((int) ((strlen(cp
) + 1) * sizeof *array
));
while (isascii(*cp
) && isspace(*cp
))
if (*cp
== '\0' || *cp
== '#')
array
[nsubs
++] = dp
= cp
;
if ((*dp
= *cp
++) != '"')
else while ((*dp
= *cp
++) != '"')
else error("Odd number of quotation marks");
} while (*cp
!= '\0' && *cp
!= '#' &&
(!isascii(*cp
) || !isspace(*cp
)));
if (isascii(*cp
) && isspace(*cp
))
if (t2
> 0 && t
<= t1
|| t2
< 0 && t
>= t1
) {
(void) exit(EXIT_FAILURE
);
if (t1
== max_time
&& t2
> 0)
if (t1
== min_time
&& t2
< 0)
if (t2
> 0 && t
<= t1
|| t2
< 0 && t
>= t1
) {
(void) exit(EXIT_FAILURE
);
** Given a rule, and a year, compute the date - in seconds since January 1,
** 1970, 00:00 LOCAL time - in that year that the rule refers to.
register const struct rule
* const rp
;
register const int wantedy
;
register long dayoff
; /* with a nod to Margaret O. */
i
= len_years
[isleap(y
)];
i
= -len_years
[isleap(y
)];
dayoff
= oadd(dayoff
, eitol(i
));
while (m
!= rp
->r_month
) {
i
= len_months
[isleap(y
)][m
];
dayoff
= oadd(dayoff
, eitol(i
));
if (m
== TM_FEBRUARY
&& i
== 29 && !isleap(y
)) {
if (rp
->r_dycode
== DC_DOWLEQ
)
error("use of 2/29 in non leap-year");
(void) exit(EXIT_FAILURE
);
dayoff
= oadd(dayoff
, eitol(i
));
if (rp
->r_dycode
== DC_DOWGEQ
|| rp
->r_dycode
== DC_DOWLEQ
) {
#define LDAYSPERWEEK ((long) DAYSPERWEEK)
wday
= eitol(EPOCH_WDAY
);
** Don't trust mod of negative numbers.
wday
= (wday
+ dayoff
) % LDAYSPERWEEK
;
wday
-= ((-dayoff
) % LDAYSPERWEEK
);
while (wday
!= eitol(rp
->r_wday
))
if (rp
->r_dycode
== DC_DOWGEQ
) {
dayoff
= oadd(dayoff
, (long) 1);
if (++wday
>= LDAYSPERWEEK
)
dayoff
= oadd(dayoff
, (long) -1);
if (i
< 0 || i
>= len_months
[isleap(y
)][m
]) {
error("no day in month matches rule");
(void) exit(EXIT_FAILURE
);
if (dayoff
< 0 && !tt_signed
) {
if (wantedy
== rp
->r_loyear
)
error("time before zero");
(void) exit(EXIT_FAILURE
);
t
= (time_t) dayoff
* SECSPERDAY
;
if (t
/ SECSPERDAY
!= dayoff
) {
if (wantedy
== rp
->r_hiyear
)
if (wantedy
== rp
->r_loyear
)
(void) exit(EXIT_FAILURE
);
return tadd(t
, rp
->r_tod
);
const char * const string
;
if (charcnt
+ i
>= TZ_MAX_CHARS
) {
error("too many, or too long, time zone abbreviations");
(void) exit(EXIT_FAILURE
);
(void) strcpy(&chars
[charcnt
], string
);
if ((cp
= name
) == NULL
|| *cp
== '\0')
while ((cp
= strchr(cp
+ 1, '/')) != 0) {
** MS-DOS drive specifier?
if (strlen(name
) == 2 && isascii(name
[0]) &&
isalpha(name
[0]) && name
[1] == ':') {
#endif /* !defined unix */
** It doesn't seem to exist, so we try to create it.
if (emkdir(name
, 0755) != 0) {
"%s: Can't create directory ",
if (i
< 0 && l
>= 0 || i
== 0 && l
!= 0 || i
> 0 && l
<= 0) {
(void) fprintf(stderr
, "%s: %d did not sign extend correctly\n",
(void) exit(EXIT_FAILURE
);
** UNIX is a registered trademark of AT&T.