1#         $NetBSD: checktab.awk,v 1.8 2014/08/15 11:04:07 christos Exp $
2
3# Check tz tables for consistency.
4
5# Contributed by Paul Eggert.
6
7BEGIN {
8          FS = "\t"
9
10          if (!iso_table) iso_table = "iso3166.tab"
11          if (!zone_table) zone_table = "zone1970.tab"
12          if (!want_warnings) want_warnings = -1
13
14          # A special (and we hope temporary) case.
15          tztab["America/Montreal"] = 1
16
17          # Some more special cases; these are zones that should probably
18          # be turned into links.
19          if (zone_table == "zone1970.tab") {
20            tztab["Africa/Addis_Ababa"] = 1
21            tztab["Africa/Asmara"] = 1
22            tztab["Africa/Bangui"] = 1
23            tztab["Africa/Blantyre"] = 1
24            tztab["Africa/Brazzaville"] = 1
25            tztab["Africa/Bujumbura"] = 1
26            tztab["Africa/Dar_es_Salaam"] = 1
27            tztab["Africa/Djibouti"] = 1
28            tztab["Africa/Douala"] = 1
29            tztab["Africa/Gaborone"] = 1
30            tztab["Africa/Harare"] = 1
31            tztab["Africa/Kampala"] = 1
32            tztab["Africa/Kigali"] = 1
33            tztab["Africa/Kinshasa"] = 1
34            tztab["Africa/Libreville"] = 1
35            tztab["Africa/Luanda"] = 1
36            tztab["Africa/Lubumbashi"] = 1
37            tztab["Africa/Lusaka"] = 1
38            tztab["Africa/Malabo"] = 1
39            tztab["Africa/Maseru"] = 1
40            tztab["Africa/Mbabane"] = 1
41            tztab["Africa/Mogadishu"] = 1
42            tztab["Africa/Niamey"] = 1
43            tztab["Africa/Porto-Novo"] = 1
44            tztab["America/Antigua"] = 1
45            tztab["America/Cayman"] = 1
46            tztab["Asia/Aden"] = 1
47            tztab["Asia/Bahrain"] = 1
48            tztab["Asia/Ho_Chi_Minh"] = 1
49            tztab["Asia/Kuwait"] = 1
50            tztab["Asia/Muscat"] = 1
51            tztab["Asia/Phnom_Penh"] = 1
52            tztab["Asia/Vientiane"] = 1
53            tztab["Indian/Antananarivo"] = 1
54            tztab["Indian/Comoro"] = 1
55            tztab["Indian/Mayotte"] = 1
56            tztab["Pacific/Midway"] = 1
57            tztab["Pacific/Saipan"] = 1
58          }
59
60          while (getline <iso_table) {
61                    iso_NR++
62                    if ($0 ~ /^#/) continue
63                    if (NF != 2) {
64                              printf "%s:%d: wrong number of columns\n", \
65                                        iso_table, iso_NR >>"/dev/stderr"
66                              status = 1
67                    }
68                    cc = $1
69                    name = $2
70                    if (cc !~ /^[A-Z][A-Z]$/) {
71                              printf "%s:%d: invalid country code '%s'\n", \
72                                        iso_table, iso_NR, cc >>"/dev/stderr"
73                              status = 1
74                    }
75                    if (cc <= cc0) {
76                              if (cc == cc0) {
77                                        s = "duplicate";
78                              } else {
79                                        s = "out of order";
80                              }
81
82                              printf "%s:%d: country code '%s' is %s\n", \
83                                        iso_table, iso_NR, cc, s \
84                                        >>"/dev/stderr"
85                              status = 1
86                    }
87                    cc0 = cc
88                    if (name2cc[name]) {
89                              printf "%s:%d: '%s' and '%s' have the sname name\n", \
90                                        iso_table, iso_NR, name2cc[name], cc \
91                                        >>"/dev/stderr"
92                              status = 1
93                    }
94                    name2cc[name] = cc
95                    cc2name[cc] = name
96                    cc2NR[cc] = iso_NR
97          }
98
99          cc0 = ""
100
101          while (getline <zone_table) {
102                    zone_NR++
103                    if ($0 ~ /^#/) continue
104                    if (NF != 3 && NF != 4) {
105                              printf "%s:%d: wrong number of columns\n", \
106                                        zone_table, zone_NR >>"/dev/stderr"
107                              status = 1
108                    }
109                    split($1, cca, /,/)
110                    cc = cca[1]
111                    coordinates = $2
112                    tz = $3
113                    comments = $4
114                    if (cc < cc0) {
115                              printf "%s:%d: country code '%s' is out of order\n", \
116                                        zone_table, zone_NR, cc >>"/dev/stderr"
117                              status = 1
118                    }
119                    cc0 = cc
120                    tztab[tz] = 1
121                    tz2comments[tz] = comments
122                    tz2NR[tz] = zone_NR
123                    for (i in cca) {
124                        cc = cca[i]
125                        cctz = cc tz
126                        cctztab[cctz] = 1
127                        if (cc2name[cc]) {
128                              cc_used[cc]++
129                        } else {
130                              printf "%s:%d: %s: unknown country code\n", \
131                                        zone_table, zone_NR, cc >>"/dev/stderr"
132                              status = 1
133                        }
134                    }
135                    if (coordinates !~ /^[-+][0-9][0-9][0-5][0-9][-+][01][0-9][0-9][0-5][0-9]$/ \
136                        && 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]$/) {
137                              printf "%s:%d: %s: invalid coordinates\n", \
138                                        zone_table, zone_NR, coordinates >>"/dev/stderr"
139                              status = 1
140                    }
141          }
142
143          for (cctz in cctztab) {
144                    cc = substr (cctz, 1, 2)
145                    tz = substr (cctz, 3)
146                    if (1 < cc_used[cc]) {
147                              comments_needed[tz] = cc
148                    }
149          }
150          for (cctz in cctztab) {
151            cc = substr (cctz, 1, 2)
152            tz = substr (cctz, 3)
153            if (!comments_needed[tz] && tz2comments[tz]) {
154              printf "%s:%d: unnecessary comment '%s'\n", \
155                    zone_table, tz2NR[tz], tz2comments[tz] \
156                    >>"/dev/stderr"
157              tz2comments[tz] = 0
158              status = 1
159            } else if (comments_needed[tz] && !tz2comments[tz]) {
160              printf "%s:%d: missing comment for %s\n", \
161                zone_table, tz2NR[tz], comments_needed[tz] \
162                >>"/dev/stderr"
163              status = 1
164            }
165          }
166          FS = " "
167}
168
169$1 ~ /^#/ { next }
170
171{
172          tz = rules = ""
173          if ($1 == "Zone") {
174                    tz = $2
175                    ruleUsed[$4] = 1
176          } else if ($1 == "Link" && zone_table == "zone.tab") {
177                    # Ignore Link commands if source and destination basenames
178                    # are identical, e.g. Europe/Istanbul versus Asia/Istanbul.
179                    src = $2
180                    dst = $3
181                    while ((i = index(src, "/"))) src = substr(src, i+1)
182                    while ((i = index(dst, "/"))) dst = substr(dst, i+1)
183                    if (src != dst) tz = $3
184          } else if ($1 == "Rule") {
185                    ruleDefined[$2] = 1
186          } else {
187                    ruleUsed[$2] = 1
188          }
189          if (tz && tz ~ /\//) {
190                    if (!tztab[tz]) {
191                              printf "%s: no data for '%s'\n", zone_table, tz \
192                                        >>"/dev/stderr"
193                              status = 1
194                    }
195                    zoneSeen[tz] = 1
196          }
197}
198
199END {
200          for (tz in ruleDefined) {
201                    if (!ruleUsed[tz]) {
202                              printf "%s: Rule never used\n", tz
203                              status = 1
204                    }
205          }
206          for (tz in tztab) {
207                    if (!zoneSeen[tz]) {
208                              printf "%s:%d: no Zone table for '%s'\n", \
209                                        zone_table, tz2NR[tz], tz >>"/dev/stderr"
210                              status = 1
211                    }
212          }
213          if (0 < want_warnings) {
214                    for (cc in cc2name) {
215                              if (!cc_used[cc]) {
216                                        printf "%s:%d: warning: " \
217                                                  "no Zone entries for %s (%s)\n", \
218                                                  iso_table, cc2NR[cc], cc, cc2name[cc]
219                              }
220                    }
221          }
222
223          exit status
224}
225