xref: /dragonfly/usr.sbin/tzsetup/tzsetup.c (revision 984263bc)
1 /*
2  * Copyright 1996 Massachusetts Institute of Technology
3  *
4  * Permission to use, copy, modify, and distribute this software and
5  * its documentation for any purpose and without fee is hereby
6  * granted, provided that both the above copyright notice and this
7  * permission notice appear in all copies, that both the above
8  * copyright notice and this permission notice appear in all
9  * supporting documentation, and that the name of M.I.T. not be used
10  * in advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.  M.I.T. makes
12  * no representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied
14  * warranty.
15  *
16  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 /*
31  * Second attempt at a `tzmenu' program, using the separate description
32  * files provided in newer tzdata releases.
33  */
34 
35 #ifndef lint
36 static const char rcsid[] =
37   "$FreeBSD: src/usr.sbin/tzsetup/tzsetup.c,v 1.16.2.2 2002/03/06 06:17:41 obrien Exp $";
38 #endif /* not lint */
39 
40 #include <sys/types.h>
41 #include <dialog.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include <sys/fcntl.h>
50 #include <sys/queue.h>
51 #include <sys/stat.h>
52 
53 #include "paths.h"
54 
55 static int reallydoit = 1;
56 
57 static int continent_country_menu(dialogMenuItem *);
58 static int set_zone_multi(dialogMenuItem *);
59 static int set_zone_whole_country(dialogMenuItem *);
60 static int set_zone_menu(dialogMenuItem *);
61 
62 struct continent {
63 	dialogMenuItem *menu;
64 	int nitems;
65 	int ch;
66 	int sc;
67 };
68 
69 static struct continent africa, america, antarctica, arctic, asia, atlantic;
70 static struct continent australia, europe, indian, pacific;
71 
72 static struct continent_names {
73 	char *name;
74 	struct continent *continent;
75 } continent_names[] = {
76 	{ "Africa", &africa }, { "America", &america },
77 	{ "Antarctica", &antarctica }, { "Arctic", &arctic },
78 	{ "Asia", &asia },
79 	{ "Atlantic", &atlantic }, { "Australia", &australia },
80 	{ "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
81 };
82 
83 static dialogMenuItem continents[] = {
84 	{ "1", "Africa", 0, continent_country_menu, 0, &africa },
85 	{ "2", "America -- North and South", 0, continent_country_menu, 0,
86 		  &america },
87 	{ "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
88 	{ "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
89 	{ "5", "Asia", 0, continent_country_menu, 0, &asia },
90 	{ "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
91 	{ "7", "Australia", 0, continent_country_menu, 0, &australia },
92 	{ "8", "Europe", 0, continent_country_menu, 0, &europe },
93 	{ "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
94 	{ "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
95 };
96 #define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
97 #define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
98 
99 static int
100 continent_country_menu(dialogMenuItem *continent)
101 {
102 	int rv;
103 	struct continent *contp = continent->data;
104 	char title[256];
105 	int isocean = OCEANP(continent - continents);
106 	int menulen;
107 
108 	/* Short cut -- if there's only one country, don't post a menu. */
109 	if (contp->nitems == 1) {
110 		return set_zone_menu(&contp->menu[0]);
111 	}
112 
113 	/* It's amazing how much good grammar really matters... */
114 	if (!isocean)
115 		snprintf(title, sizeof title, "Countries in %s",
116 			 continent->title);
117 	else
118 		snprintf(title, sizeof title, "Islands and groups in the %s",
119 			 continent->title);
120 
121 	menulen = contp->nitems < 16 ? contp->nitems : 16;
122 	rv = dialog_menu(title, (isocean ? "Select an island or group"
123                                  : "Select a country"), -1, -1, menulen,
124 			 -contp->nitems, contp->menu, 0, &contp->ch,
125 			 &contp->sc);
126 	if (rv == 0)
127 		return DITEM_LEAVE_MENU;
128 	return DITEM_RECREATE;
129 }
130 
131 static struct continent *
132 find_continent(const char *name)
133 {
134 	int i;
135 
136 	for (i = 0; i < NCONTINENTS; i++) {
137 		if (strcmp(name, continent_names[i].name) == 0)
138 			return continent_names[i].continent;
139 	}
140 	return 0;
141 }
142 
143 struct country {
144 	char *name;
145 	char *tlc;
146 	int nzones;
147 	char *filename;		/* use iff nzones < 0 */
148 	struct continent *continent; /* use iff nzones < 0 */
149 	TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
150 	dialogMenuItem *submenu; /* use iff nzones > 0 */
151 };
152 
153 struct zone {
154 	TAILQ_ENTRY(zone) link;
155 	char *descr;
156 	char *filename;
157 	struct continent *continent;
158 };
159 
160 /*
161  * This is the easiest organization... we use ISO 3166 country codes,
162  * of the two-letter variety, so we just size this array to suit.
163  * Beats worrying about dynamic allocation.
164  */
165 #define NCOUNTRIES	(26*26)
166 static struct country countries[NCOUNTRIES];
167 #define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
168 
169 /*
170  * Read the ISO 3166 country code database in _PATH_ISO3166
171  * (/usr/share/misc/iso3166).  On error, exit via err(3).
172  */
173 static void
174 read_iso3166_table(void)
175 {
176 	FILE *fp;
177 	char *s, *t, *name;
178 	size_t len;
179 	int lineno;
180 	struct country *cp;
181 
182 	fp = fopen(_PATH_ISO3166, "r");
183 	if (!fp)
184 		err(1, _PATH_ISO3166);
185 	lineno = 0;
186 
187 	while ((s = fgetln(fp, &len)) != 0) {
188 		lineno++;
189 		if (s[len - 1] != '\n')
190 			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
191 		s[len - 1] = '\0';
192 		if (s[0] == '#' || strspn(s, " \t") == len - 1)
193 			continue;
194 
195 		/* Isolate the two-letter code. */
196 		t = strsep(&s, "\t");
197 		if (t == 0 || strlen(t) != 2)
198 			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
199 		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
200 			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
201 			     lineno, t);
202 
203 		/* Now skip past the three-letter and numeric codes. */
204 		name = strsep(&s, "\t"); /* 3-let */
205 		if (name == 0 || strlen(name) != 3)
206 			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
207 		name = strsep(&s, "\t"); /* numeric */
208 		if (name == 0 || strlen(name) != 3)
209 			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
210 
211 		name = s;
212 
213 		cp = &countries[CODE2INT(t)];
214 		if (cp->name)
215 			errx(1, _PATH_ISO3166
216 			     ":%d: country code `%s' multiply defined: %s",
217 			     lineno, t, cp->name);
218 		cp->name = strdup(name);
219 		if (cp->name == NULL)
220 			errx(1, "malloc failed");
221 		cp->tlc = strdup(t);
222 		if (cp->tlc == NULL)
223 			errx(1, "malloc failed");
224 	}
225 
226 	fclose(fp);
227 }
228 
229 static void
230 add_zone_to_country(int lineno, const char *tlc, const char *descr,
231 		    const char *file, struct continent *cont)
232 {
233 	struct zone *zp;
234 	struct country *cp;
235 
236 	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
237 		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
238 		     lineno, tlc);
239 
240 	cp = &countries[CODE2INT(tlc)];
241 	if (cp->name == 0)
242 		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
243 		     lineno, tlc);
244 
245 	if (descr) {
246 		if (cp->nzones < 0)
247 			errx(1, _PATH_ZONETAB
248 			     ":%d: conflicting zone definition", lineno);
249 
250 		zp = malloc(sizeof *zp);
251 		if (zp == 0)
252 			errx(1, "malloc(%lu)", (unsigned long)sizeof *zp);
253 
254 		if (cp->nzones == 0)
255 			TAILQ_INIT(&cp->zones);
256 
257 		zp->descr = strdup(descr);
258 		if (zp->descr == NULL)
259 			errx(1, "malloc failed");
260 		zp->filename = strdup(file);
261 		if (zp->filename == NULL)
262 			errx(1, "malloc failed");
263 		zp->continent = cont;
264 		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
265 		cp->nzones++;
266 	} else {
267 		if (cp->nzones > 0)
268 			errx(1, _PATH_ZONETAB
269 			     ":%d: zone must have description", lineno);
270 		if (cp->nzones < 0)
271 			errx(1, _PATH_ZONETAB
272 			     ":%d: zone multiply defined", lineno);
273 		cp->nzones = -1;
274 		cp->filename = strdup(file);
275 		if (cp->filename == NULL)
276 			errx(1, "malloc failed");
277 		cp->continent = cont;
278 	}
279 }
280 
281 /*
282  * This comparison function intentionally sorts all of the null-named
283  * ``countries''---i.e., the codes that don't correspond to a real
284  * country---to the end.  Everything else is lexical by country name.
285  */
286 static int
287 compare_countries(const void *xa, const void *xb)
288 {
289 	const struct country *a = xa, *b = xb;
290 
291 	if (a->name == 0 && b->name == 0)
292 		return 0;
293 	if (a->name == 0 && b->name != 0)
294 		return 1;
295 	if (b->name == 0)
296 		return -1;
297 
298 	return strcmp(a->name, b->name);
299 }
300 
301 /*
302  * This must be done AFTER all zone descriptions are read, since it breaks
303  * CODE2INT().
304  */
305 static void
306 sort_countries(void)
307 {
308 	qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
309 }
310 
311 static void
312 read_zones(void)
313 {
314 	FILE *fp;
315 	char *line;
316 	size_t len;
317 	int lineno;
318 	char *tlc, *coord, *file, *descr, *p;
319 	char contbuf[16];
320 	struct continent *cont;
321 
322 	fp = fopen(_PATH_ZONETAB, "r");
323 	if (!fp)
324 		err(1, _PATH_ZONETAB);
325 	lineno = 0;
326 
327 	while ((line = fgetln(fp, &len)) != 0) {
328 		lineno++;
329 		if (line[len - 1] != '\n')
330 			errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
331 		line[len - 1] = '\0';
332 		if (line[0] == '#')
333 			continue;
334 
335 		tlc = strsep(&line, "\t");
336 		if (strlen(tlc) != 2)
337 			errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
338 			     lineno, tlc);
339 		coord = strsep(&line, "\t");
340 		file = strsep(&line, "\t");
341 		p = strchr(file, '/');
342 		if (p == 0)
343 			errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
344 			     lineno, file);
345 		contbuf[0] = '\0';
346 		strncat(contbuf, file, p - file);
347 		cont = find_continent(contbuf);
348 		if (!cont)
349 			errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
350 			     lineno, contbuf);
351 
352 		descr = (line && *line) ? line : 0;
353 
354 		add_zone_to_country(lineno, tlc, descr, file, cont);
355 	}
356 	fclose(fp);
357 }
358 
359 static void
360 make_menus(void)
361 {
362 	struct country *cp;
363 	struct zone *zp, *zp2;
364 	struct continent *cont;
365 	dialogMenuItem *dmi;
366 	int i;
367 
368 	/*
369 	 * First, count up all the countries in each continent/ocean.
370 	 * Be careful to count those countries which have multiple zones
371 	 * only once for each.  NB: some countries are in multiple
372 	 * continents/oceans.
373 	 */
374 	for (cp = countries; cp->name; cp++) {
375 		if (cp->nzones == 0)
376 			continue;
377 		if (cp->nzones < 0) {
378 			cp->continent->nitems++;
379 		} else {
380 			for (zp = cp->zones.tqh_first; zp;
381 			     zp = zp->link.tqe_next) {
382 				cont = zp->continent;
383 				for (zp2 = cp->zones.tqh_first;
384 				     zp2->continent != cont;
385 				     zp2 = zp2->link.tqe_next)
386 					;
387 				if (zp2 == zp)
388 					zp->continent->nitems++;
389 			}
390 		}
391 	}
392 
393 	/*
394 	 * Now allocate memory for the country menus.  We set
395 	 * nitems back to zero so that we can use it for counting
396 	 * again when we actually build the menus.
397 	 */
398 	for (i = 0; i < NCONTINENTS; i++) {
399 		continent_names[i].continent->menu =
400 			malloc(sizeof(dialogMenuItem) *
401 			       continent_names[i].continent->nitems);
402 		if (continent_names[i].continent->menu == 0)
403 			errx(1, "malloc for continent menu");
404 		continent_names[i].continent->nitems = 0;
405 	}
406 
407 	/*
408 	 * Now that memory is allocated, create the menu items for
409 	 * each continent.  For multiple-zone countries, also create
410 	 * the country's zone submenu.
411 	 */
412 	for (cp = countries; cp->name; cp++) {
413 		if (cp->nzones == 0)
414 			continue;
415 		if (cp->nzones < 0) {
416 			dmi = &cp->continent->menu[cp->continent->nitems];
417 			memset(dmi, 0, sizeof *dmi);
418 			asprintf(&dmi->prompt, "%d",
419 				 ++cp->continent->nitems);
420 			dmi->title = cp->name;
421 			dmi->checked = 0;
422 			dmi->fire = set_zone_whole_country;
423 			dmi->selected = 0;
424 			dmi->data = cp;
425 		} else {
426 			cp->submenu = malloc(cp->nzones * sizeof *dmi);
427 			if (cp->submenu == 0)
428 				errx(1, "malloc for submenu");
429 			cp->nzones = 0;
430 			for (zp = cp->zones.tqh_first; zp;
431 			     zp = zp->link.tqe_next) {
432 				cont = zp->continent;
433 				dmi = &cp->submenu[cp->nzones];
434 				memset(dmi, 0, sizeof *dmi);
435 				asprintf(&dmi->prompt, "%d",
436 					 ++cp->nzones);
437 				dmi->title = zp->descr;
438 				dmi->checked = 0;
439 				dmi->fire = set_zone_multi;
440 				dmi->selected = 0;
441 				dmi->data = zp;
442 
443 				for (zp2 = cp->zones.tqh_first;
444 				     zp2->continent != cont;
445 				     zp2 = zp2->link.tqe_next)
446 					;
447 				if (zp2 != zp)
448 					continue;
449 
450 				dmi = &cont->menu[cont->nitems];
451 				memset(dmi, 0, sizeof *dmi);
452 				asprintf(&dmi->prompt, "%d", ++cont->nitems);
453 				dmi->title = cp->name;
454 				dmi->checked = 0;
455 				dmi->fire = set_zone_menu;
456 				dmi->selected = 0;
457 				dmi->data = cp;
458 			}
459 		}
460 	}
461 }
462 
463 static int
464 set_zone_menu(dialogMenuItem *dmi)
465 {
466 	int rv;
467 	char buf[256];
468 	struct country *cp = dmi->data;
469 	int menulen;
470 
471 	snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
472 	menulen = cp->nzones < 16 ? cp->nzones : 16;
473 	rv = dialog_menu(buf, "Select a zone which observes the same time as "
474 			 "your locality.", -1, -1, menulen, -cp->nzones,
475 			 cp->submenu, 0, 0, 0);
476 	if (rv != 0)
477 		return DITEM_RECREATE;
478 	return DITEM_LEAVE_MENU;
479 }
480 
481 static int
482 install_zone_file(const char *filename)
483 {
484 	struct stat sb;
485 	int fd1, fd2;
486 	int copymode;
487 	char *msg;
488 	ssize_t len;
489 	char buf[1024];
490 
491 	if (lstat(_PATH_LOCALTIME, &sb) < 0)
492 		/* Nothing there yet... */
493 		copymode = 1;
494 	else if(S_ISLNK(sb.st_mode))
495 		copymode = 0;
496 	else
497 		copymode = 1;
498 
499 #ifdef VERBOSE
500 	if (copymode)
501 		asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
502 	else
503 		asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
504 			 " to %s", filename);
505 
506 	dialog_notify(msg);
507 	free(msg);
508 #endif
509 
510 	if (reallydoit) {
511 		if (copymode) {
512 			fd1 = open(filename, O_RDONLY, 0);
513 			if (fd1 < 0) {
514 				asprintf(&msg, "Could not open %s: %s",
515 					 filename, strerror(errno));
516 				dialog_mesgbox("Error", msg, 8, 72);
517 				free(msg);
518 				return DITEM_FAILURE | DITEM_RECREATE;
519 			}
520 
521 			unlink(_PATH_LOCALTIME);
522 			fd2 = open(_PATH_LOCALTIME,
523 				   O_CREAT|O_EXCL|O_WRONLY,
524 				   S_IRUSR|S_IRGRP|S_IROTH);
525 			if (fd2 < 0) {
526 				asprintf(&msg, "Could not open "
527 					 _PATH_LOCALTIME ": %s",
528 					 strerror(errno));
529 				dialog_mesgbox("Error", msg, 8, 72);
530 				free(msg);
531 				return DITEM_FAILURE | DITEM_RECREATE;
532 			}
533 
534 			while ((len = read(fd1, buf, sizeof buf)) > 0)
535 				len = write(fd2, buf, len);
536 
537 			if (len == -1) {
538 				asprintf(&msg, "Error copying %s to "
539 					 _PATH_LOCALTIME ": %s",
540 					 filename, strerror(errno));
541 				dialog_mesgbox("Error", msg, 8, 72);
542 				free(msg);
543 				/* Better to leave none than a corrupt one. */
544 				unlink(_PATH_LOCALTIME);
545 				return DITEM_FAILURE | DITEM_RECREATE;
546 			}
547 			close(fd1);
548 			close(fd2);
549 		} else {
550 			if (access(filename, R_OK) != 0) {
551 				asprintf(&msg, "Cannot access %s: %s",
552 					 filename, strerror(errno));
553 				dialog_mesgbox("Error", msg, 8, 72);
554 				free(msg);
555 				return DITEM_FAILURE | DITEM_RECREATE;
556 			}
557 			unlink(_PATH_LOCALTIME);
558 			if (symlink(filename, _PATH_LOCALTIME) < 0) {
559 				asprintf(&msg, "Cannot create symbolic link "
560 					 _PATH_LOCALTIME " to %s: %s",
561 					 filename, strerror(errno));
562 				dialog_mesgbox("Error", msg, 8, 72);
563 				free(msg);
564 				return DITEM_FAILURE | DITEM_RECREATE;
565 			}
566 		}
567 	}
568 
569 #ifdef VERBOSE
570 	if (copymode)
571 		asprintf(&msg, "Copied timezone file from %s to "
572 			 _PATH_LOCALTIME, filename);
573 	else
574 		asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
575 			 " to %s", filename);
576 
577 	dialog_mesgbox("Done", msg, 8, 72);
578 	free(msg);
579 #endif
580 	return DITEM_LEAVE_MENU;
581 }
582 
583 static int
584 confirm_zone(const char *filename)
585 {
586 	char *msg;
587 	struct tm *tm;
588 	time_t t = time(0);
589 	int rv;
590 
591 	setenv("TZ", filename, 1);
592 	tzset();
593 	tm = localtime(&t);
594 
595 	asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
596 		 tm->tm_zone);
597 	rv = !dialog_yesno("Confirmation", msg, 4, 72);
598 	free(msg);
599 	return rv;
600 }
601 
602 static int
603 set_zone_multi(dialogMenuItem *dmi)
604 {
605 	char *fn;
606 	struct zone *zp = dmi->data;
607 	int rv;
608 
609 	if (!confirm_zone(zp->filename))
610 		return DITEM_FAILURE | DITEM_RECREATE;
611 
612 	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
613 	rv = install_zone_file(fn);
614 	free(fn);
615 	return rv;
616 }
617 
618 static int
619 set_zone_whole_country(dialogMenuItem *dmi)
620 {
621 	char *fn;
622 	struct country *cp = dmi->data;
623 	int rv;
624 
625 	if (!confirm_zone(cp->filename))
626 		return DITEM_FAILURE | DITEM_RECREATE;
627 
628 	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
629 	rv = install_zone_file(fn);
630 	free(fn);
631 	return rv;
632 }
633 
634 static void
635 usage()
636 {
637 	fprintf(stderr, "usage: tzsetup [-n]\n");
638 	exit(1);
639 }
640 
641 int
642 main(int argc, char **argv)
643 {
644 	int c, fd;
645 	int (*dialog_utc)(unsigned char *, unsigned char *, int, int);
646 
647 #if defined(__alpha__)
648 	dialog_utc = dialog_yesno;
649 #else
650 	dialog_utc = dialog_noyes;
651 #endif
652 
653 	while ((c = getopt(argc, argv, "n")) != -1) {
654 		switch(c) {
655 		case 'n':
656 			reallydoit = 0;
657 			break;
658 
659 		default:
660 			usage();
661 		}
662 	}
663 
664 	if (argc - optind > 1)
665 		usage();
666 
667 	/* Override the user-supplied umask. */
668 	(void)umask(S_IWGRP|S_IWOTH);
669 
670 	read_iso3166_table();
671 	read_zones();
672 	sort_countries();
673 	make_menus();
674 
675 	init_dialog();
676 	if (!dialog_utc("Select local or UTC (Greenwich Mean Time) clock",
677 			"Is this machine's CMOS clock set to UTC?  If it is set to local time,\n"
678 			"or you don't know, please choose NO here!", 7, 72)) {
679 		if (reallydoit)
680 			unlink(_PATH_WALL_CMOS_CLOCK);
681 	} else {
682 		if (reallydoit) {
683 			fd = open(_PATH_WALL_CMOS_CLOCK,
684 				  O_WRONLY|O_CREAT|O_TRUNC,
685 				  S_IRUSR|S_IRGRP|S_IROTH);
686 			if (fd < 0)
687 				err(1, "create %s", _PATH_WALL_CMOS_CLOCK);
688 			close(fd);
689 		}
690 	}
691 	dialog_clear_norefresh();
692 	if (optind == argc - 1) {
693 		char *msg;
694 		asprintf(&msg, "\nUse the default `%s' zone?", argv[optind]);
695 		if (!dialog_yesno("Default timezone provided", msg, 7, 72)) {
696 			install_zone_file(argv[optind]);
697 			dialog_clear();
698 			end_dialog();
699 			return 0;
700 		}
701 		free(msg);
702 		dialog_clear_norefresh();
703 	}
704 	dialog_menu("Time Zone Selector", "Select a region", -1, -1,
705 		    NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
706 
707 	dialog_clear();
708 	end_dialog();
709 	return 0;
710 }
711 
712