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 monthabbr["Jan"] = 1 13 monthabbr["Feb"] = 1 14 monthabbr["Mar"] = 1 15 monthabbr["Apr"] = 1 16 monthabbr["May"] = 1 17 monthabbr["Jun"] = 1 18 monthabbr["Jul"] = 1 19 monthabbr["Aug"] = 1 20 monthabbr["Sep"] = 1 21 monthabbr["Oct"] = 1 22 monthabbr["Nov"] = 1 23 monthabbr["Dec"] = 1 24 25 while (getline <iso_table) { 26 iso_NR++ 27 if ($0 ~ /^#/) continue 28 if (NF != 2) { 29 printf "%s:%d: wrong number of columns\n", \ 30 iso_table, iso_NR >>"/dev/stderr" 31 status = 1 32 } 33 cc = $1 34 name = $2 35 if (cc !~ /^[A-Z][A-Z]$/) { 36 printf "%s:%d: invalid country code '%s'\n", \ 37 iso_table, iso_NR, cc >>"/dev/stderr" 38 status = 1 39 } 40 if (cc <= cc0) { 41 if (cc == cc0) { 42 s = "duplicate"; 43 } else { 44 s = "out of order"; 45 } 46 47 printf "%s:%d: country code '%s' is %s\n", \ 48 iso_table, iso_NR, cc, s \ 49 >>"/dev/stderr" 50 status = 1 51 } 52 cc0 = cc 53 if (name2cc[name]) { 54 printf "%s:%d: '%s' and '%s' have the same name\n", \ 55 iso_table, iso_NR, name2cc[name], cc \ 56 >>"/dev/stderr" 57 status = 1 58 } 59 name2cc[name] = cc 60 cc2name[cc] = name 61 cc2NR[cc] = iso_NR 62 } 63 64 cc0 = "" 65 66 while (getline <zone_table) { 67 zone_NR++ 68 if ($0 ~ /^#/) continue 69 if (NF != 3 && NF != 4) { 70 printf "%s:%d: wrong number of columns\n", \ 71 zone_table, zone_NR >>"/dev/stderr" 72 status = 1 73 } 74 ccs = input_ccs[zone_NR] = $1 75 coordinates = $2 76 tz = $3 77 comments = input_comments[zone_NR] = $4 78 split(ccs, cca, /,/) 79 cc = cca[1] 80 81 if (tztab[tz]) { 82 printf "%s:%d: %s: duplicate Zone from line %d\n", \ 83 zone_table, zone_NR, tz, tz2NR[tz] 84 status = 1 85 } 86 87 # Don't complain about a special case for Crimea in zone.tab. 88 # FIXME: zone.tab should be removed, since it is obsolete. 89 # Or at least put just "XX" in its country-code column. 90 if (cc < cc0 \ 91 && !(zone_table == "zone.tab" \ 92 && tz0 == "Europe/Simferopol")) { 93 printf "%s:%d: country code '%s' is out of order\n", \ 94 zone_table, zone_NR, cc >>"/dev/stderr" 95 status = 1 96 } 97 cc0 = cc 98 tz0 = tz 99 tztab[tz] = 1 100 tz2NR[tz] = zone_NR 101 for (i in cca) { 102 cc = cca[i] 103 if (cc2name[cc]) { 104 cc_used[cc]++ 105 } else if (! (cc == "XX" && zone_table == "zonenow.tab")) { 106 printf "%s:%d: %s: unknown country code\n", \ 107 zone_table, zone_NR, cc >>"/dev/stderr" 108 status = 1 109 } 110 } 111 if (coordinates !~ /^[-+][0-9][0-9][0-5][0-9][-+][01][0-9][0-9][0-5][0-9]$/ \ 112 && 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]$/) { 113 printf "%s:%d: %s: invalid coordinates\n", \ 114 zone_table, zone_NR, coordinates >>"/dev/stderr" 115 status = 1 116 } 117 } 118 119 for (i = 1; i <= zone_NR; i++) { 120 ccs = input_ccs[i] 121 if (!ccs) continue 122 comments = input_comments[i] 123 split(ccs, cca, /,/) 124 used_max = 0 125 for (j in cca) { 126 cc = cca[j] 127 if (used_max < cc_used[cc]) { 128 used_max = cc_used[cc] 129 used_max_cc = cc 130 } 131 } 132 if (used_max <= 1 && comments && zone_table != "zonenow.tab") { 133 printf "%s:%d: unnecessary comment '%s'\n", \ 134 zone_table, i, comments \ 135 >>"/dev/stderr" 136 status = 1 137 } else if (1 < used_max && !comments) { 138 printf "%s:%d: missing comment for %s\n", \ 139 zone_table, i, used_max_cc \ 140 >>"/dev/stderr" 141 status = 1 142 } 143 } 144 FS = " " 145} 146 147$1 ~ /^#/ { next } 148 149{ 150 tz = rules = stdoff = "" 151 if ($1 == "Zone") { 152 tz = $2 153 stdoff = $3 154 ruleUsed[$4] = 1 155 if ($5 ~ /%/) rulePercentUsed[$4] = 1 156 } else if ($1 == "Link") { 157 if (zone_table == "zone.tab") { 158 # Ignore Link commands if source and destination basenames 159 # are identical, e.g. Europe/Istanbul versus Asia/Istanbul. 160 src = $2 161 dst = $3 162 while ((i = index(src, "/"))) src = substr(src, i+1) 163 while ((i = index(dst, "/"))) dst = substr(dst, i+1) 164 if (src != dst) tz = $3 165 } 166 } else if ($1 == "Rule") { 167 ruleDefined[$2] = 1 168 if ($10 != "-") ruleLetters[$2] = 1 169 if (!monthabbr[$6]) { 170 printf "%s:%d: tricky month: %s\n", FILENAME, FNR, $6 \ 171 >>"/dev/stderr" 172 status = 1 173 } 174 } else { 175 stdoff = $1 176 ruleUsed[$2] = 1 177 if ($3 ~ /%/) rulePercentUsed[$2] = 1 178 } 179 180 if (stdoff && stdoff !~ /^\-?1?[0-9](:[0-5][0-9](:[0-5][0-9])?)?$/) { 181 printf "%s:%d: unlikely STDOFF: %s\n", FILENAME, FNR, stdoff \ 182 >>"/dev/stderr" 183 status = 1 184 } 185 186 if (tz && tz ~ /\// && tz !~ /^Etc\//) { 187 if (!tztab[tz] && FILENAME != "backward" \ 188 && zone_table != "zonenow.tab") { 189 printf "%s: no data for '%s'\n", zone_table, tz \ 190 >>"/dev/stderr" 191 status = 1 192 } 193 zoneSeen[tz] = 1 194 } 195} 196 197END { 198 for (tz in ruleDefined) { 199 if (!ruleUsed[tz]) { 200 printf "%s: Rule never used\n", tz 201 status = 1 202 } 203 } 204 for (tz in ruleLetters) { 205 if (!rulePercentUsed[tz]) { 206 printf "%s: Rule contains letters never used\n", tz 207 status = 1 208 } 209 } 210 for (tz in tztab) { 211 if (!zoneSeen[tz] && tz !~ /^Etc\//) { 212 printf "%s:%d: no Zone table for '%s'\n", \ 213 zone_table, tz2NR[tz], tz >>"/dev/stderr" 214 status = 1 215 } 216 } 217 if (0 < want_warnings) { 218 for (cc in cc2name) { 219 if (!cc_used[cc]) { 220 printf "%s:%d: warning: " \ 221 "no Zone entries for %s (%s)\n", \ 222 iso_table, cc2NR[cc], cc, cc2name[cc] 223 } 224 } 225 } 226 227 exit status 228} 229