[tz] [PROPOSED 08/18] Port to POSIX awk, which prohibits -v newlines

Paul Eggert eggert at cs.ucla.edu
Tue Dec 19 07:25:57 UTC 2023


* NEWS: Mention this.
* tzselect.ksh: Instead of ‘awk -v "VAR=VALUE" ...’, use
‘awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE"’.
---
 NEWS         |  3 ++
 tzselect.ksh | 83 +++++++++++++++++++++++++++++++---------------------
 2 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/NEWS b/NEWS
index 25788bd7..bad81aa2 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,9 @@ Unreleased, experimental changes
     tzselect no longer mishandles spaces and most other special
     characters in BUGEMAIL, PACKAGE, TZDIR, and VERSION.
 
+    tzselect no longer mishandles ISO 6709 coordinates when using
+    an awk that does not support newlines in -v option-arguments.
+
     zic no longer mishandles data for Palestine after the year 2075.
     Previously, it incorrectly omitted post-2075 transitions that are
     predicted for just before and just after Ramadan.  (Thanks to Ken
diff --git a/tzselect.ksh b/tzselect.ksh
index 27bddfe0..1bef35ec 100644
--- a/tzselect.ksh
+++ b/tzselect.ksh
@@ -33,7 +33,12 @@ REPORT_BUGS_TO=tz at iana.org
 #	Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
 #	mawk <https://invisible-island.net/mawk/>
 #	nawk <https://github.com/onetrueawk/awk>
-
+#
+# Because 'awk "VAR=VALUE" ...' and 'awk -v "VAR=VALUE" ...' are not portable
+# if VALUE contains \, ", or newline, awk scripts in this file use:
+#   awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE"
+# The substr avoids problems when VALUE is of the form X=Y and would be
+# misinterpreted as an assignment.
 
 # Specify default values for environment variables if they are unset.
 : ${AWK=awk}
@@ -199,7 +204,12 @@ IFS=$newline
 
 # Awk script to output a country list.
 output_country_list='
-  BEGIN { FS = "\t" }
+ BEGIN {
+  continent_re = substr(ARGV[1], 2)
+  TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
+  ARGV[1] = ARGV[2] = ""
+  FS = "\t"
+ }
   /^#$/ { next }
   /^#[^@]/ { next }
   {
@@ -249,6 +259,9 @@ output_country_list='
 # and any apostrophes are escaped for the shell.
 output_distances_or_times='
   BEGIN {
+    coord = substr(ARGV[1], 2)
+    TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
+    ARGV[1] = ARGV[2] = ""
     FS = "\t"
     if (!output_times) {
       while (getline <TZ_COUNTRY_TABLE)
@@ -420,7 +433,9 @@ while
 			echo >&2 'ahead (east) of Greenwich,' \
 				'with no daylight saving time.'
 			read tz
-			$AWK -v tz="$tz" 'BEGIN {
+			$AWK 'BEGIN {
+				tz = substr(ARGV[1], 2)
+				ARGV[1] = ""
 				tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})"
 				time = "(2[0-4]|[0-1]?[0-9])" \
 				  "(:[0-5][0-9](:[0-5][0-9])?)?"
@@ -433,7 +448,7 @@ while
 				  "(" offset ")?(" datetime datetime ")?)?)$"
 				if (tz ~ tzpattern) exit 1
 				exit 0
-			}'
+			}' ="$tz"
 		do
 		    say >&2 "'$tz' is not a conforming POSIX timezone string."
 		done
@@ -451,31 +466,32 @@ while
 			read coord;;
 		    esac
 		    distance_table=`$AWK \
-			    -v coord="$coord" \
-			    -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
-			    "$output_distances_or_times" <"$TZ_ZONE_TABLE" |
+			    "$output_distances_or_times" \
+			    ="$coord" ="$TZ_COUNTRY_TABLE" <"$TZ_ZONE_TABLE" |
 		      sort -n |
 		      sed "${location_limit}q"
 		    `
-		    regions=`$AWK \
-		      -v distance_table="$distance_table" '
+		    regions=`$AWK '
 		      BEGIN {
+		        distance_table = substr(ARGV[1], 2)
+			ARGV[1] = ""
 		        nlines = split(distance_table, line, /\n/)
 			for (nr = 1; nr <= nlines; nr++) {
 			  nf = split(line[nr], f, /\t/)
 			  print f[nf]
 			}
 		      }
-		    '`
+		    ' ="$distance_table"`
 		    echo >&2 'Please select one of the following timezones,'
 		    echo >&2 'listed roughly in increasing order' \
 			    "of distance from $coord".
 		    doselect $regions
 		    region=$select_result
-		    tz=`$AWK \
-		      -v distance_table="$distance_table" \
-		      -v region="$region" '
+		    tz=`$AWK '
 		      BEGIN {
+		        distance_table = substr(ARGV[1], 2)
+			region = substr(ARGV[2], 2)
+			ARGV[1] = ARGV[2] = ""
 		        nlines = split(distance_table, line, /\n/)
 			for (nr = 1; nr <= nlines; nr++) {
 			  nf = split(line[nr], f, /\t/)
@@ -484,7 +500,7 @@ while
 			  }
 			}
 		      }
-		    '`
+		    ' ="$distance_table" ="$region"`
 		    ;;
 		*)
 		case $continent in
@@ -493,9 +509,10 @@ while
 		  old_minute=`TZ=UTC0 date +"$minute_format"`
 		  for i in 1 2 3
 		  do
-		    time_table_command=`
-		      $AWK -v output_times=1 \
-			  "$output_distances_or_times" <"$TZ_ZONE_TABLE"
+		    time_table_command=`$AWK \
+		          -v output_times=1 \
+			  "$output_distances_or_times" \
+			  = = <"$TZ_ZONE_TABLE"
 		    `
 		    time_table=`eval "$time_table_command"`
 		    new_minute=`TZ=UTC0 date +"$minute_format"`
@@ -520,30 +537,28 @@ while
 		  time=$select_result
 		  zone_table=`
 		    say "$time_table" |
-		    $AWK -v time="$time" '{
+		    $AWK 'BEGIN { time = substr(ARGV[1], 2); ARGV[1] = "" } {
 		      if ($6 " " $7 " " $4 " " $5 == time) {
 			sub(/[^\t]*\t/, "")
 			print
 		      }
-		    }'
+		    }' ="$time"
 		  `
 		  countries=`
 		     say "$zone_table" |
 		     $AWK \
-			-v continent_re='^' \
-			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
-			"$output_country_list" |
+			="$output_country_list" ='^' ="$TZ_COUNTRY_TABLE" |
 		     sort -f
 		  `
 		  ;;
 		*)
 		  zone_table=file
 		  # Get list of names of countries in the continent or ocean.
-		  countries=`$AWK \
-			-v continent_re="^$continent/" \
-			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+		  countries=`
+		    $AWK \
 			"$output_country_list" \
-			<"$TZ_ZONE_TABLE" | sort -f
+			"=^$continent/" ="$TZ_COUNTRY_TABLE" <"$TZ_ZONE_TABLE" |
+		    sort -f
 		  `;;
 		esac
 
@@ -567,10 +582,11 @@ while
 		  *) say "$zone_table";;
 		  esac |
 		  $AWK \
-			-v country="$country" \
-			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
 		    '
 			BEGIN {
+				country = substr(ARGV[1], 2)
+				TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
+				ARGV[1] = ARGV[2] = ""
 				FS = "\t"
 				cc = country
 				while (getline <TZ_COUNTRY_TABLE) {
@@ -582,7 +598,7 @@ while
 			}
 			/^#/ { next }
 			$1 ~ cc { print $4 }
-		    '
+		    ' ="$country" ="$TZ_COUNTRY_TABLE"
 		`
 
 
@@ -601,11 +617,12 @@ while
 		  *) say "$zone_table";;
 		  esac |
 		  $AWK \
-			-v country="$country" \
-			-v region="$region" \
-			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
 		    '
 			BEGIN {
+				country = substr(ARGV[1], 2)
+				region = substr(ARGV[2], 2)
+				TZ_COUNTRY_TABLE = substr(ARGV[3], 2)
+				ARGV[1] = ARGV[2] = ARGV[3] = ""
 				FS = "\t"
 				cc = country
 				while (getline <TZ_COUNTRY_TABLE) {
@@ -617,7 +634,7 @@ while
 			}
 			/^#/ { next }
 			$1 ~ cc && ($4 == region || !region) { print $3 }
-		    '
+		    ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE"
 		`;;
 		esac
 
-- 
2.40.1




More information about the tz mailing list