]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/tzsetup/tzsetup.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / tzsetup / tzsetup.c
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$";
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 <time.h>
48 #include <unistd.h>
49
50 #include <sys/fcntl.h>
51 #include <sys/queue.h>
52 #include <sys/stat.h>
53
54 #include "paths.h"
55
56 static int reallydoit = 1;
57
58 static int continent_country_menu(dialogMenuItem *);
59 static int set_zone_multi(dialogMenuItem *);
60 static int set_zone_whole_country(dialogMenuItem *);
61 static int set_zone_menu(dialogMenuItem *);
62
63 struct continent {
64         dialogMenuItem *menu;
65         int nitems;
66         int ch;
67         int sc;
68 };
69
70 static struct continent africa, america, antarctica, arctic, asia, atlantic;
71 static struct continent australia, europe, indian, pacific;
72
73 static struct continent_names {
74         char *name;
75         struct continent *continent;
76 } continent_names[] = {
77         { "Africa", &africa }, { "America", &america },
78         { "Antarctica", &antarctica }, { "Arctic", &arctic }, 
79         { "Asia", &asia },
80         { "Atlantic", &atlantic }, { "Australia", &australia },
81         { "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
82 };
83
84 static dialogMenuItem continents[] = {
85         { "1", "Africa", 0, continent_country_menu, 0, &africa },
86         { "2", "America -- North and South", 0, continent_country_menu, 0, 
87                   &america },
88         { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
89         { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
90         { "5", "Asia", 0, continent_country_menu, 0, &asia },
91         { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
92         { "7", "Australia", 0, continent_country_menu, 0, &australia },
93         { "8", "Europe", 0, continent_country_menu, 0, &europe },
94         { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
95         { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
96 };
97 #define NCONTINENTS (int)((sizeof continents)/(sizeof continents[0]))
98 #define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
99
100 static int
101 continent_country_menu(dialogMenuItem *continent)
102 {
103         int rv;
104         struct continent *contp = continent->data;
105         char title[256];
106         int isocean = OCEANP(continent - continents);
107         int menulen;
108
109         /* Short cut -- if there's only one country, don't post a menu. */
110         if (contp->nitems == 1)
111                 return (contp->menu[0].fire(&contp->menu[0]));
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 or region"), -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                         TAILQ_FOREACH(zp, &cp->zones, link) {
381                                 cont = zp->continent;
382                                 for (zp2 = TAILQ_FIRST(&cp->zones);
383                                      zp2->continent != cont;
384                                      zp2 = TAILQ_NEXT(zp2, link))
385                                         ;
386                                 if (zp2 == zp)
387                                         zp->continent->nitems++;
388                         }
389                 }
390         }
391
392         /*
393          * Now allocate memory for the country menus.  We set
394          * nitems back to zero so that we can use it for counting
395          * again when we actually build the menus.
396          */
397         for (i = 0; i < NCONTINENTS; i++) {
398                 continent_names[i].continent->menu =
399                         malloc(sizeof(dialogMenuItem) *
400                                continent_names[i].continent->nitems);
401                 if (continent_names[i].continent->menu == 0)
402                         errx(1, "malloc for continent menu");
403                 continent_names[i].continent->nitems = 0;
404         }
405
406         /*
407          * Now that memory is allocated, create the menu items for
408          * each continent.  For multiple-zone countries, also create
409          * the country's zone submenu.
410          */
411         for (cp = countries; cp->name; cp++) {
412                 if (cp->nzones == 0)
413                         continue;
414                 if (cp->nzones < 0) {
415                         dmi = &cp->continent->menu[cp->continent->nitems];
416                         memset(dmi, 0, sizeof *dmi);
417                         asprintf(&dmi->prompt, "%d", 
418                                  ++cp->continent->nitems);
419                         dmi->title = cp->name;
420                         dmi->checked = 0;
421                         dmi->fire = set_zone_whole_country;
422                         dmi->selected = 0;
423                         dmi->data = cp;
424                 } else {
425                         cp->submenu = malloc(cp->nzones * sizeof *dmi);
426                         if (cp->submenu == 0)
427                                 errx(1, "malloc for submenu");
428                         cp->nzones = 0;
429                         TAILQ_FOREACH(zp, &cp->zones, link) {
430                                 cont = zp->continent;
431                                 dmi = &cp->submenu[cp->nzones];
432                                 memset(dmi, 0, sizeof *dmi);
433                                 asprintf(&dmi->prompt, "%d",
434                                          ++cp->nzones);
435                                 dmi->title = zp->descr;
436                                 dmi->checked = 0;
437                                 dmi->fire = set_zone_multi;
438                                 dmi->selected = 0;
439                                 dmi->data = zp;
440
441                                 for (zp2 = TAILQ_FIRST(&cp->zones);
442                                      zp2->continent != cont;
443                                      zp2 = TAILQ_NEXT(zp2, link))
444                                         ;
445                                 if (zp2 != zp)
446                                         continue;
447
448                                 dmi = &cont->menu[cont->nitems];
449                                 memset(dmi, 0, sizeof *dmi);
450                                 asprintf(&dmi->prompt, "%d", ++cont->nitems);
451                                 dmi->title = cp->name;
452                                 dmi->checked = 0;
453                                 dmi->fire = set_zone_menu;
454                                 dmi->selected = 0;
455                                 dmi->data = cp;
456                         }
457                 }
458         }
459 }
460
461 static int
462 set_zone_menu(dialogMenuItem *dmi)
463 {
464         int rv;
465         char buf[256];
466         struct country *cp = dmi->data;
467         int menulen;
468
469         snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
470         menulen = cp->nzones < 16 ? cp->nzones : 16;
471         rv = dialog_menu(buf, "Select a zone which observes the same time as "
472                          "your locality.", -1, -1, menulen, -cp->nzones,
473                          cp->submenu, 0, 0, 0);
474         if (rv != 0)
475                 return DITEM_RECREATE;
476         return DITEM_LEAVE_MENU;
477 }
478
479 static int
480 install_zone_file(const char *filename)
481 {
482         struct stat sb;
483         int fd1, fd2;
484         int copymode;
485         char *msg;
486         ssize_t len;
487         char buf[1024];
488
489         if (lstat(_PATH_LOCALTIME, &sb) < 0)
490                 /* Nothing there yet... */
491                 copymode = 1;
492         else if(S_ISLNK(sb.st_mode))
493                 copymode = 0;
494         else
495                 copymode = 1;
496
497 #ifdef VERBOSE
498         if (copymode)
499                 asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
500         else
501                 asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
502                          " to %s", filename);
503
504         dialog_notify(msg);
505         free(msg);
506 #endif
507
508         if (reallydoit) {
509                 if (copymode) {
510                         fd1 = open(filename, O_RDONLY, 0);
511                         if (fd1 < 0) {
512                                 asprintf(&msg, "Could not open %s: %s",
513                                          filename, strerror(errno));
514                                 dialog_mesgbox("Error", msg, 8, 72);
515                                 free(msg);
516                                 return DITEM_FAILURE | DITEM_RECREATE;
517                         }
518
519                         unlink(_PATH_LOCALTIME);
520                         fd2 = open(_PATH_LOCALTIME, 
521                                    O_CREAT|O_EXCL|O_WRONLY,
522                                    S_IRUSR|S_IRGRP|S_IROTH);
523                         if (fd2 < 0) {
524                                 asprintf(&msg, "Could not open "
525                                          _PATH_LOCALTIME ": %s", 
526                                          strerror(errno));
527                                 dialog_mesgbox("Error", msg, 8, 72);
528                                 free(msg);
529                                 return DITEM_FAILURE | DITEM_RECREATE;
530                         }
531
532                         while ((len = read(fd1, buf, sizeof buf)) > 0)
533                                 len = write(fd2, buf, len);
534
535                         if (len == -1) {
536                                 asprintf(&msg, "Error copying %s to "
537                                          _PATH_LOCALTIME ": %s",
538                                          filename, strerror(errno));
539                                 dialog_mesgbox("Error", msg, 8, 72);
540                                 free(msg);
541                                 /* Better to leave none than a corrupt one. */
542                                 unlink(_PATH_LOCALTIME);
543                                 return DITEM_FAILURE | DITEM_RECREATE;
544                         }
545                         close(fd1);
546                         close(fd2);
547                 } else {
548                         if (access(filename, R_OK) != 0) {
549                                 asprintf(&msg, "Cannot access %s: %s",
550                                          filename, strerror(errno));
551                                 dialog_mesgbox("Error", msg, 8, 72);
552                                 free(msg);
553                                 return DITEM_FAILURE | DITEM_RECREATE;
554                         }
555                         unlink(_PATH_LOCALTIME);
556                         if (symlink(filename, _PATH_LOCALTIME) < 0) {
557                                 asprintf(&msg, "Cannot create symbolic link "
558                                          _PATH_LOCALTIME " to %s: %s",
559                                          filename, strerror(errno));
560                                 dialog_mesgbox("Error", msg, 8, 72);
561                                 free(msg);
562                                 return DITEM_FAILURE | DITEM_RECREATE;
563                         }
564                 }
565         }
566
567 #ifdef VERBOSE
568         if (copymode)
569                 asprintf(&msg, "Copied timezone file from %s to " 
570                          _PATH_LOCALTIME, filename);
571         else
572                 asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
573                          " to %s", filename);
574
575         dialog_mesgbox("Done", msg, 8, 72);
576         free(msg);
577 #endif
578         return DITEM_LEAVE_MENU;
579 }
580
581 static int
582 confirm_zone(const char *filename)
583 {
584         char *msg;
585         struct tm *tm;
586         time_t t = time(0);
587         int rv;
588         
589         setenv("TZ", filename, 1);
590         tzset();
591         tm = localtime(&t);
592
593         asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
594                  tm->tm_zone);
595         rv = !dialog_yesno("Confirmation", msg, 5, 72);
596         free(msg);
597         return rv;
598 }
599
600 static int
601 set_zone_multi(dialogMenuItem *dmi)
602 {
603         char *fn;
604         struct zone *zp = dmi->data;
605         int rv;
606
607         if (!confirm_zone(zp->filename))
608                 return DITEM_FAILURE | DITEM_RECREATE;
609
610         asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
611         rv = install_zone_file(fn);
612         free(fn);
613         return rv;
614 }
615
616 static int
617 set_zone_whole_country(dialogMenuItem *dmi)
618 {
619         char *fn;
620         struct country *cp = dmi->data;
621         int rv;
622
623         if (!confirm_zone(cp->filename))
624                 return DITEM_FAILURE | DITEM_RECREATE;
625
626         asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
627         rv = install_zone_file(fn);
628         free(fn);
629         return rv;
630 }
631
632 static void
633 usage()
634 {
635         fprintf(stderr, "usage: tzsetup [-n]\n");
636         exit(1);
637 }
638
639 int
640 main(int argc, char **argv)
641 {
642         int c, fd;
643         int (*dialog_utc)(unsigned char *, unsigned char *, int, int);
644
645 #if defined(__alpha__) || defined(__sparc64__)
646         dialog_utc = dialog_yesno;
647 #else
648         dialog_utc = dialog_noyes;
649 #endif
650
651         while ((c = getopt(argc, argv, "n")) != -1) {
652                 switch(c) {
653                 case 'n':
654                         reallydoit = 0;
655                         break;
656
657                 default:
658                         usage();
659                 }
660         }
661
662         if (argc - optind > 1)
663                 usage();
664
665         /* Override the user-supplied umask. */
666         (void)umask(S_IWGRP|S_IWOTH);
667
668         read_iso3166_table();
669         read_zones();
670         sort_countries();
671         make_menus();
672
673         init_dialog();
674         if (!dialog_utc("Select local or UTC (Greenwich Mean Time) clock",
675                         "Is this machine's CMOS clock set to UTC?  If it is set to local time,\n"
676                         "or you don't know, please choose NO here!", 7, 72)) {
677                 if (reallydoit)
678                         unlink(_PATH_WALL_CMOS_CLOCK);
679         } else {
680                 if (reallydoit) {
681                         fd = open(_PATH_WALL_CMOS_CLOCK,
682                                   O_WRONLY|O_CREAT|O_TRUNC,
683                                   S_IRUSR|S_IRGRP|S_IROTH);
684                         if (fd < 0)
685                                 err(1, "create %s", _PATH_WALL_CMOS_CLOCK);
686                         close(fd);
687                 }
688         }
689         dialog_clear_norefresh();
690         if (optind == argc - 1) {
691                 char *msg;
692                 asprintf(&msg, "\nUse the default `%s' zone?", argv[optind]);
693                 if (!dialog_yesno("Default timezone provided", msg, 7, 72)) {
694                         install_zone_file(argv[optind]);
695                         dialog_clear();
696                         end_dialog();
697                         return 0;
698                 }
699                 free(msg);
700                 dialog_clear_norefresh();
701         }
702         dialog_menu("Time Zone Selector", "Select a region", -1, -1, 
703                     NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
704
705         dialog_clear();
706         end_dialog();
707         return 0;
708 }
709