tzsetup.c revision 198254
119872Swollman/* 219872Swollman * Copyright 1996 Massachusetts Institute of Technology 319872Swollman * 419872Swollman * Permission to use, copy, modify, and distribute this software and 519872Swollman * its documentation for any purpose and without fee is hereby 619872Swollman * granted, provided that both the above copyright notice and this 719872Swollman * permission notice appear in all copies, that both the above 819872Swollman * copyright notice and this permission notice appear in all 919872Swollman * supporting documentation, and that the name of M.I.T. not be used 1019872Swollman * in advertising or publicity pertaining to distribution of the 1119872Swollman * software without specific, written prior permission. M.I.T. makes 1219872Swollman * no representations about the suitability of this software for any 1319872Swollman * purpose. It is provided "as is" without express or implied 1419872Swollman * warranty. 15179530Sjkim * 1619872Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1719872Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1819872Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1919872Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2019872Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2119872Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2219872Swollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2319872Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2419872Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2519872Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2619872Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2719872Swollman * SUCH DAMAGE. 2819872Swollman */ 2919872Swollman 3019872Swollman/* 3119872Swollman * Second attempt at a `tzmenu' program, using the separate description 3219872Swollman * files provided in newer tzdata releases. 3319872Swollman */ 3419872Swollman 35179530Sjkim#include <sys/cdefs.h> 36179530Sjkim__FBSDID("$FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 198254 2009-10-19 21:24:19Z edwin $"); 3730763Scharnier 3819872Swollman#include <dialog.h> 3919872Swollman#include <err.h> 4019872Swollman#include <errno.h> 4119872Swollman#include <stdio.h> 4219872Swollman#include <stdlib.h> 4319872Swollman#include <string.h> 4466907Swollman#include <time.h> 4519872Swollman#include <unistd.h> 4619872Swollman 4719872Swollman#include <sys/fcntl.h> 4819872Swollman#include <sys/queue.h> 4919872Swollman#include <sys/stat.h> 5019872Swollman 51179530Sjkim#define _PATH_ZONETAB "/usr/share/zoneinfo/zone.tab" 52179530Sjkim#define _PATH_ISO3166 "/usr/share/misc/iso3166" 53179530Sjkim#define _PATH_ZONEINFO "/usr/share/zoneinfo" 54179530Sjkim#define _PATH_LOCALTIME "/etc/localtime" 55179530Sjkim#define _PATH_WALL_CMOS_CLOCK "/etc/wall_cmos_clock" 5619872Swollman 5719872Swollmanstatic int reallydoit = 1; 5819872Swollman 59179530Sjkimstatic void usage(void); 60179530Sjkimstatic int continent_country_menu(dialogMenuItem *); 61179530Sjkimstatic int set_zone_multi(dialogMenuItem *); 62179530Sjkimstatic int set_zone_whole_country(dialogMenuItem *); 63179530Sjkimstatic int set_zone_menu(dialogMenuItem *); 6419872Swollman 6519872Swollmanstruct continent { 6619872Swollman dialogMenuItem *menu; 67179530Sjkim int nitems; 68179530Sjkim int ch; 69179530Sjkim int sc; 7019872Swollman}; 7119872Swollman 72179530Sjkimstatic struct continent africa, america, antarctica, arctic, asia, atlantic; 73179530Sjkimstatic struct continent australia, europe, indian, pacific; 7419872Swollman 7519872Swollmanstatic struct continent_names { 76179530Sjkim const char *name; 7719872Swollman struct continent *continent; 7819872Swollman} continent_names[] = { 79179530Sjkim { "Africa", &africa }, 80179530Sjkim { "America", &america }, 81179530Sjkim { "Antarctica", &antarctica }, 82179530Sjkim { "Arctic", &arctic }, 83179530Sjkim { "Asia", &asia }, 84179530Sjkim { "Atlantic", &atlantic }, 85179530Sjkim { "Australia", &australia }, 86179530Sjkim { "Europe", &europe }, 87179530Sjkim { "Indian", &indian }, 88179530Sjkim { "Pacific", &pacific } 8919872Swollman}; 9019872Swollman 91179530Sjkimstatic struct continent_items { 92179530Sjkim char prompt[2]; 93179530Sjkim char title[30]; 94179530Sjkim} continent_items[] = { 95179530Sjkim { "1", "Africa" }, 96179530Sjkim { "2", "America -- North and South" }, 97179530Sjkim { "3", "Antarctica" }, 98179530Sjkim { "4", "Arctic Ocean" }, 99179530Sjkim { "5", "Asia" }, 100179530Sjkim { "6", "Atlantic Ocean" }, 101179530Sjkim { "7", "Australia" }, 102179530Sjkim { "8", "Europe" }, 103179530Sjkim { "9", "Indian Ocean" }, 104179530Sjkim { "0", "Pacific Ocean" } 10519872Swollman}; 10619872Swollman 107179530Sjkim#define NCONTINENTS \ 108179530Sjkim (int)((sizeof(continent_items)) / (sizeof(continent_items[0]))) 109179530Sjkimstatic dialogMenuItem continents[NCONTINENTS]; 110179530Sjkim 111179530Sjkim#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) 112179530Sjkim 11319872Swollmanstatic int 11419872Swollmancontinent_country_menu(dialogMenuItem *continent) 11519872Swollman{ 116179530Sjkim char title[64], prompt[64]; 11719872Swollman struct continent *contp = continent->data; 118179530Sjkim int isocean = OCEANP(continent - continents); 119179530Sjkim int menulen; 120179530Sjkim int rv; 12119872Swollman 12219872Swollman /* Short cut -- if there's only one country, don't post a menu. */ 123179497Sjkim if (contp->nitems == 1) 124179497Sjkim return (contp->menu[0].fire(&contp->menu[0])); 12519872Swollman 12619872Swollman /* It's amazing how much good grammar really matters... */ 127179530Sjkim if (!isocean) { 128179530Sjkim snprintf(title, sizeof(title), "Countries in %s", 129179530Sjkim continent->title); 130179530Sjkim snprintf(prompt, sizeof(prompt), "Select a country or region"); 131179530Sjkim } else { 132179530Sjkim snprintf(title, sizeof(title), "Islands and groups in the %s", 133179530Sjkim continent->title); 134179530Sjkim snprintf(prompt, sizeof(prompt), "Select an island or group"); 135179530Sjkim } 13619872Swollman 13719872Swollman menulen = contp->nitems < 16 ? contp->nitems : 16; 138179530Sjkim rv = dialog_menu(title, prompt, -1, -1, menulen, -contp->nitems, 139179530Sjkim contp->menu, 0, &contp->ch, &contp->sc); 14019872Swollman if (rv == 0) 141179530Sjkim return (DITEM_LEAVE_MENU); 142179530Sjkim return (DITEM_RECREATE); 14319872Swollman} 14419872Swollman 14519872Swollmanstatic struct continent * 14619872Swollmanfind_continent(const char *name) 14719872Swollman{ 148179530Sjkim int i; 14919872Swollman 150179530Sjkim for (i = 0; i < NCONTINENTS; i++) 15119872Swollman if (strcmp(name, continent_names[i].name) == 0) 152179530Sjkim return (continent_names[i].continent); 153179530Sjkim return (0); 15419872Swollman} 15519872Swollman 15619872Swollmanstruct country { 157179530Sjkim char *name; 158179530Sjkim char *tlc; 159179530Sjkim int nzones; 160179530Sjkim char *filename; /* use iff nzones < 0 */ 161179530Sjkim struct continent *continent; /* use iff nzones < 0 */ 162179530Sjkim TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ 163179530Sjkim dialogMenuItem *submenu; /* use iff nzones > 0 */ 16419872Swollman}; 16519872Swollman 16619872Swollmanstruct zone { 16760938Sjake TAILQ_ENTRY(zone) link; 168179530Sjkim char *descr; 169179530Sjkim char *filename; 17019872Swollman struct continent *continent; 17119872Swollman}; 17219872Swollman 17319872Swollman/* 17419872Swollman * This is the easiest organization... we use ISO 3166 country codes, 17519872Swollman * of the two-letter variety, so we just size this array to suit. 17619872Swollman * Beats worrying about dynamic allocation. 17719872Swollman */ 178179530Sjkim#define NCOUNTRIES (26 * 26) 17922181Sjhaystatic struct country countries[NCOUNTRIES]; 18019872Swollman 181179530Sjkim#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A')) 182179530Sjkim 18319872Swollman/* 18419872Swollman * Read the ISO 3166 country code database in _PATH_ISO3166 18519872Swollman * (/usr/share/misc/iso3166). On error, exit via err(3). 18619872Swollman */ 18719872Swollmanstatic void 18819872Swollmanread_iso3166_table(void) 18919872Swollman{ 190179530Sjkim FILE *fp; 191179530Sjkim struct country *cp; 192179530Sjkim size_t len; 193179530Sjkim char *s, *t, *name; 194179530Sjkim int lineno; 19519872Swollman 19619872Swollman fp = fopen(_PATH_ISO3166, "r"); 19719872Swollman if (!fp) 19819872Swollman err(1, _PATH_ISO3166); 19919872Swollman lineno = 0; 20019872Swollman 20119872Swollman while ((s = fgetln(fp, &len)) != 0) { 20219872Swollman lineno++; 20319872Swollman if (s[len - 1] != '\n') 20419872Swollman errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 20519872Swollman s[len - 1] = '\0'; 20630999Sjoerg if (s[0] == '#' || strspn(s, " \t") == len - 1) 20719872Swollman continue; 20819872Swollman 20919872Swollman /* Isolate the two-letter code. */ 21019872Swollman t = strsep(&s, "\t"); 21119872Swollman if (t == 0 || strlen(t) != 2) 21219872Swollman errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 21319872Swollman if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') 21419872Swollman errx(1, _PATH_ISO3166 ":%d: invalid code `%s'", 215179530Sjkim lineno, t); 21619872Swollman 21719872Swollman /* Now skip past the three-letter and numeric codes. */ 218179530Sjkim name = strsep(&s, "\t"); /* 3-let */ 21919872Swollman if (name == 0 || strlen(name) != 3) 22019872Swollman errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 221179530Sjkim name = strsep(&s, "\t"); /* numeric */ 22219872Swollman if (name == 0 || strlen(name) != 3) 22319872Swollman errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 22419872Swollman 22519872Swollman name = s; 22619872Swollman 22719872Swollman cp = &countries[CODE2INT(t)]; 22819872Swollman if (cp->name) 229179530Sjkim errx(1, _PATH_ISO3166 230179530Sjkim ":%d: country code `%s' multiply defined: %s", 231179530Sjkim lineno, t, cp->name); 23219872Swollman cp->name = strdup(name); 23356487Scharnier if (cp->name == NULL) 23456487Scharnier errx(1, "malloc failed"); 23519872Swollman cp->tlc = strdup(t); 23656487Scharnier if (cp->tlc == NULL) 23756487Scharnier errx(1, "malloc failed"); 23819872Swollman } 23919872Swollman 24019872Swollman fclose(fp); 24119872Swollman} 24219872Swollman 24319872Swollmanstatic void 24419872Swollmanadd_zone_to_country(int lineno, const char *tlc, const char *descr, 245179530Sjkim const char *file, struct continent *cont) 24619872Swollman{ 247179530Sjkim struct zone *zp; 248179530Sjkim struct country *cp; 24919872Swollman 25019872Swollman if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') 25119872Swollman errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid", 252179530Sjkim lineno, tlc); 25319872Swollman 25419872Swollman cp = &countries[CODE2INT(tlc)]; 25519872Swollman if (cp->name == 0) 25619872Swollman errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown", 257179530Sjkim lineno, tlc); 25819872Swollman 25919872Swollman if (descr) { 26019872Swollman if (cp->nzones < 0) 261179530Sjkim errx(1, _PATH_ZONETAB 262179530Sjkim ":%d: conflicting zone definition", lineno); 26319872Swollman 264179530Sjkim zp = malloc(sizeof(*zp)); 26519872Swollman if (zp == 0) 266179530Sjkim errx(1, "malloc(%zu)", sizeof(*zp)); 26719872Swollman 26819872Swollman if (cp->nzones == 0) 26919872Swollman TAILQ_INIT(&cp->zones); 27019872Swollman 27119872Swollman zp->descr = strdup(descr); 27256487Scharnier if (zp->descr == NULL) 27356487Scharnier errx(1, "malloc failed"); 27419872Swollman zp->filename = strdup(file); 27556487Scharnier if (zp->filename == NULL) 27656487Scharnier errx(1, "malloc failed"); 27719872Swollman zp->continent = cont; 27819872Swollman TAILQ_INSERT_TAIL(&cp->zones, zp, link); 27919872Swollman cp->nzones++; 28019872Swollman } else { 28119872Swollman if (cp->nzones > 0) 282179530Sjkim errx(1, _PATH_ZONETAB 283179530Sjkim ":%d: zone must have description", lineno); 28419872Swollman if (cp->nzones < 0) 28519872Swollman errx(1, _PATH_ZONETAB 286179530Sjkim ":%d: zone multiply defined", lineno); 28719872Swollman cp->nzones = -1; 28819872Swollman cp->filename = strdup(file); 28956487Scharnier if (cp->filename == NULL) 29056487Scharnier errx(1, "malloc failed"); 29119872Swollman cp->continent = cont; 29219872Swollman } 29319872Swollman} 29419872Swollman 29519872Swollman/* 29619872Swollman * This comparison function intentionally sorts all of the null-named 29719872Swollman * ``countries''---i.e., the codes that don't correspond to a real 29819872Swollman * country---to the end. Everything else is lexical by country name. 29919872Swollman */ 30019872Swollmanstatic int 30119872Swollmancompare_countries(const void *xa, const void *xb) 30219872Swollman{ 30319872Swollman const struct country *a = xa, *b = xb; 30419872Swollman 30519872Swollman if (a->name == 0 && b->name == 0) 306179530Sjkim return (0); 30719872Swollman if (a->name == 0 && b->name != 0) 308179530Sjkim return (1); 30919872Swollman if (b->name == 0) 310179530Sjkim return (-1); 31119872Swollman 312179530Sjkim return (strcmp(a->name, b->name)); 31319872Swollman} 31419872Swollman 31519872Swollman/* 31619872Swollman * This must be done AFTER all zone descriptions are read, since it breaks 31719872Swollman * CODE2INT(). 31819872Swollman */ 31919872Swollmanstatic void 32019872Swollmansort_countries(void) 32119872Swollman{ 322179530Sjkim 323179530Sjkim qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries); 32419872Swollman} 32519872Swollman 32619872Swollmanstatic void 32719872Swollmanread_zones(void) 32819872Swollman{ 329179530Sjkim char contbuf[16]; 330179530Sjkim FILE *fp; 33119872Swollman struct continent *cont; 332179530Sjkim size_t len; 333179530Sjkim char *line, *tlc, *coord, *file, *descr, *p; 334179530Sjkim int lineno; 33519872Swollman 33619872Swollman fp = fopen(_PATH_ZONETAB, "r"); 33719872Swollman if (!fp) 33819872Swollman err(1, _PATH_ZONETAB); 33919872Swollman lineno = 0; 34019872Swollman 34119872Swollman while ((line = fgetln(fp, &len)) != 0) { 34219872Swollman lineno++; 34319872Swollman if (line[len - 1] != '\n') 34419872Swollman errx(1, _PATH_ZONETAB ":%d: invalid format", lineno); 34519872Swollman line[len - 1] = '\0'; 34619872Swollman if (line[0] == '#') 34719872Swollman continue; 34819872Swollman 34919872Swollman tlc = strsep(&line, "\t"); 35019872Swollman if (strlen(tlc) != 2) 35119872Swollman errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'", 352179530Sjkim lineno, tlc); 35319872Swollman coord = strsep(&line, "\t"); 35419872Swollman file = strsep(&line, "\t"); 35519872Swollman p = strchr(file, '/'); 35619872Swollman if (p == 0) 35719872Swollman errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'", 358179530Sjkim lineno, file); 35919872Swollman contbuf[0] = '\0'; 36019872Swollman strncat(contbuf, file, p - file); 36119872Swollman cont = find_continent(contbuf); 36219872Swollman if (!cont) 36319872Swollman errx(1, _PATH_ZONETAB ":%d: invalid region `%s'", 364179530Sjkim lineno, contbuf); 36519872Swollman 366179530Sjkim descr = (line != NULL && *line != '\0') ? line : NULL; 36719872Swollman 36819872Swollman add_zone_to_country(lineno, tlc, descr, file, cont); 36919872Swollman } 37019872Swollman fclose(fp); 37119872Swollman} 37219872Swollman 37319872Swollmanstatic void 37419872Swollmanmake_menus(void) 37519872Swollman{ 376179530Sjkim struct country *cp; 377179530Sjkim struct zone *zp, *zp2; 37819872Swollman struct continent *cont; 379179530Sjkim dialogMenuItem *dmi; 380179530Sjkim int i; 38119872Swollman 38219872Swollman /* 38319872Swollman * First, count up all the countries in each continent/ocean. 38419872Swollman * Be careful to count those countries which have multiple zones 38519872Swollman * only once for each. NB: some countries are in multiple 38619872Swollman * continents/oceans. 38719872Swollman */ 38819872Swollman for (cp = countries; cp->name; cp++) { 38919872Swollman if (cp->nzones == 0) 39019872Swollman continue; 39119872Swollman if (cp->nzones < 0) { 39219872Swollman cp->continent->nitems++; 39319872Swollman } else { 39470486Sben TAILQ_FOREACH(zp, &cp->zones, link) { 39519872Swollman cont = zp->continent; 39670486Sben for (zp2 = TAILQ_FIRST(&cp->zones); 397179530Sjkim zp2->continent != cont; 398179530Sjkim zp2 = TAILQ_NEXT(zp2, link)) 39919872Swollman ; 40019872Swollman if (zp2 == zp) 40119872Swollman zp->continent->nitems++; 40219872Swollman } 40319872Swollman } 40419872Swollman } 40519872Swollman 40619872Swollman /* 407179530Sjkim * Now allocate memory for the country menus and initialize 408179530Sjkim * continent menus. We set nitems back to zero so that we can 409179530Sjkim * use it for counting again when we actually build the menus. 41019872Swollman */ 411179530Sjkim memset(continents, 0, sizeof(continents)); 41219872Swollman for (i = 0; i < NCONTINENTS; i++) { 41319872Swollman continent_names[i].continent->menu = 414179530Sjkim malloc(sizeof(dialogMenuItem) * 415179530Sjkim continent_names[i].continent->nitems); 41619872Swollman if (continent_names[i].continent->menu == 0) 41756487Scharnier errx(1, "malloc for continent menu"); 41819872Swollman continent_names[i].continent->nitems = 0; 419179530Sjkim continents[i].prompt = continent_items[i].prompt; 420179530Sjkim continents[i].title = continent_items[i].title; 421179530Sjkim continents[i].fire = continent_country_menu; 422179530Sjkim continents[i].data = continent_names[i].continent; 42319872Swollman } 42419872Swollman 42519872Swollman /* 42619872Swollman * Now that memory is allocated, create the menu items for 42719872Swollman * each continent. For multiple-zone countries, also create 42819872Swollman * the country's zone submenu. 42919872Swollman */ 43019872Swollman for (cp = countries; cp->name; cp++) { 43119872Swollman if (cp->nzones == 0) 43219872Swollman continue; 43319872Swollman if (cp->nzones < 0) { 43419872Swollman dmi = &cp->continent->menu[cp->continent->nitems]; 435179530Sjkim memset(dmi, 0, sizeof(*dmi)); 436179530Sjkim asprintf(&dmi->prompt, "%d", ++cp->continent->nitems); 43719872Swollman dmi->title = cp->name; 43819872Swollman dmi->checked = 0; 43919872Swollman dmi->fire = set_zone_whole_country; 44019872Swollman dmi->selected = 0; 44119872Swollman dmi->data = cp; 44219872Swollman } else { 443179530Sjkim cp->submenu = malloc(cp->nzones * sizeof(*dmi)); 44419872Swollman if (cp->submenu == 0) 44556487Scharnier errx(1, "malloc for submenu"); 44619872Swollman cp->nzones = 0; 44770486Sben TAILQ_FOREACH(zp, &cp->zones, link) { 44819872Swollman cont = zp->continent; 44919872Swollman dmi = &cp->submenu[cp->nzones]; 450179530Sjkim memset(dmi, 0, sizeof(*dmi)); 451179530Sjkim asprintf(&dmi->prompt, "%d", ++cp->nzones); 45219872Swollman dmi->title = zp->descr; 45319872Swollman dmi->checked = 0; 45419872Swollman dmi->fire = set_zone_multi; 45519872Swollman dmi->selected = 0; 45619872Swollman dmi->data = zp; 45719872Swollman 45870486Sben for (zp2 = TAILQ_FIRST(&cp->zones); 459179530Sjkim zp2->continent != cont; 460179530Sjkim zp2 = TAILQ_NEXT(zp2, link)) 46119872Swollman ; 46219872Swollman if (zp2 != zp) 46319872Swollman continue; 46419872Swollman 46519872Swollman dmi = &cont->menu[cont->nitems]; 466179530Sjkim memset(dmi, 0, sizeof(*dmi)); 46719872Swollman asprintf(&dmi->prompt, "%d", ++cont->nitems); 46819872Swollman dmi->title = cp->name; 46919872Swollman dmi->checked = 0; 47019872Swollman dmi->fire = set_zone_menu; 47119872Swollman dmi->selected = 0; 47219872Swollman dmi->data = cp; 47319872Swollman } 47419872Swollman } 47519872Swollman } 47619872Swollman} 47719872Swollman 47819872Swollmanstatic int 47919872Swollmanset_zone_menu(dialogMenuItem *dmi) 48019872Swollman{ 481179530Sjkim char title[64], prompt[64]; 482179530Sjkim struct country *cp = dmi->data; 483179530Sjkim int menulen; 484179530Sjkim int rv; 48519872Swollman 486179530Sjkim snprintf(title, sizeof(title), "%s Time Zones", cp->name); 487179530Sjkim snprintf(prompt, sizeof(prompt), 488179530Sjkim "Select a zone which observes the same time as your locality."); 48919872Swollman menulen = cp->nzones < 16 ? cp->nzones : 16; 490179530Sjkim rv = dialog_menu(title, prompt, -1, -1, menulen, -cp->nzones, 491179530Sjkim cp->submenu, 0, 0, 0); 49219872Swollman if (rv != 0) 493179530Sjkim return (DITEM_RECREATE); 494179530Sjkim return (DITEM_LEAVE_MENU); 49519872Swollman} 49619872Swollman 49719872Swollmanstatic int 49819872Swollmaninstall_zone_file(const char *filename) 49919872Swollman{ 500179530Sjkim char buf[1024]; 501179530Sjkim char title[64], prompt[64]; 502179530Sjkim struct stat sb; 503179530Sjkim ssize_t len; 504179530Sjkim int fd1, fd2, copymode; 50519872Swollman 506179530Sjkim if (lstat(_PATH_LOCALTIME, &sb) < 0) { 50719872Swollman /* Nothing there yet... */ 50819872Swollman copymode = 1; 509179530Sjkim } else if (S_ISLNK(sb.st_mode)) 51019872Swollman copymode = 0; 51119872Swollman else 51219872Swollman copymode = 1; 51319872Swollman 51421915Sjkh#ifdef VERBOSE 51519872Swollman if (copymode) 516179530Sjkim snprintf(prompt, sizeof(prompt), 517179530Sjkim "Copying %s to " _PATH_LOCALTIME, filename); 51819872Swollman else 519179530Sjkim snprintf(prompt, sizeof(prompt), 520179530Sjkim "Creating symbolic link " _PATH_LOCALTIME " to %s", 521179530Sjkim filename); 522179530Sjkim dialog_notify(prompt); 52321915Sjkh#endif 52419872Swollman 52519872Swollman if (reallydoit) { 52619872Swollman if (copymode) { 52719872Swollman fd1 = open(filename, O_RDONLY, 0); 52819872Swollman if (fd1 < 0) { 529179530Sjkim snprintf(title, sizeof(title), "Error"); 530179530Sjkim snprintf(prompt, sizeof(prompt), 531179530Sjkim "Could not open %s: %s", filename, 532179530Sjkim strerror(errno)); 533179530Sjkim dialog_mesgbox(title, prompt, 8, 72); 534179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 53519872Swollman } 53619872Swollman 53719872Swollman unlink(_PATH_LOCALTIME); 538179530Sjkim fd2 = open(_PATH_LOCALTIME, O_CREAT | O_EXCL | O_WRONLY, 539179530Sjkim S_IRUSR | S_IRGRP | S_IROTH); 54019872Swollman if (fd2 < 0) { 541179530Sjkim snprintf(title, sizeof(title), "Error"); 542179530Sjkim snprintf(prompt, sizeof(prompt), 543179530Sjkim "Could not open " _PATH_LOCALTIME ": %s", 544179530Sjkim strerror(errno)); 545179530Sjkim dialog_mesgbox(title, prompt, 8, 72); 546179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 54719872Swollman } 54819872Swollman 549179530Sjkim while ((len = read(fd1, buf, sizeof(buf))) > 0) 55019872Swollman len = write(fd2, buf, len); 55119872Swollman 55219872Swollman if (len == -1) { 553179530Sjkim snprintf(title, sizeof(title), "Error"); 554179530Sjkim snprintf(prompt, sizeof(prompt), 555179530Sjkim "Error copying %s to " _PATH_LOCALTIME 556179530Sjkim ": %s", filename, strerror(errno)); 557179530Sjkim dialog_mesgbox(title, prompt, 8, 72); 55819872Swollman /* Better to leave none than a corrupt one. */ 55919872Swollman unlink(_PATH_LOCALTIME); 560179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 56119872Swollman } 56219872Swollman close(fd1); 56319872Swollman close(fd2); 56419872Swollman } else { 56519872Swollman if (access(filename, R_OK) != 0) { 566179530Sjkim snprintf(title, sizeof(title), "Error"); 567179530Sjkim snprintf(prompt, sizeof(prompt), 568179530Sjkim "Cannot access %s: %s", filename, 569179530Sjkim strerror(errno)); 570179530Sjkim dialog_mesgbox(title, prompt, 8, 72); 571179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 57219872Swollman } 57319872Swollman unlink(_PATH_LOCALTIME); 57419872Swollman if (symlink(filename, _PATH_LOCALTIME) < 0) { 575179530Sjkim snprintf(title, sizeof(title), "Error"); 576179530Sjkim snprintf(prompt, sizeof(prompt), 577179530Sjkim "Cannot create symbolic link " 578179530Sjkim _PATH_LOCALTIME " to %s: %s", filename, 579179530Sjkim strerror(errno)); 580179530Sjkim dialog_mesgbox(title, prompt, 8, 72); 581179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 58219872Swollman } 58319872Swollman } 58419872Swollman } 58519872Swollman 58621915Sjkh#ifdef VERBOSE 587179530Sjkim snprintf(title, sizeof(title), "Done"); 58819872Swollman if (copymode) 589179530Sjkim snprintf(prompt, sizeof(prompt), 590179530Sjkim "Copied timezone file from %s to " _PATH_LOCALTIME, 591179530Sjkim filename); 59219872Swollman else 593179530Sjkim snprintf(prompt, sizeof(prompt), "Created symbolic link from " 594179530Sjkim _PATH_LOCALTIME " to %s", filename); 595179530Sjkim dialog_mesgbox(title, prompt, 8, 72); 59621915Sjkh#endif 597179530Sjkim return (DITEM_LEAVE_MENU); 59819872Swollman} 59919872Swollman 60019872Swollmanstatic int 60119872Swollmanconfirm_zone(const char *filename) 60219872Swollman{ 603179530Sjkim char title[64], prompt[64]; 604179530Sjkim time_t t = time(0); 605179530Sjkim struct tm *tm; 606179530Sjkim int rv; 60719872Swollman 60819872Swollman setenv("TZ", filename, 1); 60919872Swollman tzset(); 61019872Swollman tm = localtime(&t); 61119872Swollman 612179530Sjkim snprintf(title, sizeof(title), "Confirmation"); 613179530Sjkim snprintf(prompt, sizeof(prompt), 614179530Sjkim "Does the abbreviation `%s' look reasonable?", tm->tm_zone); 615179530Sjkim rv = !dialog_yesno(title, prompt, 5, 72); 616179530Sjkim return (rv); 61719872Swollman} 61819872Swollman 61919872Swollmanstatic int 62019872Swollmanset_zone_multi(dialogMenuItem *dmi) 62119872Swollman{ 622179530Sjkim struct zone *zp = dmi->data; 623179530Sjkim char *fn; 624179530Sjkim int rv; 62519872Swollman 62619872Swollman if (!confirm_zone(zp->filename)) 627179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 62819872Swollman 62919872Swollman asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename); 63019872Swollman rv = install_zone_file(fn); 63119872Swollman free(fn); 632179530Sjkim return (rv); 63319872Swollman} 63419872Swollman 63519872Swollmanstatic int 63619872Swollmanset_zone_whole_country(dialogMenuItem *dmi) 63719872Swollman{ 638179530Sjkim struct country *cp = dmi->data; 639179530Sjkim char *fn; 640179530Sjkim int rv; 64119872Swollman 64219872Swollman if (!confirm_zone(cp->filename)) 643179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 64419872Swollman 64519872Swollman asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename); 64619872Swollman rv = install_zone_file(fn); 64719872Swollman free(fn); 648179530Sjkim return (rv); 64919872Swollman} 65019872Swollman 65130763Scharnierstatic void 652179530Sjkimusage(void) 65330763Scharnier{ 654179530Sjkim 655195339Sattilio fprintf(stderr, "usage: tzsetup [-ns]\n"); 65630763Scharnier exit(1); 65730763Scharnier} 65830763Scharnier 659179530Sjkim#if defined(__sparc64__) 660179530Sjkim#define DIALOG_UTC dialog_yesno 661179530Sjkim#else 662179530Sjkim#define DIALOG_UTC dialog_noyes 663179530Sjkim#endif 664179530Sjkim 66519872Swollmanint 66619872Swollmanmain(int argc, char **argv) 66719872Swollman{ 668179530Sjkim char title[64], prompt[128]; 669195339Sattilio int c, fd, skiputc; 67019872Swollman 671195339Sattilio skiputc = 0; 672195339Sattilio while ((c = getopt(argc, argv, "ns")) != -1) { 67319872Swollman switch(c) { 67419872Swollman case 'n': 67519872Swollman reallydoit = 0; 67619872Swollman break; 677195339Sattilio case 's': 678195339Sattilio skiputc = 1; 679195339Sattilio break; 68019872Swollman default: 68130763Scharnier usage(); 68219872Swollman } 68319872Swollman } 68419872Swollman 68543544Swollman if (argc - optind > 1) 68630763Scharnier usage(); 68719872Swollman 68849435Sru /* Override the user-supplied umask. */ 689179530Sjkim (void)umask(S_IWGRP | S_IWOTH); 69049435Sru 69119872Swollman read_iso3166_table(); 69219872Swollman read_zones(); 69319872Swollman sort_countries(); 69419872Swollman make_menus(); 69519872Swollman 69619872Swollman init_dialog(); 697195339Sattilio if (skiputc == 0) { 698195339Sattilio snprintf(title, sizeof(title), 699195339Sattilio "Select local or UTC (Greenwich Mean Time) clock"); 700195339Sattilio snprintf(prompt, sizeof(prompt), 701195339Sattilio "Is this machine's CMOS clock set to UTC? " 702195339Sattilio "If it is set to local time,\n" 703195339Sattilio "or you don't know, please choose NO here!"); 704195339Sattilio if (!DIALOG_UTC(title, prompt, 7, 72)) { 705195339Sattilio if (reallydoit) 706195339Sattilio unlink(_PATH_WALL_CMOS_CLOCK); 707195339Sattilio } else { 708195339Sattilio if (reallydoit) { 709195339Sattilio fd = open(_PATH_WALL_CMOS_CLOCK, 710195339Sattilio O_WRONLY | O_CREAT | O_TRUNC, 711195339Sattilio S_IRUSR | S_IRGRP | S_IROTH); 712198254Sedwin if (fd < 0) { 713198254Sedwin end_dialog(); 714195339Sattilio err(1, "create %s", 715195339Sattilio _PATH_WALL_CMOS_CLOCK); 716198254Sedwin } 717195339Sattilio close(fd); 718195339Sattilio } 71941852Speter } 720195339Sattilio dialog_clear_norefresh(); 72132394Ssteve } 72243544Swollman if (optind == argc - 1) { 723179530Sjkim snprintf(title, sizeof(title), "Default timezone provided"); 724179530Sjkim snprintf(prompt, sizeof(prompt), 725179530Sjkim "\nUse the default `%s' zone?", argv[optind]); 726179530Sjkim if (!dialog_yesno(title, prompt, 7, 72)) { 72743544Swollman install_zone_file(argv[optind]); 72843544Swollman dialog_clear(); 72943544Swollman end_dialog(); 730179530Sjkim return (0); 73143544Swollman } 73243544Swollman dialog_clear_norefresh(); 73343544Swollman } 734179530Sjkim snprintf(title, sizeof(title), "Time Zone Selector"); 735179530Sjkim snprintf(prompt, sizeof(prompt), "Select a region"); 736179530Sjkim dialog_menu(title, prompt, -1, -1, NCONTINENTS, -NCONTINENTS, 737179530Sjkim continents, 0, NULL, NULL); 73843544Swollman 73922815Sjkh dialog_clear(); 74019872Swollman end_dialog(); 741179530Sjkim return (0); 74219872Swollman} 743