1 #! /usr/bin/ksh 2 # 3 # Copyright 2005 Sun Microsystems, Inc. All rights reserved. 4 # Use is subject to license terms. 5 # 6 # ident "%Z%%M% %I% %E% SMI" 7 # 8 # '@(#)tzselect.ksh 1.8' 9 10 # Ask the user about the time zone, and output the resulting TZ value to stdout. 11 # Interact with the user via stderr and stdin. 12 13 # Contributed by Paul Eggert 14 15 # Porting notes: 16 # 17 # This script requires several features of the Korn shell. 18 # If your host lacks the Korn shell, 19 # you can use either of the following free programs instead: 20 # 21 # <a href=ftp://ftp.gnu.org/pub/gnu/> 22 # Bourne-Again shell (bash) 23 # </a> 24 # 25 # <a href=ftp://ftp.cs.mun.ca/pub/pdksh/pdksh.tar.gz> 26 # Public domain ksh 27 # </a> 28 # 29 # This script also uses several features of modern awk programs. 30 # If your host lacks awk, or has an old awk that does not conform to POSIX.2, 31 # you can use either of the following free programs instead: 32 # 33 # <a href=ftp://ftp.gnu.org/pub/gnu/> 34 # GNU awk (gawk) 35 # </a> 36 # 37 # <a href=ftp://ftp.whidbey.net/pub/brennan/> 38 # mawk 39 # </a> 40 41 AWK=/usr/xpg4/bin/awk 42 GREP=/usr/bin/grep 43 EXPR=/usr/bin/expr 44 LOCALE=/usr/bin/locale 45 SORT=/usr/bin/sort 46 PRINTF=/usr/bin/printf 47 DATE=/usr/bin/date 48 GETTEXT=/usr/bin/gettext 49 50 TZDIR=/usr/share/lib/zoneinfo 51 52 # Messages 53 ERR_NO_SETUP="%s: time zone files are not set up correctly" 54 INFO_LOCATION="Please identify a location so that time zone rules \ 55 can be set correctly." 56 INFO_SELECT_CONT="Please select a continent or ocean." 57 INFO_POSIX="none - I want to specify the time zone using the POSIX \ 58 TZ format." 59 WARN_ENTER_NUM="Please enter a number in range." 60 INFO_ENTER_POSIX="Please enter the desired value of the TZ environment \ 61 variable." 62 INFO_POSIX_EX="For example, GST-10 is a zone named GST that is 10 hours \ 63 ahead (east) of UTC." 64 ERR_INV_POSIX="\`%s\' is not a conforming POSIX time zone string." 65 INFO_SELECT_CNTRY="Please select a country or region." 66 INFO_SELECT_TZ="Please select one of the following time zone regions." 67 INFO_EXTRA1="Local time is now: %s" 68 INFO_EXTRA2="Universal Time is now: %s" 69 INFO_INFO="The following information has been given:" 70 INFO_TZ="Therefore TZ='%s' will be used." 71 INFO_OK="Is the above information OK?" 72 INFO_YES="Yes" 73 INFO_NO="No" 74 WARN_ENTER_YORN="Please enter 1 for Yes, or 2 for No." 75 INFO_FINE="Here is the TZ value again, this time on standard output:" 76 77 # I18n support 78 TEXTDOMAINDIR=/usr/lib/locale; export TEXTDOMAINDIR 79 TEXTDOMAIN=SUNW_OST_OSCMD; export TEXTDOMAIN 80 DOMAIN2=SUNW_OST_ZONEINFO 81 82 # Make sure the tables are readable. 83 TZ_COUNTRY_TABLE=$TZDIR/tab/country.tab 84 TZ_ZONE_TABLE=$TZDIR/tab/zone_sun.tab 85 for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE 86 do 87 <$f || { 88 $PRINTF >&2 "`$GETTEXT "$ERR_NO_SETUP"`\n" $0 89 exit 1 90 } 91 done 92 93 newline=' 94 ' 95 IFS=$newline 96 97 # For C locale, don't need to call gettext(1) 98 loc_messages=`$LOCALE | $GREP LC_MESSAGES | $AWK -F"=" '{print $2}` 99 if [ "$loc_messages" = "\"C\"" -o "$loc_messages" = "C" ]; then 100 is_C=1 101 else 102 is_C=0 103 fi 104 105 iafrica=`$GETTEXT $DOMAIN2 Africa` 106 iamerica=`$GETTEXT $DOMAIN2 Americas` 107 iantarctica=`$GETTEXT $DOMAIN2 Antarctica` 108 iarcticocean=`$GETTEXT $DOMAIN2 "Arctic Ocean"` 109 iasia=`$GETTEXT $DOMAIN2 Asia` 110 iatlanticocean=`$GETTEXT $DOMAIN2 "Atlantic Ocean"` 111 iaustralia=`$GETTEXT $DOMAIN2 Australia` 112 ieurope=`$GETTEXT $DOMAIN2 Europe` 113 ipacificocean=`$GETTEXT $DOMAIN2 "Pacific Ocean"` 114 iindianocean=`$GETTEXT $DOMAIN2 "Indian Ocean"` 115 none=`$GETTEXT "$INFO_POSIX"` 116 117 # Begin the main loop. We come back here if the user wants to retry. 118 while 119 $PRINTF >&2 "`$GETTEXT "$INFO_LOCATION"`\n" 120 121 continent= 122 country= 123 region= 124 125 # Ask the user for continent or ocean. 126 $PRINTF >&2 "`$GETTEXT "$INFO_SELECT_CONT"`\n" 127 128 select continent in \ 129 $iafrica \ 130 $iamerica \ 131 $iantarctica \ 132 $iarcticocean \ 133 $iasia \ 134 $iatlanticocean \ 135 $iaustralia \ 136 $ieurope \ 137 $iindianocean \ 138 $ipacificocean \ 139 $none 140 141 do 142 case $continent in 143 '') 144 $PRINTF >&2 "`$GETTEXT "$WARN_ENTER_NUM"`\n";; 145 146 ?*) 147 case $continent in 148 $iafrica) continent=Africa;; 149 $iamerica) continent=America;; 150 $iantarctica) continent=Antarctica;; 151 $iarcticocean) continent=Arctic;; 152 $iasia) continent=Asia;; 153 $iatlanticocean) continent=Atlantic;; 154 $iaustralia) continent=Australia;; 155 $ieurope) continent=Europe;; 156 $iindianocean) continent=Indian;; 157 $ipacificocean) continent=Pacific;; 158 $none) continent=none;; 159 esac 160 break 161 esac 162 done 163 case $continent in 164 '') 165 exit 1;; 166 none) 167 # Ask the user for a POSIX TZ string. Check that it conforms. 168 while 169 $PRINTF >&2 "`$GETTEXT "$INFO_ENTER_POSIX"`\n" 170 $PRINTF >&2 "`$GETTEXT "$INFO_POSIX_EX"`\n" 171 172 read TZ 173 env LC_ALL=C $AWK -v TZ="$TZ" 'BEGIN { 174 tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+" 175 time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?" 176 offset = "[-+]?" time 177 date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)" 178 datetime = "," date "(/" time ")?" 179 tzpattern = "^(:.*|" tzname offset "(" tzname \ 180 "(" offset ")?(" datetime datetime ")?)?)$" 181 if (TZ ~ tzpattern) exit 1 182 exit 0 183 }' 184 do 185 $PRINTF >&2 "`$GETTEXT "$ERR_INV_POSIX"`\n" $TZ 186 187 done 188 TZ_for_date=$TZ;; 189 *) 190 # Get list of names of countries in the continent or ocean. 191 countries=$($AWK -F'\t' \ 192 -v continent="$continent" \ 193 -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ 194 ' 195 /^#/ { next } 196 $3 ~ ("^" continent "/") { 197 if (!cc_seen[$1]++) cc_list[++ccs] = $1 198 } 199 END { 200 while (getline <TZ_COUNTRY_TABLE) { 201 if ($0 !~ /^#/) cc_name[$1] = $2 202 } 203 for (i = 1; i <= ccs; i++) { 204 country = cc_list[i] 205 if (cc_name[country]) { 206 country = cc_name[country] 207 } 208 print country 209 } 210 } 211 ' <$TZ_ZONE_TABLE | $SORT -f) 212 213 # i18n country names 214 c=0 215 set -A icountry 216 for country in $countries 217 do 218 if [ $is_C -eq 1 ]; then 219 icountry[c]=$country 220 else 221 icountry[c]=`${GETTEXT} ${DOMAIN2} $country` 222 fi 223 ocountry[c]="$country" 224 c=$(( $c + 1 )) 225 done 226 maxnum=$c 227 228 # If there's more than one country, ask the user which one. 229 case $countries in 230 *"$newline"*) 231 $PRINTF >&2 "`$GETTEXT "$INFO_SELECT_CNTRY"`\n" 232 select xcountry in ${icountry[*]} 233 do 234 case $xcountry in 235 '') 236 $PRINTF >&2 "`$GETTEXT "$WARN_ENTER_NUM"`\n" 237 ;; 238 ?*) c=0 239 while true; do 240 if [ "$xcountry" = "${icountry[$c]}" ]; 241 then 242 country="${ocountry[$c]}" 243 break 244 fi 245 if [ $c -lt $maxnum ]; then 246 c=$(( $c + 1 )) 247 else 248 break 249 fi 250 done 251 break 252 esac 253 done 254 255 case $xcountry in 256 '') exit 1 257 esac;; 258 *) 259 country=$countries 260 xcountry=$countries 261 esac 262 263 264 # Get list of names of time zone rule regions in the country. 265 regions=$($AWK -F'\t' \ 266 -v country="$country" \ 267 -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ 268 ' 269 BEGIN { 270 cc = country 271 while (getline <TZ_COUNTRY_TABLE) { 272 if ($0 !~ /^#/ && country == $2) { 273 cc = $1 274 break 275 } 276 } 277 } 278 $1 == cc { print $5 } 279 ' <$TZ_ZONE_TABLE) 280 281 # I18n region names 282 c=0 283 set -A iregion 284 for region in $regions 285 do 286 if [ $is_C -eq 1 ]; then 287 iregion[c]=$region 288 else 289 iregion[c]=`${GETTEXT} ${DOMAIN2} $region` 290 fi 291 oregion[c]="$region" 292 c=$(( $c + 1 )) 293 done 294 maxnum=$c 295 296 # If there's more than one region, ask the user which one. 297 case $regions in 298 *"$newline"*) 299 $PRINTF >&2 "`$GETTEXT "$INFO_SELECT_TZ"`\n" 300 301 select xregion in ${iregion[*]} 302 do 303 case $xregion in 304 '') 305 $PRINTF >&2 "`$GETTEXT "$WARN_ENTER_NUM"`\n" 306 ;; 307 ?*) c=0 308 while true; do 309 if [ "$xregion" = "${iregion[$c]}" ]; 310 then 311 region="${oregion[$c]}" 312 break 313 fi 314 if [ $c -lt $maxnum ]; then 315 c=$(( $c + 1 )) 316 else 317 break 318 fi 319 done 320 break 321 esac 322 done 323 324 case $region in 325 '') exit 1 326 esac;; 327 *) 328 region=$regions 329 xregion=$regions 330 esac 331 332 # Determine TZ from country and region. 333 TZ=$($AWK -F'\t' \ 334 -v country="$country" \ 335 -v region="$region" \ 336 -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ 337 ' 338 BEGIN { 339 cc = country 340 while (getline <TZ_COUNTRY_TABLE) { 341 if ($0 !~ /^#/ && country == $2) { 342 cc = $1 343 break 344 } 345 } 346 } 347 348 $1 == cc && $5 == region { 349 # Check if tzname mapped to 350 # backward compatible tzname 351 if ($4 == "-") { 352 print $3 353 } else { 354 print $4 355 } 356 } 357 ' <$TZ_ZONE_TABLE) 358 359 # Make sure the corresponding zoneinfo file exists. 360 TZ_for_date=$TZDIR/$TZ 361 <$TZ_for_date || { 362 $PRINTF >&2 "`$GETTEXT "$ERR_NO_SETUP"`\n" $0 363 exit 1 364 } 365 # Absolute path TZ's not supported 366 TZ_for_date=$TZ 367 esac 368 369 370 # Use the proposed TZ to output the current date relative to UTC. 371 # Loop until they agree in seconds. 372 # Give up after 8 unsuccessful tries. 373 374 extra_info1= 375 extra_info2= 376 for i in 1 2 3 4 5 6 7 8 377 do 378 TZdate=$(LANG=C TZ="$TZ_for_date" $DATE) 379 UTdate=$(LANG=C TZ=UTC0 $DATE) 380 TZsec=$($EXPR "$TZdate" : '.*:\([0-5][0-9]\)') 381 UTsec=$($EXPR "$UTdate" : '.*:\([0-5][0-9]\)') 382 case $TZsec in 383 $UTsec) 384 extra_info1=$($PRINTF "`$GETTEXT "$INFO_EXTRA1"`" \ 385 "$TZdate") 386 extra_info2=$($PRINTF "`$GETTEXT "$INFO_EXTRA2"`" \ 387 "$UTdate") 388 break 389 esac 390 done 391 392 393 # Output TZ info and ask the user to confirm. 394 395 $PRINTF >&2 "\n" 396 $PRINTF >&2 "`$GETTEXT "$INFO_INFO"`\n" 397 $PRINTF >&2 "\n" 398 399 case $country+$region in 400 ?*+?*) $PRINTF >&2 " $xcountry$newline $xregion\n";; 401 ?*+) $PRINTF >&2 " $xcountry\n";; 402 +) $PRINTF >&2 " TZ='$TZ'\n" 403 esac 404 $PRINTF >&2 "\n" 405 $PRINTF >&2 "`$GETTEXT "$INFO_TZ"`\n" "$TZ" 406 $PRINTF >&2 "$extra_info1\n" 407 $PRINTF >&2 "$extra_info2\n" 408 $PRINTF >&2 "`$GETTEXT "$INFO_OK"`\n" 409 410 ok= 411 # select ok in Yes No 412 Yes="`$GETTEXT "$INFO_YES"`" 413 No="`$GETTEXT "$INFO_NO"`" 414 select ok in $Yes $No 415 do 416 case $ok in 417 '') 418 $PRINTF >&2 "`$GETTEXT "$WARN_ENTER_YORN"`\n" 419 ;; 420 ?*) break 421 esac 422 done 423 case $ok in 424 '') exit 1;; 425 $Yes) break 426 esac 427 do : 428 done 429 430 $PRINTF >&2 "`$GETTEXT "$INFO_FINE"`\n" 431 432 $PRINTF "%s\n" "$TZ"