Home | History | Annotate | Download | only in zic
      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/bin/nawk
     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"
    433