diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c --- a/usr.sbin/tzsetup/tzsetup.c +++ b/usr.sbin/tzsetup/tzsetup.c @@ -162,7 +162,7 @@ int nitems; }; -static struct continent africa, america, antarctica, asia, atlantic; +static struct continent africa, america, antarctica, arctic, asia, atlantic; static struct continent australia, europe, indian, pacific, utc; static struct continent_names { @@ -172,6 +172,7 @@ { "Africa", &africa }, { "America", &america }, { "Antarctica", &antarctica }, + { "Arctic", &arctic }, { "Asia", &asia }, { "Atlantic", &atlantic }, { "Australia", &australia }, @@ -182,26 +183,27 @@ }; static struct continent_items { - char prompt[2]; + char prompt[3]; char title[30]; } continent_items[] = { { "1", "Africa" }, { "2", "America -- North and South" }, { "3", "Antarctica" }, - { "4", "Asia" }, - { "5", "Atlantic Ocean" }, - { "6", "Australia" }, - { "7", "Europe" }, - { "8", "Indian Ocean" }, - { "9", "Pacific Ocean" }, - { "0", "UTC" } + { "4", "Arctic Ocean" }, + { "5", "Asia" }, + { "6", "Atlantic Ocean" }, + { "7", "Australia" }, + { "8", "Europe" }, + { "9", "Indian Ocean" }, + { "10", "Pacific Ocean" }, + { "11", "UTC" } }; #define NCONTINENTS \ (int)((sizeof(continent_items)) / (sizeof(continent_items[0]))) static dialogMenuItem continents[NCONTINENTS]; -#define OCEANP(x) ((x) == 4 || (x) == 7 || (x) == 8) +#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) static int continent_country_menu(dialogMenuItem *continent) @@ -214,10 +216,6 @@ if (strcmp(continent->title, "UTC") == 0) return (set_zone_utc()); - /* Short cut -- if there's only one country, don't post a menu. */ - if (contp->nitems == 1) - return (contp->menu[0].fire(&contp->menu[0])); - /* It's amazing how much good grammar really matters... */ if (!isocean) { snprintf(title, sizeof(title), "Countries in %s", @@ -234,14 +232,30 @@ } static struct continent * -find_continent(const char *name) +find_continent(int lineno, const char *name) { + char *cname, *cp; int i; + /* + * Both normal (the ones in zone filename, e.g. Europe/Andorra) and + * override (e.g. Atlantic/) entries should contain '/'. + */ + cp = strdup(name); + if (cp == NULL) + err(1, "strdup"); + cname = strsep(&cp, "/"); + if (cp == NULL) + errx(1, "%s:%d: invalid entry `%s'", path_zonetab, lineno, + cname); + for (i = 0; i < NCONTINENTS; i++) - if (strcmp(name, continent_names[i].name) == 0) + if (strcmp(cname, continent_names[i].name) == 0) { + free(cname); return (continent_names[i].continent); - return (0); + } + + errx(1, "%s:%d: continent `%s' unknown", path_zonetab, lineno, cname); } struct country { @@ -250,6 +264,7 @@ int nzones; char *filename; /* use iff nzones < 0 */ struct continent *continent; /* use iff nzones < 0 */ + struct continent *override; /* continent override */ TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ dialogMenuItem *submenu; /* use iff nzones > 0 */ }; @@ -330,22 +345,30 @@ fclose(fp); } -static void -add_zone_to_country(int lineno, const char *tlc, const char *descr, - const char *file, struct continent *cont) +static struct country * +find_country(int lineno, const char *tlc) { - struct zone *zp; struct country *cp; - if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') + if (strlen(tlc) != 2 || + tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') errx(1, "%s:%d: country code `%s' invalid", path_zonetab, lineno, tlc); cp = &countries[CODE2INT(tlc)]; - if (cp->name == 0) + if (cp->name == NULL) errx(1, "%s:%d: country code `%s' unknown", path_zonetab, lineno, tlc); + return (cp); +} + +static void +add_zone_to_country(int lineno, struct country *cp, const char *descr, + const char *file, struct continent *cont) +{ + struct zone *zp; + if (descr) { if (cp->nzones < 0) errx(1, "%s:%d: conflicting zone definition", @@ -364,7 +387,7 @@ zp->filename = strdup(file); if (zp->filename == NULL) errx(1, "malloc failed"); - zp->continent = cont; + zp->continent = cp->override != NULL ? cp->override : cont; TAILQ_INSERT_TAIL(&cp->zones, zp, link); cp->nzones++; } else { @@ -378,7 +401,7 @@ cp->filename = strdup(file); if (cp->filename == NULL) errx(1, "malloc failed"); - cp->continent = cont; + cp->continent = cp->override != NULL ? cp->override : cont; } } @@ -416,54 +439,79 @@ static void read_zones(void) { - char contbuf[16]; FILE *fp; struct continent *cont; - size_t len, contlen; - char *line, *country_list, *tlc, *file, *descr, *p; + struct country *cp; + size_t len; + char *line, *country_list, *tlc, *file, *descr; int lineno; + bool pass1; fp = fopen(path_zonetab, "r"); if (!fp) err(1, "%s", path_zonetab); - lineno = 0; + pass1 = true; +again: + lineno = 0; while ((line = fgetln(fp, &len)) != NULL) { lineno++; if (line[len - 1] != '\n') errx(1, "%s:%d: invalid format", path_zonetab, lineno); line[len - 1] = '\0'; - if (line[0] == '#') - continue; - country_list = strsep(&line, "\t"); - /* coord = */ strsep(&line, "\t"); /* Unused */ - file = strsep(&line, "\t"); - /* get continent portion from continent/country */ - p = strchr(file, '/'); - if (p == NULL) - errx(1, "%s:%d: invalid zone name `%s'", path_zonetab, - lineno, file); - contlen = p - file + 1; /* trailing nul */ - if (contlen > sizeof(contbuf)) - errx(1, "%s:%d: continent name in zone name `%s' too long", - path_zonetab, lineno, file); - strlcpy(contbuf, file, contlen); - cont = find_continent(contbuf); - if (!cont) - errx(1, "%s:%d: invalid region `%s'", path_zonetab, - lineno, contbuf); - - descr = (line != NULL && *line != '\0') ? line : NULL; - - while (country_list != NULL) { - tlc = strsep(&country_list, ","); - if (strlen(tlc) != 2) - errx(1, "%s:%d: invalid country code `%s'", - path_zonetab, lineno, tlc); - add_zone_to_country(lineno, tlc, descr, file, cont); + if (pass1) { + /* + * First pass: collect overrides, only looking for + * single continent ones for the moment. + * + * zone1970.tab introduced continent overrides in the + * following format: + * + * #@TLC[,TLC...]CONTINENT/[,CONTINENT/...] + */ + if (strncmp(line, "#@", strlen("#@")) != 0) + continue; + line += 2; + country_list = strsep(&line, "\t"); + /* Skip multi-continent overrides */ + if (strchr(line, ',') != NULL) + continue; + cont = find_continent(lineno, line); + /* Parse and store overrides */ + while (country_list != NULL) { + tlc = strsep(&country_list, ","); + cp = find_country(lineno, tlc); + cp->override = cont; + } + } else { + /* Second pass: parse actual data */ + if (line[0] == '#') + continue; + + country_list = strsep(&line, "\t"); + /* coord = */ strsep(&line, "\t"); /* Unused */ + file = strsep(&line, "\t"); + cont = find_continent(lineno, file); + descr = (line != NULL && *line != '\0') ? line : NULL; + + while (country_list != NULL) { + tlc = strsep(&country_list, ","); + cp = find_country(lineno, tlc); + add_zone_to_country(lineno, cp, descr, file, + cont); + } } } + + if (pass1) { + pass1 = false; + errno = 0; + rewind(fp); + if (errno != 0) + err(1, "failed to rewind %s", path_zonetab); + goto again; + } fclose(fp); }