tzsetup.c revision 198254
119872Swollman/*
219872Swollman * Copyright 1996 Massachusetts Institute of Technology
319872Swollman *
419872Swollman * Permission to use, copy, modify, and distribute this software and
519872Swollman * its documentation for any purpose and without fee is hereby
619872Swollman * granted, provided that both the above copyright notice and this
719872Swollman * permission notice appear in all copies, that both the above
819872Swollman * copyright notice and this permission notice appear in all
919872Swollman * supporting documentation, and that the name of M.I.T. not be used
1019872Swollman * in advertising or publicity pertaining to distribution of the
1119872Swollman * software without specific, written prior permission.  M.I.T. makes
1219872Swollman * no representations about the suitability of this software for any
1319872Swollman * purpose.  It is provided "as is" without express or implied
1419872Swollman * warranty.
15179530Sjkim *
1619872Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1719872Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1819872Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1919872Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2019872Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2119872Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2219872Swollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2319872Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2419872Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2519872Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2619872Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2719872Swollman * SUCH DAMAGE.
2819872Swollman */
2919872Swollman
3019872Swollman/*
3119872Swollman * Second attempt at a `tzmenu' program, using the separate description
3219872Swollman * files provided in newer tzdata releases.
3319872Swollman */
3419872Swollman
35179530Sjkim#include <sys/cdefs.h>
36179530Sjkim__FBSDID("$FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 198254 2009-10-19 21:24:19Z edwin $");
3730763Scharnier
3819872Swollman#include <dialog.h>
3919872Swollman#include <err.h>
4019872Swollman#include <errno.h>
4119872Swollman#include <stdio.h>
4219872Swollman#include <stdlib.h>
4319872Swollman#include <string.h>
4466907Swollman#include <time.h>
4519872Swollman#include <unistd.h>
4619872Swollman
4719872Swollman#include <sys/fcntl.h>
4819872Swollman#include <sys/queue.h>
4919872Swollman#include <sys/stat.h>
5019872Swollman
51179530Sjkim#define	_PATH_ZONETAB		"/usr/share/zoneinfo/zone.tab"
52179530Sjkim#define	_PATH_ISO3166		"/usr/share/misc/iso3166"
53179530Sjkim#define	_PATH_ZONEINFO		"/usr/share/zoneinfo"
54179530Sjkim#define	_PATH_LOCALTIME		"/etc/localtime"
55179530Sjkim#define	_PATH_WALL_CMOS_CLOCK	"/etc/wall_cmos_clock"
5619872Swollman
5719872Swollmanstatic int reallydoit = 1;
5819872Swollman
59179530Sjkimstatic void	usage(void);
60179530Sjkimstatic int	continent_country_menu(dialogMenuItem *);
61179530Sjkimstatic int	set_zone_multi(dialogMenuItem *);
62179530Sjkimstatic int	set_zone_whole_country(dialogMenuItem *);
63179530Sjkimstatic int	set_zone_menu(dialogMenuItem *);
6419872Swollman
6519872Swollmanstruct continent {
6619872Swollman	dialogMenuItem *menu;
67179530Sjkim	int		nitems;
68179530Sjkim	int		ch;
69179530Sjkim	int		sc;
7019872Swollman};
7119872Swollman
72179530Sjkimstatic struct continent	africa, america, antarctica, arctic, asia, atlantic;
73179530Sjkimstatic struct continent	australia, europe, indian, pacific;
7419872Swollman
7519872Swollmanstatic struct continent_names {
76179530Sjkim	const char	*name;
7719872Swollman	struct continent *continent;
7819872Swollman} continent_names[] = {
79179530Sjkim	{ "Africa",	&africa },
80179530Sjkim	{ "America",	&america },
81179530Sjkim	{ "Antarctica",	&antarctica },
82179530Sjkim	{ "Arctic",	&arctic },
83179530Sjkim	{ "Asia",	&asia },
84179530Sjkim	{ "Atlantic",	&atlantic },
85179530Sjkim	{ "Australia",	&australia },
86179530Sjkim	{ "Europe",	&europe },
87179530Sjkim	{ "Indian",	&indian },
88179530Sjkim	{ "Pacific",	&pacific }
8919872Swollman};
9019872Swollman
91179530Sjkimstatic struct continent_items {
92179530Sjkim	char		prompt[2];
93179530Sjkim	char		title[30];
94179530Sjkim} continent_items[] = {
95179530Sjkim	{ "1",	"Africa" },
96179530Sjkim	{ "2",	"America -- North and South" },
97179530Sjkim	{ "3",	"Antarctica" },
98179530Sjkim	{ "4",	"Arctic Ocean" },
99179530Sjkim	{ "5",	"Asia" },
100179530Sjkim	{ "6",	"Atlantic Ocean" },
101179530Sjkim	{ "7",	"Australia" },
102179530Sjkim	{ "8",	"Europe" },
103179530Sjkim	{ "9",	"Indian Ocean" },
104179530Sjkim	{ "0",	"Pacific Ocean" }
10519872Swollman};
10619872Swollman
107179530Sjkim#define	NCONTINENTS	\
108179530Sjkim    (int)((sizeof(continent_items)) / (sizeof(continent_items[0])))
109179530Sjkimstatic dialogMenuItem continents[NCONTINENTS];
110179530Sjkim
111179530Sjkim#define	OCEANP(x)	((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
112179530Sjkim
11319872Swollmanstatic int
11419872Swollmancontinent_country_menu(dialogMenuItem *continent)
11519872Swollman{
116179530Sjkim	char		title[64], prompt[64];
11719872Swollman	struct continent *contp = continent->data;
118179530Sjkim	int		isocean = OCEANP(continent - continents);
119179530Sjkim	int		menulen;
120179530Sjkim	int		rv;
12119872Swollman
12219872Swollman	/* Short cut -- if there's only one country, don't post a menu. */
123179497Sjkim	if (contp->nitems == 1)
124179497Sjkim		return (contp->menu[0].fire(&contp->menu[0]));
12519872Swollman
12619872Swollman	/* It's amazing how much good grammar really matters... */
127179530Sjkim	if (!isocean) {
128179530Sjkim		snprintf(title, sizeof(title), "Countries in %s",
129179530Sjkim		    continent->title);
130179530Sjkim		snprintf(prompt, sizeof(prompt), "Select a country or region");
131179530Sjkim	} else {
132179530Sjkim		snprintf(title, sizeof(title), "Islands and groups in the %s",
133179530Sjkim		    continent->title);
134179530Sjkim		snprintf(prompt, sizeof(prompt), "Select an island or group");
135179530Sjkim	}
13619872Swollman
13719872Swollman	menulen = contp->nitems < 16 ? contp->nitems : 16;
138179530Sjkim	rv = dialog_menu(title, prompt, -1, -1, menulen, -contp->nitems,
139179530Sjkim	    contp->menu, 0, &contp->ch, &contp->sc);
14019872Swollman	if (rv == 0)
141179530Sjkim		return (DITEM_LEAVE_MENU);
142179530Sjkim	return (DITEM_RECREATE);
14319872Swollman}
14419872Swollman
14519872Swollmanstatic struct continent *
14619872Swollmanfind_continent(const char *name)
14719872Swollman{
148179530Sjkim	int		i;
14919872Swollman
150179530Sjkim	for (i = 0; i < NCONTINENTS; i++)
15119872Swollman		if (strcmp(name, continent_names[i].name) == 0)
152179530Sjkim			return (continent_names[i].continent);
153179530Sjkim	return (0);
15419872Swollman}
15519872Swollman
15619872Swollmanstruct country {
157179530Sjkim	char		*name;
158179530Sjkim	char		*tlc;
159179530Sjkim	int		nzones;
160179530Sjkim	char		*filename;	/* use iff nzones < 0 */
161179530Sjkim	struct continent *continent;	/* use iff nzones < 0 */
162179530Sjkim	TAILQ_HEAD(, zone) zones;	/* use iff nzones > 0 */
163179530Sjkim	dialogMenuItem	*submenu;	/* use iff nzones > 0 */
16419872Swollman};
16519872Swollman
16619872Swollmanstruct zone {
16760938Sjake	TAILQ_ENTRY(zone) link;
168179530Sjkim	char		*descr;
169179530Sjkim	char		*filename;
17019872Swollman	struct continent *continent;
17119872Swollman};
17219872Swollman
17319872Swollman/*
17419872Swollman * This is the easiest organization... we use ISO 3166 country codes,
17519872Swollman * of the two-letter variety, so we just size this array to suit.
17619872Swollman * Beats worrying about dynamic allocation.
17719872Swollman */
178179530Sjkim#define	NCOUNTRIES	(26 * 26)
17922181Sjhaystatic struct country countries[NCOUNTRIES];
18019872Swollman
181179530Sjkim#define	CODE2INT(s)	((s[0] - 'A') * 26 + (s[1] - 'A'))
182179530Sjkim
18319872Swollman/*
18419872Swollman * Read the ISO 3166 country code database in _PATH_ISO3166
18519872Swollman * (/usr/share/misc/iso3166).  On error, exit via err(3).
18619872Swollman */
18719872Swollmanstatic void
18819872Swollmanread_iso3166_table(void)
18919872Swollman{
190179530Sjkim	FILE		*fp;
191179530Sjkim	struct country	*cp;
192179530Sjkim	size_t		len;
193179530Sjkim	char		*s, *t, *name;
194179530Sjkim	int		lineno;
19519872Swollman
19619872Swollman	fp = fopen(_PATH_ISO3166, "r");
19719872Swollman	if (!fp)
19819872Swollman		err(1, _PATH_ISO3166);
19919872Swollman	lineno = 0;
20019872Swollman
20119872Swollman	while ((s = fgetln(fp, &len)) != 0) {
20219872Swollman		lineno++;
20319872Swollman		if (s[len - 1] != '\n')
20419872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
20519872Swollman		s[len - 1] = '\0';
20630999Sjoerg		if (s[0] == '#' || strspn(s, " \t") == len - 1)
20719872Swollman			continue;
20819872Swollman
20919872Swollman		/* Isolate the two-letter code. */
21019872Swollman		t = strsep(&s, "\t");
21119872Swollman		if (t == 0 || strlen(t) != 2)
21219872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
21319872Swollman		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
21419872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
215179530Sjkim			    lineno, t);
21619872Swollman
21719872Swollman		/* Now skip past the three-letter and numeric codes. */
218179530Sjkim		name = strsep(&s, "\t");	/* 3-let */
21919872Swollman		if (name == 0 || strlen(name) != 3)
22019872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
221179530Sjkim		name = strsep(&s, "\t");	/* numeric */
22219872Swollman		if (name == 0 || strlen(name) != 3)
22319872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
22419872Swollman
22519872Swollman		name = s;
22619872Swollman
22719872Swollman		cp = &countries[CODE2INT(t)];
22819872Swollman		if (cp->name)
229179530Sjkim			errx(1, _PATH_ISO3166
230179530Sjkim			    ":%d: country code `%s' multiply defined: %s",
231179530Sjkim			    lineno, t, cp->name);
23219872Swollman		cp->name = strdup(name);
23356487Scharnier		if (cp->name == NULL)
23456487Scharnier			errx(1, "malloc failed");
23519872Swollman		cp->tlc = strdup(t);
23656487Scharnier		if (cp->tlc == NULL)
23756487Scharnier			errx(1, "malloc failed");
23819872Swollman	}
23919872Swollman
24019872Swollman	fclose(fp);
24119872Swollman}
24219872Swollman
24319872Swollmanstatic void
24419872Swollmanadd_zone_to_country(int lineno, const char *tlc, const char *descr,
245179530Sjkim    const char *file, struct continent *cont)
24619872Swollman{
247179530Sjkim	struct zone	*zp;
248179530Sjkim	struct country	*cp;
24919872Swollman
25019872Swollman	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
25119872Swollman		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
252179530Sjkim		    lineno, tlc);
25319872Swollman
25419872Swollman	cp = &countries[CODE2INT(tlc)];
25519872Swollman	if (cp->name == 0)
25619872Swollman		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
257179530Sjkim		    lineno, tlc);
25819872Swollman
25919872Swollman	if (descr) {
26019872Swollman		if (cp->nzones < 0)
261179530Sjkim			errx(1, _PATH_ZONETAB
262179530Sjkim			    ":%d: conflicting zone definition", lineno);
26319872Swollman
264179530Sjkim		zp = malloc(sizeof(*zp));
26519872Swollman		if (zp == 0)
266179530Sjkim			errx(1, "malloc(%zu)", sizeof(*zp));
26719872Swollman
26819872Swollman		if (cp->nzones == 0)
26919872Swollman			TAILQ_INIT(&cp->zones);
27019872Swollman
27119872Swollman		zp->descr = strdup(descr);
27256487Scharnier		if (zp->descr == NULL)
27356487Scharnier			errx(1, "malloc failed");
27419872Swollman		zp->filename = strdup(file);
27556487Scharnier		if (zp->filename == NULL)
27656487Scharnier			errx(1, "malloc failed");
27719872Swollman		zp->continent = cont;
27819872Swollman		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
27919872Swollman		cp->nzones++;
28019872Swollman	} else {
28119872Swollman		if (cp->nzones > 0)
282179530Sjkim			errx(1, _PATH_ZONETAB
283179530Sjkim			    ":%d: zone must have description", lineno);
28419872Swollman		if (cp->nzones < 0)
28519872Swollman			errx(1, _PATH_ZONETAB
286179530Sjkim			    ":%d: zone multiply defined", lineno);
28719872Swollman		cp->nzones = -1;
28819872Swollman		cp->filename = strdup(file);
28956487Scharnier		if (cp->filename == NULL)
29056487Scharnier			errx(1, "malloc failed");
29119872Swollman		cp->continent = cont;
29219872Swollman	}
29319872Swollman}
29419872Swollman
29519872Swollman/*
29619872Swollman * This comparison function intentionally sorts all of the null-named
29719872Swollman * ``countries''---i.e., the codes that don't correspond to a real
29819872Swollman * country---to the end.  Everything else is lexical by country name.
29919872Swollman */
30019872Swollmanstatic int
30119872Swollmancompare_countries(const void *xa, const void *xb)
30219872Swollman{
30319872Swollman	const struct country *a = xa, *b = xb;
30419872Swollman
30519872Swollman	if (a->name == 0 && b->name == 0)
306179530Sjkim		return (0);
30719872Swollman	if (a->name == 0 && b->name != 0)
308179530Sjkim		return (1);
30919872Swollman	if (b->name == 0)
310179530Sjkim		return (-1);
31119872Swollman
312179530Sjkim	return (strcmp(a->name, b->name));
31319872Swollman}
31419872Swollman
31519872Swollman/*
31619872Swollman * This must be done AFTER all zone descriptions are read, since it breaks
31719872Swollman * CODE2INT().
31819872Swollman */
31919872Swollmanstatic void
32019872Swollmansort_countries(void)
32119872Swollman{
322179530Sjkim
323179530Sjkim	qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries);
32419872Swollman}
32519872Swollman
32619872Swollmanstatic void
32719872Swollmanread_zones(void)
32819872Swollman{
329179530Sjkim	char		contbuf[16];
330179530Sjkim	FILE		*fp;
33119872Swollman	struct continent *cont;
332179530Sjkim	size_t		len;
333179530Sjkim	char		*line, *tlc, *coord, *file, *descr, *p;
334179530Sjkim	int		lineno;
33519872Swollman
33619872Swollman	fp = fopen(_PATH_ZONETAB, "r");
33719872Swollman	if (!fp)
33819872Swollman		err(1, _PATH_ZONETAB);
33919872Swollman	lineno = 0;
34019872Swollman
34119872Swollman	while ((line = fgetln(fp, &len)) != 0) {
34219872Swollman		lineno++;
34319872Swollman		if (line[len - 1] != '\n')
34419872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
34519872Swollman		line[len - 1] = '\0';
34619872Swollman		if (line[0] == '#')
34719872Swollman			continue;
34819872Swollman
34919872Swollman		tlc = strsep(&line, "\t");
35019872Swollman		if (strlen(tlc) != 2)
35119872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
352179530Sjkim			    lineno, tlc);
35319872Swollman		coord = strsep(&line, "\t");
35419872Swollman		file = strsep(&line, "\t");
35519872Swollman		p = strchr(file, '/');
35619872Swollman		if (p == 0)
35719872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
358179530Sjkim			    lineno, file);
35919872Swollman		contbuf[0] = '\0';
36019872Swollman		strncat(contbuf, file, p - file);
36119872Swollman		cont = find_continent(contbuf);
36219872Swollman		if (!cont)
36319872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
364179530Sjkim			    lineno, contbuf);
36519872Swollman
366179530Sjkim		descr = (line != NULL && *line != '\0') ? line : NULL;
36719872Swollman
36819872Swollman		add_zone_to_country(lineno, tlc, descr, file, cont);
36919872Swollman	}
37019872Swollman	fclose(fp);
37119872Swollman}
37219872Swollman
37319872Swollmanstatic void
37419872Swollmanmake_menus(void)
37519872Swollman{
376179530Sjkim	struct country	*cp;
377179530Sjkim	struct zone	*zp, *zp2;
37819872Swollman	struct continent *cont;
379179530Sjkim	dialogMenuItem	*dmi;
380179530Sjkim	int		i;
38119872Swollman
38219872Swollman	/*
38319872Swollman	 * First, count up all the countries in each continent/ocean.
38419872Swollman	 * Be careful to count those countries which have multiple zones
38519872Swollman	 * only once for each.  NB: some countries are in multiple
38619872Swollman	 * continents/oceans.
38719872Swollman	 */
38819872Swollman	for (cp = countries; cp->name; cp++) {
38919872Swollman		if (cp->nzones == 0)
39019872Swollman			continue;
39119872Swollman		if (cp->nzones < 0) {
39219872Swollman			cp->continent->nitems++;
39319872Swollman		} else {
39470486Sben			TAILQ_FOREACH(zp, &cp->zones, link) {
39519872Swollman				cont = zp->continent;
39670486Sben				for (zp2 = TAILQ_FIRST(&cp->zones);
397179530Sjkim				    zp2->continent != cont;
398179530Sjkim				    zp2 = TAILQ_NEXT(zp2, link))
39919872Swollman					;
40019872Swollman				if (zp2 == zp)
40119872Swollman					zp->continent->nitems++;
40219872Swollman			}
40319872Swollman		}
40419872Swollman	}
40519872Swollman
40619872Swollman	/*
407179530Sjkim	 * Now allocate memory for the country menus and initialize
408179530Sjkim	 * continent menus.  We set nitems back to zero so that we can
409179530Sjkim	 * use it for counting again when we actually build the menus.
41019872Swollman	 */
411179530Sjkim	memset(continents, 0, sizeof(continents));
41219872Swollman	for (i = 0; i < NCONTINENTS; i++) {
41319872Swollman		continent_names[i].continent->menu =
414179530Sjkim		    malloc(sizeof(dialogMenuItem) *
415179530Sjkim		    continent_names[i].continent->nitems);
41619872Swollman		if (continent_names[i].continent->menu == 0)
41756487Scharnier			errx(1, "malloc for continent menu");
41819872Swollman		continent_names[i].continent->nitems = 0;
419179530Sjkim		continents[i].prompt = continent_items[i].prompt;
420179530Sjkim		continents[i].title = continent_items[i].title;
421179530Sjkim		continents[i].fire = continent_country_menu;
422179530Sjkim		continents[i].data = continent_names[i].continent;
42319872Swollman	}
42419872Swollman
42519872Swollman	/*
42619872Swollman	 * Now that memory is allocated, create the menu items for
42719872Swollman	 * each continent.  For multiple-zone countries, also create
42819872Swollman	 * the country's zone submenu.
42919872Swollman	 */
43019872Swollman	for (cp = countries; cp->name; cp++) {
43119872Swollman		if (cp->nzones == 0)
43219872Swollman			continue;
43319872Swollman		if (cp->nzones < 0) {
43419872Swollman			dmi = &cp->continent->menu[cp->continent->nitems];
435179530Sjkim			memset(dmi, 0, sizeof(*dmi));
436179530Sjkim			asprintf(&dmi->prompt, "%d", ++cp->continent->nitems);
43719872Swollman			dmi->title = cp->name;
43819872Swollman			dmi->checked = 0;
43919872Swollman			dmi->fire = set_zone_whole_country;
44019872Swollman			dmi->selected = 0;
44119872Swollman			dmi->data = cp;
44219872Swollman		} else {
443179530Sjkim			cp->submenu = malloc(cp->nzones * sizeof(*dmi));
44419872Swollman			if (cp->submenu == 0)
44556487Scharnier				errx(1, "malloc for submenu");
44619872Swollman			cp->nzones = 0;
44770486Sben			TAILQ_FOREACH(zp, &cp->zones, link) {
44819872Swollman				cont = zp->continent;
44919872Swollman				dmi = &cp->submenu[cp->nzones];
450179530Sjkim				memset(dmi, 0, sizeof(*dmi));
451179530Sjkim				asprintf(&dmi->prompt, "%d", ++cp->nzones);
45219872Swollman				dmi->title = zp->descr;
45319872Swollman				dmi->checked = 0;
45419872Swollman				dmi->fire = set_zone_multi;
45519872Swollman				dmi->selected = 0;
45619872Swollman				dmi->data = zp;
45719872Swollman
45870486Sben				for (zp2 = TAILQ_FIRST(&cp->zones);
459179530Sjkim				    zp2->continent != cont;
460179530Sjkim				    zp2 = TAILQ_NEXT(zp2, link))
46119872Swollman					;
46219872Swollman				if (zp2 != zp)
46319872Swollman					continue;
46419872Swollman
46519872Swollman				dmi = &cont->menu[cont->nitems];
466179530Sjkim				memset(dmi, 0, sizeof(*dmi));
46719872Swollman				asprintf(&dmi->prompt, "%d", ++cont->nitems);
46819872Swollman				dmi->title = cp->name;
46919872Swollman				dmi->checked = 0;
47019872Swollman				dmi->fire = set_zone_menu;
47119872Swollman				dmi->selected = 0;
47219872Swollman				dmi->data = cp;
47319872Swollman			}
47419872Swollman		}
47519872Swollman	}
47619872Swollman}
47719872Swollman
47819872Swollmanstatic int
47919872Swollmanset_zone_menu(dialogMenuItem *dmi)
48019872Swollman{
481179530Sjkim	char		title[64], prompt[64];
482179530Sjkim	struct country	*cp = dmi->data;
483179530Sjkim	int		menulen;
484179530Sjkim	int		rv;
48519872Swollman
486179530Sjkim	snprintf(title, sizeof(title), "%s Time Zones", cp->name);
487179530Sjkim	snprintf(prompt, sizeof(prompt),
488179530Sjkim	    "Select a zone which observes the same time as your locality.");
48919872Swollman	menulen = cp->nzones < 16 ? cp->nzones : 16;
490179530Sjkim	rv = dialog_menu(title, prompt, -1, -1, menulen, -cp->nzones,
491179530Sjkim	    cp->submenu, 0, 0, 0);
49219872Swollman	if (rv != 0)
493179530Sjkim		return (DITEM_RECREATE);
494179530Sjkim	return (DITEM_LEAVE_MENU);
49519872Swollman}
49619872Swollman
49719872Swollmanstatic int
49819872Swollmaninstall_zone_file(const char *filename)
49919872Swollman{
500179530Sjkim	char		buf[1024];
501179530Sjkim	char		title[64], prompt[64];
502179530Sjkim	struct stat	sb;
503179530Sjkim	ssize_t		len;
504179530Sjkim	int		fd1, fd2, copymode;
50519872Swollman
506179530Sjkim	if (lstat(_PATH_LOCALTIME, &sb) < 0) {
50719872Swollman		/* Nothing there yet... */
50819872Swollman		copymode = 1;
509179530Sjkim	} else if (S_ISLNK(sb.st_mode))
51019872Swollman		copymode = 0;
51119872Swollman	else
51219872Swollman		copymode = 1;
51319872Swollman
51421915Sjkh#ifdef VERBOSE
51519872Swollman	if (copymode)
516179530Sjkim		snprintf(prompt, sizeof(prompt),
517179530Sjkim		    "Copying %s to " _PATH_LOCALTIME, filename);
51819872Swollman	else
519179530Sjkim		snprintf(prompt, sizeof(prompt),
520179530Sjkim		    "Creating symbolic link " _PATH_LOCALTIME " to %s",
521179530Sjkim		    filename);
522179530Sjkim	dialog_notify(prompt);
52321915Sjkh#endif
52419872Swollman
52519872Swollman	if (reallydoit) {
52619872Swollman		if (copymode) {
52719872Swollman			fd1 = open(filename, O_RDONLY, 0);
52819872Swollman			if (fd1 < 0) {
529179530Sjkim				snprintf(title, sizeof(title), "Error");
530179530Sjkim				snprintf(prompt, sizeof(prompt),
531179530Sjkim				    "Could not open %s: %s", filename,
532179530Sjkim				    strerror(errno));
533179530Sjkim				dialog_mesgbox(title, prompt, 8, 72);
534179530Sjkim				return (DITEM_FAILURE | DITEM_RECREATE);
53519872Swollman			}
53619872Swollman
53719872Swollman			unlink(_PATH_LOCALTIME);
538179530Sjkim			fd2 = open(_PATH_LOCALTIME, O_CREAT | O_EXCL | O_WRONLY,
539179530Sjkim			    S_IRUSR | S_IRGRP | S_IROTH);
54019872Swollman			if (fd2 < 0) {
541179530Sjkim				snprintf(title, sizeof(title), "Error");
542179530Sjkim				snprintf(prompt, sizeof(prompt),
543179530Sjkim				    "Could not open " _PATH_LOCALTIME ": %s",
544179530Sjkim				    strerror(errno));
545179530Sjkim				dialog_mesgbox(title, prompt, 8, 72);
546179530Sjkim				return (DITEM_FAILURE | DITEM_RECREATE);
54719872Swollman			}
54819872Swollman
549179530Sjkim			while ((len = read(fd1, buf, sizeof(buf))) > 0)
55019872Swollman				len = write(fd2, buf, len);
55119872Swollman
55219872Swollman			if (len == -1) {
553179530Sjkim				snprintf(title, sizeof(title), "Error");
554179530Sjkim				snprintf(prompt, sizeof(prompt),
555179530Sjkim				    "Error copying %s to " _PATH_LOCALTIME
556179530Sjkim				    ": %s", filename, strerror(errno));
557179530Sjkim				dialog_mesgbox(title, prompt, 8, 72);
55819872Swollman				/* Better to leave none than a corrupt one. */
55919872Swollman				unlink(_PATH_LOCALTIME);
560179530Sjkim				return (DITEM_FAILURE | DITEM_RECREATE);
56119872Swollman			}
56219872Swollman			close(fd1);
56319872Swollman			close(fd2);
56419872Swollman		} else {
56519872Swollman			if (access(filename, R_OK) != 0) {
566179530Sjkim				snprintf(title, sizeof(title), "Error");
567179530Sjkim				snprintf(prompt, sizeof(prompt),
568179530Sjkim				    "Cannot access %s: %s", filename,
569179530Sjkim				    strerror(errno));
570179530Sjkim				dialog_mesgbox(title, prompt, 8, 72);
571179530Sjkim				return (DITEM_FAILURE | DITEM_RECREATE);
57219872Swollman			}
57319872Swollman			unlink(_PATH_LOCALTIME);
57419872Swollman			if (symlink(filename, _PATH_LOCALTIME) < 0) {
575179530Sjkim				snprintf(title, sizeof(title), "Error");
576179530Sjkim				snprintf(prompt, sizeof(prompt),
577179530Sjkim				    "Cannot create symbolic link "
578179530Sjkim				    _PATH_LOCALTIME " to %s: %s", filename,
579179530Sjkim				    strerror(errno));
580179530Sjkim				dialog_mesgbox(title, prompt, 8, 72);
581179530Sjkim				return (DITEM_FAILURE | DITEM_RECREATE);
58219872Swollman			}
58319872Swollman		}
58419872Swollman	}
58519872Swollman
58621915Sjkh#ifdef VERBOSE
587179530Sjkim	snprintf(title, sizeof(title), "Done");
58819872Swollman	if (copymode)
589179530Sjkim		snprintf(prompt, sizeof(prompt),
590179530Sjkim		    "Copied timezone file from %s to " _PATH_LOCALTIME,
591179530Sjkim		    filename);
59219872Swollman	else
593179530Sjkim		snprintf(prompt, sizeof(prompt), "Created symbolic link from "
594179530Sjkim		    _PATH_LOCALTIME " to %s", filename);
595179530Sjkim	dialog_mesgbox(title, prompt, 8, 72);
59621915Sjkh#endif
597179530Sjkim	return (DITEM_LEAVE_MENU);
59819872Swollman}
59919872Swollman
60019872Swollmanstatic int
60119872Swollmanconfirm_zone(const char *filename)
60219872Swollman{
603179530Sjkim	char		title[64], prompt[64];
604179530Sjkim	time_t		t = time(0);
605179530Sjkim	struct tm	*tm;
606179530Sjkim	int		rv;
60719872Swollman
60819872Swollman	setenv("TZ", filename, 1);
60919872Swollman	tzset();
61019872Swollman	tm = localtime(&t);
61119872Swollman
612179530Sjkim	snprintf(title, sizeof(title), "Confirmation");
613179530Sjkim	snprintf(prompt, sizeof(prompt),
614179530Sjkim	    "Does the abbreviation `%s' look reasonable?", tm->tm_zone);
615179530Sjkim	rv = !dialog_yesno(title, prompt, 5, 72);
616179530Sjkim	return (rv);
61719872Swollman}
61819872Swollman
61919872Swollmanstatic int
62019872Swollmanset_zone_multi(dialogMenuItem *dmi)
62119872Swollman{
622179530Sjkim	struct zone	*zp = dmi->data;
623179530Sjkim	char		*fn;
624179530Sjkim	int		rv;
62519872Swollman
62619872Swollman	if (!confirm_zone(zp->filename))
627179530Sjkim		return (DITEM_FAILURE | DITEM_RECREATE);
62819872Swollman
62919872Swollman	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
63019872Swollman	rv = install_zone_file(fn);
63119872Swollman	free(fn);
632179530Sjkim	return (rv);
63319872Swollman}
63419872Swollman
63519872Swollmanstatic int
63619872Swollmanset_zone_whole_country(dialogMenuItem *dmi)
63719872Swollman{
638179530Sjkim	struct country	*cp = dmi->data;
639179530Sjkim	char		*fn;
640179530Sjkim	int		rv;
64119872Swollman
64219872Swollman	if (!confirm_zone(cp->filename))
643179530Sjkim		return (DITEM_FAILURE | DITEM_RECREATE);
64419872Swollman
64519872Swollman	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
64619872Swollman	rv = install_zone_file(fn);
64719872Swollman	free(fn);
648179530Sjkim	return (rv);
64919872Swollman}
65019872Swollman
65130763Scharnierstatic void
652179530Sjkimusage(void)
65330763Scharnier{
654179530Sjkim
655195339Sattilio	fprintf(stderr, "usage: tzsetup [-ns]\n");
65630763Scharnier	exit(1);
65730763Scharnier}
65830763Scharnier
659179530Sjkim#if defined(__sparc64__)
660179530Sjkim#define	DIALOG_UTC	dialog_yesno
661179530Sjkim#else
662179530Sjkim#define	DIALOG_UTC	dialog_noyes
663179530Sjkim#endif
664179530Sjkim
66519872Swollmanint
66619872Swollmanmain(int argc, char **argv)
66719872Swollman{
668179530Sjkim	char		title[64], prompt[128];
669195339Sattilio	int		c, fd, skiputc;
67019872Swollman
671195339Sattilio	skiputc = 0;
672195339Sattilio	while ((c = getopt(argc, argv, "ns")) != -1) {
67319872Swollman		switch(c) {
67419872Swollman		case 'n':
67519872Swollman			reallydoit = 0;
67619872Swollman			break;
677195339Sattilio		case 's':
678195339Sattilio			skiputc = 1;
679195339Sattilio			break;
68019872Swollman		default:
68130763Scharnier			usage();
68219872Swollman		}
68319872Swollman	}
68419872Swollman
68543544Swollman	if (argc - optind > 1)
68630763Scharnier		usage();
68719872Swollman
68849435Sru	/* Override the user-supplied umask. */
689179530Sjkim	(void)umask(S_IWGRP | S_IWOTH);
69049435Sru
69119872Swollman	read_iso3166_table();
69219872Swollman	read_zones();
69319872Swollman	sort_countries();
69419872Swollman	make_menus();
69519872Swollman
69619872Swollman	init_dialog();
697195339Sattilio	if (skiputc == 0) {
698195339Sattilio		snprintf(title, sizeof(title),
699195339Sattilio		    "Select local or UTC (Greenwich Mean Time) clock");
700195339Sattilio		snprintf(prompt, sizeof(prompt),
701195339Sattilio		    "Is this machine's CMOS clock set to UTC?  "
702195339Sattilio		    "If it is set to local time,\n"
703195339Sattilio		    "or you don't know, please choose NO here!");
704195339Sattilio		if (!DIALOG_UTC(title, prompt, 7, 72)) {
705195339Sattilio			if (reallydoit)
706195339Sattilio				unlink(_PATH_WALL_CMOS_CLOCK);
707195339Sattilio		} else {
708195339Sattilio			if (reallydoit) {
709195339Sattilio				fd = open(_PATH_WALL_CMOS_CLOCK,
710195339Sattilio				    O_WRONLY | O_CREAT | O_TRUNC,
711195339Sattilio				    S_IRUSR | S_IRGRP | S_IROTH);
712198254Sedwin				if (fd < 0) {
713198254Sedwin					end_dialog();
714195339Sattilio					err(1, "create %s",
715195339Sattilio					    _PATH_WALL_CMOS_CLOCK);
716198254Sedwin				}
717195339Sattilio				close(fd);
718195339Sattilio			}
71941852Speter		}
720195339Sattilio		dialog_clear_norefresh();
72132394Ssteve	}
72243544Swollman	if (optind == argc - 1) {
723179530Sjkim		snprintf(title, sizeof(title), "Default timezone provided");
724179530Sjkim		snprintf(prompt, sizeof(prompt),
725179530Sjkim		    "\nUse the default `%s' zone?", argv[optind]);
726179530Sjkim		if (!dialog_yesno(title, prompt, 7, 72)) {
72743544Swollman			install_zone_file(argv[optind]);
72843544Swollman			dialog_clear();
72943544Swollman			end_dialog();
730179530Sjkim			return (0);
73143544Swollman		}
73243544Swollman		dialog_clear_norefresh();
73343544Swollman	}
734179530Sjkim	snprintf(title, sizeof(title), "Time Zone Selector");
735179530Sjkim	snprintf(prompt, sizeof(prompt), "Select a region");
736179530Sjkim	dialog_menu(title, prompt, -1, -1, NCONTINENTS, -NCONTINENTS,
737179530Sjkim	    continents, 0, NULL, NULL);
73843544Swollman
73922815Sjkh	dialog_clear();
74019872Swollman	end_dialog();
741179530Sjkim	return (0);
74219872Swollman}
743