xref: /freebsd/contrib/tzdata/checktab.awk (revision b9f654b163bce26de79705e77b872427c9f2afa1)
1# Check tz tables for consistency.
2
3# Contributed by Paul Eggert.  This file is in the public domain.
4
5BEGIN {
6	FS = "\t"
7
8	if (!iso_table) iso_table = "iso3166.tab"
9	if (!zone_table) zone_table = "zone1970.tab"
10	if (!want_warnings) want_warnings = -1
11
12	while (getline <iso_table) {
13		iso_NR++
14		if ($0 ~ /^#/) continue
15		if (NF != 2) {
16			printf "%s:%d: wrong number of columns\n", \
17				iso_table, iso_NR >>"/dev/stderr"
18			status = 1
19		}
20		cc = $1
21		name = $2
22		if (cc !~ /^[A-Z][A-Z]$/) {
23			printf "%s:%d: invalid country code '%s'\n", \
24				iso_table, iso_NR, cc >>"/dev/stderr"
25			status = 1
26		}
27		if (cc <= cc0) {
28			if (cc == cc0) {
29				s = "duplicate";
30			} else {
31				s = "out of order";
32			}
33
34			printf "%s:%d: country code '%s' is %s\n", \
35				iso_table, iso_NR, cc, s \
36				>>"/dev/stderr"
37			status = 1
38		}
39		cc0 = cc
40		if (name2cc[name]) {
41			printf "%s:%d: '%s' and '%s' have the same name\n", \
42				iso_table, iso_NR, name2cc[name], cc \
43				>>"/dev/stderr"
44			status = 1
45		}
46		name2cc[name] = cc
47		cc2name[cc] = name
48		cc2NR[cc] = iso_NR
49	}
50
51	cc0 = ""
52
53	while (getline <zone_table) {
54		zone_NR++
55		if ($0 ~ /^#/) continue
56		if (NF != 3 && NF != 4) {
57			printf "%s:%d: wrong number of columns\n", \
58				zone_table, zone_NR >>"/dev/stderr"
59			status = 1
60		}
61		split($1, cca, /,/)
62		cc = cca[1]
63		coordinates = $2
64		tz = $3
65		comments = $4
66		if (cc < cc0) {
67			printf "%s:%d: country code '%s' is out of order\n", \
68				zone_table, zone_NR, cc >>"/dev/stderr"
69			status = 1
70		}
71		cc0 = cc
72		tztab[tz] = 1
73		tz2comments[tz] = comments
74		tz2NR[tz] = zone_NR
75		for (i in cca) {
76		    cc = cca[i]
77		    cctz = cc tz
78		    cctztab[cctz] = 1
79		    if (cc2name[cc]) {
80			cc_used[cc]++
81		    } else {
82			printf "%s:%d: %s: unknown country code\n", \
83				zone_table, zone_NR, cc >>"/dev/stderr"
84			status = 1
85		    }
86		}
87		if (coordinates !~ /^[-+][0-9][0-9][0-5][0-9][-+][01][0-9][0-9][0-5][0-9]$/ \
88		    && coordinates !~ /^[-+][0-9][0-9][0-5][0-9][0-5][0-9][-+][01][0-9][0-9][0-5][0-9][0-5][0-9]$/) {
89			printf "%s:%d: %s: invalid coordinates\n", \
90				zone_table, zone_NR, coordinates >>"/dev/stderr"
91			status = 1
92		}
93	}
94
95	for (cctz in cctztab) {
96		cc = substr (cctz, 1, 2)
97		tz = substr (cctz, 3)
98		if (1 < cc_used[cc]) {
99			comments_needed[tz] = cc
100		}
101	}
102	for (cctz in cctztab) {
103	  cc = substr (cctz, 1, 2)
104	  tz = substr (cctz, 3)
105	  if (!comments_needed[tz] && tz2comments[tz]) {
106	    printf "%s:%d: unnecessary comment '%s'\n", \
107		zone_table, tz2NR[tz], tz2comments[tz] \
108		>>"/dev/stderr"
109	    tz2comments[tz] = 0
110	    status = 1
111	  } else if (comments_needed[tz] && !tz2comments[tz]) {
112	    printf "%s:%d: missing comment for %s\n", \
113	      zone_table, tz2NR[tz], comments_needed[tz] \
114	      >>"/dev/stderr"
115	    tz2comments[tz] = 1
116	    status = 1
117	  }
118	}
119	FS = " "
120}
121
122$1 ~ /^#/ { next }
123
124{
125	tz = rules = ""
126	if ($1 == "Zone") {
127		tz = $2
128		ruleUsed[$4] = 1
129		if ($5 ~ /%/) rulePercentUsed[$4] = 1
130	} else if ($1 == "Link" && zone_table == "zone.tab") {
131		# Ignore Link commands if source and destination basenames
132		# are identical, e.g. Europe/Istanbul versus Asia/Istanbul.
133		src = $2
134		dst = $3
135		while ((i = index(src, "/"))) src = substr(src, i+1)
136		while ((i = index(dst, "/"))) dst = substr(dst, i+1)
137		if (src != dst) tz = $3
138	} else if ($1 == "Rule") {
139		ruleDefined[$2] = 1
140		if ($10 != "-") ruleLetters[$2] = 1
141	} else {
142		ruleUsed[$2] = 1
143		if ($3 ~ /%/) rulePercentUsed[$2] = 1
144	}
145	if (tz && tz ~ /\//) {
146		if (!tztab[tz]) {
147			printf "%s: no data for '%s'\n", zone_table, tz \
148				>>"/dev/stderr"
149			status = 1
150		}
151		zoneSeen[tz] = 1
152	}
153}
154
155END {
156	for (tz in ruleDefined) {
157		if (!ruleUsed[tz]) {
158			printf "%s: Rule never used\n", tz
159			status = 1
160		}
161	}
162	for (tz in ruleLetters) {
163		if (!rulePercentUsed[tz]) {
164			printf "%s: Rule contains letters never used\n", tz
165			status = 1
166		}
167	}
168	for (tz in tztab) {
169		if (!zoneSeen[tz]) {
170			printf "%s:%d: no Zone table for '%s'\n", \
171				zone_table, tz2NR[tz], tz >>"/dev/stderr"
172			status = 1
173		}
174	}
175	if (0 < want_warnings) {
176		for (cc in cc2name) {
177			if (!cc_used[cc]) {
178				printf "%s:%d: warning: " \
179					"no Zone entries for %s (%s)\n", \
180					iso_table, cc2NR[cc], cc, cc2name[cc]
181			}
182		}
183	}
184
185	exit status
186}
187