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