tzsetup.c revision 22815
11412SN/A/*
23994Sjjg * Copyright 1996 Massachusetts Institute of Technology
31412SN/A *
41412SN/A * Permission to use, copy, modify, and distribute this software and
51412SN/A * its documentation for any purpose and without fee is hereby
61412SN/A * granted, provided that both the above copyright notice and this
71412SN/A * permission notice appear in all copies, that both the above
81412SN/A * copyright notice and this permission notice appear in all
91412SN/A * supporting documentation, and that the name of M.I.T. not be used
101412SN/A * in advertising or publicity pertaining to distribution of the
111412SN/A * software without specific, written prior permission.  M.I.T. makes
121412SN/A * no representations about the suitability of this software for any
131412SN/A * purpose.  It is provided "as is" without express or implied
141412SN/A * warranty.
151412SN/A *
161412SN/A * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
171412SN/A * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
181412SN/A * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
191412SN/A * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
201412SN/A * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
211412SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
221412SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
231412SN/A * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
241412SN/A * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
253170Svasya * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
261412SN/A * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271412SN/A * SUCH DAMAGE.
281412SN/A *
291412SN/A *	$FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 22815 1997-02-16 23:51:03Z jkh $
301412SN/A */
311412SN/A
321412SN/A/*
331412SN/A * Second attempt at a `tzmenu' program, using the separate description
341412SN/A * files provided in newer tzdata releases.
351412SN/A */
364040Sjjg
374040Sjjg#include <sys/types.h>
381412SN/A#include <dialog.h>
391412SN/A#include <err.h>
401412SN/A#include <errno.h>
411412SN/A#include <stdio.h>
421412SN/A#include <stdlib.h>
431412SN/A#include <string.h>
441412SN/A#include <unistd.h>
451412SN/A
461412SN/A#include <sys/fcntl.h>
471412SN/A#include <sys/queue.h>
481412SN/A#include <sys/stat.h>
491412SN/A
501412SN/A#include "paths.h"
511412SN/A
521412SN/Astatic int reallydoit = 1;
531412SN/A
541412SN/Astatic int continent_country_menu(dialogMenuItem *);
551412SN/Astatic int set_zone_multi(dialogMenuItem *);
561412SN/Astatic int set_zone_whole_country(dialogMenuItem *);
571412SN/Astatic int set_zone_menu(dialogMenuItem *);
581412SN/A
591412SN/Astruct continent {
601412SN/A	dialogMenuItem *menu;
611412SN/A	int nitems;
623566Sksrini	int ch;
631412SN/A	int sc;
641412SN/A};
651412SN/A
661412SN/Astatic struct continent africa, america, antarctica, arctic, asia, atlantic;
671412SN/Astatic struct continent australia, europe, indian, pacific;
681412SN/A
691412SN/Astatic struct continent_names {
701412SN/A	char *name;
711412SN/A	struct continent *continent;
721412SN/A} continent_names[] = {
731412SN/A	{ "Africa", &africa }, { "America", &america },
741412SN/A	{ "Antarctica", &antarctica }, { "Arctic", &arctic },
751412SN/A	{ "Asia", &asia },
761412SN/A	{ "Atlantic", &atlantic }, { "Australia", &australia },
771412SN/A	{ "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
781412SN/A};
791412SN/A
801412SN/Astatic dialogMenuItem continents[] = {
811412SN/A	{ "1", "Africa", 0, continent_country_menu, 0, &africa },
821412SN/A	{ "2", "America -- North and South", 0, continent_country_menu, 0,
831412SN/A		  &america },
841412SN/A	{ "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
851412SN/A	{ "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
861412SN/A	{ "5", "Asia", 0, continent_country_menu, 0, &asia },
871412SN/A	{ "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
881412SN/A	{ "7", "Australia", 0, continent_country_menu, 0, &australia },
892492SN/A	{ "8", "Europe", 0, continent_country_menu, 0, &europe },
901412SN/A	{ "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
911412SN/A	{ "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
921412SN/A};
931412SN/A#define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
941412SN/A#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
951412SN/A
961412SN/Astatic int
971412SN/Acontinent_country_menu(dialogMenuItem *continent)
981412SN/A{
991412SN/A	int rv;
1001412SN/A	struct continent *contp = continent->data;
1011412SN/A	char title[256];
1021412SN/A	int isocean = OCEANP(continent - continents);
1031412SN/A	int menulen;
1041412SN/A
1051412SN/A	/* Short cut -- if there's only one country, don't post a menu. */
1061412SN/A	if (contp->nitems == 1) {
1071412SN/A		return set_zone_menu(&contp->menu[0]);
1081412SN/A	}
1091412SN/A
1101412SN/A	/* It's amazing how much good grammar really matters... */
1111412SN/A	if (!isocean)
1121412SN/A		snprintf(title, sizeof title, "Countries in %s",
1131412SN/A			 continent->title);
1141412SN/A	else
1151412SN/A		snprintf(title, sizeof title, "Islands and groups in the %s",
1161412SN/A			 continent->title);
1171412SN/A
1181412SN/A	menulen = contp->nitems < 16 ? contp->nitems : 16;
1191412SN/A	rv = dialog_menu(title, (isocean ? "Select an island or group"
1201412SN/A                                 : "Select a country"), -1, -1, menulen,
1211412SN/A			 -contp->nitems, contp->menu, 0, &contp->ch,
1221412SN/A			 &contp->sc);
1231412SN/A	if (rv == 0)
1241412SN/A		return DITEM_LEAVE_MENU;
1251412SN/A	return DITEM_RECREATE;
1263994Sjjg}
1273994Sjjg
1283994Sjjgstatic struct continent *
1293994Sjjgfind_continent(const char *name)
1303994Sjjg{
1313994Sjjg	int i;
1323994Sjjg
1333994Sjjg	for (i = 0; i < NCONTINENTS; i++) {
1343994Sjjg		if (strcmp(name, continent_names[i].name) == 0)
1354040Sjjg			return continent_names[i].continent;
1363994Sjjg	}
1373994Sjjg	return 0;
1383994Sjjg}
1393994Sjjg
1403994Sjjgstruct country {
1411412SN/A	char *name;
1421412SN/A	char *tlc;
1431412SN/A	int nzones;
1441412SN/A	char *filename;		/* use iff nzones < 0 */
1451412SN/A	struct continent *continent; /* use iff nzones < 0 */
1461412SN/A	TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
1471412SN/A	dialogMenuItem *submenu; /* use iff nzones > 0 */
1481412SN/A};
1491412SN/A
1501412SN/Astruct zone {
1511412SN/A	TAILQ_ENTRY(zone) link;
1521412SN/A	char *descr;
1531412SN/A	char *filename;
1541412SN/A	struct continent *continent;
1551412SN/A};
1561412SN/A
1571412SN/A/*
1581412SN/A * This is the easiest organization... we use ISO 3166 country codes,
1591412SN/A * of the two-letter variety, so we just size this array to suit.
1601412SN/A * Beats worrying about dynamic allocation.
1611412SN/A */
1621412SN/A#define NCOUNTRIES	(26*26)
1631412SN/Astatic struct country countries[NCOUNTRIES];
1641412SN/A#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
1651412SN/A
1661412SN/A/*
1671412SN/A * Read the ISO 3166 country code database in _PATH_ISO3166
1681412SN/A * (/usr/share/misc/iso3166).  On error, exit via err(3).
1691412SN/A */
1701412SN/Astatic void
1711412SN/Aread_iso3166_table(void)
1721412SN/A{
1731412SN/A	FILE *fp;
1741412SN/A	char *s, *t, *name;
1751412SN/A	size_t len;
1761412SN/A	int lineno;
1771412SN/A	struct country *cp;
1781412SN/A
1791412SN/A	fp = fopen(_PATH_ISO3166, "r");
1801412SN/A	if (!fp)
1811412SN/A		err(1, _PATH_ISO3166);
1821412SN/A	lineno = 0;
1831412SN/A
1841412SN/A	while ((s = fgetln(fp, &len)) != 0) {
1851412SN/A		lineno++;
1861412SN/A		if (s[len - 1] != '\n')
1871412SN/A			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
1881412SN/A		s[len - 1] = '\0';
1891412SN/A		if (s[0] == '#')
1901412SN/A			continue;
1911412SN/A
1921412SN/A		/* Isolate the two-letter code. */
1931412SN/A		t = strsep(&s, "\t");
1941412SN/A		if (t == 0 || strlen(t) != 2)
1951412SN/A			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
1961412SN/A		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
1971412SN/A			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
1981412SN/A			     lineno, t);
1991412SN/A
2001412SN/A		/* Now skip past the three-letter and numeric codes. */
201		name = strsep(&s, "\t"); /* 3-let */
202		if (name == 0 || strlen(name) != 3)
203			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
204		name = strsep(&s, "\t"); /* numeric */
205		if (name == 0 || strlen(name) != 3)
206			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
207
208		name = s;
209
210		cp = &countries[CODE2INT(t)];
211		if (cp->name)
212			errx(1, _PATH_ISO3166
213			     ":%d: country code `%s' multiply defined: %s",
214			     lineno, t, cp->name);
215		cp->name = strdup(name);
216		cp->tlc = strdup(t);
217	}
218
219	fclose(fp);
220}
221
222static void
223add_zone_to_country(int lineno, const char *tlc, const char *descr,
224		    const char *file, struct continent *cont)
225{
226	struct zone *zp;
227	struct country *cp;
228
229	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
230		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
231		     lineno, tlc);
232
233	cp = &countries[CODE2INT(tlc)];
234	if (cp->name == 0)
235		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
236		     lineno, tlc);
237
238	if (descr) {
239		if (cp->nzones < 0)
240			errx(1, _PATH_ZONETAB
241			     ":%d: conflicting zone definition", lineno);
242
243		zp = malloc(sizeof *zp);
244		if (zp == 0)
245			err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
246
247		if (cp->nzones == 0)
248			TAILQ_INIT(&cp->zones);
249
250		zp->descr = strdup(descr);
251		zp->filename = strdup(file);
252		zp->continent = cont;
253		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
254		cp->nzones++;
255	} else {
256		if (cp->nzones > 0)
257			errx(1, _PATH_ZONETAB
258			     ":%d: zone must have description", lineno);
259		if (cp->nzones < 0)
260			errx(1, _PATH_ZONETAB
261			     ":%d: zone multiply defined", lineno);
262		cp->nzones = -1;
263		cp->filename = strdup(file);
264		cp->continent = cont;
265	}
266}
267
268/*
269 * This comparison function intentionally sorts all of the null-named
270 * ``countries''---i.e., the codes that don't correspond to a real
271 * country---to the end.  Everything else is lexical by country name.
272 */
273static int
274compare_countries(const void *xa, const void *xb)
275{
276	const struct country *a = xa, *b = xb;
277
278	if (a->name == 0 && b->name == 0)
279		return 0;
280	if (a->name == 0 && b->name != 0)
281		return 1;
282	if (b->name == 0)
283		return -1;
284
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