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