1if [ ! "$_TIMEZONE_ZONES_SUBR" ]; then _TIMEZONE_ZONES_SUBR=1 2# 3# Copyright (c) 2011-2012 Devin Teske 4# All Rights Reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 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. 14# 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 (INLUDING, 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 25# SUCH DAMAGE. 26# 27# $FreeBSD$ 28# 29############################################################ INCLUDES 30 31BSDCFG_SHARE="/usr/share/bsdconfig" 32. $BSDCFG_SHARE/common.subr || exit 1 33f_include $BSDCFG_SHARE/dialog.subr 34f_include $BSDCFG_SHARE/timezone/continents.subr 35 36BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone" 37f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr 38 39############################################################ CONFIGURATION 40 41# 42# Standard pathnames 43# 44_PATH_ZONETAB="/usr/share/zoneinfo/zone.tab" 45_PATH_ZONEINFO="/usr/share/zoneinfo" 46_PATH_LOCALTIME="/etc/localtime" 47_PATH_DB="/var/db/zoneinfo" 48 49# 50# Export required i18n messages for awk(1) ENVIRON visibility 51# 52export msg_conflicting_zone_definition 53export msg_country_code_invalid 54export msg_country_code_unknown 55export msg_invalid_country_code 56export msg_invalid_format 57export msg_invalid_region 58export msg_invalid_zone_name 59export msg_zone_multiply_defined 60export msg_zone_must_have_description 61 62############################################################ FUNCTIONS 63 64# f_read_zones 65# 66# Read the zone descriptions database in _PATH_ZONETAB: 67# /usr/share/zoneinfo/zone.tab on all OSes 68# 69# The format of this file (on all OSes) is: 70# code coordinates TZ comments 71# 72# With each of the following elements (described below) being separated by a 73# single tab character: 74# 75# code 76# The ISO 3166 2-character country code. 77# coordinates 78# Latitude and logitude of the zone's principal location in ISO 79# 6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM 80# or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long- 81# itude (+ is east). 82# TZ 83# Zone name used in value of TZ environment variable. 84# comments 85# Comments; present if and only if the country has multiple rows. 86# 87# Required variables [from continents.subr]: 88# 89# CONTINENTS 90# Space-separated list of continents. 91# continent_*_name 92# Directory element in _PATH_ZONEINFO for the continent 93# represented by *. 94# 95# Required variables [created by f_read_iso3166_table from iso3166.subr]: 96# 97# country_CODE_name 98# Country name of the country represented by CODE, the 2- 99# character country code. 100# 101# Variables created by this function: 102# 103# country_CODE_nzones 104# Either set to `-1' to indicate that the 2-character country 105# code has only a single zone associated with it (and therefore 106# you should query the `country_CODE_*' environment variables), 107# or set to `0' or higher to indicate how many zones are assoc- 108# iated with the given country code. When multiple zones are 109# configured for a single code, you should instead query the 110# `country_CODE_*_N' environment variables (e.g., `echo 111# $country_AQ_descr_1' prints the description of the first 112# timezone in Antarctica). 113# country_CODE_filename 114# The ``filename'' portion of the TZ value that appears after the 115# `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man' 116# from `Europe/Isle_of_Man'). 117# country_CODE_cont 118# The ``continent'' portion of the TZ value that appears before 119# the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from 120# `Europe/Isle_of_Man'). 121# country_CODE_descr 122# The comments associated with the ISO 3166 code entry (if any). 123# 124# NOTE: CODE is the 2-character country code. 125# 126# This function is a two-parter. Below is the awk(1) portion of the function, 127# afterward is the sh(1) function which utilizes the below awk script. 128# 129f_read_zones_awk=' 130# Variables that should be defined on the invocation line: 131# -v progname="progname" 132# 133BEGIN { 134 lineno = 0 135 failed = 0 136 137 # 138 # Initialize continents array/map (name => id) 139 # 140 split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/) 141 for (item in array) 142 { 143 cont = array[item] 144 if (!cont) continue 145 name = ENVIRON["continent_" cont "_name"] 146 continents[name] = cont 147 } 148} 149function die(fmt, argc, argv) 150{ 151 printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname 152 for (n = 1; n <= argc; n++) 153 printf " \"%s\"", argv[n] 154 print "" 155 failed++ 156 exit 1 157} 158function find_continent(name) 159{ 160 return continents[name] 161} 162function add_zone_to_country(lineno, tlc, descr, file, cont) 163{ 164 # 165 # Validate the two-character country code 166 # 167 if (!match(tlc, /^[A-Z][A-Z]$/)) 168 { 169 argv[1] = FILENAME 170 argv[2] = lineno 171 argv[3] = tlc 172 die(ENVRION["msg_country_code_invalid"], 3, argv) 173 } 174 if (!ENVIRON["country_" tlc "_name"]) 175 { 176 argv[1] = FILENAME 177 argv[2] = lineno 178 argv[3] = tlc 179 die(ENVIRON["msg_country_code_unknown"], 3, argv) 180 } 181 182 # 183 # Add Zone to an array that we will parse at the end 184 # 185 if (length(descr) > 0) 186 { 187 if (country_nzones[tlc] < 0) 188 { 189 argv[1] = FILENAME 190 argv[2] = lineno 191 die(ENVIRON["msg_conflicting_zone_definition"], 2, argv) 192 } 193 194 n = ++country_nzones[tlc] 195 country_cont[tlc,n] = cont 196 country_filename[tlc,n] = file 197 country_descr[tlc,n] = descr 198 } 199 else 200 { 201 if (country_nzones[tlc] > 0) 202 { 203 argv[1] = FILENAME 204 argv[2] = lineno 205 die(ENVIRON["msg_zone_must_have_description"], 2, argv) 206 } 207 if (country_nzones[tlc] < 0) 208 { 209 argv[1] = FILENAME 210 argv[2] = lineno 211 die(ENVIRON["msg_zone_multiply_defined"], 2, argv) 212 } 213 214 country_nzones[tlc] = -1 215 country_cont[tlc] = cont 216 country_filename[tlc] = file 217 } 218} 219function print_country_code(tlc) 220{ 221 nz = country_nzones[tlc] 222 223 printf "country_%s_nzones=%d\n", tlc, nz 224 printf "export country_%s_nzones\n", tlc 225 226 if (nz < 0) 227 { 228 printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc] 229 printf "export country_%s_cont\n", tlc 230 printf "country_%s_filename=\"%s\"\n", 231 tlc, country_filename[tlc] 232 } 233 else 234 { 235 n = 0 236 while ( ++n <= nz ) 237 { 238 printf "country_%s_cont_%d=\"%s\"\n", 239 tlc, n, country_cont[tlc,n] 240 printf "export country_%s_cont_%d\n", tlc, n 241 printf "country_%s_filename_%d=\"%s\"\n", 242 tlc, n, country_filename[tlc,n] 243 printf "country_%s_descr_%d=\"%s\"\n", 244 tlc, n, country_descr[tlc,n] 245 } 246 } 247} 248/^#/ { 249 lineno++ 250 next 251} 252!/^#/ { 253 lineno++ 254 255 # 256 # Split the current record (on TAB) into an array 257 # 258 if (split($0, line, /\t/) < 2) 259 { 260 argv[1] = FILENAME 261 argv[2] = lineno 262 die(ENVIRON["msg_invalid_format"], 2, argv) 263 } 264 265 # Get the ISO3166-1 (Alpha 1) 2-letter country code 266 tlc = line[1] 267 268 # 269 # Validate the two-character country code 270 # 271 if (length(tlc) != 2) 272 { 273 argv[1] = FILENAME 274 argv[2] = lineno 275 argv[3] = tlc 276 die(ENVIRON["msg_invalid_country_code"], 3, argv) 277 } 278 279 # Get the TZ field 280 tz = line[3] 281 282 # 283 # Validate the TZ field 284 # 285 if (!match(tz, "/")) 286 { 287 argv[1] = FILENAME 288 argv[2] = lineno 289 argv[3] = tz 290 die(ENVIRON["msg_invalid_zone_name"], 3, argv) 291 } 292 293 # 294 # Get the continent portion of the TZ field 295 # 296 contbuf = tz 297 sub("/.*$", "", contbuf) 298 299 # 300 # Validate the continent 301 # 302 cont = find_continent(contbuf) 303 if (!cont) 304 { 305 argv[1] = FILENAME 306 argv[2] = lineno 307 argv[3] = contbuf 308 die(ENVIRON["msg_invalid_region"], 3, argv) 309 } 310 311 # 312 # Get the filename portion of the TZ field 313 # 314 filename = tz 315 sub("^[^/]*/", "", filename) 316 317 # 318 # Calculate the substr start-position of the comment 319 # 320 descr_start = 0 321 n = 4 322 while (--n) 323 descr_start += length(line[n]) + 1 324 325 # Get the comment field 326 descr = substr($0, descr_start + 1) 327 328 add_zone_to_country(lineno, tlc, descr, filename, cont) 329} 330END { 331 if (failed) exit failed 332 for (tlc in country_nzones) 333 print_country_code(tlc) 334} 335' 336f_read_zones() 337{ 338 eval $( awk -v progname="$pgm" \ 339 "$f_read_zones_awk" \ 340 "$_PATH_ZONETAB" ) 341} 342 343# f_install_zoneinfo_file $filename 344# 345# Installs a zone file to _PATH_LOCALTIME. 346# 347f_install_zoneinfo_file() 348{ 349 local zoneinfo_file="$1" 350 local copymode title msg err size 351 352 if [ -L "$_PATH_LOCALTIME" ]; then 353 copymode= 354 elif [ ! -e "$_PATH_LOCALTIME" ]; then 355 # Nothing there yet... 356 copymode=1 357 else 358 copymode=1 359 fi 360 361 if [ "$VERBOSE" ]; then 362 if [ ! "$zoneinfo_file" ]; then 363 msg=$( printf "$msg_removing_file" "$_PATH_LOCALTIME" ) 364 elif [ "$copymode" ]; then 365 msg=$( printf "$msg_copying_file" \ 366 "$zoneinfo_file" "$_PATH_LOCALTIME" ) 367 else 368 msg=$( printf "$msg_creating_symlink" \ 369 "$_PATH_LOCALTIME" "$zoneinfo_file" ) 370 fi 371 if [ "$USEDIALOG" ]; then 372 f_dialog_title "$msg_info" 373 title="$DIALOG_TITLE" 374 btitle="$DIALOG_BACKTITLE" 375 f_dialog_title_restore 376 size=$( f_dialog_buttonbox_size "$title" \ 377 "$btitle" "$msg" ) 378 eval $DIALOG \ 379 --title \"\$title\" \ 380 --backtitle \"\$btitle\" \ 381 --ok-label \"\$msg_ok\" \ 382 --msgbox \"\$msg\" $size 383 else 384 printf "%s\n" "$msg" 385 fi 386 fi 387 388 if [ "$REALLYDOIT" ]; then 389 f_dialog_title "$msg_error" 390 title="$DIALOG_TITLE" 391 btitle="$DIALOG_BACKTITLE" 392 f_dialog_title_restore 393 394 if [ ! "$zoneinfo_file" ]; then 395 396 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) 397 if [ "$err" ]; then 398 if [ "$USEDIALOG" ]; then 399 size=$( f_dialog_buttonbox_size \ 400 "$title" \ 401 "$btitle" \ 402 "$err" ) 403 eval $DIALOG \ 404 --title \"\$title\" \ 405 --backtitle \"\$btitle\" \ 406 --ok-label \"\$msg_ok\" \ 407 --msgbox \"\$err\" $size 408 else 409 f_err "%s\n" "$err" 410 fi 411 return $FAILURE 412 fi 413 414 err=$( rm -f "$_PATH_DB" 2>&1 ) 415 if [ "$err" ]; then 416 if [ "$USEDIALOG" ]; then 417 size=$( f_dialog_buttonbox_size \ 418 "$title" \ 419 "$btitle" \ 420 "$err" ) 421 eval $DIALOG \ 422 --title \"\$title\" \ 423 --backtitle \"\$btitle\" \ 424 --ok-label \"\$msg_ok\" \ 425 --msgbox \"\$err\" $size 426 else 427 f_err "%s\n" "$err" 428 fi 429 return $FAILURE 430 fi 431 432 if [ "$VERBOSE" ]; then 433 title="$msg_done" 434 msg=$( printf "$msg_removed_file" \ 435 "$_PATH_LOCALTIME" ) 436 if [ "$USEDIALOG" ]; then 437 size=$( f_dialog_buttonbox_size \ 438 "$title" \ 439 "$btitle" \ 440 "$msg" ) 441 eval $DIALOG \ 442 --title \"\$title\" \ 443 --backtitle \"\$btitle\" \ 444 --ok-label \"\$msg_ok\" \ 445 --msgbox \"\$msg\" $size 446 else 447 printf "%s\n" "$msg" 448 fi 449 fi 450 451 return $SUCCESS 452 453 fi # ! zoneinfo_file 454 455 if [ "$copymode" ]; then 456 457 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) 458 if [ "$err" ]; then 459 if [ "$USEDIALOG" ]; then 460 size=$( f_dialog_buttonbox_size \ 461 "$title" \ 462 "$btitle" \ 463 "$err" ) 464 eval $DIALOG \ 465 --title \"\$title\" \ 466 --backtitle \"\$btitle\" \ 467 --ok-label \"\$msg_ok\" \ 468 --msgbox \"\$err\" $size 469 else 470 f_err "%s\n" "$err" 471 fi 472 return $FAILURE 473 fi 474 475 err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" ) 476 if [ "$err" ]; then 477 if [ "$USEDIALOG" ]; then 478 size=$( f_dialog_buttonbox_size \ 479 "$title" \ 480 "$btitle" \ 481 "$err" ) 482 eval $DIALOG \ 483 --title \"\$title\" \ 484 --backtitle \"\$btitle\" \ 485 --ok-label \"\$msg_ok\" \ 486 --msgbox \"\$err\" $size 487 else 488 f_err "%s\n" "$err" 489 fi 490 return $FAILURE 491 fi 492 493 err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" ) 494 if [ "$err" ]; then 495 if [ "$USEDIALOG" ]; then 496 size=$( f_dialog_buttonbox_size \ 497 "$title" \ 498 "$btitle" \ 499 "$err" ) 500 eval $DIALOG \ 501 --title \"\$title\" \ 502 --backtitle \"\$btitle\" \ 503 --ok-label \"\$msg_ok\" \ 504 --msgbox \"\$err\" $size 505 else 506 f_err "%s\n" "$err" 507 fi 508 return $FAILURE 509 fi 510 511 else # ! copymode 512 513 err=$( ( :< "$zoneinfo_file" ) 2>&1 ) 514 if [ "$err" ]; then 515 if [ "$USEDIALOG" ]; then 516 size=$( f_dialog_buttonbox_size \ 517 "$title" \ 518 "$btitle" \ 519 "$err" ) 520 eval $DIALOG \ 521 --title \"\$title\" \ 522 --backtitle \"\$btitle\" \ 523 --ok-label \"\$msg_ok\" \ 524 --msgbox \"\$err\" $size 525 else 526 f_err "%s\n" "$err" 527 fi 528 return $FAILURE 529 fi 530 531 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) 532 if [ "$err" ]; then 533 if [ "$USEDIALOG" ]; then 534 size=$( f_dialog_buttonbox_size \ 535 "$title" \ 536 "$btitle" \ 537 "$err" ) 538 eval $DIALOG \ 539 --title \"\$title\" \ 540 --backtitle \"\$btitle\" \ 541 --ok-label \"\$msg_ok\" \ 542 --msgbox \"\$err\" $size 543 else 544 f_err "%s\n" "$err" 545 fi 546 return $FAILURE 547 fi 548 549 err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 ) 550 if [ "$err" ]; then 551 if [ "$USEDIALOG" ]; then 552 size=$( f_dialog_buttonbox_size \ 553 "$title" \ 554 "$btitle" \ 555 "$err" ) 556 eval $DIALOG \ 557 --title \"\$title\" \ 558 --backtitle \"\$btitle\" \ 559 --ok-label \"\$msg_ok\" \ 560 --msgbox \"\$err\" $size 561 else 562 f_err "%s\n" "$err" 563 fi 564 return $FAILURE 565 fi 566 567 fi # copymode 568 569 if [ "$VERBOSE" ]; then 570 title="$msg_done" 571 if [ "$copymode" ]; then 572 msg=$( printf "$msg_copied_timezone_file" \ 573 "$zoneinfo_file" \ 574 "$_PATH_LOCALTIME" ) 575 else 576 msg=$( printf "$msg_created_symlink" \ 577 "$_PATH_LOCALTIME" \ 578 "$zoneinfo_file" ) 579 fi 580 if [ "$USEDIALOG" ]; then 581 size=$( f_dialog_buttonbox_size \ 582 "$title" "$btitle" "$msg" ) 583 eval $DIALOG \ 584 --title \"\$title\" \ 585 --backtitle \"\$btitle\" \ 586 --ok-label \"\$msg_ok\" \ 587 --msgbox \"\$msg\" $size 588 else 589 printf "%s\n" "$msg" 590 fi 591 fi 592 593 fi # REALLYDOIT 594 595 return $SUCCESS 596} 597 598# f_install_zoneinfo $zoneinfo 599# 600# Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo 601# will be written to _PATH_DB (usable later with the `-r' flag). 602# 603f_install_zoneinfo() 604{ 605 local zoneinfo="$1" 606 local rv 607 608 f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo" 609 rv=$? 610 611 # Save knowledge for later 612 if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then 613 if : 2>&- > "$_PATH_DB"; then 614 cat <<-EOF > "$_PATH_DB" 615 $zoneinfo 616 EOF 617 fi 618 fi 619 620 return $rv 621} 622 623# f_confirm_zone $filename 624# 625# Prompt the user to confirm the new timezone data. The first (and only) 626# argument should be the pathname to the zoneinfo file, either absolute or 627# relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles"). 628# 629# The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is 630# pressed (see dialog(1) for additional details). 631# 632f_confirm_zone() 633{ 634 local filename="$1" 635 f_dialog_title "$msg_confirmation" 636 local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE" 637 f_dialog_title_restore 638 local tm_zone="$( TZ="$filename" date +%Z )" 639 local prompt="$( printf "$msg_look_reasonable" "$tm_zone" )" 640 local height=5 width=72 641 642 if [ "$USE_XDIALOG" ]; then 643 height=$(( $height + 4 )) 644 $DIALOG \ 645 --title "$title" \ 646 --backtitle "$btitle" \ 647 --ok-label "$msg_yes" \ 648 --cancel-label "$msg_no" \ 649 --yesno "$prompt" $height $width 650 else 651 $DIALOG \ 652 --title "$title" \ 653 --backtitle "$btitle" \ 654 --yes-label "$msg_yes" \ 655 --no-label "$msg_no" \ 656 --yesno "$prompt" $height $width 657 fi 658} 659 660# f_set_zone_utc 661# 662# Resets to the UTC timezone. 663# 664f_set_zone_utc() 665{ 666 f_confirm_zone "" || return $FAILURE 667 f_install_zoneinfo_file "" 668} 669 670fi # ! $_TIMEZONE_ZONES_SUBR 671