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