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