git: cfd0217aa9a9 - main - tzsetup: adopt zone1970.tab changes

From: Yuri Pankov <yuripv_at_FreeBSD.org>
Date: Thu, 27 Apr 2023 17:40:04 UTC
The branch main has been updated by yuripv:

URL: https://cgit.FreeBSD.org/src/commit/?id=cfd0217aa9a929ac625db90828a56dfe4dc1dbd2

commit cfd0217aa9a929ac625db90828a56dfe4dc1dbd2
Author:     Yuri Pankov <yuripv@FreeBSD.org>
AuthorDate: 2023-04-27 17:31:48 +0000
Commit:     Yuri Pankov <yuripv@FreeBSD.org>
CommitDate: 2023-04-27 17:31:48 +0000

    tzsetup: adopt zone1970.tab changes
    
    - assumption that single-zone countries do not have description
      is no longer correct; do not try to optimize this case as it's
      only going to make the code more confusing and we now have menus
      with a single zone selection because of this
    - remove the single-country continent short cut, it also only serves
      to confuse users as we now have such a continent
    - instead add a single-zone contry short cut (see above), now all
      single-zone countries fall here
    - use the #@ continent overrides that zone1970.tab introduces (this is
      visible at least fixing Iceland being currently listed under Africa)
    - add Arctic Ocean "continent" coming only from the overrides at the
      moment
    - update baseline with the changes
    
    Reviewed by:    bapt, philip
    Differential Revision:  https://reviews.freebsd.org/D39606
---
 usr.sbin/tzsetup/baseline  |  24 ++--
 usr.sbin/tzsetup/tzsetup.c | 310 +++++++++++++++++++++++----------------------
 2 files changed, 169 insertions(+), 165 deletions(-)

diff --git a/usr.sbin/tzsetup/baseline b/usr.sbin/tzsetup/baseline
index 79a7f2a869b6..de9b5f638d09 100644
--- a/usr.sbin/tzsetup/baseline
+++ b/usr.sbin/tzsetup/baseline
@@ -19,10 +19,10 @@ AQ:Antarctica
   Antarctica:Antarctica/Palmer
   Antarctica:Antarctica/Rothera
   Antarctica:Antarctica/Troll
-  Asia:Asia/Urumqi
-  Pacific:Pacific/Auckland
-  Pacific:Pacific/Port_Moresby
-  Asia:Asia/Riyadh
+  Antarctica:Asia/Urumqi
+  Antarctica:Pacific/Auckland
+  Antarctica:Pacific/Port_Moresby
+  Antarctica:Asia/Riyadh
 AG:Antigua and Barbuda
   America:America/Puerto_Rico
 AR:Argentina
@@ -159,13 +159,13 @@ CN:China
   Asia:Asia/Shanghai
   Asia:Asia/Urumqi
 CX:Christmas Island
-  Asia:Asia/Bangkok
+  Indian:Asia/Bangkok
 CC:Cocos (Keeling) Islands
-  Asia:Asia/Yangon
+  Indian:Asia/Yangon
 CO:Colombia
   America:America/Bogota
 KM:Comoros
-  Africa:Africa/Nairobi
+  Indian:Africa/Nairobi
 CG:Congo
   Africa:Africa/Lagos
 CD:Congo, Democratic Republic of the
@@ -280,7 +280,7 @@ HK:Hong Kong
 HU:Hungary
   Europe:Europe/Budapest
 IS:Iceland
-  Africa:Africa/Abidjan
+  Atlantic:Africa/Abidjan
 IN:India
   Asia:Asia/Kolkata
 ID:Indonesia
@@ -353,7 +353,7 @@ MO:Macao
 MK:Macedonia (the former Yugoslav Republic of)
   Europe:Europe/Belgrade
 MG:Madagascar
-  Africa:Africa/Nairobi
+  Indian:Africa/Nairobi
 MW:Malawi
   Africa:Africa/Maputo
 MY:Malaysia
@@ -375,7 +375,7 @@ MR:Mauritania
 MU:Mauritius
   Indian:Indian/Mauritius
 YT:Mayotte
-  Africa:Africa/Nairobi
+  Indian:Africa/Nairobi
 MX:Mexico
   America:America/Mexico_City
   America:America/Cancun
@@ -507,7 +507,7 @@ RE:Réunion
 BL:Saint Barthélemy
   America:America/Puerto_Rico
 SH:Saint Helena Ascension and Tristan da Cunha
-  Africa:Africa/Abidjan
+  Atlantic:Africa/Abidjan
 KN:Saint Kitts and Nevis
   America:America/Puerto_Rico
 LC:Saint Lucia
@@ -563,7 +563,7 @@ SD:Sudan
 SR:Suriname
   America:America/Paramaribo
 SJ:Svalbard and Jan Mayen
-  Europe:Europe/Berlin
+  Arctic:Europe/Berlin
 SE:Sweden
   Europe:Europe/Berlin
 CH:Switzerland
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
index b4d906d90b7e..a20c0730c48e 100644
--- a/usr.sbin/tzsetup/tzsetup.c
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -157,8 +157,7 @@ static int usedialog = 1;
 
 static int	confirm_zone(const char *filename);
 static int	continent_country_menu(dialogMenuItem *);
-static int	set_zone_multi(dialogMenuItem *);
-static int	set_zone_whole_country(dialogMenuItem *);
+static int	set_zone(dialogMenuItem *);
 static int	set_zone_menu(dialogMenuItem *);
 static int	set_zone_utc(void);
 
@@ -167,7 +166,7 @@ struct continent {
 	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 {
@@ -177,6 +176,7 @@ static struct continent_names {
 	{ "Africa",	&africa },
 	{ "America",	&america },
 	{ "Antarctica",	&antarctica },
+	{ "Arctic",	&arctic },
 	{ "Asia",	&asia },
 	{ "Atlantic",	&atlantic },
 	{ "Australia",	&australia },
@@ -187,26 +187,27 @@ static struct continent_names {
 };
 
 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)
@@ -219,10 +220,6 @@ continent_country_menu(dialogMenuItem *continent)
 	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",
@@ -239,14 +236,30 @@ continent_country_menu(dialogMenuItem *continent)
 }
 
 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);
 }
 
 static const char *
@@ -264,10 +277,9 @@ struct country {
 	char		*name;
 	char		*tlc;
 	int		nzones;
-	char		*filename;	/* use iff nzones < 0 */
-	struct continent *continent;	/* use iff nzones < 0 */
-	TAILQ_HEAD(, zone) zones;	/* use iff nzones > 0 */
-	dialogMenuItem	*submenu;	/* use iff nzones > 0 */
+	struct continent *override;	/* continent override */
+	TAILQ_HEAD(, zone) zones;
+	dialogMenuItem	*submenu;
 };
 
 struct zone {
@@ -346,56 +358,50 @@ read_iso3166_table(void)
 	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);
 
-	if (descr) {
-		if (cp->nzones < 0)
-			errx(1, "%s:%d: conflicting zone definition",
-			    path_zonetab, lineno);
+	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;
 
-		zp = malloc(sizeof(*zp));
-		if (zp == NULL)
-			errx(1, "malloc(%zu)", sizeof(*zp));
+	zp = malloc(sizeof(*zp));
+	if (zp == NULL)
+		errx(1, "malloc(%zu)", sizeof(*zp));
 
-		if (cp->nzones == 0)
-			TAILQ_INIT(&cp->zones);
+	if (cp->nzones == 0)
+		TAILQ_INIT(&cp->zones);
 
+	if (descr != NULL) {
 		zp->descr = strdup(descr);
 		if (zp->descr == NULL)
 			errx(1, "malloc failed");
-		zp->filename = strdup(file);
-		if (zp->filename == NULL)
-			errx(1, "malloc failed");
-		zp->continent = cont;
-		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
-		cp->nzones++;
 	} else {
-		if (cp->nzones > 0)
-			errx(1, "%s:%d: zone must have description",
-			    path_zonetab, lineno);
-		if (cp->nzones < 0)
-			errx(1, "%s:%d: zone multiply defined",
-			    path_zonetab, lineno);
-		cp->nzones = -1;
-		cp->filename = strdup(file);
-		if (cp->filename == NULL)
-			errx(1, "malloc failed");
-		cp->continent = cont;
+		zp->descr = NULL;
 	}
+	zp->filename = strdup(file);
+	if (zp->filename == NULL)
+		errx(1, "malloc failed");
+	zp->continent = cp->override != NULL ? cp->override : cont;
+	TAILQ_INSERT_TAIL(&cp->zones, zp, link);
+	cp->nzones++;
 }
 
 /*
@@ -432,54 +438,79 @@ sort_countries(void)
 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...]<tab>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);
 }
 
@@ -492,14 +523,9 @@ dump_zonetab(void)
 
 	for (cp = countries; cp->name != NULL; cp++) {
 		printf("%s:%s\n", cp->tlc, cp->name);
-		if (cp->nzones < 0) {
-			cont = find_continent_name(cp->continent);
-			printf("  %s:%s\n", cont, cp->filename);
-		} else {
-			TAILQ_FOREACH(zp, &cp->zones, link) {
-				cont = find_continent_name(zp->continent);
-				printf("  %s:%s\n", cont, zp->filename);
-			}
+		TAILQ_FOREACH(zp, &cp->zones, link) {
+			cont = find_continent_name(zp->continent);
+			printf("  %s:%s\n", cont, zp->filename);
 		}
 	}
 }
@@ -522,18 +548,14 @@ make_menus(void)
 	for (cp = countries; cp->name; cp++) {
 		if (cp->nzones == 0)
 			continue;
-		if (cp->nzones < 0) {
-			cp->continent->nitems++;
-		} else {
-			TAILQ_FOREACH(zp, &cp->zones, link) {
-				cont = zp->continent;
-				for (zp2 = TAILQ_FIRST(&cp->zones);
-				    zp2->continent != cont;
-				    zp2 = TAILQ_NEXT(zp2, link))
-					;
-				if (zp2 == zp)
-					zp->continent->nitems++;
-			}
+		TAILQ_FOREACH(zp, &cp->zones, link) {
+			cont = zp->continent;
+			for (zp2 = TAILQ_FIRST(&cp->zones);
+			    zp2->continent != cont;
+			    zp2 = TAILQ_NEXT(zp2, link))
+				;
+			if (zp2 == zp)
+				zp->continent->nitems++;
 		}
 	}
 
@@ -564,41 +586,32 @@ make_menus(void)
 	for (cp = countries; cp->name; cp++) {
 		if (cp->nzones == 0)
 			continue;
-		if (cp->nzones < 0) {
-			dmi = &cp->continent->menu[cp->continent->nitems];
+		cp->submenu = malloc(cp->nzones * sizeof(*dmi));
+		if (cp->submenu == 0)
+			errx(1, "malloc for submenu");
+		cp->nzones = 0;
+		TAILQ_FOREACH(zp, &cp->zones, link) {
+			cont = zp->continent;
+			dmi = &cp->submenu[cp->nzones];
+			memset(dmi, 0, sizeof(*dmi));
+			asprintf(&dmi->prompt, "%d", ++cp->nzones);
+			dmi->title = zp->descr;
+			dmi->fire = set_zone;
+			dmi->data = zp;
+
+			for (zp2 = TAILQ_FIRST(&cp->zones);
+			    zp2->continent != cont;
+			    zp2 = TAILQ_NEXT(zp2, link))
+				;
+			if (zp2 != zp)
+				continue;
+
+			dmi = &cont->menu[cont->nitems];
 			memset(dmi, 0, sizeof(*dmi));
-			asprintf(&dmi->prompt, "%d", ++cp->continent->nitems);
+			asprintf(&dmi->prompt, "%d", ++cont->nitems);
 			dmi->title = cp->name;
-			dmi->fire = set_zone_whole_country;
+			dmi->fire = set_zone_menu;
 			dmi->data = cp;
-		} else {
-			cp->submenu = malloc(cp->nzones * sizeof(*dmi));
-			if (cp->submenu == 0)
-				errx(1, "malloc for submenu");
-			cp->nzones = 0;
-			TAILQ_FOREACH(zp, &cp->zones, link) {
-				cont = zp->continent;
-				dmi = &cp->submenu[cp->nzones];
-				memset(dmi, 0, sizeof(*dmi));
-				asprintf(&dmi->prompt, "%d", ++cp->nzones);
-				dmi->title = zp->descr;
-				dmi->fire = set_zone_multi;
-				dmi->data = zp;
-
-				for (zp2 = TAILQ_FIRST(&cp->zones);
-				    zp2->continent != cont;
-				    zp2 = TAILQ_NEXT(zp2, link))
-					;
-				if (zp2 != zp)
-					continue;
-
-				dmi = &cont->menu[cont->nitems];
-				memset(dmi, 0, sizeof(*dmi));
-				asprintf(&dmi->prompt, "%d", ++cont->nitems);
-				dmi->title = cp->name;
-				dmi->fire = set_zone_menu;
-				dmi->data = cp;
-			}
 		}
 	}
 }
@@ -610,6 +623,10 @@ set_zone_menu(dialogMenuItem *dmi)
 	struct country	*cp = dmi->data;
 	int		rv;
 
+	/* Short cut -- if there's only one zone, don't post a menu. */
+	if (cp->nzones == 1)
+		return (cp->submenu[0].fire(&cp->submenu[0]));
+
 	snprintf(title, sizeof(title), "%s Time Zones", cp->name);
 	snprintf(prompt, sizeof(prompt),
 	    "Select a zone which observes the same time as your locality.");
@@ -646,7 +663,7 @@ confirm_zone(const char *filename)
 }
 
 static int
-set_zone_multi(dialogMenuItem *dmi)
+set_zone(dialogMenuItem *dmi)
 {
 	struct zone	*zp = dmi->data;
 	int		rv;
@@ -658,19 +675,6 @@ set_zone_multi(dialogMenuItem *dmi)
 	return (rv);
 }
 
-static int
-set_zone_whole_country(dialogMenuItem *dmi)
-{
-	struct country	*cp = dmi->data;
-	int		rv;
-
-	if (!confirm_zone(cp->filename))
-		return (DITEM_FAILURE | DITEM_RECREATE);
-
-	rv = install_zoneinfo(cp->filename);
-	return (rv);
-}
-
 #endif
 
 static void message_zoneinfo_file(const char *title, char *prompt)