tzsetup.c revision 30999
169783Smsmith/* 269783Smsmith * Copyright 1996 Massachusetts Institute of Technology 369783Smsmith * 469783Smsmith * Permission to use, copy, modify, and distribute this software and 569783Smsmith * its documentation for any purpose and without fee is hereby 669783Smsmith * granted, provided that both the above copyright notice and this 769783Smsmith * permission notice appear in all copies, that both the above 869783Smsmith * copyright notice and this permission notice appear in all 969783Smsmith * supporting documentation, and that the name of M.I.T. not be used 1069783Smsmith * in advertising or publicity pertaining to distribution of the 1169783Smsmith * software without specific, written prior permission. M.I.T. makes 1269783Smsmith * no representations about the suitability of this software for any 1369783Smsmith * purpose. It is provided "as is" without express or implied 1469783Smsmith * warranty. 1569783Smsmith * 1669783Smsmith * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1769783Smsmith * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1869783Smsmith * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1969783Smsmith * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2069783Smsmith * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2169783Smsmith * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2269783Smsmith * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2369783Smsmith * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2469783Smsmith * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2569783Smsmith * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2669783Smsmith * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2769783Smsmith * SUCH DAMAGE. 2869783Smsmith */ 2969783Smsmith 3069783Smsmith/* 31119418Sobrien * Second attempt at a `tzmenu' program, using the separate description 32119418Sobrien * files provided in newer tzdata releases. 33119418Sobrien */ 3469783Smsmith 3569783Smsmith#ifndef lint 3669783Smsmithstatic const char rcsid[] = 3769783Smsmith "$Id: tzsetup.c,v 1.8 1997/10/27 07:49:47 charnier Exp $"; 3869783Smsmith#endif /* not lint */ 3969783Smsmith 4069783Smsmith#include <sys/types.h> 41129876Sphk#include <dialog.h> 4269783Smsmith#include <err.h> 43107546Simp#include <errno.h> 44107546Simp#include <stdio.h> 45106844Smdodd#include <stdlib.h> 4669783Smsmith#include <string.h> 4769783Smsmith#include <unistd.h> 4869783Smsmith 49119285Simp#include <sys/fcntl.h> 50119285Simp#include <sys/queue.h> 51119285Simp#include <sys/stat.h> 5269783Smsmith 5369783Smsmith#include "paths.h" 5469783Smsmith 55200341Sjkimstatic int reallydoit = 1; 56200341Sjkim 57200341Sjkimstatic int continent_country_menu(dialogMenuItem *); 58200341Sjkimstatic int set_zone_multi(dialogMenuItem *); 59200341Sjkimstatic int set_zone_whole_country(dialogMenuItem *); 60200341Sjkimstatic int set_zone_menu(dialogMenuItem *); 61200341Sjkim 62200341Sjkimstruct continent { 63200341Sjkim dialogMenuItem *menu; 6469783Smsmith int nitems; 65200341Sjkim int ch; 66200341Sjkim int sc; 6769783Smsmith}; 6869783Smsmith 6969783Smsmithstatic struct continent africa, america, antarctica, arctic, asia, atlantic; 7069783Smsmithstatic struct continent australia, europe, indian, pacific; 7169783Smsmith 72145661Simpstatic struct continent_names { 7369783Smsmith char *name; 74200341Sjkim struct continent *continent; 75200341Sjkim} continent_names[] = { 7669783Smsmith { "Africa", &africa }, { "America", &america }, 7769783Smsmith { "Antarctica", &antarctica }, { "Arctic", &arctic }, 7869783Smsmith { "Asia", &asia }, 7969783Smsmith { "Atlantic", &atlantic }, { "Australia", &australia }, 8069783Smsmith { "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific } 8169783Smsmith}; 8269783Smsmith 8369783Smsmithstatic dialogMenuItem continents[] = { 8469783Smsmith { "1", "Africa", 0, continent_country_menu, 0, &africa }, 8569783Smsmith { "2", "America -- North and South", 0, continent_country_menu, 0, 8669783Smsmith &america }, 8769783Smsmith { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica }, 8869783Smsmith { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic }, 8969783Smsmith { "5", "Asia", 0, continent_country_menu, 0, &asia }, 9069783Smsmith { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic }, 9169783Smsmith { "7", "Australia", 0, continent_country_menu, 0, &australia }, 9269783Smsmith { "8", "Europe", 0, continent_country_menu, 0, &europe }, 93164264Sjhb { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian }, 94164264Sjhb { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific } 95164264Sjhb}; 96164264Sjhb#define NCONTINENTS ((sizeof continents)/(sizeof continents[0])) 97169221Sjhb#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) 9869783Smsmith 9969783Smsmithstatic int 10069783Smsmithcontinent_country_menu(dialogMenuItem *continent) 10169783Smsmith{ 102154079Sjhb int rv; 10369783Smsmith struct continent *contp = continent->data; 104154079Sjhb char title[256]; 10569783Smsmith int isocean = OCEANP(continent - continents); 10669783Smsmith int menulen; 10769783Smsmith 108163805Simp /* Short cut -- if there's only one country, don't post a menu. */ 109163805Simp if (contp->nitems == 1) { 110163805Simp return set_zone_menu(&contp->menu[0]); 111163805Simp } 112163805Simp 113163805Simp /* It's amazing how much good grammar really matters... */ 114163805Simp if (!isocean) 115163805Simp snprintf(title, sizeof title, "Countries in %s", 116163805Simp continent->title); 117163805Simp else 118163805Simp snprintf(title, sizeof title, "Islands and groups in the %s", 119163805Simp continent->title); 120163805Simp 121163805Simp menulen = contp->nitems < 16 ? contp->nitems : 16; 122163805Simp rv = dialog_menu(title, (isocean ? "Select an island or group" 123163805Simp : "Select a country"), -1, -1, menulen, 124163805Simp -contp->nitems, contp->menu, 0, &contp->ch, 125163805Simp &contp->sc); 126163805Simp if (rv == 0) 127163805Simp return DITEM_LEAVE_MENU; 128163805Simp return DITEM_RECREATE; 129163805Simp} 130163805Simp 131163805Simpstatic struct continent * 132163805Simpfind_continent(const char *name) 133163805Simp{ 134163805Simp int i; 135200341Sjkim 136200341Sjkim for (i = 0; i < NCONTINENTS; i++) { 137200341Sjkim if (strcmp(name, continent_names[i].name) == 0) 138200341Sjkim return continent_names[i].continent; 139200341Sjkim } 140200341Sjkim return 0; 141200341Sjkim} 142200341Sjkim 143200341Sjkimstruct country { 144200341Sjkim char *name; 145200341Sjkim char *tlc; 146200341Sjkim int nzones; 147200341Sjkim char *filename; /* use iff nzones < 0 */ 148200341Sjkim struct continent *continent; /* use iff nzones < 0 */ 149200341Sjkim TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ 150200341Sjkim dialogMenuItem *submenu; /* use iff nzones > 0 */ 151200341Sjkim}; 152200341Sjkim 153200341Sjkimstruct zone { 154200341Sjkim TAILQ_ENTRY(zone) link; 155200341Sjkim char *descr; 156200341Sjkim char *filename; 157200341Sjkim struct continent *continent; 158200341Sjkim}; 159200341Sjkim 160200341Sjkim/* 161200341Sjkim * This is the easiest organization... we use ISO 3166 country codes, 162200341Sjkim * of the two-letter variety, so we just size this array to suit. 163200341Sjkim * Beats worrying about dynamic allocation. 164200341Sjkim */ 165200341Sjkim#define NCOUNTRIES (26*26) 166200341Sjkimstatic struct country countries[NCOUNTRIES]; 167200341Sjkim#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A')) 168200341Sjkim 169200341Sjkim/* 170200341Sjkim * Read the ISO 3166 country code database in _PATH_ISO3166 171200341Sjkim * (/usr/share/misc/iso3166). On error, exit via err(3). 172200341Sjkim */ 173200341Sjkimstatic void 174200341Sjkimread_iso3166_table(void) 175200341Sjkim{ 176200341Sjkim FILE *fp; 177200341Sjkim char *s, *t, *name; 178200341Sjkim size_t len; 179200341Sjkim int lineno; 180200341Sjkim struct country *cp; 181200341Sjkim 182200341Sjkim fp = fopen(_PATH_ISO3166, "r"); 183200341Sjkim if (!fp) 184200341Sjkim err(1, _PATH_ISO3166); 185200341Sjkim lineno = 0; 186200341Sjkim 187200341Sjkim while ((s = fgetln(fp, &len)) != 0) { 188200341Sjkim lineno++; 189200341Sjkim if (s[len - 1] != '\n') 190200341Sjkim errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 191200341Sjkim s[len - 1] = '\0'; 192200341Sjkim if (s[0] == '#' || strspn(s, " \t") == len - 1) 193200341Sjkim continue; 194200341Sjkim 195200341Sjkim /* Isolate the two-letter code. */ 196200341Sjkim t = strsep(&s, "\t"); 197200341Sjkim if (t == 0 || strlen(t) != 2) 198200341Sjkim errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 199200341Sjkim if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') 200200341Sjkim errx(1, _PATH_ISO3166 ":%d: invalid code `%s'", 201200341Sjkim lineno, t); 202200341Sjkim 203200341Sjkim /* Now skip past the three-letter and numeric codes. */ 204200341Sjkim name = strsep(&s, "\t"); /* 3-let */ 205200341Sjkim if (name == 0 || strlen(name) != 3) 206200341Sjkim errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 207200341Sjkim name = strsep(&s, "\t"); /* numeric */ 208200341Sjkim if (name == 0 || strlen(name) != 3) 209200341Sjkim errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 210200341Sjkim 211200341Sjkim name = s; 212200341Sjkim 213200341Sjkim cp = &countries[CODE2INT(t)]; 214200341Sjkim if (cp->name) 215200341Sjkim errx(1, _PATH_ISO3166 216200341Sjkim ":%d: country code `%s' multiply defined: %s", 217200341Sjkim lineno, t, cp->name); 218200341Sjkim cp->name = strdup(name); 219200341Sjkim cp->tlc = strdup(t); 220200341Sjkim } 221200341Sjkim 222200341Sjkim fclose(fp); 223200341Sjkim} 224200341Sjkim 225200341Sjkimstatic void 226200341Sjkimadd_zone_to_country(int lineno, const char *tlc, const char *descr, 227200341Sjkim const char *file, struct continent *cont) 228200341Sjkim{ 229200341Sjkim struct zone *zp; 230200341Sjkim struct country *cp; 231200341Sjkim 232200341Sjkim if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') 233200341Sjkim errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid", 234200341Sjkim lineno, tlc); 235200341Sjkim 236200341Sjkim cp = &countries[CODE2INT(tlc)]; 237200341Sjkim if (cp->name == 0) 238200341Sjkim errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown", 239200341Sjkim lineno, tlc); 240200341Sjkim 241200341Sjkim if (descr) { 242200341Sjkim if (cp->nzones < 0) 243200341Sjkim errx(1, _PATH_ZONETAB 244200341Sjkim ":%d: conflicting zone definition", lineno); 245200341Sjkim 246200341Sjkim zp = malloc(sizeof *zp); 247200341Sjkim if (zp == 0) 248200341Sjkim err(1, "malloc(%lu)", (unsigned long)sizeof *zp); 249200341Sjkim 250200341Sjkim if (cp->nzones == 0) 251200341Sjkim TAILQ_INIT(&cp->zones); 252200341Sjkim 253200341Sjkim zp->descr = strdup(descr); 254200341Sjkim zp->filename = strdup(file); 255200341Sjkim zp->continent = cont; 256200341Sjkim TAILQ_INSERT_TAIL(&cp->zones, zp, link); 257200341Sjkim cp->nzones++; 258200341Sjkim } else { 259200341Sjkim if (cp->nzones > 0) 260200341Sjkim errx(1, _PATH_ZONETAB 261200341Sjkim ":%d: zone must have description", lineno); 262200341Sjkim if (cp->nzones < 0) 263200341Sjkim errx(1, _PATH_ZONETAB 264200341Sjkim ":%d: zone multiply defined", lineno); 265200341Sjkim cp->nzones = -1; 266200341Sjkim cp->filename = strdup(file); 267200341Sjkim cp->continent = cont; 268200341Sjkim } 269200341Sjkim} 270200341Sjkim 271200341Sjkim/* 272200341Sjkim * This comparison function intentionally sorts all of the null-named 273200341Sjkim * ``countries''---i.e., the codes that don't correspond to a real 274200341Sjkim * country---to the end. Everything else is lexical by country name. 275200341Sjkim */ 276200341Sjkimstatic int 277200341Sjkimcompare_countries(const void *xa, const void *xb) 278200341Sjkim{ 279200341Sjkim const struct country *a = xa, *b = xb; 280200341Sjkim 281200341Sjkim if (a->name == 0 && b->name == 0) 282200341Sjkim return 0; 28369783Smsmith if (a->name == 0 && b->name != 0) 28469783Smsmith return 1; 28569783Smsmith if (b->name == 0) 28669783Smsmith return -1; 28769783Smsmith 28869783Smsmith return strcmp(a->name, b->name); 28969783Smsmith} 29069783Smsmith 29169783Smsmith/* 29269783Smsmith * This must be done AFTER all zone descriptions are read, since it breaks 29369783Smsmith * CODE2INT(). 29469783Smsmith */ 29569783Smsmithstatic void 296102441Sjhbsort_countries(void) 297102441Sjhb{ 29869783Smsmith qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries); 29969783Smsmith} 300181789Simp 301181789Simpstatic void 30269783Smsmithread_zones(void) 30369783Smsmith{ 30469783Smsmith FILE *fp; 30569783Smsmith char *line; 30669908Smsmith size_t len; 30769908Smsmith int lineno; 30869908Smsmith char *tlc, *coord, *file, *descr, *p; 309200341Sjkim char contbuf[16]; 310200341Sjkim struct continent *cont; 311200341Sjkim 31269783Smsmith fp = fopen(_PATH_ZONETAB, "r"); 31369908Smsmith if (!fp) 314181789Simp err(1, _PATH_ZONETAB); 315181789Simp lineno = 0; 316181789Simp 317181789Simp while ((line = fgetln(fp, &len)) != 0) { 318181789Simp lineno++; 319182706Simp if (line[len - 1] != '\n') 320181789Simp errx(1, _PATH_ZONETAB ":%d: invalid format", lineno); 321182706Simp line[len - 1] = '\0'; 322181789Simp if (line[0] == '#') 323182706Simp continue; 324181789Simp 325182706Simp tlc = strsep(&line, "\t"); 326181789Simp if (strlen(tlc) != 2) 327181789Simp errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'", 32869908Smsmith lineno, tlc); 32969908Smsmith coord = strsep(&line, "\t"); 33069908Smsmith file = strsep(&line, "\t"); 331124365Simp p = strchr(file, '/'); 33269908Smsmith if (p == 0) 333119266Simp errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'", 33469908Smsmith lineno, file); 33569908Smsmith contbuf[0] = '\0'; 33669908Smsmith strncat(contbuf, file, p - file); 33769908Smsmith cont = find_continent(contbuf); 33869908Smsmith if (!cont) 33969908Smsmith errx(1, _PATH_ZONETAB ":%d: invalid region `%s'", 340124365Simp lineno, contbuf); 34169908Smsmith 342124365Simp descr = (line && *line) ? line : 0; 343124365Simp 344124365Simp add_zone_to_country(lineno, tlc, descr, file, cont); 345124365Simp } 346124365Simp fclose(fp); 347124365Simp} 348124365Simp 349124365Simpstatic void 350124365Simpmake_menus(void) 351124365Simp{ 352124365Simp struct country *cp; 35369908Smsmith struct zone *zp, *zp2; 354149521Sjkim struct continent *cont; 355149521Sjkim dialogMenuItem *dmi; 356149521Sjkim int i; 357149521Sjkim 358149521Sjkim /* 359149521Sjkim * First, count up all the countries in each continent/ocean. 360157949Sjkim * Be careful to count those countries which have multiple zones 361149521Sjkim * only once for each. NB: some countries are in multiple 362157949Sjkim * continents/oceans. 363157949Sjkim */ 364149521Sjkim for (cp = countries; cp->name; cp++) { 365157949Sjkim if (cp->nzones == 0) 366157949Sjkim continue; 367157949Sjkim if (cp->nzones < 0) { 368157949Sjkim cp->continent->nitems++; 369157949Sjkim } else { 370157949Sjkim for (zp = cp->zones.tqh_first; zp; 371157949Sjkim zp = zp->link.tqe_next) { 372157949Sjkim cont = zp->continent; 373157949Sjkim for (zp2 = cp->zones.tqh_first; 374149521Sjkim zp2->continent != cont; 375149521Sjkim zp2 = zp2->link.tqe_next) 376149521Sjkim ; 377149521Sjkim if (zp2 == zp) 378149521Sjkim zp->continent->nitems++; 379149521Sjkim } 38069908Smsmith } 38169908Smsmith } 382165995Sjhb 383165995Sjhb /* 384165995Sjhb * Now allocate memory for the country menus. We set 385124365Simp * nitems back to zero so that we can use it for counting 386124365Simp * again when we actually build the menus. 387124365Simp */ 388124365Simp for (i = 0; i < NCONTINENTS; i++) { 389124365Simp continent_names[i].continent->menu = 390124365Simp malloc(sizeof(dialogMenuItem) * 391124365Simp continent_names[i].continent->nitems); 392124365Simp if (continent_names[i].continent->menu == 0) 393124365Simp err(1, "malloc for continent menu"); 394168157Sjhb continent_names[i].continent->nitems = 0; 395124365Simp } 396124365Simp 39769783Smsmith /* 398172394Smarius * Now that memory is allocated, create the menu items for 39969783Smsmith * each continent. For multiple-zone countries, also create 40069783Smsmith * the country's zone submenu. 40169783Smsmith */ 402163805Simp for (cp = countries; cp->name; cp++) { 403163805Simp if (cp->nzones == 0) 404163805Simp continue; 405163805Simp if (cp->nzones < 0) { 406163805Simp dmi = &cp->continent->menu[cp->continent->nitems]; 407163805Simp memset(dmi, 0, sizeof *dmi); 408163805Simp asprintf(&dmi->prompt, "%d", 409163805Simp ++cp->continent->nitems); 410124365Simp dmi->title = cp->name; 411124365Simp dmi->checked = 0; 41269783Smsmith dmi->fire = set_zone_whole_country; 41369783Smsmith dmi->selected = 0; 41469783Smsmith dmi->data = cp; 41569783Smsmith } else { 416181798Simp cp->submenu = malloc(cp->nzones * sizeof *dmi); 417181798Simp if (cp->submenu == 0) 418181798Simp err(1, "malloc for submenu"); 419181798Simp cp->nzones = 0; 420181798Simp for (zp = cp->zones.tqh_first; zp; 421181798Simp zp = zp->link.tqe_next) { 42269783Smsmith cont = zp->continent; 42369783Smsmith dmi = &cp->submenu[cp->nzones]; 424181798Simp memset(dmi, 0, sizeof *dmi); 425181798Simp asprintf(&dmi->prompt, "%d", 42669783Smsmith ++cp->nzones); 427102441Sjhb dmi->title = zp->descr; 42869783Smsmith dmi->checked = 0; 429103042Sjhb dmi->fire = set_zone_multi; 430102441Sjhb dmi->selected = 0; 431102441Sjhb dmi->data = zp; 432102441Sjhb 433102441Sjhb for (zp2 = cp->zones.tqh_first; 434102441Sjhb zp2->continent != cont; 435102441Sjhb zp2 = zp2->link.tqe_next) 436102441Sjhb ; 43769783Smsmith if (zp2 != zp) 438103016Sjhb continue; 43969783Smsmith 44069783Smsmith dmi = &cont->menu[cont->nitems]; 441181798Simp memset(dmi, 0, sizeof *dmi); 44269783Smsmith asprintf(&dmi->prompt, "%d", ++cont->nitems); 44369783Smsmith dmi->title = cp->name; 44469783Smsmith dmi->checked = 0; 44569783Smsmith dmi->fire = set_zone_menu; 44669783Smsmith dmi->selected = 0; 447102441Sjhb dmi->data = cp; 448200341Sjkim } 449200341Sjkim } 450200341Sjkim } 451200341Sjkim} 452200341Sjkim 453200341Sjkimstatic int 454200341Sjkimset_zone_menu(dialogMenuItem *dmi) 455200341Sjkim{ 456200341Sjkim int rv; 457200341Sjkim char buf[256]; 458200341Sjkim struct country *cp = dmi->data; 459200341Sjkim int menulen; 460200341Sjkim 461200341Sjkim snprintf(buf, sizeof buf, "%s Time Zones", cp->name); 462200341Sjkim menulen = cp->nzones < 16 ? cp->nzones : 16; 463200341Sjkim rv = dialog_menu(buf, "Select a zone which observes the same time as " 464200341Sjkim "your locality.", -1, -1, menulen, -cp->nzones, 465200341Sjkim cp->submenu, 0, 0, 0); 466200341Sjkim if (rv != 0) 467200341Sjkim return DITEM_RECREATE; 468200341Sjkim return DITEM_LEAVE_MENU; 469200341Sjkim} 470200341Sjkim 471200341Sjkimstatic int 472200341Sjkiminstall_zone_file(const char *filename) 473200341Sjkim{ 474200341Sjkim struct stat sb; 475200341Sjkim int fd1, fd2; 476200341Sjkim int copymode; 477200341Sjkim char *msg; 478200341Sjkim ssize_t len; 479200341Sjkim char buf[1024]; 480200341Sjkim 481200341Sjkim if (lstat(_PATH_LOCALTIME, &sb) < 0) 482200341Sjkim /* Nothing there yet... */ 48369783Smsmith copymode = 1; 48469783Smsmith else if(S_ISLNK(sb.st_mode)) 48569783Smsmith copymode = 0; 48669783Smsmith else 48769783Smsmith copymode = 1; 488172394Smarius 489172394Smarius#ifdef VERBOSE 490172394Smarius if (copymode) 49169783Smsmith asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename); 49269783Smsmith else 49369783Smsmith asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME 49469783Smsmith " to %s", filename); 49569783Smsmith 49669783Smsmith dialog_notify(msg); 49769783Smsmith free(msg); 498102441Sjhb#endif 49969783Smsmith 50069783Smsmith if (reallydoit) { 50169783Smsmith if (copymode) { 50269783Smsmith fd1 = open(filename, O_RDONLY, 0); 50369783Smsmith if (fd1 < 0) { 504172394Smarius asprintf(&msg, "Could not open %s: %s", 505172394Smarius filename, strerror(errno)); 50669783Smsmith dialog_mesgbox("Error", msg, 8, 72); 50769783Smsmith free(msg); 508172394Smarius return DITEM_FAILURE | DITEM_RECREATE; 50969783Smsmith } 51069783Smsmith 51169783Smsmith unlink(_PATH_LOCALTIME); 51269783Smsmith fd2 = open(_PATH_LOCALTIME, 51369783Smsmith O_CREAT | O_EXCL | O_WRONLY, 51469783Smsmith 0444); 51569783Smsmith if (fd2 < 0) { 51669783Smsmith asprintf(&msg, "Could not open " 517102441Sjhb _PATH_LOCALTIME ": %s", 51869783Smsmith strerror(errno)); 519142051Simp dialog_mesgbox("Error", msg, 8, 72); 52069783Smsmith free(msg); 521124365Simp return DITEM_FAILURE | DITEM_RECREATE; 522164130Sjhb } 523124365Simp 52469783Smsmith while ((len = read(fd1, buf, sizeof buf)) > 0) 52569783Smsmith len = write(fd2, buf, len); 52669783Smsmith 52769783Smsmith if (len == -1) { 528164130Sjhb asprintf(&msg, "Error copying %s to " 529164130Sjhb _PATH_LOCALTIME ": %s", 530164130Sjhb strerror(errno)); 531164130Sjhb dialog_mesgbox("Error", msg, 8, 72); 532164130Sjhb free(msg); 533164130Sjhb /* Better to leave none than a corrupt one. */ 53469783Smsmith unlink(_PATH_LOCALTIME); 53569783Smsmith return DITEM_FAILURE | DITEM_RECREATE; 536107546Simp } 537124365Simp close(fd1); 538124365Simp close(fd2); 539124365Simp } else { 540145652Smarcel if (access(filename, R_OK) != 0) { 541145652Smarcel asprintf(&msg, "Cannot access %s: %s", 542145652Smarcel filename, strerror(errno)); 543145652Smarcel dialog_mesgbox("Error", msg, 8, 72); 544145652Smarcel free(msg); 545145652Smarcel return DITEM_FAILURE | DITEM_RECREATE; 546145652Smarcel } 547145652Smarcel unlink(_PATH_LOCALTIME); 548124365Simp if (symlink(filename, _PATH_LOCALTIME) < 0) { 549124365Simp asprintf(&msg, "Cannot create symbolic link " 550124365Simp _PATH_LOCALTIME " to %s: %s", 551124365Simp filename, strerror(errno)); 552124365Simp dialog_mesgbox("Error", msg, 8, 72); 553124365Simp free(msg); 554142051Simp return DITEM_FAILURE | DITEM_RECREATE; 555142051Simp } 556124365Simp } 557106844Smdodd } 558124365Simp 559189844Simp#ifdef VERBOSE 560189792Simp if (copymode) 561189792Simp asprintf(&msg, "Copied timezone file from %s to " 562189792Simp _PATH_LOCALTIME, filename); 563189792Simp else 564189792Simp asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME 565189792Simp " to %s", filename); 566189844Simp 567106844Smdodd dialog_mesgbox("Done", msg, 8, 72); 568124365Simp free(msg); 569142051Simp#endif 570142051Simp return DITEM_LEAVE_MENU; 571124365Simp} 572124365Simp 573124365Simpstatic int 574124365Simpconfirm_zone(const char *filename) 575124365Simp{ 576164130Sjhb char *msg; 577124365Simp struct tm *tm; 578164130Sjhb time_t t = time(0); 579124365Simp int rv; 580124365Simp 581124365Simp setenv("TZ", filename, 1); 582142051Simp tzset(); 583164130Sjhb tm = localtime(&t); 584164130Sjhb 585124365Simp asprintf(&msg, "Does the abbreviation `%s' look reasonable?", 58669783Smsmith tm->tm_zone); 58769783Smsmith rv = !dialog_yesno("Confirmation", msg, 4, 72); 588107546Simp free(msg); 589107546Simp return rv; 590124365Simp} 591107546Simp 592124365Simpstatic int 593145652Smarcelset_zone_multi(dialogMenuItem *dmi) 594145652Smarcel{ 595145652Smarcel char *fn; 596145652Smarcel struct zone *zp = dmi->data; 597145652Smarcel int rv; 598145652Smarcel 599145652Smarcel if (!confirm_zone(zp->filename)) 600145652Smarcel return DITEM_FAILURE | DITEM_RECREATE; 601124365Simp 602124365Simp asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename); 603124365Simp rv = install_zone_file(fn); 604124365Simp free(fn); 605124365Simp return rv; 606124365Simp} 607124365Simp 608124365Simpstatic int 609124365Simpset_zone_whole_country(dialogMenuItem *dmi) 610124365Simp{ 611124365Simp char *fn; 612124365Simp struct country *cp = dmi->data; 613124365Simp int rv; 614124365Simp 615124365Simp if (!confirm_zone(cp->filename)) 616124365Simp return DITEM_FAILURE | DITEM_RECREATE; 617124365Simp 618124365Simp asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename); 619124365Simp rv = install_zone_file(fn); 620124365Simp free(fn); 621124365Simp return rv; 622124365Simp} 623107546Simp 624107546Simpstatic void 625124365Simpusage() 626189844Simp{ 627124365Simp fprintf(stderr, "usage: tzsetup [-n]\n"); 628189792Simp exit(1); 629189792Simp} 630124365Simp 631124365Simpint 632189792Simpmain(int argc, char **argv) 633189792Simp{ 634124365Simp int c; 635189844Simp 636106844Smdodd while ((c = getopt(argc, argv, "n")) != -1) { 637124365Simp switch(c) { 638142051Simp case 'n': 639142051Simp reallydoit = 0; 640124365Simp break; 641124365Simp 642124365Simp default: 643124365Simp usage(); 644124365Simp } 645124365Simp } 646164130Sjhb 647163805Simp if (optind != argc) 648164130Sjhb usage(); 649163805Simp 650163805Simp read_iso3166_table(); 651124365Simp read_zones(); 652124365Simp sort_countries(); 653124365Simp make_menus(); 654164130Sjhb 655142051Simp init_dialog(); 656164130Sjhb if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock", 657124365Simp "Is this machine's CMOS clock set to UTC? If it is set to local time,\n" 65869908Smsmith "please choose NO here!", 7, 72)) 65969783Smsmith if (reallydoit) 660124365Simp system("rm -f /etc/wall_cmos_clock"); 66169783Smsmith else 662124365Simp if (reallydoit) 663124365Simp system("touch /etc/wall_cmos_clock"); 664124365Simp dialog_clear_norefresh(); 665142051Simp dialog_menu("Time Zone Selector", "Select a region", -1, -1, 666142051Simp NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL); 66769783Smsmith dialog_clear(); 66869783Smsmith end_dialog(); 66969783Smsmith return 0; 67069783Smsmith} 67169783Smsmith 672102441Sjhb