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 [-n]\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; 670 671 while ((c = getopt(argc, argv, "n")) != -1) { 672 switch(c) { 673 case 'n': 674 reallydoit = 0; 675 break; 676 677 default: 678 usage(); 679 } 680 } 681 682 if (argc - optind > 1) 683 usage(); 684 685 /* Override the user-supplied umask. */ 686 (void)umask(S_IWGRP | S_IWOTH); 687 688 read_iso3166_table(); 689 read_zones(); 690 sort_countries(); 691 make_menus(); 692 693 snprintf(title, sizeof(title), 694 "Select local or UTC (Greenwich Mean Time) clock"); 695 snprintf(prompt, sizeof(prompt), 696 "Is this machine's CMOS clock set to UTC? " 697 "If it is set to local time,\n" 698 "or you don't know, please choose NO here!"); 699 init_dialog(); 700 if (!DIALOG_UTC(title, prompt, 7, 72)) { 701 if (reallydoit) 702 unlink(_PATH_WALL_CMOS_CLOCK); 703 } else { 704 if (reallydoit) { 705 fd = open(_PATH_WALL_CMOS_CLOCK, 706 O_WRONLY | O_CREAT | O_TRUNC, 707 S_IRUSR | S_IRGRP | S_IROTH); 708 if (fd < 0) 709 err(1, "create %s", _PATH_WALL_CMOS_CLOCK); 710 close(fd); 711 } 712 } 713 dialog_clear_norefresh(); 714 if (optind == argc - 1) { 715 snprintf(title, sizeof(title), "Default timezone provided"); 716 snprintf(prompt, sizeof(prompt), 717 "\nUse the default `%s' zone?", argv[optind]); 718 if (!dialog_yesno(title, prompt, 7, 72)) { 719 install_zone_file(argv[optind]); 720 dialog_clear(); 721 end_dialog(); 722 return (0); 723 } 724 dialog_clear_norefresh(); 725 } 726 snprintf(title, sizeof(title), "Time Zone Selector"); 727 snprintf(prompt, sizeof(prompt), "Select a region"); 728 dialog_menu(title, prompt, -1, -1, NCONTINENTS, -NCONTINENTS, 729 continents, 0, NULL, NULL); 730 731 dialog_clear(); 732 end_dialog(); 733 return (0); 734 } 735