tzsetup.c revision 50479
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.
1519872Swollman *
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
3530763Scharnier#ifndef lint
3630763Scharnierstatic const char rcsid[] =
3750479Speter  "$FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 50479 1999-08-28 01:35:59Z peter $";
3830763Scharnier#endif /* not lint */
3930763Scharnier
4019872Swollman#include <sys/types.h>
4119872Swollman#include <dialog.h>
4219872Swollman#include <err.h>
4319872Swollman#include <errno.h>
4419872Swollman#include <stdio.h>
4519872Swollman#include <stdlib.h>
4619872Swollman#include <string.h>
4719872Swollman#include <unistd.h>
4819872Swollman
4919872Swollman#include <sys/fcntl.h>
5019872Swollman#include <sys/queue.h>
5119872Swollman#include <sys/stat.h>
5219872Swollman
5319872Swollman#include "paths.h"
5419872Swollman
5519872Swollmanstatic int reallydoit = 1;
5619872Swollman
5719872Swollmanstatic int continent_country_menu(dialogMenuItem *);
5819872Swollmanstatic int set_zone_multi(dialogMenuItem *);
5919872Swollmanstatic int set_zone_whole_country(dialogMenuItem *);
6019872Swollmanstatic int set_zone_menu(dialogMenuItem *);
6119872Swollman
6219872Swollmanstruct continent {
6319872Swollman	dialogMenuItem *menu;
6419872Swollman	int nitems;
6519872Swollman	int ch;
6619872Swollman	int sc;
6719872Swollman};
6819872Swollman
6919872Swollmanstatic struct continent africa, america, antarctica, arctic, asia, atlantic;
7019872Swollmanstatic struct continent australia, europe, indian, pacific;
7119872Swollman
7219872Swollmanstatic struct continent_names {
7319872Swollman	char *name;
7419872Swollman	struct continent *continent;
7519872Swollman} continent_names[] = {
7619872Swollman	{ "Africa", &africa }, { "America", &america },
7719872Swollman	{ "Antarctica", &antarctica }, { "Arctic", &arctic },
7819872Swollman	{ "Asia", &asia },
7919872Swollman	{ "Atlantic", &atlantic }, { "Australia", &australia },
8019872Swollman	{ "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
8119872Swollman};
8219872Swollman
8319872Swollmanstatic dialogMenuItem continents[] = {
8419872Swollman	{ "1", "Africa", 0, continent_country_menu, 0, &africa },
8519872Swollman	{ "2", "America -- North and South", 0, continent_country_menu, 0,
8619872Swollman		  &america },
8719872Swollman	{ "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
8819872Swollman	{ "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
8919872Swollman	{ "5", "Asia", 0, continent_country_menu, 0, &asia },
9019872Swollman	{ "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
9119872Swollman	{ "7", "Australia", 0, continent_country_menu, 0, &australia },
9219872Swollman	{ "8", "Europe", 0, continent_country_menu, 0, &europe },
9319872Swollman	{ "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
9419872Swollman	{ "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
9519872Swollman};
9619872Swollman#define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
9719872Swollman#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
9819872Swollman
9919872Swollmanstatic int
10019872Swollmancontinent_country_menu(dialogMenuItem *continent)
10119872Swollman{
10219872Swollman	int rv;
10319872Swollman	struct continent *contp = continent->data;
10419872Swollman	char title[256];
10519872Swollman	int isocean = OCEANP(continent - continents);
10619872Swollman	int menulen;
10719872Swollman
10819872Swollman	/* Short cut -- if there's only one country, don't post a menu. */
10919872Swollman	if (contp->nitems == 1) {
11019872Swollman		return set_zone_menu(&contp->menu[0]);
11119872Swollman	}
11219872Swollman
11319872Swollman	/* It's amazing how much good grammar really matters... */
11419872Swollman	if (!isocean)
11519872Swollman		snprintf(title, sizeof title, "Countries in %s",
11619872Swollman			 continent->title);
11719872Swollman	else
11819872Swollman		snprintf(title, sizeof title, "Islands and groups in the %s",
11919872Swollman			 continent->title);
12019872Swollman
12119872Swollman	menulen = contp->nitems < 16 ? contp->nitems : 16;
12219872Swollman	rv = dialog_menu(title, (isocean ? "Select an island or group"
12319872Swollman                                 : "Select a country"), -1, -1, menulen,
12419872Swollman			 -contp->nitems, contp->menu, 0, &contp->ch,
12519872Swollman			 &contp->sc);
12619872Swollman	if (rv == 0)
12719872Swollman		return DITEM_LEAVE_MENU;
12819872Swollman	return DITEM_RECREATE;
12919872Swollman}
13019872Swollman
13119872Swollmanstatic struct continent *
13219872Swollmanfind_continent(const char *name)
13319872Swollman{
13419872Swollman	int i;
13519872Swollman
13619872Swollman	for (i = 0; i < NCONTINENTS; i++) {
13719872Swollman		if (strcmp(name, continent_names[i].name) == 0)
13819872Swollman			return continent_names[i].continent;
13919872Swollman	}
14019872Swollman	return 0;
14119872Swollman}
14219872Swollman
14319872Swollmanstruct country {
14419872Swollman	char *name;
14519872Swollman	char *tlc;
14619872Swollman	int nzones;
14719872Swollman	char *filename;		/* use iff nzones < 0 */
14819872Swollman	struct continent *continent; /* use iff nzones < 0 */
14919872Swollman	TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
15019872Swollman	dialogMenuItem *submenu; /* use iff nzones > 0 */
15119872Swollman};
15219872Swollman
15319872Swollmanstruct zone {
15419872Swollman	TAILQ_ENTRY(zone) link;
15519872Swollman	char *descr;
15619872Swollman	char *filename;
15719872Swollman	struct continent *continent;
15819872Swollman};
15919872Swollman
16019872Swollman/*
16119872Swollman * This is the easiest organization... we use ISO 3166 country codes,
16219872Swollman * of the two-letter variety, so we just size this array to suit.
16319872Swollman * Beats worrying about dynamic allocation.
16419872Swollman */
16522181Sjhay#define NCOUNTRIES	(26*26)
16622181Sjhaystatic struct country countries[NCOUNTRIES];
16719872Swollman#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
16819872Swollman
16919872Swollman/*
17019872Swollman * Read the ISO 3166 country code database in _PATH_ISO3166
17119872Swollman * (/usr/share/misc/iso3166).  On error, exit via err(3).
17219872Swollman */
17319872Swollmanstatic void
17419872Swollmanread_iso3166_table(void)
17519872Swollman{
17619872Swollman	FILE *fp;
17719872Swollman	char *s, *t, *name;
17819872Swollman	size_t len;
17919872Swollman	int lineno;
18019872Swollman	struct country *cp;
18119872Swollman
18219872Swollman	fp = fopen(_PATH_ISO3166, "r");
18319872Swollman	if (!fp)
18419872Swollman		err(1, _PATH_ISO3166);
18519872Swollman	lineno = 0;
18619872Swollman
18719872Swollman	while ((s = fgetln(fp, &len)) != 0) {
18819872Swollman		lineno++;
18919872Swollman		if (s[len - 1] != '\n')
19019872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
19119872Swollman		s[len - 1] = '\0';
19230999Sjoerg		if (s[0] == '#' || strspn(s, " \t") == len - 1)
19319872Swollman			continue;
19419872Swollman
19519872Swollman		/* Isolate the two-letter code. */
19619872Swollman		t = strsep(&s, "\t");
19719872Swollman		if (t == 0 || strlen(t) != 2)
19819872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
19919872Swollman		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
20019872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
20119872Swollman			     lineno, t);
20219872Swollman
20319872Swollman		/* Now skip past the three-letter and numeric codes. */
20419872Swollman		name = strsep(&s, "\t"); /* 3-let */
20519872Swollman		if (name == 0 || strlen(name) != 3)
20619872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
20719872Swollman		name = strsep(&s, "\t"); /* numeric */
20819872Swollman		if (name == 0 || strlen(name) != 3)
20919872Swollman			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
21019872Swollman
21119872Swollman		name = s;
21219872Swollman
21319872Swollman		cp = &countries[CODE2INT(t)];
21419872Swollman		if (cp->name)
21519872Swollman			errx(1, _PATH_ISO3166
21619872Swollman			     ":%d: country code `%s' multiply defined: %s",
21719872Swollman			     lineno, t, cp->name);
21819872Swollman		cp->name = strdup(name);
21919872Swollman		cp->tlc = strdup(t);
22019872Swollman	}
22119872Swollman
22219872Swollman	fclose(fp);
22319872Swollman}
22419872Swollman
22519872Swollmanstatic void
22619872Swollmanadd_zone_to_country(int lineno, const char *tlc, const char *descr,
22719872Swollman		    const char *file, struct continent *cont)
22819872Swollman{
22919872Swollman	struct zone *zp;
23019872Swollman	struct country *cp;
23119872Swollman
23219872Swollman	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
23319872Swollman		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
23419872Swollman		     lineno, tlc);
23519872Swollman
23619872Swollman	cp = &countries[CODE2INT(tlc)];
23719872Swollman	if (cp->name == 0)
23819872Swollman		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
23919872Swollman		     lineno, tlc);
24019872Swollman
24119872Swollman	if (descr) {
24219872Swollman		if (cp->nzones < 0)
24319872Swollman			errx(1, _PATH_ZONETAB
24419872Swollman			     ":%d: conflicting zone definition", lineno);
24519872Swollman
24619872Swollman		zp = malloc(sizeof *zp);
24719872Swollman		if (zp == 0)
24819872Swollman			err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
24919872Swollman
25019872Swollman		if (cp->nzones == 0)
25119872Swollman			TAILQ_INIT(&cp->zones);
25219872Swollman
25319872Swollman		zp->descr = strdup(descr);
25419872Swollman		zp->filename = strdup(file);
25519872Swollman		zp->continent = cont;
25619872Swollman		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
25719872Swollman		cp->nzones++;
25819872Swollman	} else {
25919872Swollman		if (cp->nzones > 0)
26019872Swollman			errx(1, _PATH_ZONETAB
26119872Swollman			     ":%d: zone must have description", lineno);
26219872Swollman		if (cp->nzones < 0)
26319872Swollman			errx(1, _PATH_ZONETAB
26419872Swollman			     ":%d: zone multiply defined", lineno);
26519872Swollman		cp->nzones = -1;
26619872Swollman		cp->filename = strdup(file);
26719872Swollman		cp->continent = cont;
26819872Swollman	}
26919872Swollman}
27019872Swollman
27119872Swollman/*
27219872Swollman * This comparison function intentionally sorts all of the null-named
27319872Swollman * ``countries''---i.e., the codes that don't correspond to a real
27419872Swollman * country---to the end.  Everything else is lexical by country name.
27519872Swollman */
27619872Swollmanstatic int
27719872Swollmancompare_countries(const void *xa, const void *xb)
27819872Swollman{
27919872Swollman	const struct country *a = xa, *b = xb;
28019872Swollman
28119872Swollman	if (a->name == 0 && b->name == 0)
28219872Swollman		return 0;
28319872Swollman	if (a->name == 0 && b->name != 0)
28419872Swollman		return 1;
28519872Swollman	if (b->name == 0)
28619872Swollman		return -1;
28719872Swollman
28819872Swollman	return strcmp(a->name, b->name);
28919872Swollman}
29019872Swollman
29119872Swollman/*
29219872Swollman * This must be done AFTER all zone descriptions are read, since it breaks
29319872Swollman * CODE2INT().
29419872Swollman */
29519872Swollmanstatic void
29619872Swollmansort_countries(void)
29719872Swollman{
29822181Sjhay	qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
29919872Swollman}
30019872Swollman
30119872Swollmanstatic void
30219872Swollmanread_zones(void)
30319872Swollman{
30419872Swollman	FILE *fp;
30519872Swollman	char *line;
30619872Swollman	size_t len;
30719872Swollman	int lineno;
30819872Swollman	char *tlc, *coord, *file, *descr, *p;
30919872Swollman	char contbuf[16];
31019872Swollman	struct continent *cont;
31119872Swollman
31219872Swollman	fp = fopen(_PATH_ZONETAB, "r");
31319872Swollman	if (!fp)
31419872Swollman		err(1, _PATH_ZONETAB);
31519872Swollman	lineno = 0;
31619872Swollman
31719872Swollman	while ((line = fgetln(fp, &len)) != 0) {
31819872Swollman		lineno++;
31919872Swollman		if (line[len - 1] != '\n')
32019872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
32119872Swollman		line[len - 1] = '\0';
32219872Swollman		if (line[0] == '#')
32319872Swollman			continue;
32419872Swollman
32519872Swollman		tlc = strsep(&line, "\t");
32619872Swollman		if (strlen(tlc) != 2)
32719872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
32819872Swollman			     lineno, tlc);
32919872Swollman		coord = strsep(&line, "\t");
33019872Swollman		file = strsep(&line, "\t");
33119872Swollman		p = strchr(file, '/');
33219872Swollman		if (p == 0)
33319872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
33419872Swollman			     lineno, file);
33519872Swollman		contbuf[0] = '\0';
33619872Swollman		strncat(contbuf, file, p - file);
33719872Swollman		cont = find_continent(contbuf);
33819872Swollman		if (!cont)
33919872Swollman			errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
34019872Swollman			     lineno, contbuf);
34119872Swollman
34219872Swollman		descr = (line && *line) ? line : 0;
34319872Swollman
34419872Swollman		add_zone_to_country(lineno, tlc, descr, file, cont);
34519872Swollman	}
34619872Swollman	fclose(fp);
34719872Swollman}
34819872Swollman
34919872Swollmanstatic void
35019872Swollmanmake_menus(void)
35119872Swollman{
35219872Swollman	struct country *cp;
35319872Swollman	struct zone *zp, *zp2;
35419872Swollman	struct continent *cont;
35519872Swollman	dialogMenuItem *dmi;
35619872Swollman	int i;
35719872Swollman
35819872Swollman	/*
35919872Swollman	 * First, count up all the countries in each continent/ocean.
36019872Swollman	 * Be careful to count those countries which have multiple zones
36119872Swollman	 * only once for each.  NB: some countries are in multiple
36219872Swollman	 * continents/oceans.
36319872Swollman	 */
36419872Swollman	for (cp = countries; cp->name; cp++) {
36519872Swollman		if (cp->nzones == 0)
36619872Swollman			continue;
36719872Swollman		if (cp->nzones < 0) {
36819872Swollman			cp->continent->nitems++;
36919872Swollman		} else {
37019872Swollman			for (zp = cp->zones.tqh_first; zp;
37119872Swollman			     zp = zp->link.tqe_next) {
37219872Swollman				cont = zp->continent;
37319872Swollman				for (zp2 = cp->zones.tqh_first;
37419872Swollman				     zp2->continent != cont;
37519872Swollman				     zp2 = zp2->link.tqe_next)
37619872Swollman					;
37719872Swollman				if (zp2 == zp)
37819872Swollman					zp->continent->nitems++;
37919872Swollman			}
38019872Swollman		}
38119872Swollman	}
38219872Swollman
38319872Swollman	/*
38419872Swollman	 * Now allocate memory for the country menus.  We set
38519872Swollman	 * nitems back to zero so that we can use it for counting
38619872Swollman	 * again when we actually build the menus.
38719872Swollman	 */
38819872Swollman	for (i = 0; i < NCONTINENTS; i++) {
38919872Swollman		continent_names[i].continent->menu =
39019872Swollman			malloc(sizeof(dialogMenuItem) *
39119872Swollman			       continent_names[i].continent->nitems);
39219872Swollman		if (continent_names[i].continent->menu == 0)
39319872Swollman			err(1, "malloc for continent menu");
39419872Swollman		continent_names[i].continent->nitems = 0;
39519872Swollman	}
39619872Swollman
39719872Swollman	/*
39819872Swollman	 * Now that memory is allocated, create the menu items for
39919872Swollman	 * each continent.  For multiple-zone countries, also create
40019872Swollman	 * the country's zone submenu.
40119872Swollman	 */
40219872Swollman	for (cp = countries; cp->name; cp++) {
40319872Swollman		if (cp->nzones == 0)
40419872Swollman			continue;
40519872Swollman		if (cp->nzones < 0) {
40619872Swollman			dmi = &cp->continent->menu[cp->continent->nitems];
40719872Swollman			memset(dmi, 0, sizeof *dmi);
40819872Swollman			asprintf(&dmi->prompt, "%d",
40919872Swollman				 ++cp->continent->nitems);
41019872Swollman			dmi->title = cp->name;
41119872Swollman			dmi->checked = 0;
41219872Swollman			dmi->fire = set_zone_whole_country;
41319872Swollman			dmi->selected = 0;
41419872Swollman			dmi->data = cp;
41519872Swollman		} else {
41619872Swollman			cp->submenu = malloc(cp->nzones * sizeof *dmi);
41719872Swollman			if (cp->submenu == 0)
41819872Swollman				err(1, "malloc for submenu");
41919872Swollman			cp->nzones = 0;
42019872Swollman			for (zp = cp->zones.tqh_first; zp;
42119872Swollman			     zp = zp->link.tqe_next) {
42219872Swollman				cont = zp->continent;
42319872Swollman				dmi = &cp->submenu[cp->nzones];
42419872Swollman				memset(dmi, 0, sizeof *dmi);
42519872Swollman				asprintf(&dmi->prompt, "%d",
42619872Swollman					 ++cp->nzones);
42719872Swollman				dmi->title = zp->descr;
42819872Swollman				dmi->checked = 0;
42919872Swollman				dmi->fire = set_zone_multi;
43019872Swollman				dmi->selected = 0;
43119872Swollman				dmi->data = zp;
43219872Swollman
43319872Swollman				for (zp2 = cp->zones.tqh_first;
43419872Swollman				     zp2->continent != cont;
43519872Swollman				     zp2 = zp2->link.tqe_next)
43619872Swollman					;
43719872Swollman				if (zp2 != zp)
43819872Swollman					continue;
43919872Swollman
44019872Swollman				dmi = &cont->menu[cont->nitems];
44119872Swollman				memset(dmi, 0, sizeof *dmi);
44219872Swollman				asprintf(&dmi->prompt, "%d", ++cont->nitems);
44319872Swollman				dmi->title = cp->name;
44419872Swollman				dmi->checked = 0;
44519872Swollman				dmi->fire = set_zone_menu;
44619872Swollman				dmi->selected = 0;
44719872Swollman				dmi->data = cp;
44819872Swollman			}
44919872Swollman		}
45019872Swollman	}
45119872Swollman}
45219872Swollman
45319872Swollmanstatic int
45419872Swollmanset_zone_menu(dialogMenuItem *dmi)
45519872Swollman{
45619872Swollman	int rv;
45719872Swollman	char buf[256];
45819872Swollman	struct country *cp = dmi->data;
45919872Swollman	int menulen;
46019872Swollman
46119872Swollman	snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
46219872Swollman	menulen = cp->nzones < 16 ? cp->nzones : 16;
46319872Swollman	rv = dialog_menu(buf, "Select a zone which observes the same time as "
46419872Swollman			 "your locality.", -1, -1, menulen, -cp->nzones,
46519872Swollman			 cp->submenu, 0, 0, 0);
46619872Swollman	if (rv != 0)
46719872Swollman		return DITEM_RECREATE;
46819872Swollman	return DITEM_LEAVE_MENU;
46919872Swollman}
47019872Swollman
47119872Swollmanstatic int
47219872Swollmaninstall_zone_file(const char *filename)
47319872Swollman{
47419872Swollman	struct stat sb;
47519872Swollman	int fd1, fd2;
47619872Swollman	int copymode;
47719872Swollman	char *msg;
47819872Swollman	ssize_t len;
47919872Swollman	char buf[1024];
48019872Swollman
48119872Swollman	if (lstat(_PATH_LOCALTIME, &sb) < 0)
48219872Swollman		/* Nothing there yet... */
48319872Swollman		copymode = 1;
48419872Swollman	else if(S_ISLNK(sb.st_mode))
48519872Swollman		copymode = 0;
48619872Swollman	else
48719872Swollman		copymode = 1;
48819872Swollman
48921915Sjkh#ifdef VERBOSE
49019872Swollman	if (copymode)
49119872Swollman		asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
49219872Swollman	else
49319872Swollman		asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
49419872Swollman			 " to %s", filename);
49519872Swollman
49619872Swollman	dialog_notify(msg);
49719872Swollman	free(msg);
49821915Sjkh#endif
49919872Swollman
50019872Swollman	if (reallydoit) {
50119872Swollman		if (copymode) {
50219872Swollman			fd1 = open(filename, O_RDONLY, 0);
50319872Swollman			if (fd1 < 0) {
50419872Swollman				asprintf(&msg, "Could not open %s: %s",
50519872Swollman					 filename, strerror(errno));
50619872Swollman				dialog_mesgbox("Error", msg, 8, 72);
50719872Swollman				free(msg);
50819872Swollman				return DITEM_FAILURE | DITEM_RECREATE;
50919872Swollman			}
51019872Swollman
51119872Swollman			unlink(_PATH_LOCALTIME);
51219872Swollman			fd2 = open(_PATH_LOCALTIME,
51349435Sru				   O_CREAT|O_EXCL|O_WRONLY,
51449435Sru				   S_IRUSR|S_IRGRP|S_IROTH);
51519872Swollman			if (fd2 < 0) {
51619872Swollman				asprintf(&msg, "Could not open "
51719872Swollman					 _PATH_LOCALTIME ": %s",
51819872Swollman					 strerror(errno));
51919872Swollman				dialog_mesgbox("Error", msg, 8, 72);
52019872Swollman				free(msg);
52119872Swollman				return DITEM_FAILURE | DITEM_RECREATE;
52219872Swollman			}
52319872Swollman
52419872Swollman			while ((len = read(fd1, buf, sizeof buf)) > 0)
52519872Swollman				len = write(fd2, buf, len);
52619872Swollman
52719872Swollman			if (len == -1) {
52819872Swollman				asprintf(&msg, "Error copying %s to "
52919872Swollman					 _PATH_LOCALTIME ": %s",
53049435Sru					 filename, strerror(errno));
53119872Swollman				dialog_mesgbox("Error", msg, 8, 72);
53219872Swollman				free(msg);
53319872Swollman				/* Better to leave none than a corrupt one. */
53419872Swollman				unlink(_PATH_LOCALTIME);
53519872Swollman				return DITEM_FAILURE | DITEM_RECREATE;
53619872Swollman			}
53719872Swollman			close(fd1);
53819872Swollman			close(fd2);
53919872Swollman		} else {
54019872Swollman			if (access(filename, R_OK) != 0) {
54119872Swollman				asprintf(&msg, "Cannot access %s: %s",
54219872Swollman					 filename, strerror(errno));
54319872Swollman				dialog_mesgbox("Error", msg, 8, 72);
54419872Swollman				free(msg);
54519872Swollman				return DITEM_FAILURE | DITEM_RECREATE;
54619872Swollman			}
54719872Swollman			unlink(_PATH_LOCALTIME);
54819872Swollman			if (symlink(filename, _PATH_LOCALTIME) < 0) {
54919872Swollman				asprintf(&msg, "Cannot create symbolic link "
55019872Swollman					 _PATH_LOCALTIME " to %s: %s",
55119872Swollman					 filename, strerror(errno));
55219872Swollman				dialog_mesgbox("Error", msg, 8, 72);
55319872Swollman				free(msg);
55419872Swollman				return DITEM_FAILURE | DITEM_RECREATE;
55519872Swollman			}
55619872Swollman		}
55719872Swollman	}
55819872Swollman
55921915Sjkh#ifdef VERBOSE
56019872Swollman	if (copymode)
56119872Swollman		asprintf(&msg, "Copied timezone file from %s to "
56219872Swollman			 _PATH_LOCALTIME, filename);
56319872Swollman	else
56419872Swollman		asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
56519872Swollman			 " to %s", filename);
56619872Swollman
56719872Swollman	dialog_mesgbox("Done", msg, 8, 72);
56819872Swollman	free(msg);
56921915Sjkh#endif
57019872Swollman	return DITEM_LEAVE_MENU;
57119872Swollman}
57219872Swollman
57319872Swollmanstatic int
57419872Swollmanconfirm_zone(const char *filename)
57519872Swollman{
57619872Swollman	char *msg;
57719872Swollman	struct tm *tm;
57819872Swollman	time_t t = time(0);
57919872Swollman	int rv;
58019872Swollman
58119872Swollman	setenv("TZ", filename, 1);
58219872Swollman	tzset();
58319872Swollman	tm = localtime(&t);
58419872Swollman
58519872Swollman	asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
58619872Swollman		 tm->tm_zone);
58719872Swollman	rv = !dialog_yesno("Confirmation", msg, 4, 72);
58819872Swollman	free(msg);
58919872Swollman	return rv;
59019872Swollman}
59119872Swollman
59219872Swollmanstatic int
59319872Swollmanset_zone_multi(dialogMenuItem *dmi)
59419872Swollman{
59519872Swollman	char *fn;
59619872Swollman	struct zone *zp = dmi->data;
59719872Swollman	int rv;
59819872Swollman
59919872Swollman	if (!confirm_zone(zp->filename))
60019872Swollman		return DITEM_FAILURE | DITEM_RECREATE;
60119872Swollman
60219872Swollman	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
60319872Swollman	rv = install_zone_file(fn);
60419872Swollman	free(fn);
60519872Swollman	return rv;
60619872Swollman}
60719872Swollman
60819872Swollmanstatic int
60919872Swollmanset_zone_whole_country(dialogMenuItem *dmi)
61019872Swollman{
61119872Swollman	char *fn;
61219872Swollman	struct country *cp = dmi->data;
61319872Swollman	int rv;
61419872Swollman
61519872Swollman	if (!confirm_zone(cp->filename))
61619872Swollman		return DITEM_FAILURE | DITEM_RECREATE;
61719872Swollman
61819872Swollman	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
61919872Swollman	rv = install_zone_file(fn);
62019872Swollman	free(fn);
62119872Swollman	return rv;
62219872Swollman}
62319872Swollman
62430763Scharnierstatic void
62530763Scharnierusage()
62630763Scharnier{
62730763Scharnier	fprintf(stderr, "usage: tzsetup [-n]\n");
62830763Scharnier	exit(1);
62930763Scharnier}
63030763Scharnier
63119872Swollmanint
63219872Swollmanmain(int argc, char **argv)
63319872Swollman{
63441852Speter	int c, fd;
63519872Swollman
63619872Swollman	while ((c = getopt(argc, argv, "n")) != -1) {
63719872Swollman		switch(c) {
63819872Swollman		case 'n':
63919872Swollman			reallydoit = 0;
64019872Swollman			break;
64119872Swollman
64219872Swollman		default:
64330763Scharnier			usage();
64419872Swollman		}
64519872Swollman	}
64619872Swollman
64743544Swollman	if (argc - optind > 1)
64830763Scharnier		usage();
64919872Swollman
65049435Sru	/* Override the user-supplied umask. */
65149435Sru	(void)umask(S_IWGRP|S_IWOTH);
65249435Sru
65319872Swollman	read_iso3166_table();
65419872Swollman	read_zones();
65519872Swollman	sort_countries();
65619872Swollman	make_menus();
65719872Swollman
65819872Swollman	init_dialog();
65922815Sjkh	if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock",
66022815Sjkh			  "Is this machine's CMOS clock set to UTC?  If it is set to local time,\n"
66148121Smharo			  "or you don't know, please choose NO here!", 7, 72)) {
66230763Scharnier		if (reallydoit)
66341852Speter			unlink(_PATH_WALL_CMOS_CLOCK);
66432394Ssteve	} else {
66541852Speter		if (reallydoit) {
66641852Speter			fd = open(_PATH_WALL_CMOS_CLOCK,
66749435Sru				  O_WRONLY|O_CREAT|O_TRUNC,
66849435Sru				  S_IRUSR|S_IRGRP|S_IROTH);
66941852Speter			if (fd < 0)
67041852Speter				err(1, "create %s", _PATH_WALL_CMOS_CLOCK);
67141852Speter			close(fd);
67241852Speter		}
67332394Ssteve	}
67422815Sjkh	dialog_clear_norefresh();
67543544Swollman	if (optind == argc - 1) {
67643544Swollman		char *msg;
67743544Swollman		asprintf(&msg, "\nUse the default `%s' zone?", argv[optind]);
67843544Swollman		if (!dialog_yesno("Default timezone provided", msg, 7, 72)) {
67943544Swollman			install_zone_file(argv[optind]);
68043544Swollman			dialog_clear();
68143544Swollman			end_dialog();
68243544Swollman			return 0;
68343544Swollman		}
68443544Swollman		free(msg);
68543544Swollman		dialog_clear_norefresh();
68643544Swollman	}
68719872Swollman	dialog_menu("Time Zone Selector", "Select a region", -1, -1,
68819887Sjoerg		    NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
68943544Swollman
69022815Sjkh	dialog_clear();
69119872Swollman	end_dialog();
69219872Swollman	return 0;
69319872Swollman}
69419872Swollman
695