tzsetup.c revision 30763
112115Sdyson/* 212115Sdyson * Copyright 1996 Massachusetts Institute of Technology 312115Sdyson * 412115Sdyson * Permission to use, copy, modify, and distribute this software and 512115Sdyson * its documentation for any purpose and without fee is hereby 612115Sdyson * granted, provided that both the above copyright notice and this 712115Sdyson * permission notice appear in all copies, that both the above 812115Sdyson * copyright notice and this permission notice appear in all 912115Sdyson * supporting documentation, and that the name of M.I.T. not be used 1012115Sdyson * in advertising or publicity pertaining to distribution of the 1112115Sdyson * software without specific, written prior permission. M.I.T. makes 1212115Sdyson * no representations about the suitability of this software for any 1312115Sdyson * purpose. It is provided "as is" without express or implied 1412115Sdyson * warranty. 1512115Sdyson * 1612115Sdyson * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1712115Sdyson * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1812115Sdyson * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1912115Sdyson * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2012115Sdyson * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2112115Sdyson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2212115Sdyson * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2312115Sdyson * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2412115Sdyson * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2512115Sdyson * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2612115Sdyson * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2712115Sdyson * SUCH DAMAGE. 2812115Sdyson */ 2912115Sdyson 3012115Sdyson/* 3112115Sdyson * Second attempt at a `tzmenu' program, using the separate description 3212115Sdyson * files provided in newer tzdata releases. 3312115Sdyson */ 3412115Sdyson 3512115Sdyson#ifndef lint 3612115Sdysonstatic const char rcsid[] = 3712115Sdyson "$Id$"; 3812115Sdyson#endif /* not lint */ 3912115Sdyson 4012115Sdyson#include <sys/types.h> 4112115Sdyson#include <dialog.h> 4213260Swollman#include <err.h> 4312115Sdyson#include <errno.h> 4412115Sdyson#include <stdio.h> 4512115Sdyson#include <stdlib.h> 4612115Sdyson#include <string.h> 4712115Sdyson#include <unistd.h> 4812115Sdyson 4912115Sdyson#include <sys/fcntl.h> 5012115Sdyson#include <sys/queue.h> 5112115Sdyson#include <sys/stat.h> 5229906Skato 5324131Sbde#include "paths.h" 5412115Sdyson 5512115Sdysonstatic int reallydoit = 1; 5612115Sdyson 5712115Sdysonstatic int continent_country_menu(dialogMenuItem *); 5812115Sdysonstatic int set_zone_multi(dialogMenuItem *); 5912115Sdysonstatic int set_zone_whole_country(dialogMenuItem *); 6012115Sdysonstatic int set_zone_menu(dialogMenuItem *); 6112115Sdyson 6212115Sdysonstruct continent { 6312115Sdyson dialogMenuItem *menu; 6412115Sdyson int nitems; 6512115Sdyson int ch; 6612115Sdyson int sc; 6712115Sdyson}; 6812115Sdyson 6912115Sdysonstatic struct continent africa, america, antarctica, arctic, asia, atlantic; 7028270Swollmanstatic struct continent australia, europe, indian, pacific; 7112911Sphk 7212911Sphkstatic struct continent_names { 7312911Sphk char *name; 7412911Sphk struct continent *continent; 7512911Sphk} continent_names[] = { 7612911Sphk { "Africa", &africa }, { "America", &america }, 7712911Sphk { "Antarctica", &antarctica }, { "Arctic", &arctic }, 7812911Sphk { "Asia", &asia }, 7912911Sphk { "Atlantic", &atlantic }, { "Australia", &australia }, 8012911Sphk { "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific } 8112911Sphk}; 8212911Sphk 8312911Sphkstatic dialogMenuItem continents[] = { 8412115Sdyson { "1", "Africa", 0, continent_country_menu, 0, &africa }, 8531315Sbde { "2", "America -- North and South", 0, continent_country_menu, 0, 8630280Sphk &america }, 8712911Sphk { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica }, 8812115Sdyson { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic }, 8912115Sdyson { "5", "Asia", 0, continent_country_menu, 0, &asia }, 9012115Sdyson { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic }, 9112115Sdyson { "7", "Australia", 0, continent_country_menu, 0, &australia }, 9212115Sdyson { "8", "Europe", 0, continent_country_menu, 0, &europe }, 9312115Sdyson { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian }, 9412115Sdyson { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific } 9512115Sdyson}; 9612115Sdyson#define NCONTINENTS ((sizeof continents)/(sizeof continents[0])) 9712115Sdyson#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) 9812115Sdyson 9912115Sdysonstatic int 10012115Sdysoncontinent_country_menu(dialogMenuItem *continent) 10138909Sbde{ 10212115Sdyson int rv; 10312115Sdyson struct continent *contp = continent->data; 10412115Sdyson char title[256]; 10512911Sphk int isocean = OCEANP(continent - continents); 10612115Sdyson int menulen; 10716322Sgpalmer 10816322Sgpalmer /* Short cut -- if there's only one country, don't post a menu. */ 10916322Sgpalmer if (contp->nitems == 1) { 11016322Sgpalmer return set_zone_menu(&contp->menu[0]); 11116322Sgpalmer } 11216322Sgpalmer 11316322Sgpalmer /* It's amazing how much good grammar really matters... */ 11412115Sdyson if (!isocean) 11516322Sgpalmer snprintf(title, sizeof title, "Countries in %s", 11612115Sdyson continent->title); 11712115Sdyson else 11812115Sdyson snprintf(title, sizeof title, "Islands and groups in the %s", 11912115Sdyson continent->title); 12012115Sdyson 12112911Sphk menulen = contp->nitems < 16 ? contp->nitems : 16; 12212115Sdyson rv = dialog_menu(title, (isocean ? "Select an island or group" 12312115Sdyson : "Select a country"), -1, -1, menulen, 12412115Sdyson -contp->nitems, contp->menu, 0, &contp->ch, 12512115Sdyson &contp->sc); 12612115Sdyson if (rv == 0) 12712115Sdyson return DITEM_LEAVE_MENU; 12812115Sdyson return DITEM_RECREATE; 12912115Sdyson} 13012115Sdyson 13129208Sbdestatic struct continent * 13229208Sbdefind_continent(const char *name) 13329208Sbde{ 13429208Sbde int i; 13512115Sdyson 13612115Sdyson for (i = 0; i < NCONTINENTS; i++) { 13712115Sdyson if (strcmp(name, continent_names[i].name) == 0) 13812115Sdyson return continent_names[i].continent; 13929888Skato } 14029888Skato return 0; 14129888Skato} 14229888Skato 14312115Sdysonstruct country { 14412115Sdyson char *name; 14512115Sdyson char *tlc; 14612115Sdyson int nzones; 14712115Sdyson char *filename; /* use iff nzones < 0 */ 14812115Sdyson struct continent *continent; /* use iff nzones < 0 */ 14912115Sdyson TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ 15012115Sdyson dialogMenuItem *submenu; /* use iff nzones > 0 */ 15112115Sdyson}; 15230469Sjulian 15312115Sdysonstruct zone { 15412115Sdyson TAILQ_ENTRY(zone) link; 15512115Sdyson char *descr; 15612115Sdyson char *filename; 15712115Sdyson struct continent *continent; 15812115Sdyson}; 15912115Sdyson 16012115Sdyson/* 16112115Sdyson * This is the easiest organization... we use ISO 3166 country codes, 16212115Sdyson * of the two-letter variety, so we just size this array to suit. 16312115Sdyson * Beats worrying about dynamic allocation. 16412115Sdyson */ 16512115Sdyson#define NCOUNTRIES (26*26) 16612115Sdysonstatic struct country countries[NCOUNTRIES]; 16712115Sdyson#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A')) 16812115Sdyson 16916322Sgpalmer/* 17012115Sdyson * Read the ISO 3166 country code database in _PATH_ISO3166 17112115Sdyson * (/usr/share/misc/iso3166). On error, exit via err(3). 17212115Sdyson */ 17312115Sdysonstatic void 17412115Sdysonread_iso3166_table(void) 17512115Sdyson{ 17612911Sphk FILE *fp; 17712115Sdyson char *s, *t, *name; 17812115Sdyson size_t len; 17912115Sdyson int lineno; 18012115Sdyson struct country *cp; 18112115Sdyson 18212115Sdyson fp = fopen(_PATH_ISO3166, "r"); 18312115Sdyson if (!fp) 18412115Sdyson err(1, _PATH_ISO3166); 18512115Sdyson lineno = 0; 18612115Sdyson 18712115Sdyson while ((s = fgetln(fp, &len)) != 0) { 18812115Sdyson lineno++; 18912115Sdyson if (s[len - 1] != '\n') 19012115Sdyson errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 19112115Sdyson s[len - 1] = '\0'; 19212115Sdyson if (s[0] == '#') 19312115Sdyson continue; 19412115Sdyson 19512115Sdyson /* Isolate the two-letter code. */ 19629888Skato t = strsep(&s, "\t"); 19729888Skato if (t == 0 || strlen(t) != 2) 19812115Sdyson errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 19912115Sdyson if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') 20012115Sdyson errx(1, _PATH_ISO3166 ":%d: invalid code `%s'", 20112115Sdyson lineno, t); 20212115Sdyson 20329888Skato /* Now skip past the three-letter and numeric codes. */ 20429888Skato name = strsep(&s, "\t"); /* 3-let */ 20529888Skato if (name == 0 || strlen(name) != 3) 20629888Skato errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 20712115Sdyson name = strsep(&s, "\t"); /* numeric */ 20812115Sdyson if (name == 0 || strlen(name) != 3) 20912115Sdyson errx(1, _PATH_ISO3166 ":%d: invalid format", lineno); 21012115Sdyson 21122521Sdyson name = s; 21212115Sdyson 21312115Sdyson cp = &countries[CODE2INT(t)]; 21422521Sdyson if (cp->name) 21512115Sdyson errx(1, _PATH_ISO3166 21612115Sdyson ":%d: country code `%s' multiply defined: %s", 21712115Sdyson lineno, t, cp->name); 21812115Sdyson cp->name = strdup(name); 21912115Sdyson cp->tlc = strdup(t); 22031132Sjulian } 22112115Sdyson 22212115Sdyson fclose(fp); 22312115Sdyson} 22412115Sdyson 22512115Sdysonstatic void 22612115Sdysonadd_zone_to_country(int lineno, const char *tlc, const char *descr, 22712115Sdyson const char *file, struct continent *cont) 22812115Sdyson{ 22912115Sdyson struct zone *zp; 23012115Sdyson struct country *cp; 23112115Sdyson 23212115Sdyson if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') 23312115Sdyson errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid", 23412115Sdyson lineno, tlc); 23512115Sdyson 23612115Sdyson cp = &countries[CODE2INT(tlc)]; 23712115Sdyson if (cp->name == 0) 23812115Sdyson errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown", 23912115Sdyson lineno, tlc); 24012115Sdyson 24112115Sdyson if (descr) { 24212115Sdyson if (cp->nzones < 0) 24312115Sdyson errx(1, _PATH_ZONETAB 24412115Sdyson ":%d: conflicting zone definition", lineno); 24512115Sdyson 24612115Sdyson zp = malloc(sizeof *zp); 24712115Sdyson if (zp == 0) 24812115Sdyson err(1, "malloc(%lu)", (unsigned long)sizeof *zp); 24912115Sdyson 25012115Sdyson if (cp->nzones == 0) 25129888Skato TAILQ_INIT(&cp->zones); 25229888Skato 25329888Skato zp->descr = strdup(descr); 25429888Skato zp->filename = strdup(file); 25529888Skato zp->continent = cont; 25612115Sdyson TAILQ_INSERT_TAIL(&cp->zones, zp, link); 25729888Skato cp->nzones++; 25812115Sdyson } else { 25912115Sdyson if (cp->nzones > 0) 26012115Sdyson errx(1, _PATH_ZONETAB 26112115Sdyson ":%d: zone must have description", lineno); 26212115Sdyson if (cp->nzones < 0) 26312115Sdyson errx(1, _PATH_ZONETAB 26412115Sdyson ":%d: zone multiply defined", lineno); 26512115Sdyson cp->nzones = -1; 26612115Sdyson cp->filename = strdup(file); 26712115Sdyson cp->continent = cont; 26812115Sdyson } 26912115Sdyson} 27012115Sdyson 27112115Sdyson/* 27212115Sdyson * This comparison function intentionally sorts all of the null-named 27312115Sdyson * ``countries''---i.e., the codes that don't correspond to a real 27412115Sdyson * country---to the end. Everything else is lexical by country name. 27512115Sdyson */ 27612115Sdysonstatic int 27712115Sdysoncompare_countries(const void *xa, const void *xb) 27812115Sdyson{ 27912115Sdyson const struct country *a = xa, *b = xb; 28012115Sdyson 28112115Sdyson if (a->name == 0 && b->name == 0) 28212115Sdyson return 0; 28312115Sdyson if (a->name == 0 && b->name != 0) 28412115Sdyson return 1; 28512115Sdyson if (b->name == 0) 28612115Sdyson return -1; 28712115Sdyson 28812115Sdyson return strcmp(a->name, b->name); 28912115Sdyson} 29012115Sdyson 29112115Sdyson/* 29212115Sdyson * This must be done AFTER all zone descriptions are read, since it breaks 29312115Sdyson * CODE2INT(). 29412115Sdyson */ 29512115Sdysonstatic void 29612115Sdysonsort_countries(void) 29712115Sdyson{ 29812115Sdyson qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries); 29912115Sdyson} 30012115Sdyson 30112115Sdysonstatic void 30212115Sdysonread_zones(void) 30312115Sdyson{ 30412115Sdyson FILE *fp; 30512115Sdyson char *line; 30612115Sdyson size_t len; 30712115Sdyson int lineno; 30812115Sdyson char *tlc, *coord, *file, *descr, *p; 30912115Sdyson char contbuf[16]; 31012115Sdyson struct continent *cont; 31112115Sdyson 31212115Sdyson fp = fopen(_PATH_ZONETAB, "r"); 31312115Sdyson if (!fp) 31412115Sdyson err(1, _PATH_ZONETAB); 31512115Sdyson lineno = 0; 31612115Sdyson 31712115Sdyson while ((line = fgetln(fp, &len)) != 0) { 31812115Sdyson lineno++; 31912115Sdyson if (line[len - 1] != '\n') 32012115Sdyson errx(1, _PATH_ZONETAB ":%d: invalid format", lineno); 32112115Sdyson line[len - 1] = '\0'; 32212115Sdyson if (line[0] == '#') 32312115Sdyson continue; 32412115Sdyson 32512115Sdyson tlc = strsep(&line, "\t"); 32612115Sdyson if (strlen(tlc) != 2) 32712115Sdyson errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'", 32812115Sdyson lineno, tlc); 32912115Sdyson coord = strsep(&line, "\t"); 33012115Sdyson file = strsep(&line, "\t"); 33112115Sdyson p = strchr(file, '/'); 33212115Sdyson if (p == 0) 33312115Sdyson errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'", 33412115Sdyson lineno, file); 33512115Sdyson contbuf[0] = '\0'; 33612115Sdyson strncat(contbuf, file, p - file); 33712115Sdyson cont = find_continent(contbuf); 33812115Sdyson if (!cont) 33912115Sdyson errx(1, _PATH_ZONETAB ":%d: invalid region `%s'", 34012115Sdyson lineno, contbuf); 34112115Sdyson 34212115Sdyson descr = (line && *line) ? line : 0; 34312115Sdyson 34412115Sdyson add_zone_to_country(lineno, tlc, descr, file, cont); 34512115Sdyson } 34612115Sdyson fclose(fp); 34712115Sdyson} 34812115Sdyson 34912115Sdysonstatic void 35012115Sdysonmake_menus(void) 35112115Sdyson{ 35212115Sdyson struct country *cp; 35312115Sdyson struct zone *zp, *zp2; 35412115Sdyson struct continent *cont; 35512115Sdyson dialogMenuItem *dmi; 35612115Sdyson int i; 35712115Sdyson 35812115Sdyson /* 35912115Sdyson * First, count up all the countries in each continent/ocean. 36012115Sdyson * Be careful to count those countries which have multiple zones 36112115Sdyson * only once for each. NB: some countries are in multiple 36212115Sdyson * continents/oceans. 36312115Sdyson */ 36412115Sdyson for (cp = countries; cp->name; cp++) { 36512115Sdyson if (cp->nzones == 0) 36612115Sdyson continue; 36712115Sdyson if (cp->nzones < 0) { 36812115Sdyson cp->continent->nitems++; 36912115Sdyson } else { 37012115Sdyson for (zp = cp->zones.tqh_first; zp; 37112115Sdyson zp = zp->link.tqe_next) { 37212115Sdyson cont = zp->continent; 37312115Sdyson for (zp2 = cp->zones.tqh_first; 37412115Sdyson zp2->continent != cont; 37512115Sdyson zp2 = zp2->link.tqe_next) 37612115Sdyson ; 37712115Sdyson if (zp2 == zp) 37812115Sdyson zp->continent->nitems++; 37912115Sdyson } 38012115Sdyson } 38112115Sdyson } 38212115Sdyson 38312115Sdyson /* 38412115Sdyson * Now allocate memory for the country menus. We set 38512115Sdyson * nitems back to zero so that we can use it for counting 38612115Sdyson * again when we actually build the menus. 38712115Sdyson */ 38812115Sdyson for (i = 0; i < NCONTINENTS; i++) { 38912115Sdyson continent_names[i].continent->menu = 39012115Sdyson malloc(sizeof(dialogMenuItem) * 39112115Sdyson continent_names[i].continent->nitems); 39212115Sdyson if (continent_names[i].continent->menu == 0) 39312115Sdyson err(1, "malloc for continent menu"); 39412115Sdyson continent_names[i].continent->nitems = 0; 39512115Sdyson } 39612115Sdyson 39712115Sdyson /* 39812115Sdyson * Now that memory is allocated, create the menu items for 39912115Sdyson * each continent. For multiple-zone countries, also create 40012115Sdyson * the country's zone submenu. 40112115Sdyson */ 40212115Sdyson for (cp = countries; cp->name; cp++) { 40312115Sdyson if (cp->nzones == 0) 40412115Sdyson continue; 40512115Sdyson if (cp->nzones < 0) { 40612115Sdyson dmi = &cp->continent->menu[cp->continent->nitems]; 40712115Sdyson memset(dmi, 0, sizeof *dmi); 40812115Sdyson asprintf(&dmi->prompt, "%d", 40912115Sdyson ++cp->continent->nitems); 41027881Sdyson dmi->title = cp->name; 41127881Sdyson dmi->checked = 0; 41212115Sdyson dmi->fire = set_zone_whole_country; 41312115Sdyson dmi->selected = 0; 41412115Sdyson dmi->data = cp; 41527881Sdyson } else { 41612115Sdyson cp->submenu = malloc(cp->nzones * sizeof *dmi); 41712115Sdyson if (cp->submenu == 0) 41812115Sdyson err(1, "malloc for submenu"); 41912115Sdyson cp->nzones = 0; 42012115Sdyson for (zp = cp->zones.tqh_first; zp; 42112115Sdyson zp = zp->link.tqe_next) { 42212115Sdyson cont = zp->continent; 42312115Sdyson dmi = &cp->submenu[cp->nzones]; 42412115Sdyson memset(dmi, 0, sizeof *dmi); 42512115Sdyson asprintf(&dmi->prompt, "%d", 42612115Sdyson ++cp->nzones); 42712115Sdyson dmi->title = zp->descr; 42812115Sdyson dmi->checked = 0; 42912115Sdyson dmi->fire = set_zone_multi; 43012115Sdyson dmi->selected = 0; 43112115Sdyson dmi->data = zp; 43212115Sdyson 43312115Sdyson for (zp2 = cp->zones.tqh_first; 43412115Sdyson zp2->continent != cont; 43512115Sdyson zp2 = zp2->link.tqe_next) 43612115Sdyson ; 43712115Sdyson if (zp2 != zp) 43812115Sdyson continue; 43912115Sdyson 44012115Sdyson dmi = &cont->menu[cont->nitems]; 44112115Sdyson memset(dmi, 0, sizeof *dmi); 44212115Sdyson asprintf(&dmi->prompt, "%d", ++cont->nitems); 44312115Sdyson dmi->title = cp->name; 44412115Sdyson dmi->checked = 0; 44512115Sdyson dmi->fire = set_zone_menu; 44612911Sphk dmi->selected = 0; 44712115Sdyson dmi->data = cp; 44812115Sdyson } 44912115Sdyson } 45012115Sdyson } 45112115Sdyson} 45212115Sdyson 45312115Sdysonstatic int 45412115Sdysonset_zone_menu(dialogMenuItem *dmi) 45512115Sdyson{ 45612115Sdyson int rv; 45712147Sdyson char buf[256]; 45812115Sdyson struct country *cp = dmi->data; 45912115Sdyson int menulen; 46012115Sdyson 46112115Sdyson snprintf(buf, sizeof buf, "%s Time Zones", cp->name); 46212115Sdyson menulen = cp->nzones < 16 ? cp->nzones : 16; 46312115Sdyson rv = dialog_menu(buf, "Select a zone which observes the same time as " 46412115Sdyson "your locality.", -1, -1, menulen, -cp->nzones, 46512115Sdyson cp->submenu, 0, 0, 0); 46612115Sdyson if (rv != 0) 46712115Sdyson return DITEM_RECREATE; 46812115Sdyson return DITEM_LEAVE_MENU; 46912115Sdyson} 47012115Sdyson 47112115Sdysonstatic int 47212115Sdysoninstall_zone_file(const char *filename) 47312115Sdyson{ 47412115Sdyson struct stat sb; 47512115Sdyson int fd1, fd2; 47612115Sdyson int copymode; 47712115Sdyson char *msg; 47812115Sdyson ssize_t len; 47912115Sdyson char buf[1024]; 48012115Sdyson 48112115Sdyson if (lstat(_PATH_LOCALTIME, &sb) < 0) 48212115Sdyson /* Nothing there yet... */ 48312115Sdyson copymode = 1; 48412115Sdyson else if(S_ISLNK(sb.st_mode)) 48512115Sdyson copymode = 0; 48612115Sdyson else 48712115Sdyson copymode = 1; 48812115Sdyson 48912115Sdyson#ifdef VERBOSE 49012115Sdyson if (copymode) 49112115Sdyson asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename); 49212115Sdyson else 49312115Sdyson asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME 49412115Sdyson " to %s", filename); 49512115Sdyson 49612115Sdyson dialog_notify(msg); 49712115Sdyson free(msg); 49812115Sdyson#endif 49912115Sdyson 50012115Sdyson if (reallydoit) { 50112115Sdyson if (copymode) { 50212115Sdyson fd1 = open(filename, O_RDONLY, 0); 50312115Sdyson if (fd1 < 0) { 50412115Sdyson asprintf(&msg, "Could not open %s: %s", 50512115Sdyson filename, strerror(errno)); 50612115Sdyson dialog_mesgbox("Error", msg, 8, 72); 50712115Sdyson free(msg); 50812115Sdyson return DITEM_FAILURE | DITEM_RECREATE; 50912115Sdyson } 51012115Sdyson 51122521Sdyson unlink(_PATH_LOCALTIME); 51212115Sdyson fd2 = open(_PATH_LOCALTIME, 51312115Sdyson O_CREAT | O_EXCL | O_WRONLY, 51412115Sdyson 0444); 51512115Sdyson if (fd2 < 0) { 51612115Sdyson asprintf(&msg, "Could not open " 51712115Sdyson _PATH_LOCALTIME ": %s", 51812115Sdyson strerror(errno)); 51912115Sdyson dialog_mesgbox("Error", msg, 8, 72); 52012115Sdyson free(msg); 52112115Sdyson return DITEM_FAILURE | DITEM_RECREATE; 52212115Sdyson } 52312115Sdyson 52412115Sdyson while ((len = read(fd1, buf, sizeof buf)) > 0) 52512115Sdyson len = write(fd2, buf, len); 52612115Sdyson 52712115Sdyson if (len == -1) { 52812115Sdyson asprintf(&msg, "Error copying %s to " 52912115Sdyson _PATH_LOCALTIME ": %s", 53012115Sdyson strerror(errno)); 53112115Sdyson dialog_mesgbox("Error", msg, 8, 72); 53212115Sdyson free(msg); 53312115Sdyson /* Better to leave none than a corrupt one. */ 53412115Sdyson unlink(_PATH_LOCALTIME); 53512115Sdyson return DITEM_FAILURE | DITEM_RECREATE; 53612115Sdyson } 53712115Sdyson close(fd1); 53812115Sdyson close(fd2); 53912911Sphk } else { 54012115Sdyson if (access(filename, R_OK) != 0) { 54112115Sdyson asprintf(&msg, "Cannot access %s: %s", 54212115Sdyson filename, strerror(errno)); 54312115Sdyson dialog_mesgbox("Error", msg, 8, 72); 54412115Sdyson free(msg); 54512115Sdyson return DITEM_FAILURE | DITEM_RECREATE; 54612115Sdyson } 54712115Sdyson unlink(_PATH_LOCALTIME); 54812115Sdyson if (symlink(filename, _PATH_LOCALTIME) < 0) { 54912115Sdyson asprintf(&msg, "Cannot create symbolic link " 55012115Sdyson _PATH_LOCALTIME " to %s: %s", 55112115Sdyson filename, strerror(errno)); 55212115Sdyson dialog_mesgbox("Error", msg, 8, 72); 55312115Sdyson free(msg); 55412115Sdyson return DITEM_FAILURE | DITEM_RECREATE; 55512115Sdyson } 55612115Sdyson } 55712115Sdyson } 55812115Sdyson 55912115Sdyson#ifdef VERBOSE 56012115Sdyson if (copymode) 56112115Sdyson asprintf(&msg, "Copied timezone file from %s to " 56212115Sdyson _PATH_LOCALTIME, filename); 56312115Sdyson else 56412115Sdyson asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME 56512115Sdyson " to %s", filename); 56612115Sdyson 56712115Sdyson dialog_mesgbox("Done", msg, 8, 72); 56812115Sdyson free(msg); 56912115Sdyson#endif 57012115Sdyson return DITEM_LEAVE_MENU; 57112115Sdyson} 57212115Sdyson 57312115Sdysonstatic int 57412115Sdysonconfirm_zone(const char *filename) 57512115Sdyson{ 57612115Sdyson char *msg; 57712115Sdyson struct tm *tm; 57812115Sdyson time_t t = time(0); 57912115Sdyson int rv; 58012115Sdyson 58112115Sdyson setenv("TZ", filename, 1); 58212115Sdyson tzset(); 58312115Sdyson tm = localtime(&t); 58412115Sdyson 58512115Sdyson asprintf(&msg, "Does the abbreviation `%s' look reasonable?", 58612115Sdyson tm->tm_zone); 58712115Sdyson rv = !dialog_yesno("Confirmation", msg, 4, 72); 58812115Sdyson free(msg); 58912115Sdyson return rv; 59012115Sdyson} 59112115Sdyson 59212115Sdysonstatic int 59312115Sdysonset_zone_multi(dialogMenuItem *dmi) 59412115Sdyson{ 59512115Sdyson char *fn; 59612115Sdyson struct zone *zp = dmi->data; 59712115Sdyson int rv; 59812115Sdyson 59912115Sdyson if (!confirm_zone(zp->filename)) 60030280Sphk return DITEM_FAILURE | DITEM_RECREATE; 60130474Sphk 60230474Sphk asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename); 60330492Sphk rv = install_zone_file(fn); 60430474Sphk free(fn); 60530474Sphk return rv; 60612115Sdyson} 60712115Sdyson 60812115Sdysonstatic int 60912115Sdysonset_zone_whole_country(dialogMenuItem *dmi) 61012115Sdyson{ 61112115Sdyson char *fn; 61212115Sdyson struct country *cp = dmi->data; 61312115Sdyson int rv; 61412115Sdyson 61512115Sdyson if (!confirm_zone(cp->filename)) 61612115Sdyson return DITEM_FAILURE | DITEM_RECREATE; 61712115Sdyson 61812115Sdyson asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename); 61912115Sdyson rv = install_zone_file(fn); 62012115Sdyson free(fn); 62112115Sdyson return rv; 62212115Sdyson} 62312115Sdyson 62412115Sdysonstatic void 62512115Sdysonusage() 62612115Sdyson{ 62712115Sdyson fprintf(stderr, "usage: tzsetup [-n]\n"); 62812115Sdyson exit(1); 62912115Sdyson} 63012115Sdyson 63112115Sdysonint 63212115Sdysonmain(int argc, char **argv) 63312115Sdyson{ 63412115Sdyson int c; 63512115Sdyson 63638909Sbde while ((c = getopt(argc, argv, "n")) != -1) { 63712115Sdyson switch(c) { 63812115Sdyson case 'n': 63912115Sdyson reallydoit = 0; 64012115Sdyson break; 64112115Sdyson 64212115Sdyson default: 64312115Sdyson usage(); 64412115Sdyson } 64512115Sdyson } 64612115Sdyson 64712115Sdyson if (optind != argc) 64812115Sdyson usage(); 64912115Sdyson 65034430Seivind read_iso3166_table(); 65134430Seivind read_zones(); 65234430Seivind sort_countries(); 65312115Sdyson make_menus(); 65412115Sdyson 65512115Sdyson init_dialog(); 65612115Sdyson if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock", 65712115Sdyson "Is this machine's CMOS clock set to UTC? If it is set to local time,\n" 65812115Sdyson "please choose NO here!", 7, 72)) 65912115Sdyson if (reallydoit) 66012115Sdyson system("rm -f /etc/wall_cmos_clock"); 66112115Sdyson else 66212115Sdyson if (reallydoit) 66312115Sdyson system("touch /etc/wall_cmos_clock"); 66412115Sdyson dialog_clear_norefresh(); 66512115Sdyson dialog_menu("Time Zone Selector", "Select a region", -1, -1, 66612115Sdyson NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL); 66712115Sdyson dialog_clear(); 66812115Sdyson end_dialog(); 66912911Sphk return 0; 67012115Sdyson} 67112115Sdyson 67212115Sdyson