xref: /freebsd/contrib/tzcode/tzselect.ksh (revision a979394afeb5c20fc58c5f5b005d51eb8f92f666)
1bc421551SDag-Erling Smørgrav#!/bin/bash
2bc421551SDag-Erling Smørgrav# Ask the user about the time zone, and output the resulting TZ value to stdout.
3bc421551SDag-Erling Smørgrav# Interact with the user via stderr and stdin.
4bc421551SDag-Erling Smørgrav
5bc421551SDag-Erling SmørgravPKGVERSION='(tzcode) '
6bc421551SDag-Erling SmørgravTZVERSION=see_Makefile
7bc421551SDag-Erling SmørgravREPORT_BUGS_TO=tz@iana.org
8bc421551SDag-Erling Smørgrav
9bc421551SDag-Erling Smørgrav# Contributed by Paul Eggert.  This file is in the public domain.
10bc421551SDag-Erling Smørgrav
11bc421551SDag-Erling Smørgrav# Porting notes:
12bc421551SDag-Erling Smørgrav#
1346c59934SDag-Erling Smørgrav# This script requires a POSIX-like shell and prefers the extension of a
14bc421551SDag-Erling Smørgrav# 'select' statement.  The 'select' statement was introduced in the
15bc421551SDag-Erling Smørgrav# Korn shell and is available in Bash and other shell implementations.
16bc421551SDag-Erling Smørgrav# If your host lacks both Bash and the Korn shell, you can get their
17bc421551SDag-Erling Smørgrav# source from one of these locations:
18bc421551SDag-Erling Smørgrav#
19bc421551SDag-Erling Smørgrav#	Bash <https://www.gnu.org/software/bash/>
20bc421551SDag-Erling Smørgrav#	Korn Shell <http://www.kornshell.com/>
21bc421551SDag-Erling Smørgrav#	MirBSD Korn Shell <http://www.mirbsd.org/mksh.htm>
22bc421551SDag-Erling Smørgrav#
2346c59934SDag-Erling Smørgrav# This script also uses several features of POSIX awk.
2446c59934SDag-Erling Smørgrav# If your host lacks awk, or has an old awk that does not conform to POSIX,
2546c59934SDag-Erling Smørgrav# you can use any of the following free programs instead:
26bc421551SDag-Erling Smørgrav#
27bc421551SDag-Erling Smørgrav#	Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
28bc421551SDag-Erling Smørgrav#	mawk <https://invisible-island.net/mawk/>
29bc421551SDag-Erling Smørgrav#	nawk <https://github.com/onetrueawk/awk>
3046c59934SDag-Erling Smørgrav#
3146c59934SDag-Erling Smørgrav# Because 'awk "VAR=VALUE" ...' and 'awk -v "VAR=VALUE" ...' are not portable
3246c59934SDag-Erling Smørgrav# if VALUE contains \, ", or newline, awk scripts in this file use:
3346c59934SDag-Erling Smørgrav#   awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE"
3446c59934SDag-Erling Smørgrav# The substr avoids problems when VALUE is of the form X=Y and would be
3546c59934SDag-Erling Smørgrav# misinterpreted as an assignment.
36bc421551SDag-Erling Smørgrav
3746c59934SDag-Erling Smørgrav# This script does not want path expansion.
3846c59934SDag-Erling Smørgravset -f
39bc421551SDag-Erling Smørgrav
40bc421551SDag-Erling Smørgrav# Specify default values for environment variables if they are unset.
41bc421551SDag-Erling Smørgrav: ${AWK=awk}
4246c59934SDag-Erling Smørgrav: ${TZDIR=$PWD}
43bc421551SDag-Erling Smørgrav
4475411d15SDag-Erling Smørgrav# Output one argument as-is to standard output, with trailing newline.
45bc421551SDag-Erling Smørgrav# Safer than 'echo', which can mishandle '\' or leading '-'.
46bc421551SDag-Erling Smørgravsay() {
47bc421551SDag-Erling Smørgrav  printf '%s\n' "$1"
48bc421551SDag-Erling Smørgrav}
49bc421551SDag-Erling Smørgrav
50bc421551SDag-Erling Smørgravcoord=
51bc421551SDag-Erling Smørgravlocation_limit=10
52bc421551SDag-Erling Smørgravzonetabtype=zone1970
53bc421551SDag-Erling Smørgrav
54bc421551SDag-Erling Smørgravusage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
55bc421551SDag-Erling SmørgravSelect a timezone interactively.
56bc421551SDag-Erling Smørgrav
57bc421551SDag-Erling SmørgravOptions:
58bc421551SDag-Erling Smørgrav
59bc421551SDag-Erling Smørgrav  -c COORD
60bc421551SDag-Erling Smørgrav    Instead of asking for continent and then country and then city,
61bc421551SDag-Erling Smørgrav    ask for selection from time zones whose largest cities
62bc421551SDag-Erling Smørgrav    are closest to the location with geographical coordinates COORD.
63bc421551SDag-Erling Smørgrav    COORD should use ISO 6709 notation, for example, '-c +4852+00220'
64bc421551SDag-Erling Smørgrav    for Paris (in degrees and minutes, North and East), or
65bc421551SDag-Erling Smørgrav    '-c -35-058' for Buenos Aires (in degrees, South and West).
66bc421551SDag-Erling Smørgrav
67bc421551SDag-Erling Smørgrav  -n LIMIT
68bc421551SDag-Erling Smørgrav    Display at most LIMIT locations when -c is used (default $location_limit).
69bc421551SDag-Erling Smørgrav
70bc421551SDag-Erling Smørgrav  --version
71bc421551SDag-Erling Smørgrav    Output version information.
72bc421551SDag-Erling Smørgrav
73bc421551SDag-Erling Smørgrav  --help
74bc421551SDag-Erling Smørgrav    Output this help.
75bc421551SDag-Erling Smørgrav
76bc421551SDag-Erling SmørgravReport bugs to $REPORT_BUGS_TO."
77bc421551SDag-Erling Smørgrav
78bc421551SDag-Erling Smørgrav# Ask the user to select from the function's arguments,
79bc421551SDag-Erling Smørgrav# and assign the selected argument to the variable 'select_result'.
8075411d15SDag-Erling Smørgrav# Exit on EOF or I/O error.  Use the shell's nicer 'select' builtin if
8175411d15SDag-Erling Smørgrav# available, falling back on a portable substitute otherwise.
82bc421551SDag-Erling Smørgravif
83bc421551SDag-Erling Smørgrav  case $BASH_VERSION in
84bc421551SDag-Erling Smørgrav  ?*) :;;
85bc421551SDag-Erling Smørgrav  '')
86bc421551SDag-Erling Smørgrav    # '; exit' should be redundant, but Dash doesn't properly fail without it.
8746c59934SDag-Erling Smørgrav    (eval 'set --; select x; do break; done; exit') <>/dev/null 2>&0
88bc421551SDag-Erling Smørgrav  esac
89bc421551SDag-Erling Smørgravthen
90bc421551SDag-Erling Smørgrav  # Do this inside 'eval', as otherwise the shell might exit when parsing it
91bc421551SDag-Erling Smørgrav  # even though it is never executed.
92bc421551SDag-Erling Smørgrav  eval '
93bc421551SDag-Erling Smørgrav    doselect() {
94bc421551SDag-Erling Smørgrav      select select_result
95bc421551SDag-Erling Smørgrav      do
96bc421551SDag-Erling Smørgrav	case $select_result in
97bc421551SDag-Erling Smørgrav	"") echo >&2 "Please enter a number in range.";;
98bc421551SDag-Erling Smørgrav	?*) break
99bc421551SDag-Erling Smørgrav	esac
100bc421551SDag-Erling Smørgrav      done || exit
101bc421551SDag-Erling Smørgrav    }
102bc421551SDag-Erling Smørgrav  '
103bc421551SDag-Erling Smørgravelse
104bc421551SDag-Erling Smørgrav  doselect() {
105bc421551SDag-Erling Smørgrav    # Field width of the prompt numbers.
106*a979394aSDag-Erling Smørgrav    select_width=${##}
107bc421551SDag-Erling Smørgrav
108bc421551SDag-Erling Smørgrav    select_i=
109bc421551SDag-Erling Smørgrav
110bc421551SDag-Erling Smørgrav    while :
111bc421551SDag-Erling Smørgrav    do
112bc421551SDag-Erling Smørgrav      case $select_i in
113bc421551SDag-Erling Smørgrav      '')
114bc421551SDag-Erling Smørgrav	select_i=0
115bc421551SDag-Erling Smørgrav	for select_word
116bc421551SDag-Erling Smørgrav	do
117*a979394aSDag-Erling Smørgrav	  select_i=$(($select_i + 1))
118bc421551SDag-Erling Smørgrav	  printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
119bc421551SDag-Erling Smørgrav	done;;
120bc421551SDag-Erling Smørgrav      *[!0-9]*)
121bc421551SDag-Erling Smørgrav	echo >&2 'Please enter a number in range.';;
122bc421551SDag-Erling Smørgrav      *)
123bc421551SDag-Erling Smørgrav	if test 1 -le $select_i && test $select_i -le $#; then
124*a979394aSDag-Erling Smørgrav	  shift $(($select_i - 1))
125bc421551SDag-Erling Smørgrav	  select_result=$1
126bc421551SDag-Erling Smørgrav	  break
127bc421551SDag-Erling Smørgrav	fi
128bc421551SDag-Erling Smørgrav	echo >&2 'Please enter a number in range.'
129bc421551SDag-Erling Smørgrav      esac
130bc421551SDag-Erling Smørgrav
131bc421551SDag-Erling Smørgrav      # Prompt and read input.
132bc421551SDag-Erling Smørgrav      printf >&2 %s "${PS3-#? }"
133bc421551SDag-Erling Smørgrav      read select_i || exit
134bc421551SDag-Erling Smørgrav    done
135bc421551SDag-Erling Smørgrav  }
136bc421551SDag-Erling Smørgravfi
137bc421551SDag-Erling Smørgrav
138bc421551SDag-Erling Smørgravwhile getopts c:n:t:-: opt
139bc421551SDag-Erling Smørgravdo
140bc421551SDag-Erling Smørgrav  case $opt$OPTARG in
141bc421551SDag-Erling Smørgrav  c*)
142bc421551SDag-Erling Smørgrav    coord=$OPTARG;;
143bc421551SDag-Erling Smørgrav  n*)
144bc421551SDag-Erling Smørgrav    location_limit=$OPTARG;;
145bc421551SDag-Erling Smørgrav  t*) # Undocumented option, used for developer testing.
146bc421551SDag-Erling Smørgrav    zonetabtype=$OPTARG;;
147bc421551SDag-Erling Smørgrav  -help)
148bc421551SDag-Erling Smørgrav    exec echo "$usage";;
149bc421551SDag-Erling Smørgrav  -version)
150bc421551SDag-Erling Smørgrav    exec echo "tzselect $PKGVERSION$TZVERSION";;
151bc421551SDag-Erling Smørgrav  -*)
152bc421551SDag-Erling Smørgrav    say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1;;
153bc421551SDag-Erling Smørgrav  *)
15446c59934SDag-Erling Smørgrav    say >&2 "$0: try '$0 --help'"; exit 1
155bc421551SDag-Erling Smørgrav  esac
156bc421551SDag-Erling Smørgravdone
157bc421551SDag-Erling Smørgrav
158*a979394aSDag-Erling Smørgravshift $(($OPTIND - 1))
159bc421551SDag-Erling Smørgravcase $# in
160bc421551SDag-Erling Smørgrav0) ;;
16146c59934SDag-Erling Smørgrav*) say >&2 "$0: $1: unknown argument"; exit 1
162bc421551SDag-Erling Smørgravesac
163bc421551SDag-Erling Smørgrav
16446c59934SDag-Erling Smørgrav# translit=true to try transliteration.
16546c59934SDag-Erling Smørgrav# This is false if U+12345 CUNEIFORM SIGN URU TIMES KI has length 1
166*a979394aSDag-Erling Smørgrav# which means the shell and (presumably) awk do not need transliteration.
167*a979394aSDag-Erling Smørgrav# It is true if the byte string has some other length in characters, or
168*a979394aSDag-Erling Smørgrav# if this is a POSIX.1-2017 or earlier shell that does not support $'...'.
169*a979394aSDag-Erling SmørgravCUNEIFORM_SIGN_URU_TIMES_KI=$'\360\222\215\205'
170*a979394aSDag-Erling Smørgravif test ${#CUNEIFORM_SIGN_URU_TIMES_KI} = 1
171*a979394aSDag-Erling Smørgravthen translit=false
172*a979394aSDag-Erling Smørgravelse translit=true
17346c59934SDag-Erling Smørgravfi
17446c59934SDag-Erling Smørgrav
17546c59934SDag-Erling Smørgrav# Read into shell variable $1 the contents of file $2.
17646c59934SDag-Erling Smørgrav# Convert to the current locale's encoding if possible,
17746c59934SDag-Erling Smørgrav# as the shell aligns columns better that way.
17846c59934SDag-Erling Smørgrav# If GNU iconv's //TRANSLIT does not work, fall back on POSIXish iconv;
17946c59934SDag-Erling Smørgrav# if that does not work, fall back on 'cat'.
18046c59934SDag-Erling Smørgravread_file() {
18146c59934SDag-Erling Smørgrav  { $translit && {
182*a979394aSDag-Erling Smørgrav    eval "$1=\$( (iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\")" ||
183*a979394aSDag-Erling Smørgrav    eval "$1=\$( (iconv -f UTF-8) 2>/dev/null <\"\$2\")"
18446c59934SDag-Erling Smørgrav  }; } ||
185*a979394aSDag-Erling Smørgrav  eval "$1=\$(cat <\"\$2\")" || {
186bc421551SDag-Erling Smørgrav    say >&2 "$0: time zone files are not set up correctly"
187bc421551SDag-Erling Smørgrav    exit 1
188bc421551SDag-Erling Smørgrav  }
189bc421551SDag-Erling Smørgrav}
19046c59934SDag-Erling Smørgravread_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab"
19146c59934SDag-Erling Smørgravread_file TZ_ZONETABTYPE_TABLE "$TZDIR/$zonetabtype.tab"
19246c59934SDag-Erling SmørgravTZ_ZONENOW_TABLE=
193bc421551SDag-Erling Smørgrav
194bc421551SDag-Erling Smørgravnewline='
195bc421551SDag-Erling Smørgrav'
196bc421551SDag-Erling SmørgravIFS=$newline
197bc421551SDag-Erling Smørgrav
19875411d15SDag-Erling Smørgrav# Awk script to output a country list.
19975411d15SDag-Erling Smørgravoutput_country_list='
20046c59934SDag-Erling Smørgrav  BEGIN {
20146c59934SDag-Erling Smørgrav    continent_re = substr(ARGV[1], 2)
20246c59934SDag-Erling Smørgrav    TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
20346c59934SDag-Erling Smørgrav    TZ_ZONE_TABLE = substr(ARGV[3], 2)
20446c59934SDag-Erling Smørgrav    ARGV[1] = ARGV[2] = ARGV[3] = ""
20546c59934SDag-Erling Smørgrav    FS = "\t"
20646c59934SDag-Erling Smørgrav    nlines = split(TZ_ZONE_TABLE, line, /\n/)
20746c59934SDag-Erling Smørgrav    for (iline = 1; iline <= nlines; iline++) {
20846c59934SDag-Erling Smørgrav      $0 = line[iline]
20975411d15SDag-Erling Smørgrav      commentary = $0 ~ /^#@/
21075411d15SDag-Erling Smørgrav      if (commentary) {
21146c59934SDag-Erling Smørgrav	if ($0 !~ /^#@/)
21246c59934SDag-Erling Smørgrav	  continue
21375411d15SDag-Erling Smørgrav	col1ccs = substr($1, 3)
21475411d15SDag-Erling Smørgrav	conts = $2
21575411d15SDag-Erling Smørgrav      } else {
21675411d15SDag-Erling Smørgrav	col1ccs = $1
21775411d15SDag-Erling Smørgrav	conts = $3
21875411d15SDag-Erling Smørgrav      }
21975411d15SDag-Erling Smørgrav      ncc = split(col1ccs, cc, /,/)
22075411d15SDag-Erling Smørgrav      ncont = split(conts, cont, /,/)
22175411d15SDag-Erling Smørgrav      for (i = 1; i <= ncc; i++) {
22275411d15SDag-Erling Smørgrav	elsewhere = commentary
22375411d15SDag-Erling Smørgrav	for (ci = 1; ci <= ncont; ci++) {
22475411d15SDag-Erling Smørgrav	  if (cont[ci] ~ continent_re) {
22546c59934SDag-Erling Smørgrav	    if (!cc_seen[cc[i]]++)
22646c59934SDag-Erling Smørgrav	      cc_list[++ccs] = cc[i]
22775411d15SDag-Erling Smørgrav	    elsewhere = 0
22875411d15SDag-Erling Smørgrav	  }
22975411d15SDag-Erling Smørgrav	}
23046c59934SDag-Erling Smørgrav	if (elsewhere)
23146c59934SDag-Erling Smørgrav	  for (i = 1; i <= ncc; i++)
23275411d15SDag-Erling Smørgrav	    cc_elsewhere[cc[i]] = 1
23375411d15SDag-Erling Smørgrav      }
23475411d15SDag-Erling Smørgrav    }
23546c59934SDag-Erling Smørgrav    nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
23646c59934SDag-Erling Smørgrav    for (i = 1; i <= nlines; i++) {
23746c59934SDag-Erling Smørgrav      $0 = line[i]
23846c59934SDag-Erling Smørgrav      if ($0 !~ /^#/)
23946c59934SDag-Erling Smørgrav	cc_name[$1] = $2
24075411d15SDag-Erling Smørgrav    }
24175411d15SDag-Erling Smørgrav    for (i = 1; i <= ccs; i++) {
24275411d15SDag-Erling Smørgrav      country = cc_list[i]
24346c59934SDag-Erling Smørgrav      if (cc_elsewhere[country])
24446c59934SDag-Erling Smørgrav	continue
24546c59934SDag-Erling Smørgrav      if (cc_name[country])
24675411d15SDag-Erling Smørgrav	country = cc_name[country]
24775411d15SDag-Erling Smørgrav      print country
24875411d15SDag-Erling Smørgrav    }
24975411d15SDag-Erling Smørgrav  }
25075411d15SDag-Erling Smørgrav'
251bc421551SDag-Erling Smørgrav
25246c59934SDag-Erling Smørgrav# Awk script to process a time zone table and output the same table,
25375411d15SDag-Erling Smørgrav# with each row preceded by its distance from 'here'.
25475411d15SDag-Erling Smørgrav# If output_times is set, each row is instead preceded by its local time
25575411d15SDag-Erling Smørgrav# and any apostrophes are escaped for the shell.
25675411d15SDag-Erling Smørgravoutput_distances_or_times='
257bc421551SDag-Erling Smørgrav  BEGIN {
25846c59934SDag-Erling Smørgrav    coord = substr(ARGV[1], 2)
25946c59934SDag-Erling Smørgrav    TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
26046c59934SDag-Erling Smørgrav    TZ_ZONE_TABLE = substr(ARGV[3], 2)
26146c59934SDag-Erling Smørgrav    ARGV[1] = ARGV[2] = ARGV[3] = ""
262bc421551SDag-Erling Smørgrav    FS = "\t"
26375411d15SDag-Erling Smørgrav    if (!output_times) {
26446c59934SDag-Erling Smørgrav      nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
26546c59934SDag-Erling Smørgrav      for (i = 1; i <= nlines; i++) {
26646c59934SDag-Erling Smørgrav	$0 = line[i]
26746c59934SDag-Erling Smørgrav	if ($0 ~ /^#/)
26846c59934SDag-Erling Smørgrav	  continue
269bc421551SDag-Erling Smørgrav	country[$1] = $2
27046c59934SDag-Erling Smørgrav      }
271bc421551SDag-Erling Smørgrav      country["US"] = "US" # Otherwise the strings get too long.
272bc421551SDag-Erling Smørgrav    }
27375411d15SDag-Erling Smørgrav  }
274bc421551SDag-Erling Smørgrav  function abs(x) {
275bc421551SDag-Erling Smørgrav    return x < 0 ? -x : x;
276bc421551SDag-Erling Smørgrav  }
277bc421551SDag-Erling Smørgrav  function min(x, y) {
278bc421551SDag-Erling Smørgrav    return x < y ? x : y;
279bc421551SDag-Erling Smørgrav  }
280bc421551SDag-Erling Smørgrav  function convert_coord(coord, deg, minute, ilen, sign, sec) {
281bc421551SDag-Erling Smørgrav    if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
282bc421551SDag-Erling Smørgrav      degminsec = coord
283bc421551SDag-Erling Smørgrav      intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000)
284bc421551SDag-Erling Smørgrav      minsec = degminsec - intdeg * 10000
285bc421551SDag-Erling Smørgrav      intmin = minsec < 0 ? -int(-minsec / 100) : int(minsec / 100)
286bc421551SDag-Erling Smørgrav      sec = minsec - intmin * 100
287bc421551SDag-Erling Smørgrav      deg = (intdeg * 3600 + intmin * 60 + sec) / 3600
288bc421551SDag-Erling Smørgrav    } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
289bc421551SDag-Erling Smørgrav      degmin = coord
290bc421551SDag-Erling Smørgrav      intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
291bc421551SDag-Erling Smørgrav      minute = degmin - intdeg * 100
292bc421551SDag-Erling Smørgrav      deg = (intdeg * 60 + minute) / 60
293bc421551SDag-Erling Smørgrav    } else
294bc421551SDag-Erling Smørgrav      deg = coord
295bc421551SDag-Erling Smørgrav    return deg * 0.017453292519943296
296bc421551SDag-Erling Smørgrav  }
297bc421551SDag-Erling Smørgrav  function convert_latitude(coord) {
298bc421551SDag-Erling Smørgrav    match(coord, /..*[-+]/)
299bc421551SDag-Erling Smørgrav    return convert_coord(substr(coord, 1, RLENGTH - 1))
300bc421551SDag-Erling Smørgrav  }
301bc421551SDag-Erling Smørgrav  function convert_longitude(coord) {
302bc421551SDag-Erling Smørgrav    match(coord, /..*[-+]/)
303bc421551SDag-Erling Smørgrav    return convert_coord(substr(coord, RLENGTH))
304bc421551SDag-Erling Smørgrav  }
305bc421551SDag-Erling Smørgrav  # Great-circle distance between points with given latitude and longitude.
306bc421551SDag-Erling Smørgrav  # Inputs and output are in radians.  This uses the great-circle special
307bc421551SDag-Erling Smørgrav  # case of the Vicenty formula for distances on ellipsoids.
308bc421551SDag-Erling Smørgrav  function gcdist(lat1, long1, lat2, long2, dlong, x, y, num, denom) {
309bc421551SDag-Erling Smørgrav    dlong = long2 - long1
310bc421551SDag-Erling Smørgrav    x = cos(lat2) * sin(dlong)
311bc421551SDag-Erling Smørgrav    y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlong)
312bc421551SDag-Erling Smørgrav    num = sqrt(x * x + y * y)
313bc421551SDag-Erling Smørgrav    denom = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(dlong)
314bc421551SDag-Erling Smørgrav    return atan2(num, denom)
315bc421551SDag-Erling Smørgrav  }
316bc421551SDag-Erling Smørgrav  # Parallel distance between points with given latitude and longitude.
317bc421551SDag-Erling Smørgrav  # This is the product of the longitude difference and the cosine
318bc421551SDag-Erling Smørgrav  # of the latitude of the point that is further from the equator.
319bc421551SDag-Erling Smørgrav  # I.e., it considers longitudes to be further apart if they are
320bc421551SDag-Erling Smørgrav  # nearer the equator.
321bc421551SDag-Erling Smørgrav  function pardist(lat1, long1, lat2, long2) {
322bc421551SDag-Erling Smørgrav    return abs(long1 - long2) * min(cos(lat1), cos(lat2))
323bc421551SDag-Erling Smørgrav  }
324bc421551SDag-Erling Smørgrav  # The distance function is the sum of the great-circle distance and
325bc421551SDag-Erling Smørgrav  # the parallel distance.  It could be weighted.
326bc421551SDag-Erling Smørgrav  function dist(lat1, long1, lat2, long2) {
327bc421551SDag-Erling Smørgrav    return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2)
328bc421551SDag-Erling Smørgrav  }
329bc421551SDag-Erling Smørgrav  BEGIN {
330bc421551SDag-Erling Smørgrav    coord_lat = convert_latitude(coord)
331bc421551SDag-Erling Smørgrav    coord_long = convert_longitude(coord)
33246c59934SDag-Erling Smørgrav    nlines = split(TZ_ZONE_TABLE, line, /\n/)
33346c59934SDag-Erling Smørgrav    for (h = 1; h <= nlines; h++) {
33446c59934SDag-Erling Smørgrav      $0 = line[h]
33546c59934SDag-Erling Smørgrav      if ($0 ~ /^#/)
33646c59934SDag-Erling Smørgrav	continue
33775411d15SDag-Erling Smørgrav      inline[inlines++] = $0
33875411d15SDag-Erling Smørgrav      ncc = split($1, cc, /,/)
33975411d15SDag-Erling Smørgrav      for (i = 1; i <= ncc; i++)
34075411d15SDag-Erling Smørgrav	cc_used[cc[i]]++
34175411d15SDag-Erling Smørgrav    }
34275411d15SDag-Erling Smørgrav    for (h = 0; h < inlines; h++) {
34375411d15SDag-Erling Smørgrav      $0 = inline[h]
34446c59934SDag-Erling Smørgrav      outline = $1 "\t" $2 "\t" $3
345bc421551SDag-Erling Smørgrav      sep = "\t"
346bc421551SDag-Erling Smørgrav      ncc = split($1, cc, /,/)
34775411d15SDag-Erling Smørgrav      split("", item_seen)
34875411d15SDag-Erling Smørgrav      item_seen[""] = 1
349bc421551SDag-Erling Smørgrav      for (i = 1; i <= ncc; i++) {
35075411d15SDag-Erling Smørgrav	item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
35146c59934SDag-Erling Smørgrav	if (item_seen[item]++)
35246c59934SDag-Erling Smørgrav	  continue
35346c59934SDag-Erling Smørgrav	outline = outline sep item
35475411d15SDag-Erling Smørgrav	sep = "; "
355bc421551SDag-Erling Smørgrav      }
35675411d15SDag-Erling Smørgrav      if (output_times) {
35775411d15SDag-Erling Smørgrav	fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n"
35846c59934SDag-Erling Smørgrav	gsub(/'\''/, "&\\\\&&", outline)
35946c59934SDag-Erling Smørgrav	printf fmt, $3, h, outline
36075411d15SDag-Erling Smørgrav      } else {
36175411d15SDag-Erling Smørgrav	here_lat = convert_latitude($2)
36275411d15SDag-Erling Smørgrav	here_long = convert_longitude($2)
36346c59934SDag-Erling Smørgrav	printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \
36446c59934SDag-Erling Smørgrav	  outline
365bc421551SDag-Erling Smørgrav      }
36675411d15SDag-Erling Smørgrav    }
36775411d15SDag-Erling Smørgrav  }
368bc421551SDag-Erling Smørgrav'
369bc421551SDag-Erling Smørgrav
370bc421551SDag-Erling Smørgrav# Begin the main loop.  We come back here if the user wants to retry.
371bc421551SDag-Erling Smørgravwhile
372bc421551SDag-Erling Smørgrav
373bc421551SDag-Erling Smørgrav  echo >&2 'Please identify a location' \
374bc421551SDag-Erling Smørgrav    'so that time zone rules can be set correctly.'
375bc421551SDag-Erling Smørgrav
376bc421551SDag-Erling Smørgrav  continent=
377bc421551SDag-Erling Smørgrav  country=
37846c59934SDag-Erling Smørgrav  country_result=
379bc421551SDag-Erling Smørgrav  region=
38046c59934SDag-Erling Smørgrav  time=
38146c59934SDag-Erling Smørgrav  TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE
382bc421551SDag-Erling Smørgrav
383bc421551SDag-Erling Smørgrav  case $coord in
384bc421551SDag-Erling Smørgrav  ?*)
385bc421551SDag-Erling Smørgrav    continent=coord;;
386bc421551SDag-Erling Smørgrav  '')
387bc421551SDag-Erling Smørgrav
388bc421551SDag-Erling Smørgrav    # Ask the user for continent or ocean.
389bc421551SDag-Erling Smørgrav
39046c59934SDag-Erling Smørgrav    echo >&2 \
39146c59934SDag-Erling Smørgrav      'Please select a continent, ocean, "coord", "TZ", "time", or "now".'
392bc421551SDag-Erling Smørgrav
393*a979394aSDag-Erling Smørgrav    quoted_continents=$(
394bc421551SDag-Erling Smørgrav      $AWK '
395bc421551SDag-Erling Smørgrav	function handle_entry(entry) {
396bc421551SDag-Erling Smørgrav	  entry = substr(entry, 1, index(entry, "/") - 1)
397bc421551SDag-Erling Smørgrav	  if (entry == "America")
398bc421551SDag-Erling Smørgrav	    entry = entry "s"
399bc421551SDag-Erling Smørgrav	  if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
400bc421551SDag-Erling Smørgrav	    entry = entry " Ocean"
401bc421551SDag-Erling Smørgrav	  printf "'\''%s'\''\n", entry
402bc421551SDag-Erling Smørgrav	}
40346c59934SDag-Erling Smørgrav	BEGIN {
40446c59934SDag-Erling Smørgrav	  TZ_ZONETABTYPE_TABLE = substr(ARGV[1], 2)
40546c59934SDag-Erling Smørgrav	  ARGV[1] = ""
40646c59934SDag-Erling Smørgrav	  FS = "\t"
40746c59934SDag-Erling Smørgrav	  nlines = split(TZ_ZONETABTYPE_TABLE, line, /\n/)
40846c59934SDag-Erling Smørgrav	  for (i = 1; i <= nlines; i++) {
40946c59934SDag-Erling Smørgrav	    $0 = line[i]
41046c59934SDag-Erling Smørgrav	    if ($0 ~ /^[^#]/)
411bc421551SDag-Erling Smørgrav	      handle_entry($3)
41246c59934SDag-Erling Smørgrav	    else if ($0 ~ /^#@/) {
413bc421551SDag-Erling Smørgrav	      ncont = split($2, cont, /,/)
41446c59934SDag-Erling Smørgrav	      for (ci = 1; ci <= ncont; ci++)
415bc421551SDag-Erling Smørgrav		handle_entry(cont[ci])
416bc421551SDag-Erling Smørgrav	    }
417bc421551SDag-Erling Smørgrav	  }
41846c59934SDag-Erling Smørgrav	}
41946c59934SDag-Erling Smørgrav      ' ="$TZ_ZONETABTYPE_TABLE" |
420bc421551SDag-Erling Smørgrav      sort -u |
421bc421551SDag-Erling Smørgrav      tr '\n' ' '
422bc421551SDag-Erling Smørgrav      echo ''
423*a979394aSDag-Erling Smørgrav    )
424bc421551SDag-Erling Smørgrav
425bc421551SDag-Erling Smørgrav    eval '
426bc421551SDag-Erling Smørgrav      doselect '"$quoted_continents"' \
427bc421551SDag-Erling Smørgrav	"coord - I want to use geographical coordinates." \
428*a979394aSDag-Erling Smørgrav	"TZ - I want to specify the timezone using a proleptic TZ string." \
42946c59934SDag-Erling Smørgrav	"time - I know local time already." \
43046c59934SDag-Erling Smørgrav	"now - Like \"time\", but configure only for timestamps from now on."
431bc421551SDag-Erling Smørgrav      continent=$select_result
432bc421551SDag-Erling Smørgrav      case $continent in
433bc421551SDag-Erling Smørgrav      Americas) continent=America;;
43446c59934SDag-Erling Smørgrav      *)
43546c59934SDag-Erling Smørgrav	# Get the first word of $continent.  Path expansion is disabled
43646c59934SDag-Erling Smørgrav	# so this works even with "*", which should not happen.
43746c59934SDag-Erling Smørgrav	IFS=" "
43846c59934SDag-Erling Smørgrav	for continent in $continent ""; do break; done
43946c59934SDag-Erling Smørgrav	IFS=$newline;;
44046c59934SDag-Erling Smørgrav      esac
44146c59934SDag-Erling Smørgrav      case $zonetabtype,$continent in
44246c59934SDag-Erling Smørgrav      zonenow,*) ;;
44346c59934SDag-Erling Smørgrav      *,now)
44446c59934SDag-Erling Smørgrav	${TZ_ZONENOW_TABLE:+:} read_file TZ_ZONENOW_TABLE "$TZDIR/zonenow.tab"
44546c59934SDag-Erling Smørgrav	TZ_ZONE_TABLE=$TZ_ZONENOW_TABLE
446bc421551SDag-Erling Smørgrav      esac
447bc421551SDag-Erling Smørgrav    '
448bc421551SDag-Erling Smørgrav  esac
449bc421551SDag-Erling Smørgrav
450bc421551SDag-Erling Smørgrav  case $continent in
451bc421551SDag-Erling Smørgrav  TZ)
452*a979394aSDag-Erling Smørgrav    # Ask the user for a proleptic TZ string.  Check that it conforms.
45346c59934SDag-Erling Smørgrav    check_POSIX_TZ_string='
45446c59934SDag-Erling Smørgrav      BEGIN {
45546c59934SDag-Erling Smørgrav	tz = substr(ARGV[1], 2)
45646c59934SDag-Erling Smørgrav	ARGV[1] = ""
45746c59934SDag-Erling Smørgrav	tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \
45846c59934SDag-Erling Smørgrav		  "|[[:alpha:]][[:alpha:]][[:alpha:]]+)")
459*a979394aSDag-Erling Smørgrav	sign = "[-+]?"
460*a979394aSDag-Erling Smørgrav	hhmm = "(:[0-5][0-9](:[0-5][0-9])?)?"
461*a979394aSDag-Erling Smørgrav	offset = sign "(2[0-4]|[0-1]?[0-9])" hhmm
462*a979394aSDag-Erling Smørgrav	time = sign "(16[0-7]|(1[0-5]|[0-9]?)[0-9])" hhmm
46346c59934SDag-Erling Smørgrav	mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
46446c59934SDag-Erling Smørgrav	jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \
46546c59934SDag-Erling Smørgrav		 "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])")
46646c59934SDag-Erling Smørgrav	datetime = ",(" mdate "|" jdate ")(/" time ")?"
46746c59934SDag-Erling Smørgrav	tzpattern = ("^(:.*|" tzname offset "(" tzname \
46846c59934SDag-Erling Smørgrav		     "(" offset ")?(" datetime datetime ")?)?)$")
46946c59934SDag-Erling Smørgrav	exit tz ~ tzpattern
47046c59934SDag-Erling Smørgrav      }
47146c59934SDag-Erling Smørgrav    '
47246c59934SDag-Erling Smørgrav
473bc421551SDag-Erling Smørgrav    while
474bc421551SDag-Erling Smørgrav      echo >&2 'Please enter the desired value' \
475bc421551SDag-Erling Smørgrav	'of the TZ environment variable.'
476bc421551SDag-Erling Smørgrav      echo >&2 'For example, AEST-10 is abbreviated' \
477bc421551SDag-Erling Smørgrav	'AEST and is 10 hours'
478bc421551SDag-Erling Smørgrav      echo >&2 'ahead (east) of Greenwich,' \
479bc421551SDag-Erling Smørgrav	'with no daylight saving time.'
48046c59934SDag-Erling Smørgrav      read tz
48146c59934SDag-Erling Smørgrav      $AWK "$check_POSIX_TZ_string" ="$tz"
482bc421551SDag-Erling Smørgrav    do
483*a979394aSDag-Erling Smørgrav      say >&2 "'$tz' is not a conforming POSIX proleptic TZ string."
484bc421551SDag-Erling Smørgrav    done
48546c59934SDag-Erling Smørgrav    TZ_for_date=$tz;;
486bc421551SDag-Erling Smørgrav  *)
487bc421551SDag-Erling Smørgrav    case $continent in
488bc421551SDag-Erling Smørgrav    coord)
489bc421551SDag-Erling Smørgrav      case $coord in
490bc421551SDag-Erling Smørgrav      '')
491bc421551SDag-Erling Smørgrav	echo >&2 'Please enter coordinates' \
492bc421551SDag-Erling Smørgrav	  'in ISO 6709 notation.'
493bc421551SDag-Erling Smørgrav	echo >&2 'For example, +4042-07403 stands for'
494bc421551SDag-Erling Smørgrav	echo >&2 '40 degrees 42 minutes north,' \
495bc421551SDag-Erling Smørgrav	  '74 degrees 3 minutes west.'
49646c59934SDag-Erling Smørgrav	read coord
497bc421551SDag-Erling Smørgrav      esac
498*a979394aSDag-Erling Smørgrav      distance_table=$(
49946c59934SDag-Erling Smørgrav	$AWK \
50046c59934SDag-Erling Smørgrav	  "$output_distances_or_times" \
50146c59934SDag-Erling Smørgrav	  ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" |
502bc421551SDag-Erling Smørgrav	sort -n |
50346c59934SDag-Erling Smørgrav	$AWK "{print} NR == $location_limit { exit }"
504*a979394aSDag-Erling Smørgrav      )
505*a979394aSDag-Erling Smørgrav      regions=$(
50646c59934SDag-Erling Smørgrav	$AWK '
50775411d15SDag-Erling Smørgrav	  BEGIN {
50846c59934SDag-Erling Smørgrav	    distance_table = substr(ARGV[1], 2)
50946c59934SDag-Erling Smørgrav	    ARGV[1] = ""
51075411d15SDag-Erling Smørgrav	    nlines = split(distance_table, line, /\n/)
51175411d15SDag-Erling Smørgrav	    for (nr = 1; nr <= nlines; nr++) {
51275411d15SDag-Erling Smørgrav	      nf = split(line[nr], f, /\t/)
51375411d15SDag-Erling Smørgrav	      print f[nf]
51475411d15SDag-Erling Smørgrav	    }
51575411d15SDag-Erling Smørgrav	  }
51646c59934SDag-Erling Smørgrav	' ="$distance_table"
517*a979394aSDag-Erling Smørgrav      )
51875411d15SDag-Erling Smørgrav      echo >&2 'Please select one of the following timezones,'
519bc421551SDag-Erling Smørgrav      echo >&2 'listed roughly in increasing order' \
520bc421551SDag-Erling Smørgrav	"of distance from $coord".
521bc421551SDag-Erling Smørgrav      doselect $regions
522bc421551SDag-Erling Smørgrav      region=$select_result
523*a979394aSDag-Erling Smørgrav      tz=$(
52446c59934SDag-Erling Smørgrav	$AWK '
52575411d15SDag-Erling Smørgrav	  BEGIN {
52646c59934SDag-Erling Smørgrav	    distance_table = substr(ARGV[1], 2)
52746c59934SDag-Erling Smørgrav	    region = substr(ARGV[2], 2)
52846c59934SDag-Erling Smørgrav	    ARGV[1] = ARGV[2] = ""
52975411d15SDag-Erling Smørgrav	    nlines = split(distance_table, line, /\n/)
53075411d15SDag-Erling Smørgrav	    for (nr = 1; nr <= nlines; nr++) {
53175411d15SDag-Erling Smørgrav	      nf = split(line[nr], f, /\t/)
53246c59934SDag-Erling Smørgrav	      if (f[nf] == region)
53375411d15SDag-Erling Smørgrav		print f[4]
53475411d15SDag-Erling Smørgrav	    }
53575411d15SDag-Erling Smørgrav	  }
53646c59934SDag-Erling Smørgrav	' ="$distance_table" ="$region"
537*a979394aSDag-Erling Smørgrav      );;
538bc421551SDag-Erling Smørgrav    *)
53975411d15SDag-Erling Smørgrav      case $continent in
54046c59934SDag-Erling Smørgrav      now|time)
54175411d15SDag-Erling Smørgrav	minute_format='%a %b %d %H:%M'
542*a979394aSDag-Erling Smørgrav	old_minute=$(TZ=UTC0 date +"$minute_format")
54375411d15SDag-Erling Smørgrav	for i in 1 2 3
54475411d15SDag-Erling Smørgrav	do
545*a979394aSDag-Erling Smørgrav	  time_table_command=$(
54646c59934SDag-Erling Smørgrav	    $AWK \
54746c59934SDag-Erling Smørgrav	      -v output_times=1 \
54846c59934SDag-Erling Smørgrav	      "$output_distances_or_times" \
54946c59934SDag-Erling Smørgrav	      = = ="$TZ_ZONE_TABLE"
550*a979394aSDag-Erling Smørgrav	  )
551*a979394aSDag-Erling Smørgrav	  time_table=$(eval "$time_table_command")
552*a979394aSDag-Erling Smørgrav	  new_minute=$(TZ=UTC0 date +"$minute_format")
55375411d15SDag-Erling Smørgrav	  case $old_minute in
55446c59934SDag-Erling Smørgrav	  "$new_minute") break
55575411d15SDag-Erling Smørgrav	  esac
55675411d15SDag-Erling Smørgrav	  old_minute=$new_minute
55775411d15SDag-Erling Smørgrav	done
55875411d15SDag-Erling Smørgrav	echo >&2 "The system says Universal Time is $new_minute."
55975411d15SDag-Erling Smørgrav	echo >&2 "Assuming that's correct, what is the local time?"
560*a979394aSDag-Erling Smørgrav	sorted_table=$(say "$time_table" | sort -k2n -k2,5 -k1n) || {
56146c59934SDag-Erling Smørgrav	  say >&2 "$0: cannot sort time table"
56246c59934SDag-Erling Smørgrav	  exit 1
56346c59934SDag-Erling Smørgrav	}
564*a979394aSDag-Erling Smørgrav	eval doselect $(
56546c59934SDag-Erling Smørgrav	  $AWK '
56646c59934SDag-Erling Smørgrav	    BEGIN {
56746c59934SDag-Erling Smørgrav	      sorted_table = substr(ARGV[1], 2)
56846c59934SDag-Erling Smørgrav	      ARGV[1] = ""
56946c59934SDag-Erling Smørgrav	      nlines = split(sorted_table, line, /\n/)
57046c59934SDag-Erling Smørgrav	      for (i = 1; i <= nlines; i++) {
57146c59934SDag-Erling Smørgrav		$0 = line[i]
57246c59934SDag-Erling Smørgrav		outline = $6 " " $7 " " $4 " " $5
57346c59934SDag-Erling Smørgrav		if (outline == oldline)
57446c59934SDag-Erling Smørgrav		  continue
57546c59934SDag-Erling Smørgrav		oldline = outline
57646c59934SDag-Erling Smørgrav		gsub(/'\''/, "&\\\\&&", outline)
57746c59934SDag-Erling Smørgrav		printf "'\''%s'\''\n", outline
57846c59934SDag-Erling Smørgrav	      }
57946c59934SDag-Erling Smørgrav	    }
58046c59934SDag-Erling Smørgrav	  ' ="$sorted_table"
581*a979394aSDag-Erling Smørgrav	)
58275411d15SDag-Erling Smørgrav	time=$select_result
58346c59934SDag-Erling Smørgrav	continent_re='^'
584*a979394aSDag-Erling Smørgrav	zone_table=$(
58546c59934SDag-Erling Smørgrav	  $AWK '
58646c59934SDag-Erling Smørgrav	    BEGIN {
58746c59934SDag-Erling Smørgrav	      time = substr(ARGV[1], 2)
58846c59934SDag-Erling Smørgrav	      time_table = substr(ARGV[2], 2)
58946c59934SDag-Erling Smørgrav	      ARGV[1] = ARGV[2] = ""
59046c59934SDag-Erling Smørgrav	      nlines = split(time_table, line, /\n/)
59146c59934SDag-Erling Smørgrav	      for (i = 1; i <= nlines; i++) {
59246c59934SDag-Erling Smørgrav		$0 = line[i]
59375411d15SDag-Erling Smørgrav		if ($6 " " $7 " " $4 " " $5 == time) {
59475411d15SDag-Erling Smørgrav		  sub(/[^\t]*\t/, "")
59575411d15SDag-Erling Smørgrav		  print
59675411d15SDag-Erling Smørgrav		}
59746c59934SDag-Erling Smørgrav	      }
59846c59934SDag-Erling Smørgrav	    }
59946c59934SDag-Erling Smørgrav	  ' ="$time" ="$time_table"
600*a979394aSDag-Erling Smørgrav	)
601*a979394aSDag-Erling Smørgrav	countries=$(
60275411d15SDag-Erling Smørgrav	  $AWK \
60346c59934SDag-Erling Smørgrav	    "$output_country_list" \
60446c59934SDag-Erling Smørgrav	    ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" |
60575411d15SDag-Erling Smørgrav	  sort -f
606*a979394aSDag-Erling Smørgrav	)
60775411d15SDag-Erling Smørgrav	;;
60875411d15SDag-Erling Smørgrav      *)
60946c59934SDag-Erling Smørgrav	continent_re="^$continent/"
61046c59934SDag-Erling Smørgrav	zone_table=$TZ_ZONE_TABLE
61175411d15SDag-Erling Smørgrav      esac
612bc421551SDag-Erling Smørgrav
61346c59934SDag-Erling Smørgrav      # Get list of names of countries in the continent or ocean.
614*a979394aSDag-Erling Smørgrav      countries=$(
61546c59934SDag-Erling Smørgrav	$AWK \
61646c59934SDag-Erling Smørgrav	  "$output_country_list" \
61746c59934SDag-Erling Smørgrav	  ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" |
61846c59934SDag-Erling Smørgrav	sort -f
619*a979394aSDag-Erling Smørgrav      )
62046c59934SDag-Erling Smørgrav      # If all zone table entries have comments, and there are
62146c59934SDag-Erling Smørgrav      # at most 22 entries, asked based on those comments.
62246c59934SDag-Erling Smørgrav      # This fits the prompt onto old-fashioned 24-line screens.
623*a979394aSDag-Erling Smørgrav      regions=$(
62446c59934SDag-Erling Smørgrav	$AWK '
62546c59934SDag-Erling Smørgrav	  BEGIN {
62646c59934SDag-Erling Smørgrav	    TZ_ZONE_TABLE = substr(ARGV[1], 2)
62746c59934SDag-Erling Smørgrav	    ARGV[1] = ""
62846c59934SDag-Erling Smørgrav	    FS = "\t"
62946c59934SDag-Erling Smørgrav	    nlines = split(TZ_ZONE_TABLE, line, /\n/)
63046c59934SDag-Erling Smørgrav	    for (i = 1; i <= nlines; i++) {
63146c59934SDag-Erling Smørgrav	      $0 = line[i]
63246c59934SDag-Erling Smørgrav	      if ($0 ~ /^[^#]/ && !missing_comment) {
63346c59934SDag-Erling Smørgrav		if ($4)
63446c59934SDag-Erling Smørgrav		  comment[++inlines] = $4
63546c59934SDag-Erling Smørgrav		else
63646c59934SDag-Erling Smørgrav		  missing_comment = 1
63746c59934SDag-Erling Smørgrav	      }
63846c59934SDag-Erling Smørgrav	    }
63946c59934SDag-Erling Smørgrav	    if (!missing_comment && inlines <= 22)
64046c59934SDag-Erling Smørgrav	      for (i = 1; i <= inlines; i++)
64146c59934SDag-Erling Smørgrav		print comment[i]
64246c59934SDag-Erling Smørgrav	  }
64346c59934SDag-Erling Smørgrav	' ="$zone_table"
644*a979394aSDag-Erling Smørgrav      )
64546c59934SDag-Erling Smørgrav
646bc421551SDag-Erling Smørgrav      # If there's more than one country, ask the user which one.
647bc421551SDag-Erling Smørgrav      case $countries in
648bc421551SDag-Erling Smørgrav      *"$newline"*)
649bc421551SDag-Erling Smørgrav	echo >&2 'Please select a country' \
650bc421551SDag-Erling Smørgrav	  'whose clocks agree with yours.'
651bc421551SDag-Erling Smørgrav	doselect $countries
65275411d15SDag-Erling Smørgrav	country_result=$select_result
653bc421551SDag-Erling Smørgrav	country=$select_result;;
654bc421551SDag-Erling Smørgrav      *)
655bc421551SDag-Erling Smørgrav	country=$countries
656bc421551SDag-Erling Smørgrav      esac
657bc421551SDag-Erling Smørgrav
658bc421551SDag-Erling Smørgrav
659bc421551SDag-Erling Smørgrav      # Get list of timezones in the country.
660*a979394aSDag-Erling Smørgrav      regions=$(
66146c59934SDag-Erling Smørgrav	$AWK '
662bc421551SDag-Erling Smørgrav	  BEGIN {
66346c59934SDag-Erling Smørgrav	    country = substr(ARGV[1], 2)
66446c59934SDag-Erling Smørgrav	    TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
66546c59934SDag-Erling Smørgrav	    TZ_ZONE_TABLE = substr(ARGV[3], 2)
66646c59934SDag-Erling Smørgrav	    ARGV[1] = ARGV[2] = ARGV[3] = ""
667bc421551SDag-Erling Smørgrav	    FS = "\t"
668bc421551SDag-Erling Smørgrav	    cc = country
66946c59934SDag-Erling Smørgrav	    nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
67046c59934SDag-Erling Smørgrav	    for (i = 1; i <= nlines; i++) {
67146c59934SDag-Erling Smørgrav	      $0 = line[i]
672bc421551SDag-Erling Smørgrav	      if ($0 !~ /^#/  &&  country == $2) {
673bc421551SDag-Erling Smørgrav		cc = $1
674bc421551SDag-Erling Smørgrav		break
675bc421551SDag-Erling Smørgrav	      }
676bc421551SDag-Erling Smørgrav	    }
67746c59934SDag-Erling Smørgrav	    nlines = split(TZ_ZONE_TABLE, line, /\n/)
67846c59934SDag-Erling Smørgrav	    for (i = 1; i <= nlines; i++) {
67946c59934SDag-Erling Smørgrav	      $0 = line[i]
68046c59934SDag-Erling Smørgrav	      if ($0 ~ /^#/)
68146c59934SDag-Erling Smørgrav		continue
68246c59934SDag-Erling Smørgrav	      if ($1 ~ cc)
68346c59934SDag-Erling Smørgrav		print $4
684bc421551SDag-Erling Smørgrav	    }
68546c59934SDag-Erling Smørgrav	  }
68646c59934SDag-Erling Smørgrav	' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table"
687*a979394aSDag-Erling Smørgrav      )
688bc421551SDag-Erling Smørgrav
689bc421551SDag-Erling Smørgrav      # If there's more than one region, ask the user which one.
690bc421551SDag-Erling Smørgrav      case $regions in
691bc421551SDag-Erling Smørgrav      *"$newline"*)
692bc421551SDag-Erling Smørgrav	echo >&2 'Please select one of the following timezones.'
693bc421551SDag-Erling Smørgrav	doselect $regions
69475411d15SDag-Erling Smørgrav	region=$select_result
695bc421551SDag-Erling Smørgrav      esac
696bc421551SDag-Erling Smørgrav
69746c59934SDag-Erling Smørgrav      # Determine tz from country and region.
698*a979394aSDag-Erling Smørgrav      tz=$(
69946c59934SDag-Erling Smørgrav	$AWK '
700bc421551SDag-Erling Smørgrav	  BEGIN {
70146c59934SDag-Erling Smørgrav	    country = substr(ARGV[1], 2)
70246c59934SDag-Erling Smørgrav	    region = substr(ARGV[2], 2)
70346c59934SDag-Erling Smørgrav	    TZ_COUNTRY_TABLE = substr(ARGV[3], 2)
70446c59934SDag-Erling Smørgrav	    TZ_ZONE_TABLE = substr(ARGV[4], 2)
70546c59934SDag-Erling Smørgrav	    ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = ""
706bc421551SDag-Erling Smørgrav	    FS = "\t"
707bc421551SDag-Erling Smørgrav	    cc = country
70846c59934SDag-Erling Smørgrav	    nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
70946c59934SDag-Erling Smørgrav	    for (i = 1; i <= nlines; i++) {
71046c59934SDag-Erling Smørgrav	      $0 = line[i]
711bc421551SDag-Erling Smørgrav	      if ($0 !~ /^#/  &&  country == $2) {
712bc421551SDag-Erling Smørgrav		cc = $1
713bc421551SDag-Erling Smørgrav		break
714bc421551SDag-Erling Smørgrav	      }
715bc421551SDag-Erling Smørgrav	    }
71646c59934SDag-Erling Smørgrav	    nlines = split(TZ_ZONE_TABLE, line, /\n/)
71746c59934SDag-Erling Smørgrav	    for (i = 1; i <= nlines; i++) {
71846c59934SDag-Erling Smørgrav	      $0 = line[i]
71946c59934SDag-Erling Smørgrav	      if ($0 ~ /^#/)
72046c59934SDag-Erling Smørgrav		continue
72146c59934SDag-Erling Smørgrav	      if ($1 ~ cc && ($4 == region || !region))
72246c59934SDag-Erling Smørgrav		print $3
723bc421551SDag-Erling Smørgrav	    }
72446c59934SDag-Erling Smørgrav	  }
72546c59934SDag-Erling Smørgrav	' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table"
726*a979394aSDag-Erling Smørgrav      )
727bc421551SDag-Erling Smørgrav    esac
728bc421551SDag-Erling Smørgrav
729bc421551SDag-Erling Smørgrav    # Make sure the corresponding zoneinfo file exists.
73046c59934SDag-Erling Smørgrav    TZ_for_date=$TZDIR/$tz
731bc421551SDag-Erling Smørgrav    <"$TZ_for_date" || {
732bc421551SDag-Erling Smørgrav      say >&2 "$0: time zone files are not set up correctly"
733bc421551SDag-Erling Smørgrav      exit 1
734bc421551SDag-Erling Smørgrav    }
735bc421551SDag-Erling Smørgrav  esac
736bc421551SDag-Erling Smørgrav
737bc421551SDag-Erling Smørgrav
738bc421551SDag-Erling Smørgrav  # Use the proposed TZ to output the current date relative to UTC.
739bc421551SDag-Erling Smørgrav  # Loop until they agree in seconds.
740bc421551SDag-Erling Smørgrav  # Give up after 8 unsuccessful tries.
741bc421551SDag-Erling Smørgrav
742bc421551SDag-Erling Smørgrav  extra_info=
743bc421551SDag-Erling Smørgrav  for i in 1 2 3 4 5 6 7 8
744bc421551SDag-Erling Smørgrav  do
745*a979394aSDag-Erling Smørgrav    TZdate=$(LANG=C TZ="$TZ_for_date" date)
746*a979394aSDag-Erling Smørgrav    UTdate=$(LANG=C TZ=UTC0 date)
747*a979394aSDag-Erling Smørgrav    TZsecsetc=${TZdate##*[0-5][0-9]:}
748*a979394aSDag-Erling Smørgrav    UTsecsetc=${UTdate##*[0-5][0-9]:}
749*a979394aSDag-Erling Smørgrav    if test "${TZsecsetc%%[!0-9]*}" = "${UTsecsetc%%[!0-9]*}"
75046c59934SDag-Erling Smørgrav    then
751bc421551SDag-Erling Smørgrav      extra_info="
752bc421551SDag-Erling SmørgravSelected time is now:	$TZdate.
753bc421551SDag-Erling SmørgravUniversal Time is now:	$UTdate."
754bc421551SDag-Erling Smørgrav      break
75546c59934SDag-Erling Smørgrav    fi
756bc421551SDag-Erling Smørgrav  done
757bc421551SDag-Erling Smørgrav
758bc421551SDag-Erling Smørgrav
759bc421551SDag-Erling Smørgrav  # Output TZ info and ask the user to confirm.
760bc421551SDag-Erling Smørgrav
761bc421551SDag-Erling Smørgrav  echo >&2 ""
76275411d15SDag-Erling Smørgrav  echo >&2 "Based on the following information:"
763bc421551SDag-Erling Smørgrav  echo >&2 ""
76475411d15SDag-Erling Smørgrav  case $time%$country_result%$region%$coord in
76575411d15SDag-Erling Smørgrav  ?*%?*%?*%)
76675411d15SDag-Erling Smørgrav    say >&2 "	$time$newline	$country_result$newline	$region";;
76775411d15SDag-Erling Smørgrav  ?*%?*%%|?*%%?*%) say >&2 "	$time$newline	$country_result$region";;
76875411d15SDag-Erling Smørgrav  ?*%%%)	say >&2 "	$time";;
76975411d15SDag-Erling Smørgrav  %?*%?*%)	say >&2 "	$country_result$newline	$region";;
77075411d15SDag-Erling Smørgrav  %?*%%)	say >&2 "	$country_result";;
77175411d15SDag-Erling Smørgrav  %%?*%?*)	say >&2 "	coord $coord$newline	$region";;
77275411d15SDag-Erling Smørgrav  %%%?*)	say >&2 "	coord $coord";;
77346c59934SDag-Erling Smørgrav  *)		say >&2 "	TZ='$tz'"
774bc421551SDag-Erling Smørgrav  esac
775bc421551SDag-Erling Smørgrav  say >&2 ""
77646c59934SDag-Erling Smørgrav  say >&2 "TZ='$tz' will be used.$extra_info"
777bc421551SDag-Erling Smørgrav  say >&2 "Is the above information OK?"
778bc421551SDag-Erling Smørgrav
779bc421551SDag-Erling Smørgrav  doselect Yes No
780bc421551SDag-Erling Smørgrav  ok=$select_result
781bc421551SDag-Erling Smørgrav  case $ok in
782bc421551SDag-Erling Smørgrav  Yes) break
783bc421551SDag-Erling Smørgrav  esac
784bc421551SDag-Erling Smørgravdo coord=
785bc421551SDag-Erling Smørgravdone
786bc421551SDag-Erling Smørgrav
787bc421551SDag-Erling Smørgravcase $SHELL in
78846c59934SDag-Erling Smørgrav*csh) file=.login line="setenv TZ '$tz'";;
789*a979394aSDag-Erling Smørgrav*)    file=.profile line="export TZ='$tz'"
790bc421551SDag-Erling Smørgravesac
791bc421551SDag-Erling Smørgrav
792bc421551SDag-Erling Smørgravtest -t 1 && say >&2 "
793bc421551SDag-Erling SmørgravYou can make this change permanent for yourself by appending the line
794bc421551SDag-Erling Smørgrav	$line
795bc421551SDag-Erling Smørgravto the file '$file' in your home directory; then log out and log in again.
796bc421551SDag-Erling Smørgrav
797bc421551SDag-Erling SmørgravHere is that TZ value again, this time on standard output so that you
798bc421551SDag-Erling Smørgravcan use the $0 command in shell scripts:"
799bc421551SDag-Erling Smørgrav
80046c59934SDag-Erling Smørgravsay "$tz"
801