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