tzsetup.c revision 22997
159191Skris/*
259191Skris * Copyright 1996 Massachusetts Institute of Technology
3160814Ssimon *
4160814Ssimon * Permission to use, copy, modify, and distribute this software and
5280304Sjkim * its documentation for any purpose and without fee is hereby
6280304Sjkim * granted, provided that both the above copyright notice and this
7280304Sjkim * permission notice appear in all copies, that both the above
8280304Sjkim * copyright notice and this permission notice appear in all
9160814Ssimon * supporting documentation, and that the name of M.I.T. not be used
10280304Sjkim * in advertising or publicity pertaining to distribution of the
11160814Ssimon * software without specific, written prior permission.  M.I.T. makes
12280304Sjkim * no representations about the suitability of this software for any
13280304Sjkim * purpose.  It is provided "as is" without express or implied
14280304Sjkim * warranty.
1559191Skris *
1659191Skris * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1759191Skris * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18280304Sjkim * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1959191Skris * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20280304Sjkim * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21280304Sjkim * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22280304Sjkim * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23280304Sjkim * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24280304Sjkim * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25280304Sjkim * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26280304Sjkim * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27280304Sjkim * SUCH DAMAGE.
28280304Sjkim *
29280304Sjkim *	$Id$
30280304Sjkim */
31280304Sjkim
3259191Skris/*
3359191Skris * Second attempt at a `tzmenu' program, using the separate description
3459191Skris * files provided in newer tzdata releases.
35280304Sjkim */
36280304Sjkim
37280304Sjkim#include <sys/types.h>
38280304Sjkim#include <dialog.h>
39280304Sjkim#include <err.h>
40280304Sjkim#include <errno.h>
41280304Sjkim#include <stdio.h>
42280304Sjkim#include <stdlib.h>
43280304Sjkim#include <string.h>
44280304Sjkim#include <unistd.h>
45280304Sjkim
46280304Sjkim#include <sys/fcntl.h>
47280304Sjkim#include <sys/queue.h>
48280304Sjkim#include <sys/stat.h>
49280304Sjkim
50280304Sjkim#include "paths.h"
51280304Sjkim
52280304Sjkimstatic int reallydoit = 1;
53280304Sjkim
54280304Sjkimstatic int continent_country_menu(dialogMenuItem *);
55280304Sjkimstatic int set_zone_multi(dialogMenuItem *);
56280304Sjkimstatic int set_zone_whole_country(dialogMenuItem *);
57280304Sjkimstatic int set_zone_menu(dialogMenuItem *);
58280304Sjkim
59280304Sjkimstruct continent {
60280304Sjkim	dialogMenuItem *menu;
61280304Sjkim	int nitems;
62280304Sjkim	int ch;
63280304Sjkim	int sc;
64280304Sjkim};
65280304Sjkim
66280304Sjkimstatic struct continent africa, america, antarctica, arctic, asia, atlantic;
67280304Sjkimstatic struct continent australia, europe, indian, pacific;
68280304Sjkim
69280304Sjkimstatic struct continent_names {
70280304Sjkim	char *name;
71280304Sjkim	struct continent *continent;
72280304Sjkim} continent_names[] = {
73280304Sjkim	{ "Africa", &africa }, { "America", &america },
74280304Sjkim	{ "Antarctica", &antarctica }, { "Arctic", &arctic },
75280304Sjkim	{ "Asia", &asia },
76280304Sjkim	{ "Atlantic", &atlantic }, { "Australia", &australia },
77280304Sjkim	{ "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
78280304Sjkim};
79280304Sjkim
80280304Sjkimstatic dialogMenuItem continents[] = {
81280304Sjkim	{ "1", "Africa", 0, continent_country_menu, 0, &africa },
82280304Sjkim	{ "2", "America -- North and South", 0, continent_country_menu, 0,
83280304Sjkim		  &america },
84280304Sjkim	{ "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
85280304Sjkim	{ "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
86280304Sjkim	{ "5", "Asia", 0, continent_country_menu, 0, &asia },
87280304Sjkim	{ "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
88280304Sjkim	{ "7", "Australia", 0, continent_country_menu, 0, &australia },
89280304Sjkim	{ "8", "Europe", 0, continent_country_menu, 0, &europe },
90280304Sjkim	{ "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
91280304Sjkim	{ "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
92280304Sjkim};
93280304Sjkim#define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
94280304Sjkim#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
95280304Sjkim
96280304Sjkimstatic int
97280304Sjkimcontinent_country_menu(dialogMenuItem *continent)
98280304Sjkim{
9959191Skris	int rv;
10059191Skris	struct continent *contp = continent->data;
10159191Skris	char title[256];
10259191Skris	int isocean = OCEANP(continent - continents);
103280304Sjkim	int menulen;
104280304Sjkim
105280304Sjkim	/* Short cut -- if there's only one country, don't post a menu. */
106280304Sjkim	if (contp->nitems == 1) {
107280304Sjkim		return set_zone_menu(&contp->menu[0]);
108280304Sjkim	}
109280304Sjkim
110280304Sjkim	/* It's amazing how much good grammar really matters... */
111280304Sjkim	if (!isocean)
112280304Sjkim		snprintf(title, sizeof title, "Countries in %s",
113280304Sjkim			 continent->title);
114280304Sjkim	else
115280304Sjkim		snprintf(title, sizeof title, "Islands and groups in the %s",
116280304Sjkim			 continent->title);
117280304Sjkim
118280304Sjkim	menulen = contp->nitems < 16 ? contp->nitems : 16;
119280304Sjkim	rv = dialog_menu(title, (isocean ? "Select an island or group"
120280304Sjkim                                 : "Select a country"), -1, -1, menulen,
121280304Sjkim			 -contp->nitems, contp->menu, 0, &contp->ch,
122280304Sjkim			 &contp->sc);
123280304Sjkim	if (rv == 0)
124280304Sjkim		return DITEM_LEAVE_MENU;
125280304Sjkim	return DITEM_RECREATE;
126280304Sjkim}
127280304Sjkim
128280304Sjkimstatic struct continent *
129280304Sjkimfind_continent(const char *name)
130280304Sjkim{
131280304Sjkim	int i;
132280304Sjkim
133280304Sjkim	for (i = 0; i < NCONTINENTS; i++) {
134280304Sjkim		if (strcmp(name, continent_names[i].name) == 0)
135280304Sjkim			return continent_names[i].continent;
136280304Sjkim	}
137280304Sjkim	return 0;
138280304Sjkim}
139280304Sjkim
140280304Sjkimstruct country {
141280304Sjkim	char *name;
142280304Sjkim	char *tlc;
143280304Sjkim	int nzones;
144280304Sjkim	char *filename;		/* use iff nzones < 0 */
145280304Sjkim	struct continent *continent; /* use iff nzones < 0 */
146280304Sjkim	TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
147280304Sjkim	dialogMenuItem *submenu; /* use iff nzones > 0 */
148280304Sjkim};
149280304Sjkim
150280304Sjkimstruct zone {
151280304Sjkim	TAILQ_ENTRY(zone) link;
152280304Sjkim	char *descr;
153280304Sjkim	char *filename;
154280304Sjkim	struct continent *continent;
155280304Sjkim};
156280304Sjkim
157280304Sjkim/*
158280304Sjkim * This is the easiest organization... we use ISO 3166 country codes,
159280304Sjkim * of the two-letter variety, so we just size this array to suit.
160280304Sjkim * Beats worrying about dynamic allocation.
161280304Sjkim */
162280304Sjkim#define NCOUNTRIES	(26*26)
163280304Sjkimstatic struct country countries[NCOUNTRIES];
164280304Sjkim#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
165280304Sjkim
166280304Sjkim/*
16759191Skris * Read the ISO 3166 country code database in _PATH_ISO3166
16859191Skris * (/usr/share/misc/iso3166).  On error, exit via err(3).
169280304Sjkim */
17059191Skrisstatic void
17159191Skrisread_iso3166_table(void)
172280304Sjkim{
173280304Sjkim	FILE *fp;
174280304Sjkim	char *s, *t, *name;
175280304Sjkim	size_t len;
176280304Sjkim	int lineno;
177280304Sjkim	struct country *cp;
17859191Skris
179280304Sjkim	fp = fopen(_PATH_ISO3166, "r");
18059191Skris	if (!fp)
181280304Sjkim		err(1, _PATH_ISO3166);
18259191Skris	lineno = 0;
183280304Sjkim
18459191Skris	while ((s = fgetln(fp, &len)) != 0) {
185280304Sjkim		lineno++;
18659191Skris		if (s[len - 1] != '\n')
187280304Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
188280304Sjkim		s[len - 1] = '\0';
189280304Sjkim		if (s[0] == '#')
19059191Skris			continue;
191280304Sjkim
19259191Skris		/* Isolate the two-letter code. */
193280304Sjkim		t = strsep(&s, "\t");
19459191Skris		if (t == 0 || strlen(t) != 2)
195280304Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
19659191Skris		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
197280304Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
19859191Skris			     lineno, t);
199280304Sjkim
20059191Skris		/* Now skip past the three-letter and numeric codes. */
201280304Sjkim		name = strsep(&s, "\t"); /* 3-let */
20259191Skris		if (name == 0 || strlen(name) != 3)
203280304Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
20459191Skris		name = strsep(&s, "\t"); /* numeric */
205280304Sjkim		if (name == 0 || strlen(name) != 3)
20659191Skris			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
207280304Sjkim
20859191Skris		name = s;
209280304Sjkim
210280304Sjkim		cp = &countries[CODE2INT(t)];
21159191Skris		if (cp->name)
21259191Skris			errx(1, _PATH_ISO3166
21359191Skris			     ":%d: country code `%s' multiply defined: %s",
214280304Sjkim			     lineno, t, cp->name);
215280304Sjkim		cp->name = strdup(name);
216280304Sjkim		cp->tlc = strdup(t);
21759191Skris	}
218280304Sjkim
21959191Skris	fclose(fp);
220280304Sjkim}
22159191Skris
222280304Sjkimstatic void
223280304Sjkimadd_zone_to_country(int lineno, const char *tlc, const char *descr,
224280304Sjkim		    const char *file, struct continent *cont)
22559191Skris{
226280304Sjkim	struct zone *zp;
22759191Skris	struct country *cp;
228280304Sjkim
22959191Skris	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
230280304Sjkim		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
23159191Skris		     lineno, tlc);
232280304Sjkim
23359191Skris	cp = &countries[CODE2INT(tlc)];
234280304Sjkim	if (cp->name == 0)
23559191Skris		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
236280304Sjkim		     lineno, tlc);
23759191Skris
238280304Sjkim	if (descr) {
23959191Skris		if (cp->nzones < 0)
240280304Sjkim			errx(1, _PATH_ZONETAB
24159191Skris			     ":%d: conflicting zone definition", lineno);
242280304Sjkim
24359191Skris		zp = malloc(sizeof *zp);
244280304Sjkim		if (zp == 0)
24559191Skris			err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
246280304Sjkim
24759191Skris		if (cp->nzones == 0)
248280304Sjkim			TAILQ_INIT(&cp->zones);
249280304Sjkim
25059191Skris		zp->descr = strdup(descr);
251280304Sjkim		zp->filename = strdup(file);
25259191Skris		zp->continent = cont;
253280304Sjkim		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
254280304Sjkim		cp->nzones++;
255280304Sjkim	} else {
256280304Sjkim		if (cp->nzones > 0)
257280304Sjkim			errx(1, _PATH_ZONETAB
25859191Skris			     ":%d: zone must have description", lineno);
25959191Skris		if (cp->nzones < 0)
260280304Sjkim			errx(1, _PATH_ZONETAB
26159191Skris			     ":%d: zone multiply defined", lineno);
26259191Skris		cp->nzones = -1;
26359191Skris		cp->filename = strdup(file);
26459191Skris		cp->continent = cont;
26559191Skris	}
26659191Skris}
26759191Skris
26859191Skris/*
26959191Skris * This comparison function intentionally sorts all of the null-named
27059191Skris * ``countries''---i.e., the codes that don't correspond to a real
27159191Skris * country---to the end.  Everything else is lexical by country name.
272280304Sjkim */
27359191Skrisstatic int
27459191Skriscompare_countries(const void *xa, const void *xb)
27559191Skris{
27659191Skris	const struct country *a = xa, *b = xb;
27759191Skris
27859191Skris	if (a->name == 0 && b->name == 0)
27959191Skris		return 0;
28059191Skris	if (a->name == 0 && b->name != 0)
28159191Skris		return 1;
28259191Skris	if (b->name == 0)
28359191Skris		return -1;
28459191Skris
285	return strcmp(a->name, b->name);
286}
287
288/*
289 * This must be done AFTER all zone descriptions are read, since it breaks
290 * CODE2INT().
291 */
292static void
293sort_countries(void)
294{
295	qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
296}
297
298static void
299read_zones(void)
300{
301	FILE *fp;
302	char *line;
303	size_t len;
304	int lineno;
305	char *tlc, *coord, *file, *descr, *p;
306	char contbuf[16];
307	struct continent *cont;
308
309	fp = fopen(_PATH_ZONETAB, "r");
310	if (!fp)
311		err(1, _PATH_ZONETAB);
312	lineno = 0;
313
314	while ((line = fgetln(fp, &len)) != 0) {
315		lineno++;
316		if (line[len - 1] != '\n')
317			errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
318		line[len - 1] = '\0';
319		if (line[0] == '#')
320			continue;
321
322		tlc = strsep(&line, "\t");
323		if (strlen(tlc) != 2)
324			errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
325			     lineno, tlc);
326		coord = strsep(&line, "\t");
327		file = strsep(&line, "\t");
328		p = strchr(file, '/');
329		if (p == 0)
330			errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
331			     lineno, file);
332		contbuf[0] = '\0';
333		strncat(contbuf, file, p - file);
334		cont = find_continent(contbuf);
335		if (!cont)
336			errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
337			     lineno, contbuf);
338
339		descr = (line && *line) ? line : 0;
340
341		add_zone_to_country(lineno, tlc, descr, file, cont);
342	}
343	fclose(fp);
344}
345
346static void
347make_menus(void)
348{
349	struct country *cp;
350	struct zone *zp, *zp2;
351	struct continent *cont;
352	dialogMenuItem *dmi;
353	int i;
354
355	/*
356	 * First, count up all the countries in each continent/ocean.
357	 * Be careful to count those countries which have multiple zones
358	 * only once for each.  NB: some countries are in multiple
359	 * continents/oceans.
360	 */
361	for (cp = countries; cp->name; cp++) {
362		if (cp->nzones == 0)
363			continue;
364		if (cp->nzones < 0) {
365			cp->continent->nitems++;
366		} else {
367			for (zp = cp->zones.tqh_first; zp;
368			     zp = zp->link.tqe_next) {
369				cont = zp->continent;
370				for (zp2 = cp->zones.tqh_first;
371				     zp2->continent != cont;
372				     zp2 = zp2->link.tqe_next)
373					;
374				if (zp2 == zp)
375					zp->continent->nitems++;
376			}
377		}
378	}
379
380	/*
381	 * Now allocate memory for the country menus.  We set
382	 * nitems back to zero so that we can use it for counting
383	 * again when we actually build the menus.
384	 */
385	for (i = 0; i < NCONTINENTS; i++) {
386		continent_names[i].continent->menu =
387			malloc(sizeof(dialogMenuItem) *
388			       continent_names[i].continent->nitems);
389		if (continent_names[i].continent->menu == 0)
390			err(1, "malloc for continent menu");
391		continent_names[i].continent->nitems = 0;
392	}
393
394	/*
395	 * Now that memory is allocated, create the menu items for
396	 * each continent.  For multiple-zone countries, also create
397	 * the country's zone submenu.
398	 */
399	for (cp = countries; cp->name; cp++) {
400		if (cp->nzones == 0)
401			continue;
402		if (cp->nzones < 0) {
403			dmi = &cp->continent->menu[cp->continent->nitems];
404			memset(dmi, 0, sizeof *dmi);
405			asprintf(&dmi->prompt, "%d",
406				 ++cp->continent->nitems);
407			dmi->title = cp->name;
408			dmi->checked = 0;
409			dmi->fire = set_zone_whole_country;
410			dmi->selected = 0;
411			dmi->data = cp;
412		} else {
413			cp->submenu = malloc(cp->nzones * sizeof *dmi);
414			if (cp->submenu == 0)
415				err(1, "malloc for submenu");
416			cp->nzones = 0;
417			for (zp = cp->zones.tqh_first; zp;
418			     zp = zp->link.tqe_next) {
419				cont = zp->continent;
420				dmi = &cp->submenu[cp->nzones];
421				memset(dmi, 0, sizeof *dmi);
422				asprintf(&dmi->prompt, "%d",
423					 ++cp->nzones);
424				dmi->title = zp->descr;
425				dmi->checked = 0;
426				dmi->fire = set_zone_multi;
427				dmi->selected = 0;
428				dmi->data = zp;
429
430				for (zp2 = cp->zones.tqh_first;
431				     zp2->continent != cont;
432				     zp2 = zp2->link.tqe_next)
433					;
434				if (zp2 != zp)
435					continue;
436
437				dmi = &cont->menu[cont->nitems];
438				memset(dmi, 0, sizeof *dmi);
439				asprintf(&dmi->prompt, "%d", ++cont->nitems);
440				dmi->title = cp->name;
441				dmi->checked = 0;
442				dmi->fire = set_zone_menu;
443				dmi->selected = 0;
444				dmi->data = cp;
445			}
446		}
447	}
448}
449
450static int
451set_zone_menu(dialogMenuItem *dmi)
452{
453	int rv;
454	char buf[256];
455	struct country *cp = dmi->data;
456	int menulen;
457
458	snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
459	menulen = cp->nzones < 16 ? cp->nzones : 16;
460	rv = dialog_menu(buf, "Select a zone which observes the same time as "
461			 "your locality.", -1, -1, menulen, -cp->nzones,
462			 cp->submenu, 0, 0, 0);
463	if (rv != 0)
464		return DITEM_RECREATE;
465	return DITEM_LEAVE_MENU;
466}
467
468static int
469install_zone_file(const char *filename)
470{
471	struct stat sb;
472	int fd1, fd2;
473	int copymode;
474	char *msg;
475	ssize_t len;
476	char buf[1024];
477
478	if (lstat(_PATH_LOCALTIME, &sb) < 0)
479		/* Nothing there yet... */
480		copymode = 1;
481	else if(S_ISLNK(sb.st_mode))
482		copymode = 0;
483	else
484		copymode = 1;
485
486#ifdef VERBOSE
487	if (copymode)
488		asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
489	else
490		asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
491			 " to %s", filename);
492
493	dialog_notify(msg);
494	free(msg);
495#endif
496
497	if (reallydoit) {
498		if (copymode) {
499			fd1 = open(filename, O_RDONLY, 0);
500			if (fd1 < 0) {
501				asprintf(&msg, "Could not open %s: %s",
502					 filename, strerror(errno));
503				dialog_mesgbox("Error", msg, 8, 72);
504				free(msg);
505				return DITEM_FAILURE | DITEM_RECREATE;
506			}
507
508			unlink(_PATH_LOCALTIME);
509			fd2 = open(_PATH_LOCALTIME,
510				   O_CREAT | O_EXCL | O_WRONLY,
511				   0444);
512			if (fd2 < 0) {
513				asprintf(&msg, "Could not open "
514					 _PATH_LOCALTIME ": %s",
515					 strerror(errno));
516				dialog_mesgbox("Error", msg, 8, 72);
517				free(msg);
518				return DITEM_FAILURE | DITEM_RECREATE;
519			}
520
521			while ((len = read(fd1, buf, sizeof buf)) > 0)
522				len = write(fd2, buf, len);
523
524			if (len == -1) {
525				asprintf(&msg, "Error copying %s to "
526					 _PATH_LOCALTIME ": %s",
527					 strerror(errno));
528				dialog_mesgbox("Error", msg, 8, 72);
529				free(msg);
530				/* Better to leave none than a corrupt one. */
531				unlink(_PATH_LOCALTIME);
532				return DITEM_FAILURE | DITEM_RECREATE;
533			}
534			close(fd1);
535			close(fd2);
536		} else {
537			if (access(filename, R_OK) != 0) {
538				asprintf(&msg, "Cannot access %s: %s",
539					 filename, strerror(errno));
540				dialog_mesgbox("Error", msg, 8, 72);
541				free(msg);
542				return DITEM_FAILURE | DITEM_RECREATE;
543			}
544			unlink(_PATH_LOCALTIME);
545			if (symlink(filename, _PATH_LOCALTIME) < 0) {
546				asprintf(&msg, "Cannot create symbolic link "
547					 _PATH_LOCALTIME " to %s: %s",
548					 filename, strerror(errno));
549				dialog_mesgbox("Error", msg, 8, 72);
550				free(msg);
551				return DITEM_FAILURE | DITEM_RECREATE;
552			}
553		}
554	}
555
556#ifdef VERBOSE
557	if (copymode)
558		asprintf(&msg, "Copied timezone file from %s to "
559			 _PATH_LOCALTIME, filename);
560	else
561		asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
562			 " to %s", filename);
563
564	dialog_mesgbox("Done", msg, 8, 72);
565	free(msg);
566#endif
567	return DITEM_LEAVE_MENU;
568}
569
570static int
571confirm_zone(const char *filename)
572{
573	char *msg;
574	struct tm *tm;
575	time_t t = time(0);
576	int rv;
577
578	setenv("TZ", filename, 1);
579	tzset();
580	tm = localtime(&t);
581
582	asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
583		 tm->tm_zone);
584	rv = !dialog_yesno("Confirmation", msg, 4, 72);
585	free(msg);
586	return rv;
587}
588
589static int
590set_zone_multi(dialogMenuItem *dmi)
591{
592	char *fn;
593	struct zone *zp = dmi->data;
594	int rv;
595
596	if (!confirm_zone(zp->filename))
597		return DITEM_FAILURE | DITEM_RECREATE;
598
599	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
600	rv = install_zone_file(fn);
601	free(fn);
602	return rv;
603}
604
605static int
606set_zone_whole_country(dialogMenuItem *dmi)
607{
608	char *fn;
609	struct country *cp = dmi->data;
610	int rv;
611
612	if (!confirm_zone(cp->filename))
613		return DITEM_FAILURE | DITEM_RECREATE;
614
615	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
616	rv = install_zone_file(fn);
617	free(fn);
618	return rv;
619}
620
621int
622main(int argc, char **argv)
623{
624	int c;
625
626	while ((c = getopt(argc, argv, "n")) != -1) {
627		switch(c) {
628		case 'n':
629			reallydoit = 0;
630			break;
631
632		default:
633			fprintf(stderr, "%s: usage:\n\t%s [-n]\n", argv[0],
634				argv[0]);
635			exit(1);
636		}
637	}
638
639	if (optind != argc) {
640		fprintf(stderr, "%s: usage:\n\t%s [-n]\n", argv[0], argv[0]);
641		exit(1);
642	}
643
644	read_iso3166_table();
645	read_zones();
646	sort_countries();
647	make_menus();
648
649	init_dialog();
650	if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock",
651			  "Is this machine's CMOS clock set to UTC?  If it is set to local time,\n"
652			  "please choose NO here!", 7, 72))
653		system("rm -f /etc/wall_cmos_clock");
654	else
655		system("touch /etc/wall_cmos_clock");
656	dialog_clear_norefresh();
657	dialog_menu("Time Zone Selector", "Select a region", -1, -1,
658		    NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
659	dialog_clear();
660	end_dialog();
661	return 0;
662}
663
664