Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | #! /bin/ksh |
michael@0 | 2 | |
michael@0 | 3 | # '@(#)tzselect.ksh 8.1' |
michael@0 | 4 | |
michael@0 | 5 | # Ask the user about the time zone, and output the resulting TZ value to stdout. |
michael@0 | 6 | # Interact with the user via stderr and stdin. |
michael@0 | 7 | |
michael@0 | 8 | # Contributed by Paul Eggert. |
michael@0 | 9 | |
michael@0 | 10 | # Porting notes: |
michael@0 | 11 | # |
michael@0 | 12 | # This script requires several features of the Korn shell. |
michael@0 | 13 | # If your host lacks the Korn shell, |
michael@0 | 14 | # you can use either of the following free programs instead: |
michael@0 | 15 | # |
michael@0 | 16 | # <a href=ftp://ftp.gnu.org/pub/gnu/> |
michael@0 | 17 | # Bourne-Again shell (bash) |
michael@0 | 18 | # </a> |
michael@0 | 19 | # |
michael@0 | 20 | # <a href=ftp://ftp.cs.mun.ca/pub/pdksh/pdksh.tar.gz> |
michael@0 | 21 | # Public domain ksh |
michael@0 | 22 | # </a> |
michael@0 | 23 | # |
michael@0 | 24 | # This script also uses several features of modern awk programs. |
michael@0 | 25 | # If your host lacks awk, or has an old awk that does not conform to Posix.2, |
michael@0 | 26 | # you can use either of the following free programs instead: |
michael@0 | 27 | # |
michael@0 | 28 | # <a href=ftp://ftp.gnu.org/pub/gnu/> |
michael@0 | 29 | # GNU awk (gawk) |
michael@0 | 30 | # </a> |
michael@0 | 31 | # |
michael@0 | 32 | # <a href=ftp://ftp.whidbey.net/pub/brennan/> |
michael@0 | 33 | # mawk |
michael@0 | 34 | # </a> |
michael@0 | 35 | |
michael@0 | 36 | |
michael@0 | 37 | # Specify default values for environment variables if they are unset. |
michael@0 | 38 | : ${AWK=awk} |
michael@0 | 39 | : ${TZDIR=$(pwd)} |
michael@0 | 40 | |
michael@0 | 41 | # Check for awk Posix compliance. |
michael@0 | 42 | ($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1 |
michael@0 | 43 | [ $? = 123 ] || { |
michael@0 | 44 | echo >&2 "$0: Sorry, your \`$AWK' program is not Posix compatible." |
michael@0 | 45 | exit 1 |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | # Make sure the tables are readable. |
michael@0 | 49 | TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab |
michael@0 | 50 | TZ_ZONE_TABLE=$TZDIR/zone.tab |
michael@0 | 51 | for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE |
michael@0 | 52 | do |
michael@0 | 53 | <$f || { |
michael@0 | 54 | echo >&2 "$0: time zone files are not set up correctly" |
michael@0 | 55 | exit 1 |
michael@0 | 56 | } |
michael@0 | 57 | done |
michael@0 | 58 | |
michael@0 | 59 | newline=' |
michael@0 | 60 | ' |
michael@0 | 61 | IFS=$newline |
michael@0 | 62 | |
michael@0 | 63 | |
michael@0 | 64 | # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout. |
michael@0 | 65 | case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in |
michael@0 | 66 | ?*) PS3= |
michael@0 | 67 | esac |
michael@0 | 68 | |
michael@0 | 69 | |
michael@0 | 70 | # Begin the main loop. We come back here if the user wants to retry. |
michael@0 | 71 | while |
michael@0 | 72 | |
michael@0 | 73 | echo >&2 'Please identify a location' \ |
michael@0 | 74 | 'so that time zone rules can be set correctly.' |
michael@0 | 75 | |
michael@0 | 76 | continent= |
michael@0 | 77 | country= |
michael@0 | 78 | region= |
michael@0 | 79 | |
michael@0 | 80 | |
michael@0 | 81 | # Ask the user for continent or ocean. |
michael@0 | 82 | |
michael@0 | 83 | echo >&2 'Please select a continent or ocean.' |
michael@0 | 84 | |
michael@0 | 85 | select continent in \ |
michael@0 | 86 | Africa \ |
michael@0 | 87 | Americas \ |
michael@0 | 88 | Antarctica \ |
michael@0 | 89 | 'Arctic Ocean' \ |
michael@0 | 90 | Asia \ |
michael@0 | 91 | 'Atlantic Ocean' \ |
michael@0 | 92 | Australia \ |
michael@0 | 93 | Europe \ |
michael@0 | 94 | 'Indian Ocean' \ |
michael@0 | 95 | 'Pacific Ocean' \ |
michael@0 | 96 | 'none - I want to specify the time zone using the Posix TZ format.' |
michael@0 | 97 | do |
michael@0 | 98 | case $continent in |
michael@0 | 99 | '') |
michael@0 | 100 | echo >&2 'Please enter a number in range.';; |
michael@0 | 101 | ?*) |
michael@0 | 102 | case $continent in |
michael@0 | 103 | Americas) continent=America;; |
michael@0 | 104 | *' '*) continent=$(expr "$continent" : '\([^ ]*\)') |
michael@0 | 105 | esac |
michael@0 | 106 | break |
michael@0 | 107 | esac |
michael@0 | 108 | done |
michael@0 | 109 | case $continent in |
michael@0 | 110 | '') |
michael@0 | 111 | exit 1;; |
michael@0 | 112 | none) |
michael@0 | 113 | # Ask the user for a Posix TZ string. Check that it conforms. |
michael@0 | 114 | while |
michael@0 | 115 | echo >&2 'Please enter the desired value' \ |
michael@0 | 116 | 'of the TZ environment variable.' |
michael@0 | 117 | echo >&2 'For example, GST-10 is a zone named GST' \ |
michael@0 | 118 | 'that is 10 hours ahead (east) of UTC.' |
michael@0 | 119 | read TZ |
michael@0 | 120 | $AWK -v TZ="$TZ" 'BEGIN { |
michael@0 | 121 | tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+" |
michael@0 | 122 | time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?" |
michael@0 | 123 | offset = "[-+]?" time |
michael@0 | 124 | date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)" |
michael@0 | 125 | datetime = "," date "(/" time ")?" |
michael@0 | 126 | tzpattern = "^(:.*|" tzname offset "(" tzname \ |
michael@0 | 127 | "(" offset ")?(" datetime datetime ")?)?)$" |
michael@0 | 128 | if (TZ ~ tzpattern) exit 1 |
michael@0 | 129 | exit 0 |
michael@0 | 130 | }' |
michael@0 | 131 | do |
michael@0 | 132 | echo >&2 "\`$TZ' is not a conforming" \ |
michael@0 | 133 | 'Posix time zone string.' |
michael@0 | 134 | done |
michael@0 | 135 | TZ_for_date=$TZ;; |
michael@0 | 136 | *) |
michael@0 | 137 | # Get list of names of countries in the continent or ocean. |
michael@0 | 138 | countries=$($AWK -F'\t' \ |
michael@0 | 139 | -v continent="$continent" \ |
michael@0 | 140 | -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ |
michael@0 | 141 | ' |
michael@0 | 142 | /^#/ { next } |
michael@0 | 143 | $3 ~ ("^" continent "/") { |
michael@0 | 144 | if (!cc_seen[$1]++) cc_list[++ccs] = $1 |
michael@0 | 145 | } |
michael@0 | 146 | END { |
michael@0 | 147 | while (getline <TZ_COUNTRY_TABLE) { |
michael@0 | 148 | if ($0 !~ /^#/) cc_name[$1] = $2 |
michael@0 | 149 | } |
michael@0 | 150 | for (i = 1; i <= ccs; i++) { |
michael@0 | 151 | country = cc_list[i] |
michael@0 | 152 | if (cc_name[country]) { |
michael@0 | 153 | country = cc_name[country] |
michael@0 | 154 | } |
michael@0 | 155 | print country |
michael@0 | 156 | } |
michael@0 | 157 | } |
michael@0 | 158 | ' <$TZ_ZONE_TABLE | sort -f) |
michael@0 | 159 | |
michael@0 | 160 | |
michael@0 | 161 | # If there's more than one country, ask the user which one. |
michael@0 | 162 | case $countries in |
michael@0 | 163 | *"$newline"*) |
michael@0 | 164 | echo >&2 'Please select a country.' |
michael@0 | 165 | select country in $countries |
michael@0 | 166 | do |
michael@0 | 167 | case $country in |
michael@0 | 168 | '') echo >&2 'Please enter a number in range.';; |
michael@0 | 169 | ?*) break |
michael@0 | 170 | esac |
michael@0 | 171 | done |
michael@0 | 172 | |
michael@0 | 173 | case $country in |
michael@0 | 174 | '') exit 1 |
michael@0 | 175 | esac;; |
michael@0 | 176 | *) |
michael@0 | 177 | country=$countries |
michael@0 | 178 | esac |
michael@0 | 179 | |
michael@0 | 180 | |
michael@0 | 181 | # Get list of names of time zone rule regions in the country. |
michael@0 | 182 | regions=$($AWK -F'\t' \ |
michael@0 | 183 | -v country="$country" \ |
michael@0 | 184 | -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ |
michael@0 | 185 | ' |
michael@0 | 186 | BEGIN { |
michael@0 | 187 | cc = country |
michael@0 | 188 | while (getline <TZ_COUNTRY_TABLE) { |
michael@0 | 189 | if ($0 !~ /^#/ && country == $2) { |
michael@0 | 190 | cc = $1 |
michael@0 | 191 | break |
michael@0 | 192 | } |
michael@0 | 193 | } |
michael@0 | 194 | } |
michael@0 | 195 | $1 == cc { print $4 } |
michael@0 | 196 | ' <$TZ_ZONE_TABLE) |
michael@0 | 197 | |
michael@0 | 198 | |
michael@0 | 199 | # If there's more than one region, ask the user which one. |
michael@0 | 200 | case $regions in |
michael@0 | 201 | *"$newline"*) |
michael@0 | 202 | echo >&2 'Please select one of the following' \ |
michael@0 | 203 | 'time zone regions.' |
michael@0 | 204 | select region in $regions |
michael@0 | 205 | do |
michael@0 | 206 | case $region in |
michael@0 | 207 | '') echo >&2 'Please enter a number in range.';; |
michael@0 | 208 | ?*) break |
michael@0 | 209 | esac |
michael@0 | 210 | done |
michael@0 | 211 | case $region in |
michael@0 | 212 | '') exit 1 |
michael@0 | 213 | esac;; |
michael@0 | 214 | *) |
michael@0 | 215 | region=$regions |
michael@0 | 216 | esac |
michael@0 | 217 | |
michael@0 | 218 | # Determine TZ from country and region. |
michael@0 | 219 | TZ=$($AWK -F'\t' \ |
michael@0 | 220 | -v country="$country" \ |
michael@0 | 221 | -v region="$region" \ |
michael@0 | 222 | -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ |
michael@0 | 223 | ' |
michael@0 | 224 | BEGIN { |
michael@0 | 225 | cc = country |
michael@0 | 226 | while (getline <TZ_COUNTRY_TABLE) { |
michael@0 | 227 | if ($0 !~ /^#/ && country == $2) { |
michael@0 | 228 | cc = $1 |
michael@0 | 229 | break |
michael@0 | 230 | } |
michael@0 | 231 | } |
michael@0 | 232 | } |
michael@0 | 233 | $1 == cc && $4 == region { print $3 } |
michael@0 | 234 | ' <$TZ_ZONE_TABLE) |
michael@0 | 235 | |
michael@0 | 236 | # Make sure the corresponding zoneinfo file exists. |
michael@0 | 237 | TZ_for_date=$TZDIR/$TZ |
michael@0 | 238 | <$TZ_for_date || { |
michael@0 | 239 | echo >&2 "$0: time zone files are not set up correctly" |
michael@0 | 240 | exit 1 |
michael@0 | 241 | } |
michael@0 | 242 | esac |
michael@0 | 243 | |
michael@0 | 244 | |
michael@0 | 245 | # Use the proposed TZ to output the current date relative to UTC. |
michael@0 | 246 | # Loop until they agree in seconds. |
michael@0 | 247 | # Give up after 8 unsuccessful tries. |
michael@0 | 248 | |
michael@0 | 249 | extra_info= |
michael@0 | 250 | for i in 1 2 3 4 5 6 7 8 |
michael@0 | 251 | do |
michael@0 | 252 | TZdate=$(LANG=C TZ="$TZ_for_date" date) |
michael@0 | 253 | UTdate=$(LANG=C TZ=UTC0 date) |
michael@0 | 254 | TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)') |
michael@0 | 255 | UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)') |
michael@0 | 256 | case $TZsec in |
michael@0 | 257 | $UTsec) |
michael@0 | 258 | extra_info=" |
michael@0 | 259 | Local time is now: $TZdate. |
michael@0 | 260 | Universal Time is now: $UTdate." |
michael@0 | 261 | break |
michael@0 | 262 | esac |
michael@0 | 263 | done |
michael@0 | 264 | |
michael@0 | 265 | |
michael@0 | 266 | # Output TZ info and ask the user to confirm. |
michael@0 | 267 | |
michael@0 | 268 | echo >&2 "" |
michael@0 | 269 | echo >&2 "The following information has been given:" |
michael@0 | 270 | echo >&2 "" |
michael@0 | 271 | case $country+$region in |
michael@0 | 272 | ?*+?*) echo >&2 " $country$newline $region";; |
michael@0 | 273 | ?*+) echo >&2 " $country";; |
michael@0 | 274 | +) echo >&2 " TZ='$TZ'" |
michael@0 | 275 | esac |
michael@0 | 276 | echo >&2 "" |
michael@0 | 277 | echo >&2 "Therefore TZ='$TZ' will be used.$extra_info" |
michael@0 | 278 | echo >&2 "Is the above information OK?" |
michael@0 | 279 | |
michael@0 | 280 | ok= |
michael@0 | 281 | select ok in Yes No |
michael@0 | 282 | do |
michael@0 | 283 | case $ok in |
michael@0 | 284 | '') echo >&2 'Please enter 1 for Yes, or 2 for No.';; |
michael@0 | 285 | ?*) break |
michael@0 | 286 | esac |
michael@0 | 287 | done |
michael@0 | 288 | case $ok in |
michael@0 | 289 | '') exit 1;; |
michael@0 | 290 | Yes) break |
michael@0 | 291 | esac |
michael@0 | 292 | do : |
michael@0 | 293 | done |
michael@0 | 294 | |
michael@0 | 295 | case $SHELL in |
michael@0 | 296 | *csh) file=.login line="setenv TZ '$TZ'";; |
michael@0 | 297 | *) file=.profile line="TZ='$TZ'; export TZ" |
michael@0 | 298 | esac |
michael@0 | 299 | |
michael@0 | 300 | echo >&2 " |
michael@0 | 301 | You can make this change permanent for yourself by appending the line |
michael@0 | 302 | $line |
michael@0 | 303 | to the file '$file' in your home directory; then log out and log in again. |
michael@0 | 304 | |
michael@0 | 305 | Here is that TZ value again, this time on standard output so that you |
michael@0 | 306 | can use the $0 command in shell scripts:" |
michael@0 | 307 | |
michael@0 | 308 | echo "$TZ" |