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