--- tzdialog-1.1.txt 2012-01-10 00:11:47.000023000 +0000 +++ tzdialog-1.2.txt 2012-01-11 20:48:49.001160000 +0000 @@ -2,12 +2,12 @@ # -*- tab-width: 4 -*- ;; Emacs # vi: set tabstop=4 :: Vi/ViM # -# Revision: 1.1 +# Revision: 1.2 # Created: June 27th, 2011 -# Last Modified: November 24th, 2011 +# Last Modified: January 11th, 2012 ############################################################ COPYRIGHT # -# Devin Teske (c)2011. All Rights Reserved. +# Devin Teske (c)2011-2012. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -48,24 +48,26 @@ # # Command Usage: # -# tzdialog [-nsvXe] [default] +# tzdialog [-enrsvX] [-C chroot_directory] [zoneinfo_file | zoneinfo_name] # # OPTIONS: +# -e Only return success on exit if user selects a timezone AND +# the selected timezone was successfully installed. By default +# (without this flag), success is always returned unless an +# error has occurred. # -n Do not create or copy files. +# -r Reinstall the zoneinfo file installed last time. The name is +# obtained from /var/db/zoneinfo. # -s Skip the initial question about adjusting the clock if # not set to UTC. # -v Verbose. Enable extra output when installing the zone file. # -X Enable the use of Xdialog(1) instead of dialog(1). -# -e Only return success on exit if user selects a timezone AND -# the selected timezone was successfully installed. By default -# (without this flag), success is always returned unless an -# error has occurred. # # Dependencies (sorted alphabetically): # -# Xdialog(1)* awk(1) cat(1) dialog(1) grep(1) id(1) -# printf(1)** rm(1) sort(1) strings(1) stty(1) uname(1) -# which(1) +# Xdialog(1)* awk(1) cat(1) dialog(1) grep(1) +# id(1) ln(1) printf(1)** rm(1) sort(1) +# strings(1) stty(1) uname(1) which(1) # # * Optional # ** Is a shell-builtin on some releases @@ -81,6 +83,7 @@ # OS Glue # UNAME_S=$( uname -s ) +UNAME_R=$( uname -r ) # # Standard pathnames @@ -88,6 +91,7 @@ UNAME_S=$( uname -s ) _PATH_ZONETAB="/usr/share/zoneinfo/zone.tab" _PATH_ZONEINFO="/usr/share/zoneinfo" _PATH_LOCALTIME="/etc/localtime" +_PATH_DB="/var/db/zoneinfo" case "$UNAME_S" in Linux|Darwin|CYGWIN_NT-*) _PATH_ISO3166="/usr/share/zoneinfo/iso3166.tab" @@ -122,6 +126,7 @@ export CONTINENTS=" europe indian pacific + utc " # @@ -137,6 +142,7 @@ export continent_australia_name="Austral export continent_europe_name="Europe" export continent_indian_name="Indian" export continent_pacific_name="Pacific" +export continent_utc_name="UTC" # # Menu text of each continent/ocean @@ -151,6 +157,7 @@ export continent_australia_title="Austra export continent_europe_title="Europe" export continent_indian_title="Indian Ocean" export continent_pacific_title="Pacific Ocean" +export continent_utc_title="UTC" ############################################################ GLOBALS @@ -212,10 +219,13 @@ XDIALOG_MAX_SIZE="31 86" # for 800x60 # Options # REALLYDOIT=1 +REINSTALL= +USEDIALOG=1 SKIPUTC= USE_XDIALOG= VERBOSE= TZ_OR_FAIL= +CHROOTENV= # # Dummy vars (populated dynamically) @@ -288,11 +298,24 @@ usage() { local optfmt="\t%-11s%s\n" - eprintf "Usage: %s [-nsvX] [default]\n" "$progname" + eprintf "Usage: %s [-enrsvX] [-C %s] [%s | %s]\n" "$progname" \ + "chroot_directory" "zoneinfo_file" "zoneinfo_name" eprintf "OPTIONS:\n" + eprintf "$optfmt" "-e" \ + "Only return success on exit if user selects a timezone AND" + eprintf "$optfmt" "" \ + "the selected timezone was successfully installed. By default" + eprintf "$optfmt" "" \ + "(without this flag), success is always returned unless an" + eprintf "$optfmt" "" \ + "error has occurred." eprintf "$optfmt" "-n" \ "Do not create or copy files." + eprintf "$optfmt" "-r" \ + "Reinstall the zoneinfo file installed last time. The name is" + eprintf "$optfmt" "" \ + "obtained from /var/db/zoneinfo." eprintf "$optfmt" "-s" \ "Skip the initial question about adjusting the clock if" eprintf "$optfmt" "" \ @@ -301,14 +324,6 @@ usage() "Verbose. Enable extra output when installing the zone file." eprintf "$optfmt" "-X" \ "Enable the use of Xdialog(1) instead of dialog(1)." - eprintf "$optfmt" "-e" \ - "Only return success on exit if user selects a timezone AND" - eprintf "$optfmt" "" \ - "the selected timezone was successfully installed. By default" - eprintf "$optfmt" "" \ - "(without this flag), success is always returned unless an" - eprintf "$optfmt" "" \ - "error has occurred." die } @@ -316,13 +331,25 @@ usage() # longest_line_length # # Simple wrapper to an awk(1) script to print the length of the longest line of -# input (read from stdin). +# input (read from stdin). Supports the newline escape-sequence `\n' for +# splitting a single line into multiple lines. # longest_line_length_awk=' BEGIN { longest = 0 } { - len = length($0) - longest = ( len > longest ? len : longest ) + if (split($0, lines, /\\n/) > 1) + { + for (n in lines) + { + len = length(lines[n]) + longest = ( len > longest ? len : longest ) + } + } + else + { + len = length($0) + longest = ( len > longest ? len : longest ) + } } END { print longest } ' @@ -331,6 +358,23 @@ longest_line_length() awk "$longest_line_length_awk" } +# number_of_lines +# +# Simple wrapper to an awk(1) script to print the number of lines read from +# stdin. Supports newline escape-sequence `\n' for splitting a single line into +# multiple lines. +# +number_of_lines_awk=' +{ + NR += gsub(/\\n/, "\n") +} +END { print NR } +' +number_of_lines() +{ + awk "$number_of_lines_awk" +} + ############################################################################### ############################# TIME-ZONE FUNCTIONS ############################# ############################################################################### @@ -748,7 +792,9 @@ END { ' read_zones() { - eval $(awk -v progname="$progname" "$read_zones_awk" "$_PATH_ZONETAB") + eval $( awk -v progname="$progname" \ + "$read_zones_awk" \ + "$_PATH_ZONETAB" ) } # sort_countries @@ -1025,66 +1071,133 @@ country() eval echo \"\${country_${code}_$property}\" } -# install_zone_file $filename +# set_zone_utc +# +# Resets to the UTC timezone. +# +set_zone_utc() +{ + confirm_zone "" || return $FAILURE + install_zoneinfo_file "" +} + +# install_zoneinfo_file $filename # # Installs a zone file to _PATH_LOCALTIME. # -install_zone_file() +install_zoneinfo_file() { - local filename="$1" + local zoneinfo_file="$1" local copymode title msg err size - if [ ! -e "$_PATH_LOCALTIME" ]; then + if [ -L "$_PATH_LOCALTIME" ]; then + copymode= + elif [ ! -e "$_PATH_LOCALTIME" ]; then # Nothing there yet... copymode=1 - elif [ -L "$_PATH_LOCALTIME" ]; then - copymode= else copymode=1 fi if [ "$VERBOSE" ]; then if [ "$copymode" ]; then - msg="Copying $filename to $_PATH_LOCALTIME" + msg="Copying $zoneinfo_file to $_PATH_LOCALTIME" else msg="Creating symbolic link $_PATH_LOCALTIME" - msg="$msg to $filename" + msg="$msg to ${zoneinfo_file:-(UTC)}" + fi + if [ "$USEDIALOG" ]; then + size=$( dialog_infobox_size "" "$msg" ) + eval $DIALOG \ + ${USE_XDIALOG:+--no-buttons} \ + --infobox \"\$msg\" $size + else + printf "%s\n" "$msg" fi - size=$( dialog_infobox_size "" "$msg" ) - eval $DIALOG \ - ${USE_XDIALOG:+--no-buttons} \ - --infobox "'$msg'" $size fi if [ "$REALLYDOIT" ]; then title="Error" - err=$( rm -f $_PATH_LOCALTIME 2>&1 ) - if [ "$err" ]; then - size=$( dialog_buttonbox_size "$title" "$err" ) - eval $DIALOG --title "'$title'" --msgbox "'$err'" $size - return $FAILURE + if [ ! "$zoneinfo_file" ]; then + err=$( rm -f "$_PATH_LOCALTIME" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size \ + "$title" "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --msgbox \"\$err\" $size + else + eprintf "%s\n" "$err" + fi + return $FAILURE + fi + err=$( rm -f "$_PATH_DB" 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size \ + "$title" "$err" ) + eval $DIALOG \ + --title \"\$title\" \ + --msgbox \"\$err\" $size + else + eprintf "%s\n" "$err" + fi + return $FAILURE + fi + return $SUCCESS fi + if [ "$copymode" ]; then - err=$( umask 222 && :> "$_PATH_LOCALTIME" ) + quietly rm -f "$_PATH_LOCALTIME" + err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" ) if [ "$err" ]; then - size=$( dialog_buttonbox_size "$title" "$err" ) - eval $DIALOG --title "'$title'" \ - --msgbox "'$err'" $size + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size \ + "$title" "$err" ) + eval $DIALOG --title \"\$title\" \ + --msgbox \"\$err\" $size + else + eprintf "%s\n" "$err" + fi return $FAILURE fi - err=$( cat "$filename" > "$_PATH_LOCALTIME" ) + err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" ) if [ "$err" ]; then - size=$( dialog_buttonbox_size "$title" "$err" ) - eval $DIALOG --title "'$title'" \ - --msgbox "'$err'" $size + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size \ + "$title" "$err" ) + eval $DIALOG --title \"\$title\" \ + --msgbox \"\$err\" $size + else + eprintf "%s\n" "$err" + fi return $FAILURE fi else - err=$( ln -s "$filename" "$_PATH_LOCALTIME" ) + err=$( ( :< "$zoneinfo_file" ) 2>&1 ) + if [ "$err" ]; then + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size \ + "$title" "$err" ) + eval $DIALOG --title \"\$title\" \ + --msgbox \"\$err\" $size + else + eprintf "%s\n" "$err" + fi + return $FAILURE + fi + quietly rm -f "$_PATH_LOCALTIME" + err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 ) if [ "$err" ]; then - size=$( dialog_buttonbox_size "$title" "$err" ) - eval $DIALOG --title "'$title'" \ - --msgbox "'$err'" $size + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size \ + "$title" "$err" ) + eval $DIALOG --title \"\$title\" \ + --msgbox \"\$err\" $size + else + eprintf "%s\n" "$err" + fi return $FAILURE fi fi @@ -1093,18 +1206,49 @@ install_zone_file() if [ "$VERBOSE" ]; then title="Done" if [ "$copymode" ]; then - msg="Copied timezone file from $filename" + msg="Copied timezone file from $zoneinfo_file" msg="$msg to $_PATH_LOCALTIME" else msg="Created symbolic link from $_PATH_LOCALTIME" - msg="$msg to $filename" + msg="$msg to $zoneinfo_file" + fi + if [ "$USEDIALOG" ]; then + size=$( dialog_buttonbox_size "$title" "$msg" ) + eval $DIALOG \ + --title \"\$title\" \ + --msgbox \"\$msg\" $size + else + printf "%s\n" "$msg" fi - size=$( dialog_buttonbox_size "$title" "$msg" ) - eval $DIALOG --title "'$title'" --msgbox "'$msg'" $size fi return $SUCCESS } + + +# install_zoneinfo $zoneinfo +# +# Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo +# will be written to _PATH_DB (usable later with the `-r' flag). +# +install_zoneinfo() +{ + local zoneinfo="$1" + local rv + + install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo" + rv=$? + + # Save knowledge for later + if : 2> /dev/null > "$_PATH_DB"; then + cat <<-EOF > "$_PATH_DB" + $zoneinfo + EOF + fi + + return $rv +} + ############################################################################### ############################ DIALOG SIZE FUNCTIONS ############################ @@ -1119,6 +1263,9 @@ install_zone_file() # appearance) the title and prompt, returning the optimal width and height for # the box (not exceeding the actual terminal width or height). # +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# # Output is in the format of "height width". # dialog_infobox_size() @@ -1176,7 +1323,7 @@ dialog_infobox_size() # # Set height based on number of rows in prompt # - height=$( echo "$prompt" | awk 'END { print NR }' ) + height=$( echo "$prompt" | number_of_lines ) height=$(( $height + 2 )) # Return both @@ -1192,6 +1339,9 @@ dialog_infobox_size() # appearance) the title and prompt, returning the optimal width and height for # the box (not exceeding the actual terminal width or height). # +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# # Output is in the format of "height width". # dialog_buttonbox_size() @@ -1221,6 +1371,9 @@ dialog_buttonbox_size() # optimal width and height for the menu (not exceeding the actual terminal # width or height). # +# Newline character sequences (``\n'') in $prompt are expanded as-is done by +# dialog(1). +# # Output is in the format of "height width rows". # dialog_menu_size() @@ -1309,7 +1462,11 @@ confirm_zone() local prompt="Does the abbreviation \`$tm_zone' look reasonable?" local height=5 width=72 - [ "$UNAME_S" = "FreeBSD" -a ! "$USE_XDIALOG" ] && height=4 + if [ "$UNAME_S" = "FreeBSD" -a ! "$USE_XDIALOG" ]; then + case "$UNAME_R" in + [12345678].*) height=4 + esac + fi $DIALOG --title "$title" --yesno "$prompt" $height $width } @@ -1378,11 +1535,11 @@ dialog_menu_root() local prompt="Select a region" local size - size=$( eval dialog_menu_size "'$title'" "'$prompt'" \ + size=$( eval dialog_menu_size \"\$title\" \"\$prompt\" \ $continent_menu_list ) eval $DIALOG \ - --title "'$title'" \ - --menu "'$prompt'" $size \ + --title \"\$title\" \ + --menu \"\$prompt\" $size \ $continent_menu_list \ 2> "$DIALOG_TMPDIR/dialog.menu.$$" } @@ -1395,19 +1552,65 @@ dialog_menu_root() # # Process command-line arguments # -while getopts nsvXe flag; do +while getopts C:enrsvX flag; do case "$flag" in + C) CHROOTENV="$OPTARG";; + e) TZ_OR_FAIL=1;; n) REALLYDOIT=;; + r) REINSTALL=1 + USEDIALOG=;; s) SKIPUTC=1;; v) VERBOSE=1;; X) USE_XDIALOG=1;; - e) TZ_OR_FAIL=1;; \?) usage;; esac done shift $(( $OPTIND - 1 )) # +# Process `-C chroot_directory' command-line argument +# +if [ "$CHROOTENV" ]; then + _PATH_ZONETAB="$CHROOTENV$_PATH_ZONETAB" + _PATH_ISO3166="$CHROOTENV$_PATH_ISO3166" + _PATH_ZONEINFO="$CHROOTENV$_PATH_ZONEINFO" + _PATH_LOCALTIME="$CHROOTENV$_PATH_LOCALTIME" + _PATH_DB="$CHROOTENV$_PATH_DB" + _PATH_WALL_CMOS_CLOCK="$CHROOTENV$_PATH_WALL_CMOS_CLOCK" +fi + +# +# Process `-r' command-line option +# +if [ "$REINSTALL" ]; then + [ -f "$_PATH_DB" -a -r "$_PATH_DB" ] || + die "Cannot open %s for reading. Does it exist?" "$_PATH_DB" + zonefile=$( cat "$_PATH_DB" ) || + die "Error reading %s." "$_PATH_DB" + [ "$zonefile" ] || + die "Unable to determine %s zoneinfo file. Check %s" \ + "earlier installed" "$_PATH_DB" + install_zoneinfo "$zonefile" + exit $? +fi + +# +# If the arguments on the command-line do not specify a file, +# then interpret it as a zoneinfo name +# +if [ $# -ge 1 ]; then + zoneinfo="$1" + + if [ ! -f "$zoneinfo" ]; then + USEDIALOG= + install_zoneinfo "$zoneinfo" + exit $? + fi + + # FALLTHROUGH +fi + +# # Trap signals so we can recover gracefully # trap 'die' SIGINT SIGTERM SIGPIPE SIGXCPU SIGXFSZ \ @@ -1502,6 +1705,8 @@ if [ "$_PATH_WALL_CMOS_CLOCK" -a ! "$SKI [ "$REALLYDOIT" ] && ( umask 222 && :> "$_PATH_WALL_CMOS_CLOCK" ) fi + + [ ! "$USE_XDIALOG" ] && $DIALOG --clear fi # @@ -1510,22 +1715,30 @@ fi if [ $# -ge 1 ]; then default="$1" - $DIALOG --title "Default timezone provided" \ - --yesno "Use the default \`$default' zone?" -1 -1 + title="Default timezone provided" + msg="\nUse the default \`$default' zone?" + size=$( dialog_buttonbox_size "$title" "$msg" ) + + eval $DIALOG --title \"\$title\" --yesno \"\$msg\" $size result=$? if [ $result -eq 0 ]; then # User chose YES - install_zone_file "$default" - else - # User chose NO, pressed ESC (or Ctrl-C), or closed box - : nothing + install_zoneinfo_file "$default" + result=$? + [ ! "$USE_XDIALOG" ] && $DIALOG --clear + exit $result fi - exit $result + [ ! "$USE_XDIALOG" ] && $DIALOG --clear fi # +# Override the user-supplied umask +# +umask 022 + +# # Read databases and perform initialization # read_iso3166_table # creates $COUNTRIES and $country_*_name @@ -1552,7 +1765,7 @@ while :; do NEED_CONTINENT= - continent=$( eval dialog_menutag2item "'$mtag'" \ + continent=$( eval dialog_menutag2item \"\$mtag\" \ $continent_menu_list ) cont=$( find_continent "$continent" ) cont_title=$( continent $cont title ) @@ -1561,6 +1774,15 @@ while :; do fi if [ "$NEED_COUNTRY" ]; then + if [ "$cont_title" = "UTC" ]; then + if set_zone_utc; then + break + else + NEED_CONTINENT=1 + continue + fi + fi + # # Short cut -- if there's only one country, don't post a menu. # @@ -1582,15 +1804,16 @@ while :; do # Calculate size of menu # menu_list=$( continent $cont menu_list ) - size=$( eval dialog_menu_size "'$title'" "'$prompt'" \ + size=$( eval dialog_menu_size \"\$title\" \ + \"\$prompt\" \ $menu_list ) # # Launch the country selection menu # eval $DIALOG \ - --title "'$title'" \ - --menu "'$prompt'" $size \ + --title \"\$title\" \ + --menu \"\$prompt\" $size \ $menu_list \ 2> "$DIALOG_TMPDIR/dialog.menu.$$" retval=$? @@ -1629,7 +1852,7 @@ while :; do prompt="Select a zone which observes the same" prompt="$prompt time as your locality." menu_list=$( country $tlc menu_list ) - size=$( eval dialog_menu_size "'$title'" "'$prompt'" \ + size=$( eval dialog_menu_size \"\$title\" \"\$prompt\" \ $menu_list ) # @@ -1637,8 +1860,8 @@ while :; do # NOTE: This is as deep as we go # eval $DIALOG \ - --title "'$title'" \ - --menu "'$prompt'" $size \ + --title \"\$title\" \ + --menu \"\$prompt\" $size \ $menu_list \ 2> "$DIALOG_TMPDIR/dialog.menu.$$" retval=$? @@ -1660,14 +1883,15 @@ while :; do [ $retval -eq 0 ] || continue # back to main menu - if ! install_zone_file "$_PATH_ZONEINFO/$real_continent/$filename" - then + if ! install_zoneinfo "$real_continent/$filename"; then [ $nzones -lt 0 ] && NEED_COUNTRY=1 else break fi done +[ ! "$USE_XDIALOG" ] && $DIALOG --clear + ################################################################################ # END ################################################################################