1#!/bin/sh
2#-
3# Copyright (c) 2011-2015 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..." "$0"
34f_include $BSDCFG_SHARE/dialog.subr
35f_include $BSDCFG_SHARE/mustberoot.subr
36f_include $BSDCFG_SHARE/strings.subr
37f_include $BSDCFG_SHARE/timezone/continents.subr
38f_include $BSDCFG_SHARE/timezone/countries.subr
39f_include $BSDCFG_SHARE/timezone/iso3166.subr
40f_include $BSDCFG_SHARE/timezone/menus.subr
41f_include $BSDCFG_SHARE/timezone/zones.subr
42
43BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone"
44f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
45
46f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm &&
47	pgm="${ipgm:-$pgm}"
48
49############################################################ CONFIGURATION
50
51#
52# Standard pathnames
53#
54_PATH_DB="/var/db/zoneinfo"
55_PATH_WALL_CMOS_CLOCK="/etc/wall_cmos_clock"
56
57############################################################ GLOBALS
58
59#
60# Options
61#
62REALLYDOIT=1
63REINSTALL=
64USEDIALOG=1
65SKIPUTC= # See MAIN
66VERBOSE=
67TZ_OR_FAIL=
68CHROOTENV=
69
70#
71# Dummy vars (populated dynamically)
72#
73COUNTRIES= # list of 2-character country codes created by f_read_iso3166_table
74
75############################################################ FUNCTIONS
76
77# dialog_menu_main
78#
79# Display the dialog(1)-based application main menu.
80#
81dialog_menu_main()
82{
83	local title="$DIALOG_TITLE"
84	local btitle="$DIALOG_BACKTITLE"
85	local prompt="$msg_select_region"
86	local defaultitem= # Calculated below
87	local hline=
88
89	local height width rows
90	eval f_dialog_menu_size height width rows \
91	                        \"\$title\"  \
92	                        \"\$btitle\" \
93	                        \"\$prompt\" \
94	                        \"\$hline\"  \
95	                        $continent_menu_list
96
97	# Obtain default-item from previously stored selection
98	f_dialog_default_fetch defaultitem
99
100	local menu_choice
101	menu_choice=$( eval $DIALOG \
102		--title \"\$title\"              \
103		--backtitle \"\$btitle\"         \
104		--hline \"\$hline\"              \
105		--ok-label \"\$msg_ok\"          \
106		--cancel-label \"\$msg_cancel\"  \
107		--default-item \"\$defaultitem\" \
108		--menu \"\$prompt\"              \
109		$height $width $rows             \
110		$continent_menu_list             \
111		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
112	)
113	local retval=$?
114	f_dialog_data_sanitize menu_choice
115	f_dialog_menutag_store "$menu_choice"
116	f_dialog_default_store "$menu_choice"
117	return $retval
118}
119
120############################################################ MAIN
121
122# Skip initial question regarding UTC v. Wall-Clock time if run in VM
123[ "$( sysctl -n kern.vm_guest 2> /dev/null )" = "none" ] || SKIPUTC=1
124
125# Incorporate rc-file if it exists
126[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
127
128#
129# Process command-line arguments
130#
131while getopts C:ehnrsv$GETOPTS_STDARGS flag; do
132	case "$flag" in
133	C) CHROOTENV="$OPTARG" ;;
134	e) TZ_OR_FAIL=1 ;;
135	n) REALLYDOIT= ;;
136	r) REINSTALL=1
137	   USEDIALOG= ;;
138	s) SKIPUTC=1 ;;
139	v) VERBOSE=1 ;;
140	h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm" ;;
141	esac
142done
143shift $(( $OPTIND - 1 ))
144
145#
146# Initialize
147#
148f_dialog_title "$msg_time_zone"
149f_dialog_backtitle "${ipgm:+bsdconfig }$pgm"
150f_mustberoot_init
151
152#
153# Process `-C chroot_directory' command-line argument
154#
155if [ "$CHROOTENV" ]; then
156	_PATH_ZONETAB="$CHROOTENV$_PATH_ZONETAB"
157	_PATH_ISO3166="$CHROOTENV$_PATH_ISO3166"
158	_PATH_ZONEINFO="$CHROOTENV$_PATH_ZONEINFO"
159	_PATH_LOCALTIME="$CHROOTENV$_PATH_LOCALTIME"
160	_PATH_DB="$CHROOTENV$_PATH_DB"
161	_PATH_WALL_CMOS_CLOCK="$CHROOTENV$_PATH_WALL_CMOS_CLOCK"
162fi
163
164#
165# Process `-r' command-line option
166#
167if [ "$REINSTALL" ]; then
168	[ -f "$_PATH_DB" -a -r "$_PATH_DB" ] ||
169		f_die 1 "$msg_cannot_open_for_reading" "$_PATH_DB"
170	f_eval_catch -dk zoneinfo "$0" cat 'cat "%s"' "$_PATH_DB" ||
171		f_die 1 "$msg_error_reading" "$_PATH_DB"
172	[ "$zoneinfo" ] ||
173		f_die 1 "$msg_unable_to_determine_name_from_db" "$_PATH_DB"
174	f_install_zoneinfo "$zoneinfo"
175	exit $?
176fi
177
178#
179# If the arguments on the command-line do not specify a file,
180# then interpret it as a zoneinfo name
181#
182if [ $# -ge 1 ]; then
183	zoneinfo="$1"
184
185	if [ ! -f "$zoneinfo" ]; then
186		USEDIALOG=
187		f_install_zoneinfo "$zoneinfo"
188		exit $?
189	fi
190
191	# FALLTHROUGH
192fi
193
194#
195# Process the UTC option
196#
197if [ "$_PATH_WALL_CMOS_CLOCK" -a ! "$SKIPUTC" ]; then
198	f_dialog_title "$msg_select_local_or_utc"
199	title="$DIALOG_TITLE"
200	btitle="$DIALOG_BACKTITLE"
201	f_dialog_title_restore
202	msg="$msg_is_machine_clock_utc"
203
204	if [ "$USE_XDIALOG" ]; then
205		defaultno="default-no"
206		height=10 width=77
207	else
208		defaultno="defaultno"
209		height=7 width=73
210	fi
211
212	if [ "$USE_XDIALOG" ]; then
213		$DIALOG \
214			--title "$title"         \
215			--backtitle "$btitle"    \
216			--$defaultno             \
217			--ok-label "$msg_yes"    \
218			--cancel-label "$msg_no" \
219			--yesno "$msg" $height $width
220		result=$?
221	else
222		$DIALOG \
223			--title "$title"       \
224			--backtitle "$btitle"  \
225			--$defaultno           \
226			--yes-label "$msg_yes" \
227			--no-label "$msg_no"   \
228			--yesno "$msg" $height $width
229		result=$?
230	fi
231
232	if [ $result -eq $DIALOG_OK ]; then
233		# User chose YES
234		[ "$REALLYDOIT" ] &&
235			f_quietly rm -f "$_PATH_WALL_CMOS_CLOCK"
236	else
237		# User chose NO, pressed ESC (or Ctrl-C), or closed box
238		[ "$REALLYDOIT" ] &&
239			( umask 222 && :> "$_PATH_WALL_CMOS_CLOCK" )
240	fi
241fi
242
243#
244# Process optional default zone argument
245#
246if [ $# -ge 1 ]; then
247	default="$1"
248
249	f_dialog_title "$msg_default_zone_provided"
250	f_sprintf msg "\n$msg_use_default_zone" "$default"
251	hline=
252	f_dialog_yesno "$msg" "$hline"
253	result=$?
254	f_dialog_title_restore
255
256	if [ $result -eq $DIALOG_OK ]; then
257		# User chose YES
258		f_install_zoneinfo_file "$default"
259		result=$?
260		[ ! "$USE_XDIALOG" ] && f_dialog_clear
261		exit $result
262	fi
263
264	[ ! "$USE_XDIALOG" ] && f_dialog_clear
265fi
266
267#
268# Override the user-supplied umask
269#
270umask 022
271
272#
273# Read databases and perform initialization
274#
275f_read_iso3166_table # creates $COUNTRIES and $country_*_name
276f_read_zones         # creates $country_*_{descr,cont,filename}
277f_sort_countries     # sorts the countries listed for each continent
278f_make_menus         # creates $continent_menu_list and $continent_*_menu_list
279
280#
281# Launch application main menu
282#
283defaultctry=
284defaultzone=
285NEED_CONTINENT=1
286NEED_COUNTRY=1
287while :; do
288	if [ "$NEED_CONTINENT" ]; then
289		dialog_menu_main # prompt the user to select a continent/ocean
290		retval=$?
291		f_dialog_menutag_fetch mtag
292
293		if [ $retval -ne $DIALOG_OK ]; then
294			[ "$TZ_OR_FAIL" ] && f_die
295			exit $SUCCESS
296		fi
297
298		NEED_CONTINENT=
299
300		continent=$( eval f_dialog_menutag2item \"\$mtag\" \
301		                  	$continent_menu_list )
302		f_find_continent "$continent" cont
303		f_continent $cont title cont_title
304		f_continent $cont nitems nitems
305		f_OCEANP $cont isocean
306	fi
307
308	if [ "$NEED_COUNTRY" ]; then
309		if [ "$cont_title" = "$continent_utc_title" ]; then
310			if f_set_zone_utc; then
311				break
312			else
313				NEED_CONTINENT=1
314				continue
315			fi
316		fi
317
318		#
319		# Short cut -- if there's only one country, don't post a menu.
320		#
321		if [ $nitems -eq 1 ]; then
322			tag=1
323		else
324			#
325			# It's amazing how much good grammar really matters...
326			#
327			if [ ! "$isocean" ]; then
328				f_sprintf title "$msg_country_title" \
329				                "$cont_title"
330				f_dialog_title "$title"
331				title="$DIALOG_TITLE"
332				btitle="$DIALOG_BACKTITLE"
333				f_dialog_title_restore
334				prompt="$msg_select_country"
335			else
336				f_sprintf title "$msg_island_and_group_title" \
337				                "$cont_title"
338				f_dialog_title "$title"
339				title="$DIALOG_TITLE"
340				btitle="$DIALOG_BACKTITLE"
341				f_dialog_title_restore
342				prompt="$msg_select_island_or_group"
343			fi
344
345			#
346			# Calculate size of menu
347			#
348			f_continent $cont menu_list menu_list
349			eval f_dialog_menu_size height width rows \
350			                        \"\$title\"  \
351			                        \"\$btitle\" \
352			                        \"\$prompt\" \
353			                        \"\"         \
354			                        $menu_list
355
356			#
357			# Launch the country selection menu
358			#
359			tag=$( eval $DIALOG \
360				--title \"\$title\"              \
361				--backtitle \"\$btitle\"         \
362				--ok-label \"\$msg_ok\"          \
363				--cancel-label \"\$msg_cancel\"  \
364				--default-item \"\$defaultctry\" \
365				--menu \"\$prompt\"              \
366				$height $width $rows             \
367				$menu_list                       \
368				2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
369			)
370			retval=$?
371			f_dialog_data_sanitize tag
372			defaultctry="$tag"
373
374			if [ $retval -ne $DIALOG_OK ]; then
375				NEED_CONTINENT=1
376				continue # back to main menu
377			fi
378		fi
379
380		# Get the country code from the user's selection 
381		f_continent $cont tlc_$tag tlc
382
383		NEED_COUNTRY=
384	fi
385
386	#
387	# If the selection has only one zone (nzones == -1),
388	# just set it.
389	#
390	f_country $tlc nzones nzones
391	if [ $nzones -lt 0 ]; then
392		f_country $tlc cont real_cont
393		f_continent $real_cont name real_continent
394		f_country $tlc name name
395		f_country $tlc filename filename
396
397		if ! f_confirm_zone "$real_continent/$filename"; then
398			[ $nitems -eq 1 ] && NEED_CONTINENT=1
399			NEED_COUNTRY=1
400			continue
401		fi
402	else
403		f_country $tlc name name
404		f_sprintf title "$msg_country_time_zones" "$name"
405		f_dialog_title "$title"
406		title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
407		f_dialog_title_restore
408		prompt="$msg_select_zone"
409		f_country $tlc menu_list menu_list
410		eval f_dialog_menu_size height width rows \
411			\"\$title\"  \"\$btitle\" \"\$prompt\" \"\" $menu_list
412
413		#
414		# Launch the zone selection menu
415		# NOTE: This is as deep as we go
416		#
417		n=$( eval $DIALOG \
418			--title \"\$title\"              \
419			--backtitle \"\$btitle\"         \
420			--ok-label \"\$msg_ok\"          \
421			--cancel-label \"\$msg_cancel\"  \
422			--default-item \"\$defaultzone\" \
423			--menu \"\$prompt\"              \
424			$height $width $rows             \
425			$menu_list                       \
426			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
427		)
428		retval=$?
429		f_dialog_data_sanitize n
430		defaultzone="$n"
431
432		if [ $retval -ne $DIALOG_OK ]; then
433			[ $nitems -eq 1 ] && NEED_CONTINENT=1
434			NEED_COUNTRY=1
435			continue
436		fi
437
438		f_country $tlc cont_$n real_cont
439		f_continent $real_cont name real_continent
440		f_country $tlc name name
441		f_country $tlc filename_$n filename
442
443		f_confirm_zone "$real_continent/$filename" || continue
444	fi
445
446	[ $retval -eq $DIALOG_OK ] || continue # back to main menu
447
448	if ! f_install_zoneinfo "$real_continent/$filename"; then
449		[ $nzones -lt 0 ] && NEED_COUNTRY=1
450	else
451		break
452	fi
453done
454
455################################################################################
456# END
457################################################################################
458