aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/tzsetup
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/tzsetup')
-rw-r--r--usr.sbin/tzsetup/Makefile20
-rw-r--r--usr.sbin/tzsetup/Makefile.depend16
-rw-r--r--usr.sbin/tzsetup/baseline670
-rw-r--r--usr.sbin/tzsetup/tzsetup.8163
-rw-r--r--usr.sbin/tzsetup/tzsetup.c996
5 files changed, 1865 insertions, 0 deletions
diff --git a/usr.sbin/tzsetup/Makefile b/usr.sbin/tzsetup/Makefile
new file mode 100644
index 000000000000..f20834baf25d
--- /dev/null
+++ b/usr.sbin/tzsetup/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PROG= tzsetup
+MAN= tzsetup.8
+
+CFLAGS+= -I.
+
+.if !defined(BOOTSTRAPPING)
+WARNS?= 3
+CFLAGS+= -I${SRCTOP}/contrib/bsddialog/lib -DHAVE_BSDDIALOG
+LIBADD= bsddialog
+.endif
+
+ZONETAB= ${SRCTOP}/contrib/tzdata/zone1970.tab
+baseline: ${PROG} ${ZONETAB}
+ ${.OBJDIR}/${PROG} -d ${ZONETAB} > ${.CURDIR}/baseline
+
+.PHONY: baseline
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tzsetup/Makefile.depend b/usr.sbin/tzsetup/Makefile.depend
new file mode 100644
index 000000000000..63fe0b5e1069
--- /dev/null
+++ b/usr.sbin/tzsetup/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libbsddialog \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/tzsetup/baseline b/usr.sbin/tzsetup/baseline
new file mode 100644
index 000000000000..de9b5f638d09
--- /dev/null
+++ b/usr.sbin/tzsetup/baseline
@@ -0,0 +1,670 @@
+AF:Afghanistan
+ Asia:Asia/Kabul
+AL:Albania
+ Europe:Europe/Tirane
+DZ:Algeria
+ Africa:Africa/Algiers
+AS:American Samoa
+ Pacific:Pacific/Pago_Pago
+AD:Andorra
+ Europe:Europe/Andorra
+AO:Angola
+ Africa:Africa/Lagos
+AI:Anguilla
+ America:America/Puerto_Rico
+AQ:Antarctica
+ Antarctica:Antarctica/Casey
+ Antarctica:Antarctica/Davis
+ Antarctica:Antarctica/Mawson
+ Antarctica:Antarctica/Palmer
+ Antarctica:Antarctica/Rothera
+ Antarctica:Antarctica/Troll
+ Antarctica:Asia/Urumqi
+ Antarctica:Pacific/Auckland
+ Antarctica:Pacific/Port_Moresby
+ Antarctica:Asia/Riyadh
+AG:Antigua and Barbuda
+ America:America/Puerto_Rico
+AR:Argentina
+ America:America/Argentina/Buenos_Aires
+ America:America/Argentina/Cordoba
+ America:America/Argentina/Salta
+ America:America/Argentina/Jujuy
+ America:America/Argentina/Tucuman
+ America:America/Argentina/Catamarca
+ America:America/Argentina/La_Rioja
+ America:America/Argentina/San_Juan
+ America:America/Argentina/Mendoza
+ America:America/Argentina/San_Luis
+ America:America/Argentina/Rio_Gallegos
+ America:America/Argentina/Ushuaia
+AM:Armenia
+ Asia:Asia/Yerevan
+AW:Aruba
+ America:America/Puerto_Rico
+AU:Australia
+ Australia:Australia/Lord_Howe
+ Antarctica:Antarctica/Macquarie
+ Australia:Australia/Hobart
+ Australia:Australia/Melbourne
+ Australia:Australia/Sydney
+ Australia:Australia/Broken_Hill
+ Australia:Australia/Brisbane
+ Australia:Australia/Lindeman
+ Australia:Australia/Adelaide
+ Australia:Australia/Darwin
+ Australia:Australia/Perth
+ Australia:Australia/Eucla
+AT:Austria
+ Europe:Europe/Vienna
+AZ:Azerbaijan
+ Asia:Asia/Baku
+BS:Bahamas
+ America:America/Toronto
+BH:Bahrain
+ Asia:Asia/Qatar
+BD:Bangladesh
+ Asia:Asia/Dhaka
+BB:Barbados
+ America:America/Barbados
+BY:Belarus
+ Europe:Europe/Minsk
+BE:Belgium
+ Europe:Europe/Brussels
+BZ:Belize
+ America:America/Belize
+BJ:Benin
+ Africa:Africa/Lagos
+BM:Bermuda
+ Atlantic:Atlantic/Bermuda
+BT:Bhutan
+ Asia:Asia/Thimphu
+BO:Bolivia, Plurinational State of
+ America:America/La_Paz
+BQ:Bonaire, Sint Eustatius and Saba
+ America:America/Puerto_Rico
+BA:Bosnia and Herzegovina
+ Europe:Europe/Belgrade
+BW:Botswana
+ Africa:Africa/Maputo
+BV:Bouvet Island
+BR:Brazil
+ America:America/Noronha
+ America:America/Belem
+ America:America/Fortaleza
+ America:America/Recife
+ America:America/Araguaina
+ America:America/Maceio
+ America:America/Bahia
+ America:America/Sao_Paulo
+ America:America/Campo_Grande
+ America:America/Cuiaba
+ America:America/Santarem
+ America:America/Porto_Velho
+ America:America/Boa_Vista
+ America:America/Manaus
+ America:America/Eirunepe
+ America:America/Rio_Branco
+IO:British Indian Ocean Territory
+ Indian:Indian/Chagos
+BN:Brunei Darussalam
+ Asia:Asia/Kuching
+BG:Bulgaria
+ Europe:Europe/Sofia
+BF:Burkina Faso
+ Africa:Africa/Abidjan
+BI:Burundi
+ Africa:Africa/Maputo
+CV:Cabo Verde
+ Atlantic:Atlantic/Cape_Verde
+KH:Cambodia
+ Asia:Asia/Bangkok
+CM:Cameroon
+ Africa:Africa/Lagos
+CA:Canada
+ America:America/St_Johns
+ America:America/Halifax
+ America:America/Glace_Bay
+ America:America/Moncton
+ America:America/Goose_Bay
+ America:America/Toronto
+ America:America/Iqaluit
+ America:America/Winnipeg
+ America:America/Resolute
+ America:America/Rankin_Inlet
+ America:America/Regina
+ America:America/Swift_Current
+ America:America/Edmonton
+ America:America/Cambridge_Bay
+ America:America/Inuvik
+ America:America/Dawson_Creek
+ America:America/Fort_Nelson
+ America:America/Whitehorse
+ America:America/Dawson
+ America:America/Vancouver
+ America:America/Panama
+ America:America/Puerto_Rico
+ America:America/Phoenix
+KY:Cayman Islands
+ America:America/Panama
+CF:Central African Republic
+ Africa:Africa/Lagos
+TD:Chad
+ Africa:Africa/Ndjamena
+CL:Chile
+ America:America/Santiago
+ America:America/Punta_Arenas
+ Pacific:Pacific/Easter
+CN:China
+ Asia:Asia/Shanghai
+ Asia:Asia/Urumqi
+CX:Christmas Island
+ Indian:Asia/Bangkok
+CC:Cocos (Keeling) Islands
+ Indian:Asia/Yangon
+CO:Colombia
+ America:America/Bogota
+KM:Comoros
+ Indian:Africa/Nairobi
+CG:Congo
+ Africa:Africa/Lagos
+CD:Congo, Democratic Republic of the
+ Africa:Africa/Maputo
+ Africa:Africa/Lagos
+CK:Cook Islands
+ Pacific:Pacific/Rarotonga
+CR:Costa Rica
+ America:America/Costa_Rica
+HR:Croatia
+ Europe:Europe/Belgrade
+CU:Cuba
+ America:America/Havana
+CW:Curaçao
+ America:America/Puerto_Rico
+CY:Cyprus
+ Asia:Asia/Nicosia
+ Asia:Asia/Famagusta
+CZ:Czech Republic
+ Europe:Europe/Prague
+CI:Côte d'Ivoire
+ Africa:Africa/Abidjan
+DK:Denmark
+ Europe:Europe/Berlin
+DJ:Djibouti
+ Africa:Africa/Nairobi
+DM:Dominica
+ America:America/Puerto_Rico
+DO:Dominican Republic
+ America:America/Santo_Domingo
+EC:Ecuador
+ America:America/Guayaquil
+ Pacific:Pacific/Galapagos
+EG:Egypt
+ Africa:Africa/Cairo
+SV:El Salvador
+ America:America/El_Salvador
+GQ:Equatorial Guinea
+ Africa:Africa/Lagos
+ER:Eritrea
+ Africa:Africa/Nairobi
+EE:Estonia
+ Europe:Europe/Tallinn
+SZ:Eswatini
+ Africa:Africa/Johannesburg
+ET:Ethiopia
+ Africa:Africa/Nairobi
+FK:Falkland Islands (Malvinas)
+ Atlantic:Atlantic/Stanley
+FO:Faroe Islands
+ Atlantic:Atlantic/Faroe
+FJ:Fiji
+ Pacific:Pacific/Fiji
+FI:Finland
+ Europe:Europe/Helsinki
+FR:France
+ Europe:Europe/Paris
+GF:French Guiana
+ America:America/Cayenne
+PF:French Polynesia
+ Pacific:Pacific/Tahiti
+ Pacific:Pacific/Marquesas
+ Pacific:Pacific/Gambier
+TF:French Southern Territories
+ Asia:Asia/Dubai
+ Indian:Indian/Maldives
+GA:Gabon
+ Africa:Africa/Lagos
+GM:Gambia
+ Africa:Africa/Abidjan
+GE:Georgia
+ Asia:Asia/Tbilisi
+DE:Germany
+ Europe:Europe/Zurich
+ Europe:Europe/Berlin
+GH:Ghana
+ Africa:Africa/Abidjan
+GI:Gibraltar
+ Europe:Europe/Gibraltar
+GR:Greece
+ Europe:Europe/Athens
+GL:Greenland
+ America:America/Nuuk
+ America:America/Danmarkshavn
+ America:America/Scoresbysund
+ America:America/Thule
+GD:Grenada
+ America:America/Puerto_Rico
+GP:Guadeloupe
+ America:America/Puerto_Rico
+GU:Guam
+ Pacific:Pacific/Guam
+GT:Guatemala
+ America:America/Guatemala
+GG:Guernsey
+ Europe:Europe/London
+GN:Guinea
+ Africa:Africa/Abidjan
+GW:Guinea-Bissau
+ Africa:Africa/Bissau
+GY:Guyana
+ America:America/Guyana
+HT:Haiti
+ America:America/Port-au-Prince
+HM:Heard Island and McDonald Islands
+VA:Holy See
+ Europe:Europe/Rome
+HN:Honduras
+ America:America/Tegucigalpa
+HK:Hong Kong
+ Asia:Asia/Hong_Kong
+HU:Hungary
+ Europe:Europe/Budapest
+IS:Iceland
+ Atlantic:Africa/Abidjan
+IN:India
+ Asia:Asia/Kolkata
+ID:Indonesia
+ Asia:Asia/Jakarta
+ Asia:Asia/Pontianak
+ Asia:Asia/Makassar
+ Asia:Asia/Jayapura
+IR:Iran (Islamic Republic of)
+ Asia:Asia/Tehran
+IQ:Iraq
+ Asia:Asia/Baghdad
+IE:Ireland
+ Europe:Europe/Dublin
+IM:Isle of Man
+ Europe:Europe/London
+IL:Israel
+ Asia:Asia/Jerusalem
+IT:Italy
+ Europe:Europe/Rome
+JM:Jamaica
+ America:America/Jamaica
+JP:Japan
+ Asia:Asia/Tokyo
+JE:Jersey
+ Europe:Europe/London
+JO:Jordan
+ Asia:Asia/Amman
+KZ:Kazakhstan
+ Asia:Asia/Almaty
+ Asia:Asia/Qyzylorda
+ Asia:Asia/Qostanay
+ Asia:Asia/Aqtobe
+ Asia:Asia/Aqtau
+ Asia:Asia/Atyrau
+ Asia:Asia/Oral
+KE:Kenya
+ Africa:Africa/Nairobi
+KI:Kiribati
+ Pacific:Pacific/Tarawa
+ Pacific:Pacific/Kanton
+ Pacific:Pacific/Kiritimati
+KP:Korea (Democratic People's Republic of)
+ Asia:Asia/Pyongyang
+KR:Korea (Republic of)
+ Asia:Asia/Seoul
+KW:Kuwait
+ Asia:Asia/Riyadh
+KG:Kyrgyzstan
+ Asia:Asia/Bishkek
+LA:Lao People's Democratic Republic
+ Asia:Asia/Bangkok
+LV:Latvia
+ Europe:Europe/Riga
+LB:Lebanon
+ Asia:Asia/Beirut
+LS:Lesotho
+ Africa:Africa/Johannesburg
+LR:Liberia
+ Africa:Africa/Monrovia
+LY:Libya
+ Africa:Africa/Tripoli
+LI:Liechtenstein
+ Europe:Europe/Zurich
+LT:Lithuania
+ Europe:Europe/Vilnius
+LU:Luxembourg
+ Europe:Europe/Brussels
+MO:Macao
+ Asia:Asia/Macau
+MK:Macedonia (the former Yugoslav Republic of)
+ Europe:Europe/Belgrade
+MG:Madagascar
+ Indian:Africa/Nairobi
+MW:Malawi
+ Africa:Africa/Maputo
+MY:Malaysia
+ Asia:Asia/Kuching
+ Asia:Asia/Singapore
+MV:Maldives
+ Indian:Indian/Maldives
+ML:Mali
+ Africa:Africa/Abidjan
+MT:Malta
+ Europe:Europe/Malta
+MH:Marshall Islands
+ Pacific:Pacific/Tarawa
+ Pacific:Pacific/Kwajalein
+MQ:Martinique
+ America:America/Martinique
+MR:Mauritania
+ Africa:Africa/Abidjan
+MU:Mauritius
+ Indian:Indian/Mauritius
+YT:Mayotte
+ Indian:Africa/Nairobi
+MX:Mexico
+ America:America/Mexico_City
+ America:America/Cancun
+ America:America/Merida
+ America:America/Monterrey
+ America:America/Matamoros
+ America:America/Chihuahua
+ America:America/Ciudad_Juarez
+ America:America/Ojinaga
+ America:America/Mazatlan
+ America:America/Bahia_Banderas
+ America:America/Hermosillo
+ America:America/Tijuana
+FM:Micronesia (Federated States of)
+ Pacific:Pacific/Kosrae
+ Pacific:Pacific/Port_Moresby
+ Pacific:Pacific/Guadalcanal
+MD:Moldova (Republic of)
+ Europe:Europe/Chisinau
+MC:Monaco
+ Europe:Europe/Paris
+MN:Mongolia
+ Asia:Asia/Ulaanbaatar
+ Asia:Asia/Hovd
+ Asia:Asia/Choibalsan
+ME:Montenegro
+ Europe:Europe/Belgrade
+MS:Montserrat
+ America:America/Puerto_Rico
+MA:Morocco
+ Africa:Africa/Casablanca
+MZ:Mozambique
+ Africa:Africa/Maputo
+MM:Myanmar
+ Asia:Asia/Yangon
+NA:Namibia
+ Africa:Africa/Windhoek
+NR:Nauru
+ Pacific:Pacific/Nauru
+NP:Nepal
+ Asia:Asia/Kathmandu
+NL:Netherlands
+ Europe:Europe/Brussels
+NC:New Caledonia
+ Pacific:Pacific/Noumea
+NZ:New Zealand
+ Pacific:Pacific/Auckland
+ Pacific:Pacific/Chatham
+NI:Nicaragua
+ America:America/Managua
+NE:Niger
+ Africa:Africa/Lagos
+NG:Nigeria
+ Africa:Africa/Lagos
+NU:Niue
+ Pacific:Pacific/Niue
+NF:Norfolk Island
+ Pacific:Pacific/Norfolk
+MP:Northern Mariana Islands
+ Pacific:Pacific/Guam
+NO:Norway
+ Europe:Europe/Berlin
+OM:Oman
+ Asia:Asia/Dubai
+PK:Pakistan
+ Asia:Asia/Karachi
+PW:Palau
+ Pacific:Pacific/Palau
+PS:Palestine, State of
+ Asia:Asia/Gaza
+ Asia:Asia/Hebron
+PA:Panama
+ America:America/Panama
+PG:Papua New Guinea
+ Pacific:Pacific/Port_Moresby
+ Pacific:Pacific/Bougainville
+PY:Paraguay
+ America:America/Asuncion
+PE:Peru
+ America:America/Lima
+PH:Philippines
+ Asia:Asia/Manila
+PN:Pitcairn
+ Pacific:Pacific/Pitcairn
+PL:Poland
+ Europe:Europe/Warsaw
+PT:Portugal
+ Europe:Europe/Lisbon
+ Atlantic:Atlantic/Madeira
+ Atlantic:Atlantic/Azores
+PR:Puerto Rico
+ America:America/Puerto_Rico
+QA:Qatar
+ Asia:Asia/Qatar
+RO:Romania
+ Europe:Europe/Bucharest
+RU:Russian Federation
+ Europe:Europe/Kaliningrad
+ Europe:Europe/Moscow
+ Europe:Europe/Simferopol
+ Europe:Europe/Kirov
+ Europe:Europe/Volgograd
+ Europe:Europe/Astrakhan
+ Europe:Europe/Saratov
+ Europe:Europe/Ulyanovsk
+ Europe:Europe/Samara
+ Asia:Asia/Yekaterinburg
+ Asia:Asia/Omsk
+ Asia:Asia/Novosibirsk
+ Asia:Asia/Barnaul
+ Asia:Asia/Tomsk
+ Asia:Asia/Novokuznetsk
+ Asia:Asia/Krasnoyarsk
+ Asia:Asia/Irkutsk
+ Asia:Asia/Chita
+ Asia:Asia/Yakutsk
+ Asia:Asia/Khandyga
+ Asia:Asia/Vladivostok
+ Asia:Asia/Ust-Nera
+ Asia:Asia/Magadan
+ Asia:Asia/Sakhalin
+ Asia:Asia/Srednekolymsk
+ Asia:Asia/Kamchatka
+ Asia:Asia/Anadyr
+RW:Rwanda
+ Africa:Africa/Maputo
+RE:Réunion
+ Asia:Asia/Dubai
+BL:Saint Barthélemy
+ America:America/Puerto_Rico
+SH:Saint Helena Ascension and Tristan da Cunha
+ Atlantic:Africa/Abidjan
+KN:Saint Kitts and Nevis
+ America:America/Puerto_Rico
+LC:Saint Lucia
+ America:America/Puerto_Rico
+MF:Saint Martin (French part)
+ America:America/Puerto_Rico
+PM:Saint Pierre and Miquelon
+ America:America/Miquelon
+VC:Saint Vincent and the Grenadines
+ America:America/Puerto_Rico
+WS:Samoa
+ Pacific:Pacific/Apia
+SM:San Marino
+ Europe:Europe/Rome
+ST:Sao Tome and Principe
+ Africa:Africa/Sao_Tome
+SA:Saudi Arabia
+ Asia:Asia/Riyadh
+SN:Senegal
+ Africa:Africa/Abidjan
+RS:Serbia
+ Europe:Europe/Belgrade
+SC:Seychelles
+ Asia:Asia/Dubai
+SL:Sierra Leone
+ Africa:Africa/Abidjan
+SG:Singapore
+ Asia:Asia/Singapore
+SX:Sint Maarten (Dutch part)
+ America:America/Puerto_Rico
+SK:Slovakia
+ Europe:Europe/Prague
+SI:Slovenia
+ Europe:Europe/Belgrade
+SB:Solomon Islands
+ Pacific:Pacific/Guadalcanal
+SO:Somalia
+ Africa:Africa/Nairobi
+ZA:South Africa
+ Africa:Africa/Johannesburg
+GS:South Georgia and the South Sandwich Islands
+ Atlantic:Atlantic/South_Georgia
+SS:South Sudan
+ Africa:Africa/Juba
+ES:Spain
+ Europe:Europe/Madrid
+ Africa:Africa/Ceuta
+ Atlantic:Atlantic/Canary
+LK:Sri Lanka
+ Asia:Asia/Colombo
+SD:Sudan
+ Africa:Africa/Khartoum
+SR:Suriname
+ America:America/Paramaribo
+SJ:Svalbard and Jan Mayen
+ Arctic:Europe/Berlin
+SE:Sweden
+ Europe:Europe/Berlin
+CH:Switzerland
+ Europe:Europe/Zurich
+SY:Syrian Arab Republic
+ Asia:Asia/Damascus
+TW:Taiwan
+ Asia:Asia/Taipei
+TJ:Tajikistan
+ Asia:Asia/Dushanbe
+TZ:Tanzania United Republic of
+ Africa:Africa/Nairobi
+TH:Thailand
+ Asia:Asia/Bangkok
+TL:Timor-Leste
+ Asia:Asia/Dili
+TG:Togo
+ Africa:Africa/Abidjan
+TK:Tokelau
+ Pacific:Pacific/Fakaofo
+TO:Tonga
+ Pacific:Pacific/Tongatapu
+TT:Trinidad and Tobago
+ America:America/Puerto_Rico
+TN:Tunisia
+ Africa:Africa/Tunis
+TR:Turkey
+ Europe:Europe/Istanbul
+TM:Turkmenistan
+ Asia:Asia/Ashgabat
+TC:Turks and Caicos Islands
+ America:America/Grand_Turk
+TV:Tuvalu
+ Pacific:Pacific/Tarawa
+UG:Uganda
+ Africa:Africa/Nairobi
+UA:Ukraine
+ Europe:Europe/Simferopol
+ Europe:Europe/Kyiv
+AE:United Arab Emirates
+ Asia:Asia/Dubai
+GB:United Kingdom of Great Britain and Northern Ireland
+ Europe:Europe/London
+UM:United States Minor Outlying Islands
+ Pacific:Pacific/Pago_Pago
+ Pacific:Pacific/Tarawa
+US:United States of America
+ America:America/New_York
+ America:America/Detroit
+ America:America/Kentucky/Louisville
+ America:America/Kentucky/Monticello
+ America:America/Indiana/Indianapolis
+ America:America/Indiana/Vincennes
+ America:America/Indiana/Winamac
+ America:America/Indiana/Marengo
+ America:America/Indiana/Petersburg
+ America:America/Indiana/Vevay
+ America:America/Chicago
+ America:America/Indiana/Tell_City
+ America:America/Indiana/Knox
+ America:America/Menominee
+ America:America/North_Dakota/Center
+ America:America/North_Dakota/New_Salem
+ America:America/North_Dakota/Beulah
+ America:America/Denver
+ America:America/Boise
+ America:America/Phoenix
+ America:America/Los_Angeles
+ America:America/Anchorage
+ America:America/Juneau
+ America:America/Sitka
+ America:America/Metlakatla
+ America:America/Yakutat
+ America:America/Nome
+ America:America/Adak
+ Pacific:Pacific/Honolulu
+UY:Uruguay
+ America:America/Montevideo
+UZ:Uzbekistan
+ Asia:Asia/Samarkand
+ Asia:Asia/Tashkent
+VU:Vanuatu
+ Pacific:Pacific/Efate
+VE:Venezuela, Bolivarian Republic of
+ America:America/Caracas
+VN:Viet Nam
+ Asia:Asia/Bangkok
+ Asia:Asia/Ho_Chi_Minh
+VG:Virgin Islands (British)
+ America:America/Puerto_Rico
+VI:Virgin Islands (U.S.)
+ America:America/Puerto_Rico
+WF:Wallis and Futuna
+ Pacific:Pacific/Tarawa
+EH:Western Sahara
+ Africa:Africa/El_Aaiun
+YE:Yemen
+ Asia:Asia/Riyadh
+ZM:Zambia
+ Africa:Africa/Maputo
+ZW:Zimbabwe
+ Africa:Africa/Maputo
+AX:Åland Islands
+ Europe:Europe/Helsinki
diff --git a/usr.sbin/tzsetup/tzsetup.8 b/usr.sbin/tzsetup/tzsetup.8
new file mode 100644
index 000000000000..3fd463c31ee5
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -0,0 +1,163 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd June 14, 2024
+.Dt TZSETUP 8
+.Os
+.Sh NAME
+.Nm tzsetup
+.Nd set local timezone
+.Sh SYNOPSIS
+.Nm
+.Op Fl nrs
+.Op Fl C Ar chroot_directory
+.Op Ar zoneinfo_file | zoneinfo_name
+.Sh DESCRIPTION
+The
+.Nm
+utility reads a database of timezone information and presents a menu
+allowing the user to select a specific zone without knowing the details
+of the database layout.
+The selected zone is installed as the system
+default zone.
+The
+.Nm
+utility also determines whether any adjustment is necessary for systems where
+the hardware clock does not keep UTC.
+.Pp
+The following options are available:
+.Bl -tag -offset indent -width Fl
+.It Fl C Ar chroot_directory
+Open all files and directories relative to
+.Ar chroot_directory .
+.It Fl n
+Do not create or symlink files.
+.It Fl r
+Reinstall the zoneinfo file installed last time.
+The name is obtained from
+.Pa /var/db/zoneinfo .
+.It Fl s
+Skip the initial question about adjusting the clock if not set to UTC.
+.Nm
+will neither create nor delete
+.Pa /etc/wall_cmos_clock .
+On a newly installed system, the hardware clock will keep UTC.
+.El
+.Pp
+It is possible to short-circuit the menu system by specifying the
+location of a
+.Ar zoneinfo_file
+or the name of the
+.Ar zoneinfo_name
+on the command line; this is intended mainly for pre-configured installation
+scripts or people who know which zoneinfo they want to install.
+.Sh TIMEZONE DATABASE
+The contents of the timezone database are indexed by
+.Pa /usr/share/zoneinfo/zone1970.tab .
+This file lists, for each timezone data file, the ISO 3166 territory code,
+approximate geographical coordinates (in ISO 6709 format),
+and location within the territory.
+.Pp
+The maintainers of the database maintain the following policies:
+.Bl -enum -offset indent
+.It
+At least one zone for every country or inhabited geographical territory.
+.It
+One zone for every distinct, documented timezone history since the
+beginning of the
+.Ux
+epoch (January 1, 1970, GMT).
+.It
+Each zone is named for the most populous city therein.
+(Where possible,
+the database includes pre-1970 history for its city.)
+.El
+.Pp
+The source code to the database
+.Pq Pa /usr/src/contrib/tzdata/[a-z]*
+contains many additional comments and documentation references for the
+historically minded.
+.Sh FILES
+.Bl -tag -width ".Pa /usr/share/zoneinfo/zone1970.tab" -compact
+.It Pa /etc/localtime
+current time zone file
+.It Pa /etc/wall_cmos_clock
+see
+.Xr adjkerntz 8
+.It Pa /usr/share/misc/iso3166
+mapping of ISO 3166 territory codes to names
+.It Pa /usr/share/zoneinfo
+directory for zoneinfo files
+.It Pa /usr/share/zoneinfo/zone1970.tab
+mapping of timezone file to country and location
+.It Pa /var/db/zoneinfo
+saved name of the timezone file installed last
+.El
+.Sh EXAMPLES
+Normal usage, to select the right zoneinfo file via the dialog-based
+user interface:
+.Pp
+.Dl # tzsetup
+.Pp
+Install the file
+.Pa /usr/share/zoneinfo/Australia/Sydney :
+.Pp
+.Dl "# tzsetup /usr/share/zoneinfo/Australia/Sydney"
+.Pp
+Install the zoneinfo file for Australia/Sydney, assumed to be located in
+.Pa /usr/share/zoneinfo :
+.Pp
+.Dl "# tzsetup Australia/Sydney"
+.Pp
+After a reinstall of the zoneinfo files, you can reinstall the
+latest installed zoneinfo file (as specified in
+.Pa /var/db/zoneinfo ) :
+.Pp
+.Dl "# tzsetup -r"
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr ctime 3 ,
+.Xr timezone 3 ,
+.Xr tzfile 5 ,
+.Xr adjkerntz 8 ,
+.Xr zdump 8 ,
+.Xr zic 8
+.Sh DISCLAIMER
+The representation of certain localities as being associated with certain
+countries and/or territories is for the purposes of identification only,
+and does not imply any endorsement or rejection on the part of the
+.Fx
+Project of the territorial claims of any entity.
+.Sh BUGS
+Programs which are already running when
+.Nm
+creates or updates
+.Pa /etc/localtime
+will not reflect the updated timezone.
+When the system is first configured for a non-UTC
+hardware clock, it is necessary to run
+.Xr adjkerntz 8
+(which normally happens as a part of system startup) in order to update
+the kernel's idea of the correct timezone offset.
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
new file mode 100644
index 000000000000..914eeb145a09
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -0,0 +1,996 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Second attempt at a `tzmenu' program, using the separate description
+ * files provided in newer tzdata releases.
+ */
+
+/*
+ * When making changes to parser code, run baseline target, check that there are
+ * no unintended changes and commit updated file.
+ */
+
+#include <sys/cdefs.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#ifdef HAVE_BSDDIALOG
+#include <bsddialog.h>
+#include <locale.h>
+#endif
+
+#define _PATH_ZONETAB "/usr/share/zoneinfo/zone1970.tab"
+#define _PATH_ISO3166 "/usr/share/misc/iso3166"
+#define _PATH_ZONEINFO "/usr/share/zoneinfo"
+#define _PATH_LOCALTIME "/etc/localtime"
+#define _PATH_DB "/var/db/zoneinfo"
+#define _PATH_WALL_CMOS_CLOCK "/etc/wall_cmos_clock"
+
+#ifdef PATH_MAX
+#define SILLY_BUFFER_SIZE (2 * PATH_MAX)
+#else
+#warning "Somebody needs to fix this to dynamically size this buffer."
+#define SILLY_BUFFER_SIZE 2048
+#endif
+
+/* special return codes for `fire' actions */
+#define DITEM_FAILURE 1
+
+/* flags - returned in upper 16 bits of return status */
+#define DITEM_LEAVE_MENU (1 << 16)
+#define DITEM_RECREATE (1 << 18)
+
+static char path_zonetab[MAXPATHLEN], path_iso3166[MAXPATHLEN],
+ path_zoneinfo[MAXPATHLEN], path_localtime[MAXPATHLEN],
+ path_db[MAXPATHLEN], path_wall_cmos_clock[MAXPATHLEN];
+
+static int reallydoit = 1;
+static int reinstall = 0;
+static char *chrootenv = NULL;
+
+static void usage(void);
+static int install_zoneinfo(const char *zoneinfo);
+static void message_zoneinfo_file(const char *title, char *prompt);
+static int install_zoneinfo_file(const char *zoneinfo_file);
+
+#ifdef HAVE_BSDDIALOG
+static struct bsddialog_conf conf;
+
+/* for use in describing more exotic behaviors */
+typedef struct dialogMenuItem {
+ char *prompt;
+ char *title;
+ int (*fire)(struct dialogMenuItem *self);
+ void *data;
+} dialogMenuItem;
+
+static int
+xdialog_menu(char *title, char *cprompt, int item_no, dialogMenuItem *ditems)
+{
+ int i, result, menurows, choice = 0;
+ struct bsddialog_menuitem *listitems;
+
+ /* initialize list items */
+ listitems = calloc(item_no + 1, sizeof(struct bsddialog_menuitem));
+ if (listitems == NULL)
+ errx(1, "Failed to allocate memory in xdialog_menu");
+ for (i = 0; i < item_no; i++) {
+ listitems[i].prefix = "";
+ listitems[i].depth = 0;
+ listitems[i].bottomdesc = "";
+ listitems[i].on = false;
+ listitems[i].name = ditems[i].prompt;
+ listitems[i].desc = ditems[i].title;
+ }
+
+again:
+ conf.title = title;
+ menurows = item_no < 16 ? item_no : 16;
+ result = bsddialog_menu(&conf, cprompt, BSDDIALOG_AUTOSIZE,
+ BSDDIALOG_AUTOSIZE, menurows, item_no, listitems, &choice);
+ switch (result) {
+ case BSDDIALOG_ESC:
+ result = -1;
+ break;
+ case BSDDIALOG_OK:
+ if (ditems[choice].fire != NULL) {
+ int status;
+
+ status = ditems[choice].fire(ditems + choice);
+ if (status & DITEM_RECREATE) {
+ goto again;
+ }
+ }
+ result = 0;
+ break;
+ case BSDDIALOG_CANCEL:
+ default:
+ result = 1;
+ break;
+ }
+
+ free(listitems);
+ return (result);
+}
+
+static int usedialog = 1;
+
+static int confirm_zone(const char *filename);
+static int continent_country_menu(dialogMenuItem *);
+static int set_zone(dialogMenuItem *);
+static int set_zone_menu(dialogMenuItem *);
+static int set_zone_utc(void);
+
+struct continent {
+ dialogMenuItem *menu;
+ int nitems;
+};
+
+static struct continent africa, america, antarctica, arctic, asia, atlantic;
+static struct continent australia, europe, indian, pacific, utc;
+
+static struct continent_names {
+ const char *name;
+ struct continent *continent;
+} continent_names[] = {
+ { "UTC", &utc },
+ { "Africa", &africa },
+ { "America", &america },
+ { "Antarctica", &antarctica },
+ { "Arctic", &arctic },
+ { "Asia", &asia },
+ { "Atlantic", &atlantic },
+ { "Australia", &australia },
+ { "Europe", &europe },
+ { "Indian", &indian },
+ { "Pacific", &pacific },
+};
+
+static struct continent_items {
+ char prompt[3];
+ char title[30];
+} continent_items[] = {
+ { "0", "UTC" },
+ { "1", "Africa" },
+ { "2", "America -- North and South" },
+ { "3", "Antarctica" },
+ { "4", "Arctic Ocean" },
+ { "5", "Asia" },
+ { "6", "Atlantic Ocean" },
+ { "7", "Australia" },
+ { "8", "Europe" },
+ { "9", "Indian Ocean" },
+ { "10", "Pacific Ocean" },
+};
+
+#define NCONTINENTS \
+ (int)((sizeof(continent_items)) / (sizeof(continent_items[0])))
+static dialogMenuItem continents[NCONTINENTS];
+
+#define OCEANP(x) ((x) == 4 || (x) == 6 || (x) == 9 || (x) == 10)
+
+static int
+continent_country_menu(dialogMenuItem *continent)
+{
+ char title[64], prompt[64];
+ struct continent *contp = continent->data;
+ int isocean = OCEANP(continent - continents);
+ int rv;
+
+ if (strcmp(continent->title, "UTC") == 0)
+ return (set_zone_utc());
+
+ /* It's amazing how much good grammar really matters... */
+ if (!isocean) {
+ snprintf(title, sizeof(title), "Countries in %s",
+ continent->title);
+ snprintf(prompt, sizeof(prompt), "Select a country or region");
+ } else {
+ snprintf(title, sizeof(title), "Islands and groups in the %s",
+ continent->title);
+ snprintf(prompt, sizeof(prompt), "Select an island or group");
+ }
+
+ rv = xdialog_menu(title, prompt, contp->nitems, contp->menu);
+ return (rv == 0 ? DITEM_LEAVE_MENU : DITEM_RECREATE);
+}
+
+static struct continent *
+find_continent(int lineno, const char *name)
+{
+ char *cname, *cp;
+ int i;
+
+ /*
+ * Both normal (the ones in zone filename, e.g. Europe/Andorra) and
+ * override (e.g. Atlantic/) entries should contain '/'.
+ */
+ cp = strdup(name);
+ if (cp == NULL)
+ err(1, "strdup");
+ cname = strsep(&cp, "/");
+ if (cp == NULL)
+ errx(1, "%s:%d: invalid entry `%s'", path_zonetab, lineno,
+ cname);
+
+ for (i = 0; i < NCONTINENTS; i++)
+ if (strcmp(cname, continent_names[i].name) == 0) {
+ free(cname);
+ return (continent_names[i].continent);
+ }
+
+ errx(1, "%s:%d: continent `%s' unknown", path_zonetab, lineno, cname);
+}
+
+static const char *
+find_continent_name(struct continent *cont)
+{
+ int i;
+
+ for (i = 0; i < NCONTINENTS; i++)
+ if (cont == continent_names[i].continent)
+ return (continent_names[i].name);
+ return ("Unknown");
+}
+
+struct country {
+ char *name;
+ char *tlc;
+ int nzones;
+ struct continent *override; /* continent override */
+ struct continent *alternate; /* extra continent */
+ TAILQ_HEAD(, zone) zones;
+ dialogMenuItem *submenu;
+};
+
+struct zone {
+ TAILQ_ENTRY(zone) link;
+ char *descr;
+ char *filename;
+ struct continent *continent;
+};
+
+/*
+ * This is the easiest organization... we use ISO 3166 country codes,
+ * of the two-letter variety, so we just size this array to suit.
+ * Beats worrying about dynamic allocation.
+ */
+#define NCOUNTRIES (26 * 26)
+static struct country countries[NCOUNTRIES];
+
+#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
+
+/*
+ * Read the ISO 3166 country code database in _PATH_ISO3166
+ * (/usr/share/misc/iso3166). On error, exit via err(3).
+ */
+static void
+read_iso3166_table(void)
+{
+ FILE *fp;
+ struct country *cp;
+ size_t len;
+ char *s, *t, *name;
+ int lineno;
+
+ fp = fopen(path_iso3166, "r");
+ if (!fp)
+ err(1, "%s", path_iso3166);
+ lineno = 0;
+
+ while ((s = fgetln(fp, &len)) != NULL) {
+ lineno++;
+ if (s[len - 1] != '\n')
+ errx(1, "%s:%d: invalid format", path_iso3166, lineno);
+ s[len - 1] = '\0';
+ if (s[0] == '#' || strspn(s, " \t") == len - 1)
+ continue;
+
+ /* Isolate the two-letter code. */
+ t = strsep(&s, "\t");
+ if (t == NULL || strlen(t) != 2)
+ errx(1, "%s:%d: invalid format", path_iso3166, lineno);
+ if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
+ errx(1, "%s:%d: invalid code `%s'", path_iso3166,
+ lineno, t);
+
+ /* Now skip past the three-letter and numeric codes. */
+ name = strsep(&s, "\t"); /* 3-let */
+ if (name == NULL || strlen(name) != 3)
+ errx(1, "%s:%d: invalid format", path_iso3166, lineno);
+ name = strsep(&s, "\t"); /* numeric */
+ if (name == NULL || strlen(name) != 3)
+ errx(1, "%s:%d: invalid format", path_iso3166, lineno);
+
+ name = s;
+
+ cp = &countries[CODE2INT(t)];
+ if (cp->name)
+ errx(1, "%s:%d: country code `%s' multiply defined: %s",
+ path_iso3166, lineno, t, cp->name);
+ cp->name = strdup(name);
+ if (cp->name == NULL)
+ errx(1, "malloc failed");
+ cp->tlc = strdup(t);
+ if (cp->tlc == NULL)
+ errx(1, "malloc failed");
+ }
+
+ fclose(fp);
+}
+
+static struct country *
+find_country(int lineno, const char *tlc)
+{
+ struct country *cp;
+
+ if (strlen(tlc) != 2 ||
+ tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
+ errx(1, "%s:%d: country code `%s' invalid", path_zonetab,
+ lineno, tlc);
+
+ cp = &countries[CODE2INT(tlc)];
+ if (cp->name == NULL)
+ errx(1, "%s:%d: country code `%s' unknown", path_zonetab,
+ lineno, tlc);
+
+ return (cp);
+}
+
+static void
+add_cont_to_country(struct country *cp, struct continent *cont)
+{
+ struct zone *zp;
+
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ if (zp->continent == cont)
+ return;
+ }
+ cp->alternate = cont;
+}
+
+static void
+add_zone_to_country(int lineno, struct country *cp, const char *descr,
+ const char *file, struct continent *cont)
+{
+ struct zone *zp;
+
+ zp = malloc(sizeof(*zp));
+ if (zp == NULL)
+ errx(1, "malloc(%zu)", sizeof(*zp));
+
+ if (cp->nzones == 0)
+ TAILQ_INIT(&cp->zones);
+
+ if (descr != NULL) {
+ zp->descr = strdup(descr);
+ if (zp->descr == NULL)
+ errx(1, "malloc failed");
+ } else {
+ zp->descr = NULL;
+ }
+ zp->filename = strdup(file);
+ if (zp->filename == NULL)
+ errx(1, "malloc failed");
+ zp->continent = cp->override != NULL ? cp->override : cont;
+ TAILQ_INSERT_TAIL(&cp->zones, zp, link);
+ cp->nzones++;
+}
+
+/*
+ * This comparison function intentionally sorts all of the null-named
+ * ``countries''---i.e., the codes that don't correspond to a real
+ * country---to the end. Everything else is lexical by country name.
+ */
+static int
+compare_countries(const void *xa, const void *xb)
+{
+ const struct country *a = xa, *b = xb;
+
+ if (a->name == 0 && b->name == 0)
+ return (0);
+ if (a->name == 0 && b->name != 0)
+ return (1);
+ if (b->name == 0)
+ return (-1);
+
+ return (strcmp(a->name, b->name));
+}
+
+/*
+ * This must be done AFTER all zone descriptions are read, since it breaks
+ * CODE2INT().
+ */
+static void
+sort_countries(void)
+{
+
+ qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries);
+}
+
+static void
+read_zones(void)
+{
+ FILE *fp;
+ struct continent *cont;
+ struct country *cp;
+ size_t len;
+ char *line, *country_list, *tlc, *file, *descr;
+ char *p, *q;
+ int lineno;
+ int pass = 1;
+
+ fp = fopen(path_zonetab, "r");
+ if (!fp)
+ err(1, "%s", path_zonetab);
+
+again:
+ lineno = 0;
+ while ((line = fgetln(fp, &len)) != NULL) {
+ lineno++;
+ if (line[len - 1] != '\n')
+ errx(1, "%s:%d: invalid format", path_zonetab, lineno);
+ line[len - 1] = '\0';
+
+ switch (pass)
+ {
+ case 1:
+ /*
+ * First pass: collect overrides, only looking for
+ * single continent ones for the moment.
+ *
+ * zone1970.tab introduced continent overrides in the
+ * following format:
+ *
+ * #@TLC[,TLC...]<tab>CONTINENT/[,CONTINENT/...]
+ */
+ if (strncmp(line, "#@", strlen("#@")) != 0)
+ continue;
+ line += 2;
+ country_list = strsep(&line, "\t");
+ /* Skip multi-continent overrides */
+ if (strchr(line, ',') != NULL)
+ continue;
+ cont = find_continent(lineno, line);
+ /* Parse and store overrides */
+ while (country_list != NULL) {
+ tlc = strsep(&country_list, ",");
+ cp = find_country(lineno, tlc);
+ cp->override = cont;
+ }
+ break;
+ case 2:
+ /* Second pass: parse actual data */
+ if (line[0] == '#')
+ continue;
+
+ country_list = strsep(&line, "\t");
+ /* coord = */ strsep(&line, "\t"); /* Unused */
+ file = strsep(&line, "\t");
+ cont = find_continent(lineno, file);
+ descr = (line != NULL && *line != '\0') ? line : NULL;
+
+ while (country_list != NULL) {
+ tlc = strsep(&country_list, ",");
+ cp = find_country(lineno, tlc);
+ add_zone_to_country(lineno, cp, descr, file,
+ cont);
+ }
+ break;
+ case 3:
+ /* Third pass: collect multi-continent overrides */
+ if (strncmp(line, "#@", strlen("#@")) != 0)
+ continue;
+ line += 2;
+ country_list = strsep(&line, "\t");
+ /* Skip single-continent overrides */
+ if (strchr(line, ',') == NULL)
+ continue;
+ while (line != NULL) {
+ cont = find_continent(lineno, line);
+ p = q = strdup(country_list);
+ if (p == NULL)
+ errx(1, "malloc failed");
+ while (q != NULL) {
+ tlc = strsep(&q, ",");
+ cp = find_country(lineno, tlc);
+ add_cont_to_country(cp, cont);
+ }
+ free(p);
+ strsep(&line, ",");
+ }
+ break;
+ }
+ }
+
+ if (pass++ < 3) {
+ errno = 0;
+ rewind(fp);
+ if (errno != 0)
+ err(1, "failed to rewind %s", path_zonetab);
+ goto again;
+ }
+ fclose(fp);
+}
+
+static void
+dump_zonetab(void)
+{
+ struct country *cp;
+ struct zone *zp;
+ const char *cont;
+
+ for (cp = countries; cp->name != NULL; cp++) {
+ printf("%s:%s\n", cp->tlc, cp->name);
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = find_continent_name(zp->continent);
+ printf(" %s:%s\n", cont, zp->filename);
+ }
+ }
+}
+
+static void
+make_menus(void)
+{
+ struct country *cp;
+ struct zone *zp, *zp2;
+ struct continent *cont;
+ dialogMenuItem *dmi;
+ int i;
+
+ /*
+ * First, count up all the countries in each continent/ocean.
+ * Be careful to count those countries which have multiple zones
+ * only once for each. NB: some countries are in multiple
+ * continents/oceans.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = zp->continent;
+ for (zp2 = TAILQ_FIRST(&cp->zones);
+ zp2->continent != cont;
+ zp2 = TAILQ_NEXT(zp2, link))
+ ;
+ if (zp2 == zp)
+ zp->continent->nitems++;
+ }
+
+ for (i = 0; i < NCONTINENTS; i++) {
+ if (cp->alternate == continent_names[i].continent) {
+ continent_names[i].continent->nitems++;
+ }
+ }
+ }
+
+ /*
+ * Now allocate memory for the country menus and initialize
+ * continent menus. We set nitems back to zero so that we can
+ * use it for counting again when we actually build the menus.
+ */
+ memset(continents, 0, sizeof(continents));
+ for (i = 0; i < NCONTINENTS; i++) {
+ continent_names[i].continent->menu =
+ malloc(sizeof(dialogMenuItem) *
+ continent_names[i].continent->nitems);
+ if (continent_names[i].continent->menu == NULL)
+ errx(1, "malloc for continent menu");
+ continent_names[i].continent->nitems = 0;
+ continents[i].prompt = continent_items[i].prompt;
+ continents[i].title = continent_items[i].title;
+ continents[i].fire = continent_country_menu;
+ continents[i].data = continent_names[i].continent;
+ }
+
+ /*
+ * Now that memory is allocated, create the menu items for
+ * each continent. For multiple-zone countries, also create
+ * the country's zone submenu.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ cp->submenu = malloc(cp->nzones * sizeof(*dmi));
+ if (cp->submenu == 0)
+ errx(1, "malloc for submenu");
+ cp->nzones = 0;
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = zp->continent;
+ dmi = &cp->submenu[cp->nzones];
+ memset(dmi, 0, sizeof(*dmi));
+ asprintf(&dmi->prompt, "%d", ++cp->nzones);
+ dmi->title = zp->descr;
+ dmi->fire = set_zone;
+ dmi->data = zp;
+
+ for (zp2 = TAILQ_FIRST(&cp->zones);
+ zp2->continent != cont;
+ zp2 = TAILQ_NEXT(zp2, link))
+ ;
+ if (zp2 != zp)
+ continue;
+
+ dmi = &cont->menu[cont->nitems];
+ memset(dmi, 0, sizeof(*dmi));
+ asprintf(&dmi->prompt, "%d", ++cont->nitems);
+ dmi->title = cp->name;
+ dmi->fire = set_zone_menu;
+ dmi->data = cp;
+ }
+
+ if (cp->alternate != NULL) {
+ cont = cp->alternate;
+ dmi = &cont->menu[cont->nitems];
+ memset(dmi, 0, sizeof(*dmi));
+ asprintf(&dmi->prompt, "%d", ++cont->nitems);
+ dmi->title = cp->name;
+ dmi->fire = set_zone_menu;
+ dmi->data = cp;
+ }
+ }
+}
+
+static int
+set_zone_menu(dialogMenuItem *dmi)
+{
+ char title[64], prompt[64];
+ struct country *cp = dmi->data;
+ int rv;
+
+ /* Short cut -- if there's only one zone, don't post a menu. */
+ if (cp->nzones == 1)
+ return (cp->submenu[0].fire(&cp->submenu[0]));
+
+ snprintf(title, sizeof(title), "%s Time Zones", cp->name);
+ snprintf(prompt, sizeof(prompt),
+ "Select a zone which observes the same time as your locality.");
+ rv = xdialog_menu(title, prompt, cp->nzones, cp->submenu);
+ return (rv != 0 ? DITEM_RECREATE : DITEM_LEAVE_MENU);
+}
+
+static int
+set_zone_utc(void)
+{
+ if (!confirm_zone("UTC"))
+ return (DITEM_FAILURE | DITEM_RECREATE);
+
+ return (install_zoneinfo("UTC"));
+}
+
+static int
+confirm_zone(const char *filename)
+{
+ char prompt[64];
+ time_t t = time(0);
+ struct tm *tm;
+ int rv;
+
+ setenv("TZ", filename, 1);
+ tzset();
+ tm = localtime(&t);
+
+ snprintf(prompt, sizeof(prompt),
+ "Does the timezone abbreviation `%s' look reasonable?", tm->tm_zone);
+ conf.title = "Confirmation";
+ rv = (bsddialog_yesno(&conf, prompt, 5, 72) == BSDDIALOG_YES);
+ return (rv);
+}
+
+static int
+set_zone(dialogMenuItem *dmi)
+{
+ struct zone *zp = dmi->data;
+ int rv;
+
+ if (!confirm_zone(zp->filename))
+ return (DITEM_FAILURE | DITEM_RECREATE);
+
+ rv = install_zoneinfo(zp->filename);
+ return (rv);
+}
+
+#endif
+
+static void message_zoneinfo_file(const char *title, char *prompt)
+{
+#ifdef HAVE_BSDDIALOG
+ if (usedialog) {
+ conf.title = title;
+ bsddialog_msgbox(&conf, prompt, 8, 72);
+ } else
+#endif
+ fprintf(stderr, "%s: %s\n", title, prompt);
+}
+
+static int
+install_zoneinfo_file(const char *zoneinfo_file)
+{
+ char prompt[SILLY_BUFFER_SIZE];
+
+#ifdef VERBOSE
+ snprintf(prompt, sizeof(prompt), "Creating symbolic link %s to %s",
+ path_localtime, zoneinfo_file);
+ message_zoneinfo_file("Info", prompt);
+#endif
+
+ if (reallydoit) {
+ if (access(zoneinfo_file, R_OK) != 0) {
+ snprintf(prompt, sizeof(prompt),
+ "Cannot access %s: %s", zoneinfo_file,
+ strerror(errno));
+ message_zoneinfo_file("Error", prompt);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+ if (unlink(path_localtime) < 0 && errno != ENOENT) {
+ snprintf(prompt, sizeof(prompt),
+ "Could not delete %s: %s",
+ path_localtime, strerror(errno));
+ message_zoneinfo_file("Error", prompt);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+ if (symlink(zoneinfo_file, path_localtime) < 0) {
+ snprintf(prompt, sizeof(prompt),
+ "Cannot create symbolic link %s to %s: %s",
+ path_localtime, zoneinfo_file,
+ strerror(errno));
+ message_zoneinfo_file("Error", prompt);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+
+#ifdef VERBOSE
+ snprintf(prompt, sizeof(prompt),
+ "Created symbolic link from %s to %s", zoneinfo_file,
+ path_localtime);
+ message_zoneinfo_file("Done", prompt);
+#endif
+ } /* reallydoit */
+
+ return (DITEM_LEAVE_MENU);
+}
+
+static int
+install_zoneinfo(const char *zoneinfo)
+{
+ int rv;
+ FILE *f;
+ char path_zoneinfo_file[MAXPATHLEN];
+
+ if ((size_t)snprintf(path_zoneinfo_file, sizeof(path_zoneinfo_file),
+ "%s/%s", path_zoneinfo, zoneinfo) >= sizeof(path_zoneinfo_file))
+ errx(1, "%s/%s name too long", path_zoneinfo, zoneinfo);
+ rv = install_zoneinfo_file(path_zoneinfo_file);
+
+ /* Save knowledge for later */
+ if (reallydoit && (rv & DITEM_FAILURE) == 0) {
+ if ((f = fopen(path_db, "w")) != NULL) {
+ fprintf(f, "%s\n", zoneinfo);
+ fclose(f);
+ }
+ }
+
+ return (rv);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: tzsetup [-nrs] [-C chroot_directory]"
+ " [zoneinfo_file | zoneinfo_name]\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+#ifdef HAVE_BSDDIALOG
+ char prompt[128];
+ int fd;
+#endif
+ int c, rv, skiputc;
+ char vm_guest[16] = "";
+ size_t len = sizeof(vm_guest);
+ char *dztpath;
+
+ dztpath = NULL;
+ skiputc = 0;
+
+#ifdef HAVE_BSDDIALOG
+ setlocale(LC_ALL, "");
+#endif
+
+ /* Default skiputc to 1 for VM guests */
+ if (sysctlbyname("kern.vm_guest", vm_guest, &len, NULL, 0) == 0 &&
+ strcmp(vm_guest, "none") != 0)
+ skiputc = 1;
+
+ while ((c = getopt(argc, argv, "C:d:nrs")) != -1) {
+ switch (c) {
+ case 'C':
+ chrootenv = optarg;
+ break;
+ case 'd':
+ dztpath = optarg;
+ break;
+ case 'n':
+ reallydoit = 0;
+ break;
+ case 'r':
+ reinstall = 1;
+#ifdef HAVE_BSDDIALOG
+ usedialog = 0;
+#endif
+ break;
+ case 's':
+ skiputc = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind > 1)
+ usage();
+
+ if (chrootenv == NULL) {
+ if (dztpath == NULL)
+ strcpy(path_zonetab, _PATH_ZONETAB);
+ else
+ strlcpy(path_zonetab, dztpath, sizeof(path_zonetab));
+ strcpy(path_iso3166, _PATH_ISO3166);
+ strcpy(path_localtime, _PATH_LOCALTIME);
+ strcpy(path_db, _PATH_DB);
+ strcpy(path_wall_cmos_clock, _PATH_WALL_CMOS_CLOCK);
+ } else {
+ sprintf(path_zonetab, "%s/%s", chrootenv, _PATH_ZONETAB);
+ sprintf(path_iso3166, "%s/%s", chrootenv, _PATH_ISO3166);
+ sprintf(path_localtime, "%s/%s", chrootenv, _PATH_LOCALTIME);
+ sprintf(path_db, "%s/%s", chrootenv, _PATH_DB);
+ sprintf(path_wall_cmos_clock, "%s/%s", chrootenv,
+ _PATH_WALL_CMOS_CLOCK);
+ }
+ /* Symlink target is the same regardless of chroot */
+ strcpy(path_zoneinfo, _PATH_ZONEINFO);
+
+ /* Override the user-supplied umask. */
+ (void)umask(S_IWGRP | S_IWOTH);
+
+ if (reinstall == 1) {
+ FILE *f;
+ char zoneinfo[MAXPATHLEN];
+
+ if ((f = fopen(path_db, "r")) != NULL) {
+ if (fgets(zoneinfo, sizeof(zoneinfo), f) != NULL) {
+ zoneinfo[sizeof(zoneinfo) - 1] = 0;
+ if (strlen(zoneinfo) > 0) {
+ zoneinfo[strlen(zoneinfo) - 1] = 0;
+ rv = install_zoneinfo(zoneinfo);
+ exit(rv & ~DITEM_LEAVE_MENU);
+ }
+ errx(1, "Error reading %s.\n", path_db);
+ }
+ fclose(f);
+ errx(1,
+ "Unable to determine earlier installed zoneinfo "
+ "name. Check %s", path_db);
+ }
+ errx(1, "Cannot open %s for reading. Does it exist?", path_db);
+ }
+
+ /*
+ * If the arguments on the command-line do not specify a file,
+ * then interpret it as a zoneinfo name
+ */
+ if (optind == argc - 1) {
+ struct stat sb;
+
+ if (stat(argv[optind], &sb) != 0) {
+#ifdef HAVE_BSDDIALOG
+ usedialog = 0;
+#endif
+ rv = install_zoneinfo(argv[optind]);
+ exit(rv & ~DITEM_LEAVE_MENU);
+ }
+ /* FALLTHROUGH */
+ }
+#ifdef HAVE_BSDDIALOG
+
+ read_iso3166_table();
+ read_zones();
+ sort_countries();
+ if (dztpath != NULL) {
+ dump_zonetab();
+ return (0);
+ }
+ make_menus();
+
+ bsddialog_initconf(&conf);
+ conf.clear = true;
+ conf.auto_minwidth = 24;
+ conf.key.enable_esc = true;
+
+ if (bsddialog_init() == BSDDIALOG_ERROR)
+ errx(1, "Error bsddialog: %s\n", bsddialog_geterror());
+
+ if (skiputc == 0) {
+ snprintf(prompt, sizeof(prompt),
+ "Is this machine's CMOS clock set to UTC? "
+ "If it is set to local time,\n"
+ "or you don't know, please choose NO here!");
+
+ conf.title = "Select local or UTC (Coordinated Universal Time) clock";
+ if (bsddialog_yesno(&conf, prompt, 7, 73) == BSDDIALOG_YES) {
+ if (reallydoit)
+ unlink(path_wall_cmos_clock);
+ } else {
+ if (reallydoit) {
+ fd = open(path_wall_cmos_clock,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ bsddialog_end();
+ err(1, "create %s",
+ path_wall_cmos_clock);
+ }
+ close(fd);
+ }
+ }
+ }
+ if (optind == argc - 1) {
+ snprintf(prompt, sizeof(prompt),
+ "\nUse the default `%s' zone?", argv[optind]);
+ conf.title = "Default timezone provided";
+ if (bsddialog_yesno(&conf, prompt, 7, 72) == BSDDIALOG_YES) {
+ rv = install_zoneinfo_file(argv[optind]);
+ bsddialog_end();
+ exit(rv & ~DITEM_LEAVE_MENU);
+ }
+ }
+ xdialog_menu("Time Zone Selector", "Select a region", NCONTINENTS,
+ continents);
+
+ bsddialog_end();
+#else
+ usage();
+#endif
+ return (0);
+}