[tz] [PATCH 1/4] tzselect: Replace Korn/Bash-only select construct.
Paul Eggert
eggert at cs.ucla.edu
Sun Oct 6 10:01:13 UTC 2013
Thanks. That patch had some problems with Solaris 9 /bin/sh,
and also its output wasn't as nice as that of the builtin
'select' command, so I pushed the following patch instead,
which I hope addresses the issues raised by your first 3
patches.
>From be06aa48dbe1d0dad499006eab66e34ed07dcb5f Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Sun, 6 Oct 2013 02:11:51 -0700
Subject: [PATCH] tzselect: port to /bin/sh
Problem reported by Patrick 'P. J.' McDermott in
<http://mm.icann.org/pipermail/tz/2013-October/020441.html>.
This code is quite a bit different from what he proposed.
* tzselect.ksh: Rewrite so that it should work with /bin/sh on
common platforms. For portability to Solaris 9 /bin/sh, use
`...`, not $(...), and avoid $((...)).
(doselect): New function. Use this instead of plain 'select'.
Callers no longer need to worry whether it sets the var to empty.
* Makefile, NEWS: Document this.
---
Makefile | 8 ++-
NEWS | 6 ++
tzselect.ksh | 185 ++++++++++++++++++++++++++++++++++-------------------------
3 files changed, 119 insertions(+), 80 deletions(-)
diff --git a/Makefile b/Makefile
index 74923e7..9b04c2d 100644
--- a/Makefile
+++ b/Makefile
@@ -246,8 +246,12 @@ ZFLAGS=
# The name of a Posix-compliant `awk' on your system.
AWK= awk
-# The full path name of a Posix-compliant shell that supports the Korn shell's
-# 'select' statement, as an extension. These days, Bash is the most popular.
+# The full path name of a Posix-compliant shell, preferably one that supports
+# the Korn shell's 'select' statement as an extension.
+# These days, Bash is the most popular.
+# It should be OK to set this to /bin/sh, on platforms where /bin/sh
+# lacks 'select' or doesn't completely conform to Posix, but /bin/bash
+# is typically nicer if it works.
KSHELL= /bin/bash
# The path where SGML DTDs are kept.
diff --git a/NEWS b/NEWS
index 58f8126..89fbd86 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,12 @@ Unreleased, experimental changes
This avoids some year-2038 glitches introduced in 2013g.
(Thanks to Yoshito Umaoka for reporting the problem.)
+ Changes affecting API
+
+ The 'tzselect' command no longer requires the 'select' command,
+ and should now work with /bin/sh on more platforms. (Thanks to
+ Patrick 'P. J.' McDermott for reporting the problem.)
+
Changes affecting the build procedure
The builder can specify which programs to use, if any, instead of
diff --git a/tzselect.ksh b/tzselect.ksh
index 1934dd0..7f789bd 100644
--- a/tzselect.ksh
+++ b/tzselect.ksh
@@ -11,7 +11,7 @@ REPORT_BUGS_TO=tz at iana.org
# Porting notes:
#
-# This script requires a Posix-like shell with the extension of a
+# This script requires a Posix-like shell and prefers the extension of a
# 'select' statement. The 'select' statement was introduced in the
# Korn shell and is available in Bash and other shell implementations.
# If your host lacks both Bash and the Korn shell, you can get their
@@ -21,6 +21,10 @@ REPORT_BUGS_TO=tz at iana.org
# Korn Shell <http://www.kornshell.com/>
# Public Domain Korn Shell <http://www.cs.mun.ca/~michael/pdksh/>
#
+# For portability to Solaris 9 /bin/sh this script avoids some POSIX
+# features and common extensions, such as $(...) (which works sometimes
+# but not others), $((...)), and $10.
+#
# This script also uses several features of modern awk programs.
# If your host lacks awk, or has an old awk that does not conform to Posix,
# you can use either of the following free programs instead:
@@ -31,7 +35,7 @@ REPORT_BUGS_TO=tz at iana.org
# Specify default values for environment variables if they are unset.
: ${AWK=awk}
-: ${TZDIR=$(pwd)}
+: ${TZDIR=`pwd`}
# Check for awk Posix compliance.
($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
@@ -67,6 +71,74 @@ Options:
Report bugs to $REPORT_BUGS_TO."
+# Ask the user to select from the function's arguments,
+# and assign the selected argument to the variable 'select_result'.
+# Exit on EOF or I/O error. Use the shell's 'select' builtin if available,
+# falling back on a less-nice but portable substitute otherwise.
+if
+ case $BASH_VERSION in
+ ?*) : ;;
+ '')
+ # '; exit' should be redundant, but Dash doesn't properly fail without it.
+ (eval 'set --; select x; do break; done; exit') 2>/dev/null
+ esac
+then
+ # Do this inside 'eval', as otherwise the shell might exit when parsing it
+ # even though it is never executed.
+ eval '
+ doselect() {
+ select select_result
+ do
+ case $select_result in
+ "") echo >&2 "Please enter a number in range." ;;
+ ?*) break
+ esac
+ done || exit
+ }
+
+ # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
+ case $BASH_VERSION in
+ [01].*)
+ case `echo 1 | (select x in x; do break; done) 2>/dev/null` in
+ ?*) PS3=
+ esac
+ esac
+ '
+else
+ doselect() {
+ # Field width of the prompt numbers.
+ select_width=`expr $# : '.*'`
+
+ select_i=
+
+ while :
+ do
+ case $select_i in
+ '')
+ select_i=0
+ for select_word
+ do
+ select_i=`expr $select_i + 1`
+ printf "%${select_width}d) %s\\n" $select_i "$select_word"
+ done ;;
+ *[!0-9]*)
+ echo >&2 'Please enter a number in range.' ;;
+ *)
+ if test 1 -le $select_i && test $select_i -le $#; then
+ shift `expr $select_i - 1`
+ select_result=$1
+ break
+ fi
+ echo >&2 'Please enter a number in range.'
+ esac
+
+ # Prompt and read input.
+ printf %s >&2 "${PS3-#? }"
+ read select_i || exit
+ done
+ }
+fi
+
while getopts c:n:-: opt
do
case $opt$OPTARG in
@@ -85,7 +157,7 @@ do
esac
done
-shift $((OPTIND-1))
+shift `expr $OPTIND - 1`
case $# in
0) ;;
*) echo >&2 "$0: $1: unknown argument"; exit 1 ;;
@@ -107,11 +179,6 @@ newline='
IFS=$newline
-# Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
-case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
-?*) PS3=
-esac
-
# Awk script to read a time zone table and output the same table,
# with each column preceded by its distance from 'here'.
output_distances='
@@ -191,7 +258,7 @@ while
echo >&2 'Please select a continent, ocean, "coord", or "TZ".'
- quoted_continents=$(
+ quoted_continents=`
$AWK -F'\t' '
/^[^#]/ {
entry = substr($3, 1, index($3, "/") - 1)
@@ -205,30 +272,21 @@ while
sort -u |
tr '\n' ' '
echo ''
- )
+ `
eval '
- select continent in '"$quoted_continents"' \
+ doselect '"$quoted_continents"' \
"coord - I want to use geographical coordinates." \
"TZ - I want to specify the time zone using the Posix TZ format."
- do
- case $continent in
- "")
- echo >&2 "Please enter a number in range.";;
- ?*)
- case $continent in
- Americas) continent=America;;
- *" "*) continent=$(expr "$continent" : '\''\([^ ]*\)'\'')
- esac
- break
- esac
- done
+ continent=$select_result
+ case $continent in
+ Americas) continent=America;;
+ *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''`
+ esac
'
esac
case $continent in
- '')
- exit 1;;
TZ)
# Ask the user for a Posix TZ string. Check that it conforms.
while
@@ -265,36 +323,31 @@ while
'74 degrees 3 minutes west.'
read coord;;
esac
- distance_table=$($AWK \
+ distance_table=`$AWK \
-v coord="$coord" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
"$output_distances" <$TZ_ZONE_TABLE |
sort -n |
sed "${location_limit}q"
- )
- regions=$(echo "$distance_table" | $AWK '
+ `
+ regions=`echo "$distance_table" | $AWK '
BEGIN { FS = "\t" }
{ print $NF }
- ')
+ '`
echo >&2 'Please select one of the following' \
'time zone regions,'
echo >&2 'listed roughly in increasing order' \
"of distance from $coord".
- select region in $regions
- do
- case $region in
- '') echo >&2 'Please enter a number in range.';;
- ?*) break;;
- esac
- done
- TZ=$(echo "$distance_table" | $AWK -v region="$region" '
+ doselect $regions
+ region=$select_result
+ TZ=`echo "$distance_table" | $AWK -v region="$region" '
BEGIN { FS="\t" }
$NF == region { print $4 }
- ')
+ '`
;;
*)
# Get list of names of countries in the continent or ocean.
- countries=$($AWK -F'\t' \
+ countries=`$AWK -F'\t' \
-v continent="$continent" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
@@ -314,7 +367,7 @@ while
print country
}
}
- ' <$TZ_ZONE_TABLE | sort -f)
+ ' <$TZ_ZONE_TABLE | sort -f`
# If there's more than one country, ask the user which one.
@@ -322,24 +375,15 @@ while
*"$newline"*)
echo >&2 'Please select a country' \
'whose clocks agree with yours.'
- select country in $countries
- do
- case $country in
- '') echo >&2 'Please enter a number in range.';;
- ?*) break
- esac
- done
-
- case $country in
- '') exit 1
- esac;;
+ doselect $countries
+ country=$select_result;;
*)
country=$countries
esac
# Get list of names of time zone rule regions in the country.
- regions=$($AWK -F'\t' \
+ regions=`$AWK -F'\t' \
-v country="$country" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
@@ -353,7 +397,7 @@ while
}
}
$1 == cc { print $4 }
- ' <$TZ_ZONE_TABLE)
+ ' <$TZ_ZONE_TABLE`
# If there's more than one region, ask the user which one.
@@ -361,22 +405,14 @@ while
*"$newline"*)
echo >&2 'Please select one of the following' \
'time zone regions.'
- select region in $regions
- do
- case $region in
- '') echo >&2 'Please enter a number in range.';;
- ?*) break
- esac
- done
- case $region in
- '') exit 1
- esac;;
+ doselect $regions
+ region=$select_result;;
*)
region=$regions
esac
# Determine TZ from country and region.
- TZ=$($AWK -F'\t' \
+ TZ=`$AWK -F'\t' \
-v country="$country" \
-v region="$region" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
@@ -391,7 +427,7 @@ while
}
}
$1 == cc && $4 == region { print $3 }
- ' <$TZ_ZONE_TABLE)
+ ' <$TZ_ZONE_TABLE`
esac
# Make sure the corresponding zoneinfo file exists.
@@ -410,10 +446,10 @@ while
extra_info=
for i in 1 2 3 4 5 6 7 8
do
- TZdate=$(LANG=C TZ="$TZ_for_date" date)
- UTdate=$(LANG=C TZ=UTC0 date)
- TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)')
- UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)')
+ TZdate=`LANG=C TZ="$TZ_for_date" date`
+ UTdate=`LANG=C TZ=UTC0 date`
+ TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'`
+ UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'`
case $TZsec in
$UTsec)
extra_info="
@@ -440,16 +476,9 @@ Universal Time is now: $UTdate."
echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
echo >&2 "Is the above information OK?"
- ok=
- select ok in Yes No
- do
- case $ok in
- '') echo >&2 'Please enter 1 for Yes, or 2 for No.';;
- ?*) break
- esac
- done
+ doselect Yes No
+ ok=$select_result
case $ok in
- '') exit 1;;
Yes) break
esac
do coord=
--
1.8.1.2
More information about the tz
mailing list