tzsetup.c revision 22815
11412SN/A/* 23994Sjjg * Copyright 1996 Massachusetts Institute of Technology 31412SN/A * 41412SN/A * Permission to use, copy, modify, and distribute this software and 51412SN/A * its documentation for any purpose and without fee is hereby 61412SN/A * granted, provided that both the above copyright notice and this 71412SN/A * permission notice appear in all copies, that both the above 81412SN/A * copyright notice and this permission notice appear in all 91412SN/A * supporting documentation, and that the name of M.I.T. not be used 101412SN/A * in advertising or publicity pertaining to distribution of the 111412SN/A * software without specific, written prior permission. M.I.T. makes 121412SN/A * no representations about the suitability of this software for any 131412SN/A * purpose. It is provided "as is" without express or implied 141412SN/A * warranty. 151412SN/A * 161412SN/A * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 171412SN/A * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 181412SN/A * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 191412SN/A * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 201412SN/A * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 211412SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 221412SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 231412SN/A * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 241412SN/A * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 253170Svasya * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 261412SN/A * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271412SN/A * SUCH DAMAGE. 281412SN/A * 291412SN/A * $FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 22815 1997-02-16 23:51:03Z jkh $ 301412SN/A */ 311412SN/A 321412SN/A/* 331412SN/A * Second attempt at a `tzmenu' program, using the separate description 341412SN/A * files provided in newer tzdata releases. 351412SN/A */ 364040Sjjg 374040Sjjg#include <sys/types.h> 381412SN/A#include <dialog.h> 391412SN/A#include <err.h> 401412SN/A#include <errno.h> 411412SN/A#include <stdio.h> 421412SN/A#include <stdlib.h> 431412SN/A#include <string.h> 441412SN/A#include <unistd.h> 451412SN/A 461412SN/A#include <sys/fcntl.h> 471412SN/A#include <sys/queue.h> 481412SN/A#include <sys/stat.h> 491412SN/A 501412SN/A#include "paths.h" 511412SN/A 521412SN/Astatic int reallydoit = 1; 531412SN/A 541412SN/Astatic int continent_country_menu(dialogMenuItem *); 551412SN/Astatic int set_zone_multi(dialogMenuItem *); 561412SN/Astatic int set_zone_whole_country(dialogMenuItem *); 571412SN/Astatic int set_zone_menu(dialogMenuItem *); 581412SN/A 591412SN/Astruct continent { 601412SN/A dialogMenuItem *menu; 611412SN/A int nitems; 623566Sksrini int ch; 631412SN/A int sc; 641412SN/A}; 651412SN/A 661412SN/Astatic struct continent africa, america, antarctica, arctic, asia, atlantic; 671412SN/Astatic struct continent australia, europe, indian, pacific; 681412SN/A 691412SN/Astatic struct continent_names { 701412SN/A char *name; 711412SN/A struct continent *continent; 721412SN/A} continent_names[] = { 731412SN/A { "Africa", &africa }, { "America", &america }, 741412SN/A { "Antarctica", &antarctica }, { "Arctic", &arctic }, 751412SN/A { "Asia", &asia }, 761412SN/A { "Atlantic", &atlantic }, { "Australia", &australia }, 771412SN/A { "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific } 781412SN/A}; 791412SN/A 801412SN/Astatic dialogMenuItem continents[] = { 811412SN/A { "1", "Africa", 0, continent_country_menu, 0, &africa }, 821412SN/A { "2", "America -- North and South", 0, continent_country_menu, 0, 831412SN/A &america }, 841412SN/A { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica }, 851412SN/A { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic }, 861412SN/A { "5", "Asia", 0, continent_country_menu, 0, &asia }, 871412SN/A { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic }, 881412SN/A { "7", "Australia", 0, continent_country_menu, 0, &australia }, 892492SN/A { "8", "Europe", 0, continent_country_menu, 0, &europe }, 901412SN/A { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian }, 911412SN/A { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific } 921412SN/A}; 931412SN/A#define NCONTINENTS ((sizeof continents)/(sizeof continents[0])) 941412SN/A#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) 951412SN/A 961412SN/Astatic int 971412SN/Acontinent_country_menu(dialogMenuItem *continent) 981412SN/A{ 991412SN/A int rv; 1001412SN/A struct continent *contp = continent->data; 1011412SN/A char title[256]; 1021412SN/A int isocean = OCEANP(continent - continents); 1031412SN/A int menulen; 1041412SN/A 1051412SN/A /* Short cut -- if there's only one country, don't post a menu. */ 1061412SN/A if (contp->nitems == 1) { 1071412SN/A return set_zone_menu(&contp->menu[0]); 1081412SN/A } 1091412SN/A 1101412SN/A /* It's amazing how much good grammar really matters... */ 1111412SN/A if (!isocean) 1121412SN/A snprintf(title, sizeof title, "Countries in %s", 1131412SN/A continent->title); 1141412SN/A else 1151412SN/A snprintf(title, sizeof title, "Islands and groups in the %s", 1161412SN/A continent->title); 1171412SN/A 1181412SN/A menulen = contp->nitems < 16 ? contp->nitems : 16; 1191412SN/A rv = dialog_menu(title, (isocean ? "Select an island or group" 1201412SN/A : "Select a country"), -1, -1, menulen, 1211412SN/A -contp->nitems, contp->menu, 0, &contp->ch, 1221412SN/A &contp->sc); 1231412SN/A if (rv == 0) 1241412SN/A return DITEM_LEAVE_MENU; 1251412SN/A return DITEM_RECREATE; 1263994Sjjg} 1273994Sjjg 1283994Sjjgstatic struct continent * 1293994Sjjgfind_continent(const char *name) 1303994Sjjg{ 1313994Sjjg int i; 1323994Sjjg 1333994Sjjg for (i = 0; i < NCONTINENTS; i++) { 1343994Sjjg if (strcmp(name, continent_names[i].name) == 0) 1354040Sjjg return continent_names[i].continent; 1363994Sjjg } 1373994Sjjg return 0; 1383994Sjjg} 1393994Sjjg 1403994Sjjgstruct country { 1411412SN/A char *name; 1421412SN/A char *tlc; 1431412SN/A int nzones; 1441412SN/A char *filename; /* use iff nzones < 0 */ 1451412SN/A struct continent *continent; /* use iff nzones < 0 */ 1461412SN/A TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ 1471412SN/A dialogMenuItem *submenu; /* use iff nzones > 0 */ 1481412SN/A}; 1491412SN/A 1501412SN/Astruct zone { 1511412SN/A TAILQ_ENTRY(zone) link; 1521412SN/A char *descr; 1531412SN/A char *filename; 1541412SN/A struct continent *continent; 1551412SN/A}; 1561412SN/A 1571412SN/A/* 1581412SN/A * This is the easiest organization... we use ISO 3166 country codes, 1591412SN/A * of the two-letter variety, so we just size this array to suit. 1601412SN/A * Beats worrying about dynamic allocation. 1611412SN/A */ 1621412SN/A#define NCOUNTRIES (26*26) 1631412SN/Astatic struct country countries[NCOUNTRIES]; 1641412SN/A#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A')) 1651412SN/A 1661412SN/A/* 1671412SN/A * Read the ISO 3166 country code database in _PATH_ISO3166 1681412SN/A * (/usr/share/misc/iso3166). On error, exit via err(3). 1691412SN/A */ 1701412SN/Astatic void 1711412SN/Aread_iso3166_table(void) 1721412SN/A{ 1731412SN/A FILE *fp; 1741412SN/A char *s, *t, *name; 1751412SN/A size_t len; 1761412SN/A int lineno; 1771412SN/A struct country *cp; 1781412SN/A 1791412SN/A fp = fopen(_PATH_ISO3166, "r"); 1801412SN/A if (!fp) 1811412SN/A err(1, _PATH_ISO3166); 1821412SN/A lineno = 0; 1831412SN/A 1841412SN/A while ((s = fgetln(fp, &len)) != 0) { 1851412SN/A lineno++; 1861412SN/A if (s[len - 1] != '\n') 1871412SN/A errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 1881412SN/A s[len - 1] = '\0'; 1891412SN/A if (s[0] == '#') 1901412SN/A continue; 1911412SN/A 1921412SN/A /* Isolate the two-letter code. */ 1931412SN/A t = strsep(&s, "\t"); 1941412SN/A if (t == 0 || strlen(t) != 2) 1951412SN/A errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 1961412SN/A if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') 1971412SN/A errx(1, _PATH_ISO3166 ":%d: invalid code `%s'", 1981412SN/A lineno, t); 1991412SN/A 2001412SN/A /* Now skip past the three-letter and numeric codes. */ 201 name = strsep(&s, "\t"); /* 3-let */ 202 if (name == 0 || strlen(name) != 3) 203 errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 204 name = strsep(&s, "\t"); /* numeric */ 205 if (name == 0 || strlen(name) != 3) 206 errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 207 208 name = s; 209 210 cp = &countries[CODE2INT(t)]; 211 if (cp->name) 212 errx(1, _PATH_ISO3166 213 ":%d: country code `%s' multiply defined: %s", 214 lineno, t, cp->name); 215 cp->name = strdup(name); 216 cp->tlc = strdup(t); 217 } 218 219 fclose(fp); 220} 221 222static void 223add_zone_to_country(int lineno, const char *tlc, const char *descr, 224 const char *file, struct continent *cont) 225{ 226 struct zone *zp; 227 struct country *cp; 228 229 if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') 230 errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid", 231 lineno, tlc); 232 233 cp = &countries[CODE2INT(tlc)]; 234 if (cp->name == 0) 235 errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown", 236 lineno, tlc); 237 238 if (descr) { 239 if (cp->nzones < 0) 240 errx(1, _PATH_ZONETAB 241 ":%d: conflicting zone definition", lineno); 242 243 zp = malloc(sizeof *zp); 244 if (zp == 0) 245 err(1, "malloc(%lu)", (unsigned long)sizeof *zp); 246 247 if (cp->nzones == 0) 248 TAILQ_INIT(&cp->zones); 249 250 zp->descr = strdup(descr); 251 zp->filename = strdup(file); 252 zp->continent = cont; 253 TAILQ_INSERT_TAIL(&cp->zones, zp, link); 254 cp->nzones++; 255 } else { 256 if (cp->nzones > 0) 257 errx(1, _PATH_ZONETAB 258 ":%d: zone must have description", lineno); 259 if (cp->nzones < 0) 260 errx(1, _PATH_ZONETAB 261 ":%d: zone multiply defined", lineno); 262 cp->nzones = -1; 263 cp->filename = strdup(file); 264 cp->continent = cont; 265 } 266} 267 268/* 269 * This comparison function intentionally sorts all of the null-named 270 * ``countries''---i.e., the codes that don't correspond to a real 271 * country---to the end. Everything else is lexical by country name. 272 */ 273static int 274compare_countries(const void *xa, const void *xb) 275{ 276 const struct country *a = xa, *b = xb; 277 278 if (a->name == 0 && b->name == 0) 279 return 0; 280 if (a->name == 0 && b->name != 0) 281 return 1; 282 if (b->name == 0) 283 return -1; 284 285 return strcmp(a->name, b->name); 286} 287 288/* 289 * This must be done AFTER all zone descriptions are read, since it breaks 290 * CODE2INT(). 291 */ 292static void 293sort_countries(void) 294{ 295 qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries); 296} 297 298static void 299read_zones(void) 300{ 301 FILE *fp; 302 char *line; 303 size_t len; 304 int lineno; 305 char *tlc, *coord, *file, *descr, *p; 306 char contbuf[16]; 307 struct continent *cont; 308 309 fp = fopen(_PATH_ZONETAB, "r"); 310 if (!fp) 311 err(1, _PATH_ZONETAB); 312 lineno = 0; 313 314 while ((line = fgetln(fp, &len)) != 0) { 315 lineno++; 316 if (line[len - 1] != '\n') 317 errx(1, _PATH_ZONETAB ":%d: invalid format", lineno); 318 line[len - 1] = '\0'; 319 if (line[0] == '#') 320 continue; 321 322 tlc = strsep(&line, "\t"); 323 if (strlen(tlc) != 2) 324 errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'", 325 lineno, tlc); 326 coord = strsep(&line, "\t"); 327 file = strsep(&line, "\t"); 328 p = strchr(file, '/'); 329 if (p == 0) 330 errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'", 331 lineno, file); 332 contbuf[0] = '\0'; 333 strncat(contbuf, file, p - file); 334 cont = find_continent(contbuf); 335 if (!cont) 336 errx(1, _PATH_ZONETAB ":%d: invalid region `%s'", 337 lineno, contbuf); 338 339 descr = (line && *line) ? line : 0; 340 341 add_zone_to_country(lineno, tlc, descr, file, cont); 342 } 343 fclose(fp); 344} 345 346static void 347make_menus(void) 348{ 349 struct country *cp; 350 struct zone *zp, *zp2; 351 struct continent *cont; 352 dialogMenuItem *dmi; 353 int i; 354 355 /* 356 * First, count up all the countries in each continent/ocean. 357 * Be careful to count those countries which have multiple zones 358 * only once for each. NB: some countries are in multiple 359 * continents/oceans. 360 */ 361 for (cp = countries; cp->name; cp++) { 362 if (cp->nzones == 0) 363 continue; 364 if (cp->nzones < 0) { 365 cp->continent->nitems++; 366 } else { 367 for (zp = cp->zones.tqh_first; zp; 368 zp = zp->link.tqe_next) { 369 cont = zp->continent; 370 for (zp2 = cp->zones.tqh_first; 371 zp2->continent != cont; 372 zp2 = zp2->link.tqe_next) 373 ; 374 if (zp2 == zp) 375 zp->continent->nitems++; 376 } 377 } 378 } 379 380 /* 381 * Now allocate memory for the country menus. We set 382 * nitems back to zero so that we can use it for counting 383 * again when we actually build the menus. 384 */ 385 for (i = 0; i < NCONTINENTS; i++) { 386 continent_names[i].continent->menu = 387 malloc(sizeof(dialogMenuItem) * 388 continent_names[i].continent->nitems); 389 if (continent_names[i].continent->menu == 0) 390 err(1, "malloc for continent menu"); 391 continent_names[i].continent->nitems = 0; 392 } 393 394 /* 395 * Now that memory is allocated, create the menu items for 396 * each continent. For multiple-zone countries, also create 397 * the country's zone submenu. 398 */ 399 for (cp = countries; cp->name; cp++) { 400 if (cp->nzones == 0) 401 continue; 402 if (cp->nzones < 0) { 403 dmi = &cp->continent->menu[cp->continent->nitems]; 404 memset(dmi, 0, sizeof *dmi); 405 asprintf(&dmi->prompt, "%d", 406 ++cp->continent->nitems); 407 dmi->title = cp->name; 408 dmi->checked = 0; 409 dmi->fire = set_zone_whole_country; 410 dmi->selected = 0; 411 dmi->data = cp; 412 } else { 413 cp->submenu = malloc(cp->nzones * sizeof *dmi); 414 if (cp->submenu == 0) 415 err(1, "malloc for submenu"); 416 cp->nzones = 0; 417 for (zp = cp->zones.tqh_first; zp; 418 zp = zp->link.tqe_next) { 419 cont = zp->continent; 420 dmi = &cp->submenu[cp->nzones]; 421 memset(dmi, 0, sizeof *dmi); 422 asprintf(&dmi->prompt, "%d", 423 ++cp->nzones); 424 dmi->title = zp->descr; 425 dmi->checked = 0; 426 dmi->fire = set_zone_multi; 427 dmi->selected = 0; 428 dmi->data = zp; 429 430 for (zp2 = cp->zones.tqh_first; 431 zp2->continent != cont; 432 zp2 = zp2->link.tqe_next) 433 ; 434 if (zp2 != zp) 435 continue; 436 437 dmi = &cont->menu[cont->nitems]; 438 memset(dmi, 0, sizeof *dmi); 439 asprintf(&dmi->prompt, "%d", ++cont->nitems); 440 dmi->title = cp->name; 441 dmi->checked = 0; 442 dmi->fire = set_zone_menu; 443 dmi->selected = 0; 444 dmi->data = cp; 445 } 446 } 447 } 448} 449 450static int 451set_zone_menu(dialogMenuItem *dmi) 452{ 453 int rv; 454 char buf[256]; 455 struct country *cp = dmi->data; 456 int menulen; 457 458 snprintf(buf, sizeof buf, "%s Time Zones", cp->name); 459 menulen = cp->nzones < 16 ? cp->nzones : 16; 460 rv = dialog_menu(buf, "Select a zone which observes the same time as " 461 "your locality.", -1, -1, menulen, -cp->nzones, 462 cp->submenu, 0, 0, 0); 463 if (rv != 0) 464 return DITEM_RECREATE; 465 return DITEM_LEAVE_MENU; 466} 467 468static int 469install_zone_file(const char *filename) 470{ 471 struct stat sb; 472 int fd1, fd2; 473 int copymode; 474 char *msg; 475 ssize_t len; 476 char buf[1024]; 477 478 if (lstat(_PATH_LOCALTIME, &sb) < 0) 479 /* Nothing there yet... */ 480 copymode = 1; 481 else if(S_ISLNK(sb.st_mode)) 482 copymode = 0; 483 else 484 copymode = 1; 485 486#ifdef VERBOSE 487 if (copymode) 488 asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename); 489 else 490 asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME 491 " to %s", filename); 492 493 dialog_notify(msg); 494 free(msg); 495#endif 496 497 if (reallydoit) { 498 if (copymode) { 499 fd1 = open(filename, O_RDONLY, 0); 500 if (fd1 < 0) { 501 asprintf(&msg, "Could not open %s: %s", 502 filename, strerror(errno)); 503 dialog_mesgbox("Error", msg, 8, 72); 504 free(msg); 505 return DITEM_FAILURE | DITEM_RECREATE; 506 } 507 508 unlink(_PATH_LOCALTIME); 509 fd2 = open(_PATH_LOCALTIME, 510 O_CREAT | O_EXCL | O_WRONLY, 511 0444); 512 if (fd2 < 0) { 513 asprintf(&msg, "Could not open " 514 _PATH_LOCALTIME ": %s", 515 strerror(errno)); 516 dialog_mesgbox("Error", msg, 8, 72); 517 free(msg); 518 return DITEM_FAILURE | DITEM_RECREATE; 519 } 520 521 while ((len = read(fd1, buf, sizeof buf)) > 0) 522 len = write(fd2, buf, len); 523 524 if (len == -1) { 525 asprintf(&msg, "Error copying %s to " 526 _PATH_LOCALTIME ": %s", 527 strerror(errno)); 528 dialog_mesgbox("Error", msg, 8, 72); 529 free(msg); 530 /* Better to leave none than a corrupt one. */ 531 unlink(_PATH_LOCALTIME); 532 return DITEM_FAILURE | DITEM_RECREATE; 533 } 534 close(fd1); 535 close(fd2); 536 } else { 537 if (access(filename, R_OK) != 0) { 538 asprintf(&msg, "Cannot access %s: %s", 539 filename, strerror(errno)); 540 dialog_mesgbox("Error", msg, 8, 72); 541 free(msg); 542 return DITEM_FAILURE | DITEM_RECREATE; 543 } 544 unlink(_PATH_LOCALTIME); 545 if (symlink(filename, _PATH_LOCALTIME) < 0) { 546 asprintf(&msg, "Cannot create symbolic link " 547 _PATH_LOCALTIME " to %s: %s", 548 filename, strerror(errno)); 549 dialog_mesgbox("Error", msg, 8, 72); 550 free(msg); 551 return DITEM_FAILURE | DITEM_RECREATE; 552 } 553 } 554 } 555 556#ifdef VERBOSE 557 if (copymode) 558 asprintf(&msg, "Copied timezone file from %s to " 559 _PATH_LOCALTIME, filename); 560 else 561 asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME 562 " to %s", filename); 563 564 dialog_mesgbox("Done", msg, 8, 72); 565 free(msg); 566#endif 567 return DITEM_LEAVE_MENU; 568} 569 570static int 571confirm_zone(const char *filename) 572{ 573 char *msg; 574 struct tm *tm; 575 time_t t = time(0); 576 int rv; 577 578 setenv("TZ", filename, 1); 579 tzset(); 580 tm = localtime(&t); 581 582 asprintf(&msg, "Does the abbreviation `%s' look reasonable?", 583 tm->tm_zone); 584 rv = !dialog_yesno("Confirmation", msg, 4, 72); 585 free(msg); 586 return rv; 587} 588 589static int 590set_zone_multi(dialogMenuItem *dmi) 591{ 592 char *fn; 593 struct zone *zp = dmi->data; 594 int rv; 595 596 if (!confirm_zone(zp->filename)) 597 return DITEM_FAILURE | DITEM_RECREATE; 598 599 asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename); 600 rv = install_zone_file(fn); 601 free(fn); 602 return rv; 603} 604 605static int 606set_zone_whole_country(dialogMenuItem *dmi) 607{ 608 char *fn; 609 struct country *cp = dmi->data; 610 int rv; 611 612 if (!confirm_zone(cp->filename)) 613 return DITEM_FAILURE | DITEM_RECREATE; 614 615 asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename); 616 rv = install_zone_file(fn); 617 free(fn); 618 return rv; 619} 620 621int 622main(int argc, char **argv) 623{ 624 int c; 625 626 while ((c = getopt(argc, argv, "n")) != -1) { 627 switch(c) { 628 case 'n': 629 reallydoit = 0; 630 break; 631 632 default: 633 fprintf(stderr, "%s: usage:\n\t%s [-n]\n", argv[0], 634 argv[0]); 635 exit(1); 636 } 637 } 638 639 if (optind != argc) { 640 fprintf(stderr, "%s: usage:\n\t%s [-n]\n", argv[0], argv[0]); 641 exit(1); 642 } 643 644 read_iso3166_table(); 645 read_zones(); 646 sort_countries(); 647 make_menus(); 648 649 init_dialog(); 650 if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock", 651 "Is this machine's CMOS clock set to UTC? If it is set to local time,\n" 652 "please choose NO here!", 7, 72)) 653 system("rm -f /etc/wall_cmos_clock"); 654 else 655 system("touch /etc/wall_cmos_clock"); 656 dialog_clear_norefresh(); 657 dialog_menu("Time Zone Selector", "Select a region", -1, -1, 658 NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL); 659 dialog_clear(); 660 end_dialog(); 661 return 0; 662} 663 664