Skip to content
/ tz Public

Commit 4878b64

Browse files
committed
zic now supports links to links
* NEWS, backward, zic.8: Mention this. * zic.c (make_links): New function, which supports links to later links. In this function, improve quality of warnings about links to links. (main): Use it.
1 parent d04a4b3 commit 4878b64

4 files changed

Lines changed: 126 additions & 20 deletions

File tree

NEWS

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Unreleased, experimental changes
55
Briefly:
66
Move links to 'backward'.
77
In vanguard form, GMT is now a Zone and Etc/GMT a link.
8+
zic now supports links to links.
89
Simplify four Ontario zones into one.
910
Fix a Y2438 bug when reading TZif data.
1011

@@ -28,6 +29,19 @@ Unreleased, experimental changes
2829

2930
Changes to code
3031

32+
zic now supports links to links regardless of input line order.
33+
For example, if Australia/Sydney is a Zone, the lines
34+
Link Australia/Canberra Australia/ACT
35+
Link Australia/Sydney Australia/Canberra
36+
now work correctly, even though the shell commands
37+
ln Australia/Canberra Australia/ACT
38+
ln Australia/Sydney Australia/Canberra
39+
would fail because the first command attempts to use a link
40+
Australia/Canberra that does not exist until after the second
41+
command is executed. Previously, zic had unspecified behavior if
42+
a Link line's target was another link, and zic often misbehaved if
43+
a Link line's target was a later Link line.
44+
3145
Fix line number in zic's diagnostic for a link to a link.
3246

3347
Fix a bug that caused localtime to mishandle timestamps starting

backward

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
# backward compatibility link. Each section is sorted by link name.
1818

1919
# A "#= TARGET1" comment labels each link inserted only because some
20-
# .zi parsers mishandle links to links. The comment says what the
21-
# target would be if these parsers were fixed so that data could
22-
# contain links to links. For example, the line
20+
# .zi parsers (including tzcode through 2022e) mishandle links to links.
21+
# The comment says what the target would be if these parsers were fixed
22+
# so that data could contain links to links. For example, the line
2323
# "Link Australia/Sydney Australia/ACT #= Australia/Canberra" would be
2424
# "Link Australia/Canberra Australia/ACT" were it not that data lines
2525
# refrain from linking to links like Australia/Canberra, which means

zic.8

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ the named file rather than in the standard location.
190190
Be more verbose, and complain about the following situations:
191191
.RS
192192
.PP
193-
The input specifies a link to a link.
193+
The input specifies a link to a link,
194+
something not supported by some older parsers, including
195+
.B zic
196+
itself through release 2022e.
194197
.PP
195198
A year that appears in a data file is outside the range
196199
of representable years.
@@ -691,19 +694,37 @@ The
691694
.B TARGET
692695
field should appear as the
693696
.B NAME
694-
field in some zone line.
697+
field in some zone line or as the
698+
.B LINK-NAME
699+
field in some link line.
695700
The
696701
.B LINK-NAME
697702
field is used as an alternative name for that zone;
698703
it has the same syntax as a zone line's
699704
.B NAME
700705
field.
706+
Links can chain together, although the behavior is unspecified if a
707+
chain of one or more links does not terminate in a Zone name.
708+
A link line can appear before the line that defines the link target.
709+
For example:
710+
.sp
711+
.ne 3
712+
.nf
713+
.in +2m
714+
.ta \w'Zone\0\0'u +\w'Greenwich\0\0'u
715+
Link Greenwich G_M_T
716+
Link Etc/GMT Greenwich
717+
Zone Etc/GMT\0\00\0\0\*-\0\0GMT
718+
.sp
719+
.in
720+
.fi
721+
The two links are chained together, and G_M_T, Greenwich, and Etc/GMT
722+
all name the same zone.
701723
.PP
702724
Except for continuation lines,
703725
lines may appear in any order in the input.
704726
However, the behavior is unspecified if multiple zone or link lines
705-
define the same name, or if the source of one link line is the target
706-
of another.
727+
define the same name.
707728
.PP
708729
The file that describes leap seconds can have leap lines and an
709730
expiration line.

zic.c

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,89 @@ bsearch_linkcmp(void const *key, void const *b)
678678
return strcmp(key, m->l_linkname);
679679
}
680680

681+
/* Make the links specified by the Link lines. */
682+
static void
683+
make_links(void)
684+
{
685+
ptrdiff_t i, j, nalinks;
686+
qsort(links, nlinks, sizeof *links, qsort_linkcmp);
687+
688+
/* Ignore each link superseded by a later link with the same name. */
689+
j = 0;
690+
for (i = 0; i < nlinks; i++) {
691+
while (i + 1 < nlinks
692+
&& strcmp(links[i].l_linkname, links[i + 1].l_linkname) == 0)
693+
i++;
694+
links[j++] = links[i];
695+
}
696+
nlinks = j;
697+
698+
/* Walk through the link array making links. However,
699+
if a link's target has not been made yet, append a copy to the
700+
end of the array. The end of the array will gradually fill
701+
up with a small sorted subsequence of not-yet-made links.
702+
nalinks counts all the links in the array, including copies.
703+
When we reach the copied subsequence, it may still contain
704+
a link to a not-yet-made link, so the process repeats.
705+
At any given point in time, the link array consists of the
706+
following subregions, where 0 <= i <= j <= nalinks and
707+
0 <= nlinks <= nalinks:
708+
709+
0 .. (i - 1):
710+
links that either have been made, or have been copied to a
711+
later point point in the array (this later point can be in
712+
any of the three subregions)
713+
i .. (j - 1):
714+
not-yet-made links for this pass
715+
j .. (nalinks - 1):
716+
not-yet-made links that this pass has skipped because
717+
they were links to not-yet-made links
718+
719+
The first subregion might not be sorted if nlinks < i;
720+
the other two subregions are sorted. This algorithm does
721+
not alter entries 0 .. (nlinks - 1), which remain sorted.
722+
723+
If there are L links, this algorithm is O(C*L*log(L)) where
724+
C is the length of the longest link chain. Usually C is
725+
short (e.g., 3) though its worst-case value is L. */
726+
727+
j = nalinks = nlinks;
728+
729+
for (i = 0; i < nalinks; i++) {
730+
struct link *l;
731+
732+
eat(links[i].l_filenum, links[i].l_linenum);
733+
734+
/* If this pass examined all its links, start the next pass. */
735+
if (i == j)
736+
j = nalinks;
737+
738+
/* Make this link unless its target has not been made yet. */
739+
l = bsearch(links[i].l_target, &links[i + 1], j - (i + 1),
740+
sizeof *links, bsearch_linkcmp);
741+
if (!l)
742+
l = bsearch(links[i].l_target, &links[j], nalinks - j,
743+
sizeof *links, bsearch_linkcmp);
744+
if (!l)
745+
dolink(links[i].l_target, links[i].l_linkname, false);
746+
else {
747+
/* The link target has not been made yet; copy the link to the end. */
748+
links = growalloc (links, sizeof *links, nalinks, &nlinks_alloc);
749+
links[nalinks++] = links[i];
750+
}
751+
752+
if (noise && i < nlinks) {
753+
if (l)
754+
warning(_("link %s targeting link %s mishandled by pre-2023 zic"),
755+
links[i].l_linkname, links[i].l_target);
756+
else if (bsearch(links[i].l_target, links, nlinks, sizeof *links,
757+
bsearch_linkcmp))
758+
warning(_("link %s targeting link %s"),
759+
links[i].l_linkname, links[i].l_target);
760+
}
761+
}
762+
}
763+
681764
/* Simple signal handling: just set a flag that is checked
682765
periodically outside critical sections. To set up the handler,
683766
prefer sigaction if available to close a signal race. */
@@ -992,19 +1075,7 @@ _("%s: invalid time range: %s\n"),
9921075
continue;
9931076
outzone(&zones[i], j - i);
9941077
}
995-
/*
996-
** Make links.
997-
*/
998-
if (noise)
999-
qsort(links, nlinks, sizeof *links, qsort_linkcmp);
1000-
for (i = 0; i < nlinks; ++i) {
1001-
eat(links[i].l_filenum, links[i].l_linenum);
1002-
dolink(links[i].l_target, links[i].l_linkname, false);
1003-
if (noise
1004-
&& bsearch(links[i].l_target, links, nlinks, sizeof *links,
1005-
bsearch_linkcmp))
1006-
warning(_("link to link"));
1007-
}
1078+
make_links();
10081079
if (lcltime != NULL) {
10091080
eat(COMMAND_LINE_FILENUM, 1);
10101081
dolink(lcltime, tzdefault, true);

0 commit comments

Comments
 (0)