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