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