tzsetup.c revision 198267
116728Swpaul/* 216728Swpaul * Copyright 1996 Massachusetts Institute of Technology 316728Swpaul * 416728Swpaul * Permission to use, copy, modify, and distribute this software and 516728Swpaul * its documentation for any purpose and without fee is hereby 616728Swpaul * granted, provided that both the above copyright notice and this 716728Swpaul * permission notice appear in all copies, that both the above 816728Swpaul * copyright notice and this permission notice appear in all 916728Swpaul * supporting documentation, and that the name of M.I.T. not be used 1016728Swpaul * in advertising or publicity pertaining to distribution of the 1116728Swpaul * software without specific, written prior permission. M.I.T. makes 1216728Swpaul * no representations about the suitability of this software for any 1316728Swpaul * purpose. It is provided "as is" without express or implied 1416728Swpaul * warranty. 1516728Swpaul * 1616728Swpaul * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1716728Swpaul * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1816728Swpaul * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1916728Swpaul * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2016728Swpaul * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2116728Swpaul * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2216728Swpaul * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2316728Swpaul * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2416728Swpaul * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2516728Swpaul * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2616728Swpaul * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2716728Swpaul * SUCH DAMAGE. 2816728Swpaul */ 2916728Swpaul 3016728Swpaul/* 3116728Swpaul * Second attempt at a `tzmenu' program, using the separate description 3216728Swpaul * files provided in newer tzdata releases. 3316728Swpaul */ 3416728Swpaul 3516728Swpaul#include <sys/cdefs.h> 3616728Swpaul__FBSDID("$FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 198267 2009-10-20 06:54:31Z edwin $"); 3716728Swpaul 3816728Swpaul#include <dialog.h> 3916728Swpaul#include <err.h> 4031385Scharnier#include <errno.h> 4150476Speter#include <stdio.h> 4231385Scharnier#include <stdlib.h> 4316728Swpaul#include <string.h> 4416728Swpaul#include <time.h> 4516728Swpaul#include <unistd.h> 4616728Swpaul 4716728Swpaul#include <sys/fcntl.h> 4816728Swpaul#include <sys/param.h> 4916728Swpaul#include <sys/queue.h> 5016728Swpaul#include <sys/stat.h> 5116728Swpaul 5216728Swpaul#define _PATH_ZONETAB "/usr/share/zoneinfo/zone.tab" 5316728Swpaul#define _PATH_ISO3166 "/usr/share/misc/iso3166" 5490779Simp#define _PATH_ZONEINFO "/usr/share/zoneinfo" 5516728Swpaul#define _PATH_LOCALTIME "/etc/localtime" 5690779Simp#define _PATH_DB "/var/db/zoneinfo" 5790779Simp#define _PATH_WALL_CMOS_CLOCK "/etc/wall_cmos_clock" 5890779Simp 5916728Swpaulstatic int reallydoit = 1; 6016728Swpaulstatic int reinstall = 0; 6116728Swpaul 6216728Swpaulstatic void usage(void); 6316728Swpaulstatic int continent_country_menu(dialogMenuItem *); 6416728Swpaulstatic int set_zone_multi(dialogMenuItem *); 6516728Swpaulstatic int set_zone_whole_country(dialogMenuItem *); 6616728Swpaulstatic int set_zone_menu(dialogMenuItem *); 6716728Swpaul 6816728Swpaulstruct continent { 6916728Swpaul dialogMenuItem *menu; 7016728Swpaul int nitems; 7116728Swpaul int ch; 7216728Swpaul int sc; 7316728Swpaul}; 7416728Swpaul 7516728Swpaulstatic struct continent africa, america, antarctica, arctic, asia, atlantic; 7616728Swpaulstatic struct continent australia, europe, indian, pacific; 7716728Swpaul 7816728Swpaulstatic struct continent_names { 7916728Swpaul const char *name; 8016728Swpaul struct continent *continent; 8116728Swpaul} continent_names[] = { 8216728Swpaul { "Africa", &africa }, 8316728Swpaul { "America", &america }, 8416728Swpaul { "Antarctica", &antarctica }, 8516728Swpaul { "Arctic", &arctic }, 8616728Swpaul { "Asia", &asia }, 8716728Swpaul { "Atlantic", &atlantic }, 8816728Swpaul { "Australia", &australia }, 8916728Swpaul { "Europe", &europe }, 9016728Swpaul { "Indian", &indian }, 9116728Swpaul { "Pacific", &pacific } 9216728Swpaul}; 9316728Swpaul 9416728Swpaulstatic struct continent_items { 9516728Swpaul char prompt[2]; 9616728Swpaul char title[30]; 9716728Swpaul} continent_items[] = { 9816728Swpaul { "1", "Africa" }, 9916728Swpaul { "2", "America -- North and South" }, 10016728Swpaul { "3", "Antarctica" }, 10116728Swpaul { "4", "Arctic Ocean" }, 10216728Swpaul { "5", "Asia" }, 10390779Simp { "6", "Atlantic Ocean" }, 10416728Swpaul { "7", "Australia" }, 10516728Swpaul { "8", "Europe" }, 10616728Swpaul { "9", "Indian Ocean" }, 10716728Swpaul { "0", "Pacific Ocean" } 10816728Swpaul}; 10916728Swpaul 11016728Swpaul#define NCONTINENTS \ 11116728Swpaul (int)((sizeof(continent_items)) / (sizeof(continent_items[0]))) 11290779Simpstatic dialogMenuItem continents[NCONTINENTS]; 11316728Swpaul 11416728Swpaul#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) 11516728Swpaul 11616728Swpaulstatic int 11716728Swpaulcontinent_country_menu(dialogMenuItem *continent) 11816728Swpaul{ 11916728Swpaul char title[64], prompt[64]; 12016728Swpaul struct continent *contp = continent->data; 12116728Swpaul int isocean = OCEANP(continent - continents); 12216728Swpaul int menulen; 12316728Swpaul int rv; 12416728Swpaul 12516728Swpaul /* Short cut -- if there's only one country, don't post a menu. */ 12616728Swpaul if (contp->nitems == 1) 12716728Swpaul return (contp->menu[0].fire(&contp->menu[0])); 12816728Swpaul 12916728Swpaul /* It's amazing how much good grammar really matters... */ 130108470Sschweikh if (!isocean) { 13116728Swpaul snprintf(title, sizeof(title), "Countries in %s", 13290779Simp continent->title); 13316728Swpaul snprintf(prompt, sizeof(prompt), "Select a country or region"); 13416728Swpaul } else { 13516728Swpaul snprintf(title, sizeof(title), "Islands and groups in the %s", 13616728Swpaul continent->title); 13716728Swpaul snprintf(prompt, sizeof(prompt), "Select an island or group"); 13816728Swpaul } 13916728Swpaul 14016728Swpaul menulen = contp->nitems < 16 ? contp->nitems : 16; 14116728Swpaul rv = dialog_menu(title, prompt, -1, -1, menulen, -contp->nitems, 14216728Swpaul contp->menu, 0, &contp->ch, &contp->sc); 14316728Swpaul if (rv == 0) 14416728Swpaul return (DITEM_LEAVE_MENU); 14516728Swpaul return (DITEM_RECREATE); 14616728Swpaul} 14716728Swpaul 14816728Swpaulstatic struct continent * 14916728Swpaulfind_continent(const char *name) 15016728Swpaul{ 15116728Swpaul int i; 15216728Swpaul 15316728Swpaul for (i = 0; i < NCONTINENTS; i++) 15416728Swpaul if (strcmp(name, continent_names[i].name) == 0) 15516728Swpaul return (continent_names[i].continent); 15616728Swpaul return (0); 15716728Swpaul} 15816728Swpaul 15916728Swpaulstruct country { 16016728Swpaul char *name; 16116728Swpaul char *tlc; 16216728Swpaul int nzones; 16316728Swpaul char *filename; /* use iff nzones < 0 */ 16416728Swpaul struct continent *continent; /* use iff nzones < 0 */ 16516728Swpaul TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ 16616728Swpaul dialogMenuItem *submenu; /* use iff nzones > 0 */ 16716728Swpaul}; 16816728Swpaul 169struct zone { 170 TAILQ_ENTRY(zone) link; 171 char *descr; 172 char *filename; 173 struct continent *continent; 174}; 175 176/* 177 * This is the easiest organization... we use ISO 3166 country codes, 178 * of the two-letter variety, so we just size this array to suit. 179 * Beats worrying about dynamic allocation. 180 */ 181#define NCOUNTRIES (26 * 26) 182static struct country countries[NCOUNTRIES]; 183 184#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A')) 185 186/* 187 * Read the ISO 3166 country code database in _PATH_ISO3166 188 * (/usr/share/misc/iso3166). On error, exit via err(3). 189 */ 190static void 191read_iso3166_table(void) 192{ 193 FILE *fp; 194 struct country *cp; 195 size_t len; 196 char *s, *t, *name; 197 int lineno; 198 199 fp = fopen(_PATH_ISO3166, "r"); 200 if (!fp) 201 err(1, _PATH_ISO3166); 202 lineno = 0; 203 204 while ((s = fgetln(fp, &len)) != 0) { 205 lineno++; 206 if (s[len - 1] != '\n') 207 errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 208 s[len - 1] = '\0'; 209 if (s[0] == '#' || strspn(s, " \t") == len - 1) 210 continue; 211 212 /* Isolate the two-letter code. */ 213 t = strsep(&s, "\t"); 214 if (t == 0 || strlen(t) != 2) 215 errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 216 if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') 217 errx(1, _PATH_ISO3166 ":%d: invalid code `%s'", 218 lineno, t); 219 220 /* Now skip past the three-letter and numeric codes. */ 221 name = strsep(&s, "\t"); /* 3-let */ 222 if (name == 0 || strlen(name) != 3) 223 errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 224 name = strsep(&s, "\t"); /* numeric */ 225 if (name == 0 || strlen(name) != 3) 226 errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 227 228 name = s; 229 230 cp = &countries[CODE2INT(t)]; 231 if (cp->name) 232 errx(1, _PATH_ISO3166 233 ":%d: country code `%s' multiply defined: %s", 234 lineno, t, cp->name); 235 cp->name = strdup(name); 236 if (cp->name == NULL) 237 errx(1, "malloc failed"); 238 cp->tlc = strdup(t); 239 if (cp->tlc == NULL) 240 errx(1, "malloc failed"); 241 } 242 243 fclose(fp); 244} 245 246static void 247add_zone_to_country(int lineno, const char *tlc, const char *descr, 248 const char *file, struct continent *cont) 249{ 250 struct zone *zp; 251 struct country *cp; 252 253 if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') 254 errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid", 255 lineno, tlc); 256 257 cp = &countries[CODE2INT(tlc)]; 258 if (cp->name == 0) 259 errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown", 260 lineno, tlc); 261 262 if (descr) { 263 if (cp->nzones < 0) 264 errx(1, _PATH_ZONETAB 265 ":%d: conflicting zone definition", lineno); 266 267 zp = malloc(sizeof(*zp)); 268 if (zp == 0) 269 errx(1, "malloc(%zu)", sizeof(*zp)); 270 271 if (cp->nzones == 0) 272 TAILQ_INIT(&cp->zones); 273 274 zp->descr = strdup(descr); 275 if (zp->descr == NULL) 276 errx(1, "malloc failed"); 277 zp->filename = strdup(file); 278 if (zp->filename == NULL) 279 errx(1, "malloc failed"); 280 zp->continent = cont; 281 TAILQ_INSERT_TAIL(&cp->zones, zp, link); 282 cp->nzones++; 283 } else { 284 if (cp->nzones > 0) 285 errx(1, _PATH_ZONETAB 286 ":%d: zone must have description", lineno); 287 if (cp->nzones < 0) 288 errx(1, _PATH_ZONETAB 289 ":%d: zone multiply defined", lineno); 290 cp->nzones = -1; 291 cp->filename = strdup(file); 292 if (cp->filename == NULL) 293 errx(1, "malloc failed"); 294 cp->continent = cont; 295 } 296} 297 298/* 299 * This comparison function intentionally sorts all of the null-named 300 * ``countries''---i.e., the codes that don't correspond to a real 301 * country---to the end. Everything else is lexical by country name. 302 */ 303static int 304compare_countries(const void *xa, const void *xb) 305{ 306 const struct country *a = xa, *b = xb; 307 308 if (a->name == 0 && b->name == 0) 309 return (0); 310 if (a->name == 0 && b->name != 0) 311 return (1); 312 if (b->name == 0) 313 return (-1); 314 315 return (strcmp(a->name, b->name)); 316} 317 318/* 319 * This must be done AFTER all zone descriptions are read, since it breaks 320 * CODE2INT(). 321 */ 322static void 323sort_countries(void) 324{ 325 326 qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries); 327} 328 329static void 330read_zones(void) 331{ 332 char contbuf[16]; 333 FILE *fp; 334 struct continent *cont; 335 size_t len; 336 char *line, *tlc, *coord, *file, *descr, *p; 337 int lineno; 338 339 fp = fopen(_PATH_ZONETAB, "r"); 340 if (!fp) 341 err(1, _PATH_ZONETAB); 342 lineno = 0; 343 344 while ((line = fgetln(fp, &len)) != 0) { 345 lineno++; 346 if (line[len - 1] != '\n') 347 errx(1, _PATH_ZONETAB ":%d: invalid format", lineno); 348 line[len - 1] = '\0'; 349 if (line[0] == '#') 350 continue; 351 352 tlc = strsep(&line, "\t"); 353 if (strlen(tlc) != 2) 354 errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'", 355 lineno, tlc); 356 coord = strsep(&line, "\t"); 357 file = strsep(&line, "\t"); 358 p = strchr(file, '/'); 359 if (p == 0) 360 errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'", 361 lineno, file); 362 contbuf[0] = '\0'; 363 strncat(contbuf, file, p - file); 364 cont = find_continent(contbuf); 365 if (!cont) 366 errx(1, _PATH_ZONETAB ":%d: invalid region `%s'", 367 lineno, contbuf); 368 369 descr = (line != NULL && *line != '\0') ? line : NULL; 370 371 add_zone_to_country(lineno, tlc, descr, file, cont); 372 } 373 fclose(fp); 374} 375 376static void 377make_menus(void) 378{ 379 struct country *cp; 380 struct zone *zp, *zp2; 381 struct continent *cont; 382 dialogMenuItem *dmi; 383 int i; 384 385 /* 386 * First, count up all the countries in each continent/ocean. 387 * Be careful to count those countries which have multiple zones 388 * only once for each. NB: some countries are in multiple 389 * continents/oceans. 390 */ 391 for (cp = countries; cp->name; cp++) { 392 if (cp->nzones == 0) 393 continue; 394 if (cp->nzones < 0) { 395 cp->continent->nitems++; 396 } else { 397 TAILQ_FOREACH(zp, &cp->zones, link) { 398 cont = zp->continent; 399 for (zp2 = TAILQ_FIRST(&cp->zones); 400 zp2->continent != cont; 401 zp2 = TAILQ_NEXT(zp2, link)) 402 ; 403 if (zp2 == zp) 404 zp->continent->nitems++; 405 } 406 } 407 } 408 409 /* 410 * Now allocate memory for the country menus and initialize 411 * continent menus. We set nitems back to zero so that we can 412 * use it for counting again when we actually build the menus. 413 */ 414 memset(continents, 0, sizeof(continents)); 415 for (i = 0; i < NCONTINENTS; i++) { 416 continent_names[i].continent->menu = 417 malloc(sizeof(dialogMenuItem) * 418 continent_names[i].continent->nitems); 419 if (continent_names[i].continent->menu == 0) 420 errx(1, "malloc for continent menu"); 421 continent_names[i].continent->nitems = 0; 422 continents[i].prompt = continent_items[i].prompt; 423 continents[i].title = continent_items[i].title; 424 continents[i].fire = continent_country_menu; 425 continents[i].data = continent_names[i].continent; 426 } 427 428 /* 429 * Now that memory is allocated, create the menu items for 430 * each continent. For multiple-zone countries, also create 431 * the country's zone submenu. 432 */ 433 for (cp = countries; cp->name; cp++) { 434 if (cp->nzones == 0) 435 continue; 436 if (cp->nzones < 0) { 437 dmi = &cp->continent->menu[cp->continent->nitems]; 438 memset(dmi, 0, sizeof(*dmi)); 439 asprintf(&dmi->prompt, "%d", ++cp->continent->nitems); 440 dmi->title = cp->name; 441 dmi->checked = 0; 442 dmi->fire = set_zone_whole_country; 443 dmi->selected = 0; 444 dmi->data = cp; 445 } else { 446 cp->submenu = malloc(cp->nzones * sizeof(*dmi)); 447 if (cp->submenu == 0) 448 errx(1, "malloc for submenu"); 449 cp->nzones = 0; 450 TAILQ_FOREACH(zp, &cp->zones, link) { 451 cont = zp->continent; 452 dmi = &cp->submenu[cp->nzones]; 453 memset(dmi, 0, sizeof(*dmi)); 454 asprintf(&dmi->prompt, "%d", ++cp->nzones); 455 dmi->title = zp->descr; 456 dmi->checked = 0; 457 dmi->fire = set_zone_multi; 458 dmi->selected = 0; 459 dmi->data = zp; 460 461 for (zp2 = TAILQ_FIRST(&cp->zones); 462 zp2->continent != cont; 463 zp2 = TAILQ_NEXT(zp2, link)) 464 ; 465 if (zp2 != zp) 466 continue; 467 468 dmi = &cont->menu[cont->nitems]; 469 memset(dmi, 0, sizeof(*dmi)); 470 asprintf(&dmi->prompt, "%d", ++cont->nitems); 471 dmi->title = cp->name; 472 dmi->checked = 0; 473 dmi->fire = set_zone_menu; 474 dmi->selected = 0; 475 dmi->data = cp; 476 } 477 } 478 } 479} 480 481static int 482set_zone_menu(dialogMenuItem *dmi) 483{ 484 char title[64], prompt[64]; 485 struct country *cp = dmi->data; 486 int menulen; 487 int rv; 488 489 snprintf(title, sizeof(title), "%s Time Zones", cp->name); 490 snprintf(prompt, sizeof(prompt), 491 "Select a zone which observes the same time as your locality."); 492 menulen = cp->nzones < 16 ? cp->nzones : 16; 493 rv = dialog_menu(title, prompt, -1, -1, menulen, -cp->nzones, 494 cp->submenu, 0, 0, 0); 495 if (rv != 0) 496 return (DITEM_RECREATE); 497 return (DITEM_LEAVE_MENU); 498} 499 500static int 501install_zone_file(const char *filename, int usedialog) 502{ 503 char buf[1024]; 504 char title[64], prompt[64]; 505 struct stat sb; 506 ssize_t len; 507 int fd1, fd2, copymode; 508 FILE *f; 509 510 if (lstat(_PATH_LOCALTIME, &sb) < 0) { 511 /* Nothing there yet... */ 512 copymode = 1; 513 } else if (S_ISLNK(sb.st_mode)) 514 copymode = 0; 515 else 516 copymode = 1; 517 518#ifdef VERBOSE 519 if (copymode) 520 snprintf(prompt, sizeof(prompt), 521 "Copying %s to " _PATH_LOCALTIME, filename); 522 else 523 snprintf(prompt, sizeof(prompt), 524 "Creating symbolic link " _PATH_LOCALTIME " to %s", 525 filename); 526 if (usedialog) 527 dialog_notify(prompt); 528 else 529 fprintf(stderr, "%s\n", prompt); 530#endif 531 532 if (reallydoit) { 533 if (copymode) { 534 fd1 = open(filename, O_RDONLY, 0); 535 if (fd1 < 0) { 536 snprintf(title, sizeof(title), "Error"); 537 snprintf(prompt, sizeof(prompt), 538 "Could not open %s: %s", filename, 539 strerror(errno)); 540 if (usedialog) 541 dialog_mesgbox(title, prompt, 8, 72); 542 else 543 fprintf(stderr, "%s\n", prompt); 544 return (DITEM_FAILURE | DITEM_RECREATE); 545 } 546 547 unlink(_PATH_LOCALTIME); 548 fd2 = open(_PATH_LOCALTIME, O_CREAT | O_EXCL | O_WRONLY, 549 S_IRUSR | S_IRGRP | S_IROTH); 550 if (fd2 < 0) { 551 snprintf(title, sizeof(title), "Error"); 552 snprintf(prompt, sizeof(prompt), 553 "Could not open " _PATH_LOCALTIME ": %s", 554 strerror(errno)); 555 if (usedialog) 556 dialog_mesgbox(title, prompt, 8, 72); 557 else 558 fprintf(stderr, "%s\n", prompt); 559 return (DITEM_FAILURE | DITEM_RECREATE); 560 } 561 562 while ((len = read(fd1, buf, sizeof(buf))) > 0) 563 len = write(fd2, buf, len); 564 565 if (len == -1) { 566 snprintf(title, sizeof(title), "Error"); 567 snprintf(prompt, sizeof(prompt), 568 "Error copying %s to " _PATH_LOCALTIME 569 ": %s", filename, strerror(errno)); 570 if (usedialog) 571 dialog_mesgbox(title, prompt, 8, 72); 572 else 573 fprintf(stderr, "%s\n", prompt); 574 /* Better to leave none than a corrupt one. */ 575 unlink(_PATH_LOCALTIME); 576 return (DITEM_FAILURE | DITEM_RECREATE); 577 } 578 close(fd1); 579 close(fd2); 580 } else { 581 if (access(filename, R_OK) != 0) { 582 snprintf(title, sizeof(title), "Error"); 583 snprintf(prompt, sizeof(prompt), 584 "Cannot access %s: %s", filename, 585 strerror(errno)); 586 if (usedialog) 587 dialog_mesgbox(title, prompt, 8, 72); 588 else 589 fprintf(stderr, "%s\n", prompt); 590 return (DITEM_FAILURE | DITEM_RECREATE); 591 } 592 unlink(_PATH_LOCALTIME); 593 if (symlink(filename, _PATH_LOCALTIME) < 0) { 594 snprintf(title, sizeof(title), "Error"); 595 snprintf(prompt, sizeof(prompt), 596 "Cannot create symbolic link " 597 _PATH_LOCALTIME " to %s: %s", filename, 598 strerror(errno)); 599 if (usedialog) 600 dialog_mesgbox(title, prompt, 8, 72); 601 else 602 fprintf(stderr, "%s\n", prompt); 603 return (DITEM_FAILURE | DITEM_RECREATE); 604 } 605 } 606 } 607 608#ifdef VERBOSE 609 snprintf(title, sizeof(title), "Done"); 610 if (copymode) 611 snprintf(prompt, sizeof(prompt), 612 "Copied timezone file from %s to " _PATH_LOCALTIME, 613 filename); 614 else 615 snprintf(prompt, sizeof(prompt), "Created symbolic link from " 616 _PATH_LOCALTIME " to %s", filename); 617 if (usedialog) 618 dialog_mesgbox(title, prompt, 8, 72); 619 else 620 fprintf(stderr, "%s\n", prompt); 621#endif 622 623 /* Save knowledge for later */ 624 if ((f = fopen(_PATH_DB, "w")) != NULL) { 625 fprintf(f, "%s\n", filename + strlen(_PATH_ZONEINFO) + 1); 626 fclose(f); 627 } 628 629 return (DITEM_LEAVE_MENU); 630} 631 632static int 633confirm_zone(const char *filename) 634{ 635 char title[64], prompt[64]; 636 time_t t = time(0); 637 struct tm *tm; 638 int rv; 639 640 setenv("TZ", filename, 1); 641 tzset(); 642 tm = localtime(&t); 643 644 snprintf(title, sizeof(title), "Confirmation"); 645 snprintf(prompt, sizeof(prompt), 646 "Does the abbreviation `%s' look reasonable?", tm->tm_zone); 647 rv = !dialog_yesno(title, prompt, 5, 72); 648 return (rv); 649} 650 651static int 652set_zone_multi(dialogMenuItem *dmi) 653{ 654 struct zone *zp = dmi->data; 655 char *fn; 656 int rv; 657 658 if (!confirm_zone(zp->filename)) 659 return (DITEM_FAILURE | DITEM_RECREATE); 660 661 asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename); 662 rv = install_zone_file(fn, 1); 663 free(fn); 664 return (rv); 665} 666 667static int 668set_zone_whole_country(dialogMenuItem *dmi) 669{ 670 struct country *cp = dmi->data; 671 char *fn; 672 int rv; 673 674 if (!confirm_zone(cp->filename)) 675 return (DITEM_FAILURE | DITEM_RECREATE); 676 677 asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename); 678 rv = install_zone_file(fn, 1); 679 free(fn); 680 return (rv); 681} 682 683static void 684usage(void) 685{ 686 687 fprintf(stderr, "usage: tzsetup [-nrs] [zoneinfo file]\n"); 688 exit(1); 689} 690 691#if defined(__sparc64__) 692#define DIALOG_UTC dialog_yesno 693#else 694#define DIALOG_UTC dialog_noyes 695#endif 696 697int 698main(int argc, char **argv) 699{ 700 char title[64], prompt[128]; 701 int c, fd, rv, skiputc; 702 703 skiputc = 0; 704 while ((c = getopt(argc, argv, "nrs")) != -1) { 705 switch(c) { 706 case 'n': 707 reallydoit = 0; 708 break; 709 case 'r': 710 reinstall = 1; 711 break; 712 case 's': 713 skiputc = 1; 714 break; 715 default: 716 usage(); 717 } 718 } 719 720 if (argc - optind > 1) 721 usage(); 722 723 /* Override the user-supplied umask. */ 724 (void)umask(S_IWGRP | S_IWOTH); 725 726 read_iso3166_table(); 727 read_zones(); 728 sort_countries(); 729 make_menus(); 730 731 if (reinstall == 1) { 732 FILE *f; 733 char zonefile[MAXPATHLEN]; 734 735 sprintf(zonefile, "%s/", _PATH_ZONEINFO); 736 if ((f = fopen(_PATH_DB, "r")) != NULL) { 737 if (fgets(zonefile + strlen(zonefile), 738 sizeof(zonefile) - strlen(zonefile), f) != NULL) { 739 zonefile[sizeof(zonefile) - 1] = 0; 740 if (strlen(zonefile) > 0) { 741 zonefile[strlen(zonefile) - 1] = 0; 742 rv = install_zone_file(zonefile, 0); 743 exit(rv & ~DITEM_LEAVE_MENU); 744 } 745 errx(1, "Error reading %s.\n", _PATH_DB); 746 } 747 fclose(f); 748 errx(1, 749 "Unable to determine earlier installed zoneinfo " 750 "file. Check %s", _PATH_DB); 751 } 752 errx(1, "Cannot open %s for reading. Does it exist?", _PATH_DB); 753 } 754 755 init_dialog(); 756 if (skiputc == 0) { 757 snprintf(title, sizeof(title), 758 "Select local or UTC (Greenwich Mean Time) clock"); 759 snprintf(prompt, sizeof(prompt), 760 "Is this machine's CMOS clock set to UTC? " 761 "If it is set to local time,\n" 762 "or you don't know, please choose NO here!"); 763 if (!DIALOG_UTC(title, prompt, 7, 72)) { 764 if (reallydoit) 765 unlink(_PATH_WALL_CMOS_CLOCK); 766 } else { 767 if (reallydoit) { 768 fd = open(_PATH_WALL_CMOS_CLOCK, 769 O_WRONLY | O_CREAT | O_TRUNC, 770 S_IRUSR | S_IRGRP | S_IROTH); 771 if (fd < 0) { 772 end_dialog(); 773 err(1, "create %s", 774 _PATH_WALL_CMOS_CLOCK); 775 } 776 close(fd); 777 } 778 } 779 dialog_clear_norefresh(); 780 } 781 if (optind == argc - 1) { 782 snprintf(title, sizeof(title), "Default timezone provided"); 783 snprintf(prompt, sizeof(prompt), 784 "\nUse the default `%s' zone?", argv[optind]); 785 if (!dialog_yesno(title, prompt, 7, 72)) { 786 rv = install_zone_file(argv[optind], 1); 787 dialog_clear(); 788 end_dialog(); 789 exit(rv & ~DITEM_LEAVE_MENU); 790 } 791 dialog_clear_norefresh(); 792 } 793 snprintf(title, sizeof(title), "Time Zone Selector"); 794 snprintf(prompt, sizeof(prompt), "Select a region"); 795 dialog_menu(title, prompt, -1, -1, NCONTINENTS, -NCONTINENTS, 796 continents, 0, NULL, NULL); 797 798 dialog_clear(); 799 end_dialog(); 800 return (0); 801} 802