1 if [ ! "$_TIMEZONE_ZONES_SUBR" ]; then _TIMEZONE_ZONES_SUBR=1
3 # Copyright (c) 2011-2012 Devin Teske
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 ############################################################ INCLUDES
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 . $BSDCFG_SHARE/common.subr || exit 1
33 f_dprintf "%s: loading includes..." timezone/zones.subr
34 f_include $BSDCFG_SHARE/dialog.subr
35 f_include $BSDCFG_SHARE/timezone/continents.subr
37 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone"
38 f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
40 ############################################################ CONFIGURATION
45 _PATH_ZONETAB="/usr/share/zoneinfo/zone.tab"
46 _PATH_ZONEINFO="/usr/share/zoneinfo"
47 _PATH_LOCALTIME="/etc/localtime"
48 _PATH_DB="/var/db/zoneinfo"
51 # Export required i18n messages for awk(1) ENVIRON visibility
53 export msg_conflicting_zone_definition
54 export msg_country_code_invalid
55 export msg_country_code_unknown
56 export msg_invalid_country_code
57 export msg_invalid_format
58 export msg_invalid_region
59 export msg_invalid_zone_name
60 export msg_zone_multiply_defined
61 export msg_zone_must_have_description
63 ############################################################ FUNCTIONS
67 # Read the zone descriptions database in _PATH_ZONETAB:
68 # /usr/share/zoneinfo/zone.tab on all OSes
70 # The format of this file (on all OSes) is:
71 # code coordinates TZ comments
73 # With each of the following elements (described below) being separated by a
74 # single tab character:
77 # The ISO 3166 2-character country code.
79 # Latitude and logitude of the zone's principal location in ISO
80 # 6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM
81 # or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long-
84 # Zone name used in value of TZ environment variable.
86 # Comments; present if and only if the country has multiple rows.
88 # Required variables [from continents.subr]:
91 # Space-separated list of continents.
93 # Directory element in _PATH_ZONEINFO for the continent
96 # Required variables [created by f_read_iso3166_table from iso3166.subr]:
99 # Country name of the country represented by CODE, the 2-
100 # character country code.
102 # Variables created by this function:
104 # country_CODE_nzones
105 # Either set to `-1' to indicate that the 2-character country
106 # code has only a single zone associated with it (and therefore
107 # you should query the `country_CODE_*' environment variables),
108 # or set to `0' or higher to indicate how many zones are assoc-
109 # iated with the given country code. When multiple zones are
110 # configured for a single code, you should instead query the
111 # `country_CODE_*_N' environment variables (e.g., `echo
112 # $country_AQ_descr_1' prints the description of the first
113 # timezone in Antarctica).
114 # country_CODE_filename
115 # The ``filename'' portion of the TZ value that appears after the
116 # `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man'
117 # from `Europe/Isle_of_Man').
119 # The ``continent'' portion of the TZ value that appears before
120 # the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from
121 # `Europe/Isle_of_Man').
123 # The comments associated with the ISO 3166 code entry (if any).
125 # NOTE: CODE is the 2-character country code.
127 # This function is a two-parter. Below is the awk(1) portion of the function,
128 # afterward is the sh(1) function which utilizes the below awk script.
131 # Variables that should be defined on the invocation line:
132 # -v progname="progname"
139 # Initialize continents array/map (name => id)
141 split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/)
146 name = ENVIRON["continent_" cont "_name"]
147 continents[name] = cont
150 function die(fmt, argc, argv)
152 printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname
153 for (n = 1; n <= argc; n++)
154 printf " \"%s\"", argv[n]
159 function find_continent(name)
161 return continents[name]
163 function add_zone_to_country(lineno, tlc, descr, file, cont)
166 # Validate the two-character country code
168 if (!match(tlc, /^[A-Z][A-Z]$/))
173 die(ENVRION["msg_country_code_invalid"], 3, argv)
175 if (!ENVIRON["country_" tlc "_name"])
180 die(ENVIRON["msg_country_code_unknown"], 3, argv)
184 # Add Zone to an array that we will parse at the end
186 if (length(descr) > 0)
188 if (country_nzones[tlc] < 0)
192 die(ENVIRON["msg_conflicting_zone_definition"], 2, argv)
195 n = ++country_nzones[tlc]
196 country_cont[tlc,n] = cont
197 country_filename[tlc,n] = file
198 country_descr[tlc,n] = descr
202 if (country_nzones[tlc] > 0)
206 die(ENVIRON["msg_zone_must_have_description"], 2, argv)
208 if (country_nzones[tlc] < 0)
212 die(ENVIRON["msg_zone_multiply_defined"], 2, argv)
215 country_nzones[tlc] = -1
216 country_cont[tlc] = cont
217 country_filename[tlc] = file
220 function print_country_code(tlc)
222 nz = country_nzones[tlc]
224 printf "country_%s_nzones=%d\n", tlc, nz
225 printf "export country_%s_nzones\n", tlc
229 printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc]
230 printf "export country_%s_cont\n", tlc
231 printf "country_%s_filename=\"%s\"\n",
232 tlc, country_filename[tlc]
239 printf "country_%s_cont_%d=\"%s\"\n",
240 tlc, n, country_cont[tlc,n]
241 printf "export country_%s_cont_%d\n", tlc, n
242 printf "country_%s_filename_%d=\"%s\"\n",
243 tlc, n, country_filename[tlc,n]
244 printf "country_%s_descr_%d=\"%s\"\n",
245 tlc, n, country_descr[tlc,n]
257 # Split the current record (on TAB) into an array
259 if (split($0, line, /\t/) < 2)
263 die(ENVIRON["msg_invalid_format"], 2, argv)
266 # Get the ISO3166-1 (Alpha 1) 2-letter country code
270 # Validate the two-character country code
272 if (length(tlc) != 2)
277 die(ENVIRON["msg_invalid_country_code"], 3, argv)
284 # Validate the TZ field
291 die(ENVIRON["msg_invalid_zone_name"], 3, argv)
295 # Get the continent portion of the TZ field
298 sub("/.*$", "", contbuf)
301 # Validate the continent
303 cont = find_continent(contbuf)
309 die(ENVIRON["msg_invalid_region"], 3, argv)
313 # Get the filename portion of the TZ field
316 sub("^[^/]*/", "", filename)
319 # Calculate the substr start-position of the comment
324 descr_start += length(line[n]) + 1
326 # Get the comment field
327 descr = substr($0, descr_start + 1)
329 add_zone_to_country(lineno, tlc, descr, filename, cont)
332 if (failed) exit failed
333 for (tlc in country_nzones)
334 print_country_code(tlc)
339 eval $( awk -v progname="$pgm" \
340 "$f_read_zones_awk" \
344 # f_install_zoneinfo_file $filename
346 # Installs a zone file to _PATH_LOCALTIME.
348 f_install_zoneinfo_file()
350 local zoneinfo_file="$1"
351 local copymode title msg err height width
353 if [ -L "$_PATH_LOCALTIME" ]; then
355 elif [ ! -e "$_PATH_LOCALTIME" ]; then
356 # Nothing there yet...
362 if [ "$VERBOSE" ]; then
363 if [ ! "$zoneinfo_file" ]; then
364 msg=$( printf "$msg_removing_file" "$_PATH_LOCALTIME" )
365 elif [ "$copymode" ]; then
366 msg=$( printf "$msg_copying_file" \
367 "$zoneinfo_file" "$_PATH_LOCALTIME" )
369 msg=$( printf "$msg_creating_symlink" \
370 "$_PATH_LOCALTIME" "$zoneinfo_file" )
372 if [ "$USEDIALOG" ]; then
373 f_dialog_title "$msg_info"
374 f_dialog_msgbox "$msg"
375 f_dialog_title_restore
381 [ "$REALLYDOIT" ] || return $SUCCESS
383 if [ ! "$zoneinfo_file" ]; then
384 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 )
386 if [ "$USEDIALOG" ]; then
387 f_dialog_title "$msg_error"
388 f_dialog_msgbox "$err"
389 f_dialog_title_restore
396 err=$( rm -f "$_PATH_DB" 2>&1 )
398 if [ "$USEDIALOG" ]; then
399 f_dialog_title "$msg_error"
400 f_dialog_msgbox "$err"
401 f_dialog_title_restore
408 if [ "$VERBOSE" ]; then
409 msg=$( printf "$msg_removed_file" "$_PATH_LOCALTIME" )
410 if [ "$USEDIALOG" ]; then
411 f_dialog_title "$msg_done"
412 f_dialog_msgbox "$msg"
413 f_dialog_title_restore
423 if [ "$copymode" ]; then
425 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 )
427 if [ "$USEDIALOG" ]; then
428 f_dialog_title "$msg_error"
429 f_dialog_msgbox "$err"
430 f_dialog_title_restore
437 err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" )
439 if [ "$USEDIALOG" ]; then
440 f_dialog_title "$msg_error"
441 f_dialog_msgbox "$err"
442 f_dialog_title_restore
449 err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" )
451 if [ "$USEDIALOG" ]; then
452 f_dialog_title "$msg_error"
453 f_dialog_msgbox "$err"
454 f_dialog_title_restore
463 err=$( ( :< "$zoneinfo_file" ) 2>&1 )
465 if [ "$USEDIALOG" ]; then
466 f_dialog_title "$msg_error"
467 f_dialog_msgbox "$err"
468 f_dialog_title_restore
475 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 )
477 if [ "$USEDIALOG" ]; then
478 f_dialog_title "$msg_error"
479 f_dialog_msgbox "$err"
480 f_dialog_title_restore
487 err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 )
489 if [ "$USEDIALOG" ]; then
490 f_dialog_title "$msg_error"
491 f_dialog_msgbox "$err"
492 f_dialog_title_restore
501 if [ "$VERBOSE" ]; then
502 if [ "$copymode" ]; then
503 msg=$( printf "$msg_copied_timezone_file" \
504 "$zoneinfo_file" "$_PATH_LOCALTIME" )
506 msg=$( printf "$msg_created_symlink" \
507 "$_PATH_LOCALTIME" "$zoneinfo_file" )
509 if [ "$USEDIALOG" ]; then
510 f_dialog_title "$msg_done"
511 f_dialog_msgbox "$msg"
512 f_dialog_title_restore
521 # f_install_zoneinfo $zoneinfo
523 # Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo
524 # will be written to _PATH_DB (usable later with the `-r' flag).
531 f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo"
534 # Save knowledge for later
535 if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then
536 if true 2> /dev/null > "$_PATH_DB"; then
537 cat <<-EOF > "$_PATH_DB"
546 # f_confirm_zone $filename
548 # Prompt the user to confirm the new timezone data. The first (and only)
549 # argument should be the pathname to the zoneinfo file, either absolute or
550 # relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles").
552 # The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is
553 # pressed (see dialog(1) for additional details).
558 f_dialog_title "$msg_confirmation"
559 local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
560 f_dialog_title_restore
561 local tm_zone="$( TZ="$filename" date +%Z )"
562 local prompt="$( printf "$msg_look_reasonable" "$tm_zone" )"
563 local height=5 width=72
565 if [ "$USE_XDIALOG" ]; then
566 height=$(( $height + 4 ))
569 --backtitle "$btitle" \
570 --ok-label "$msg_yes" \
571 --cancel-label "$msg_no" \
572 --yesno "$prompt" $height $width
576 --backtitle "$btitle" \
577 --yes-label "$msg_yes" \
578 --no-label "$msg_no" \
579 --yesno "$prompt" $height $width
585 # Resets to the UTC timezone.
589 f_confirm_zone "" || return $FAILURE
590 f_install_zoneinfo_file ""
593 ############################################################ MAIN
595 f_dprintf "%s: Successfully loaded." timezone/zones.subr
597 fi # ! $_TIMEZONE_ZONES_SUBR