Module Name:    src
Committed By:   kre
Date:           Sat May  4 02:52:22 UTC 2019

Modified Files:
        src/bin/sh: parser.c
        src/tests/bin/sh: t_expand.sh

Log Message:
Fix an (apparent) ancient ash bug, that was apparently fixed sometime
in the past, but managed to re-surface...

The expression "${0+\}}" should expand to "}" not "\}"
Almost all other shells handle it that way (incl FreeBSD & dash).

Issue pointed out by Martijn Dekker.

Add ATF sub-tests for the 4 old var expand operators (${var+word}
${var-word} ${var-word} and ${var?word} - including the forms
with the ':' included) and amongst those tests include test cases
for this issue, so if the bug tries to appear again, we can squash
it quicker.   (The newer pattern matching operators are already
well tested as part of testing patterns.)


To generate a diff of this commit:
cvs rdiff -u -r1.167 -r1.168 src/bin/sh/parser.c
cvs rdiff -u -r1.21 -r1.22 src/tests/bin/sh/t_expand.sh

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/sh/parser.c
diff -u src/bin/sh/parser.c:1.167 src/bin/sh/parser.c:1.168
--- src/bin/sh/parser.c:1.167	Wed Feb 27 04:10:56 2019
+++ src/bin/sh/parser.c	Sat May  4 02:52:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: parser.c,v 1.167 2019/02/27 04:10:56 kre Exp $	*/
+/*	$NetBSD: parser.c,v 1.168 2019/05/04 02:52:22 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
 #else
-__RCSID("$NetBSD: parser.c,v 1.167 2019/02/27 04:10:56 kre Exp $");
+__RCSID("$NetBSD: parser.c,v 1.168 2019/05/04 02:52:22 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -1974,6 +1974,7 @@ readtoken1(int firstc, char const *syn, 
 			CVTRACE(DBG_LEXER, quotef==0, (" QF=1 "));
 			quotef = 1;	/* current token is quoted */
 			if (quoted && c != '\\' && c != '`' &&
+			    (c != '}' || varnest == 0) &&
 			    c != '$' && (c != '"' || magicq)) {
 				/*
 				 * retain the \ (which we *know* needs CTLESC)

Index: src/tests/bin/sh/t_expand.sh
diff -u src/tests/bin/sh/t_expand.sh:1.21 src/tests/bin/sh/t_expand.sh:1.22
--- src/tests/bin/sh/t_expand.sh:1.21	Wed Apr 10 08:13:11 2019
+++ src/tests/bin/sh/t_expand.sh	Sat May  4 02:52:22 2019
@@ -1,4 +1,4 @@
-# $NetBSD: t_expand.sh,v 1.21 2019/04/10 08:13:11 kre Exp $
+# $NetBSD: t_expand.sh,v 1.22 2019/05/04 02:52:22 kre Exp $
 #
 # Copyright (c) 2007, 2009 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -1332,10 +1332,283 @@ embedded_nl_body() {
 		EOF
 }
 
+check3()
+{
+	check "X=foo; ${1}"		"$2" 0
+	check "X=; ${1}"		"$3" 0
+	check "unset X; ${1}"		"$4" 0
+}
+
+atf_test_case alternative
+alternative_head() {
+	atf_set descr 'Test various possibilities for ${var+xxx}'
+}
+alternative_body() {
+	reset alternative
+
+	# just to verify (validate) that the test method works as expected
+	# (this is currently the very first test performed in this test set)
+	check	'printf %s a b'				ab	0	#  1
+
+	check3	'set -- ${X+bar}; echo "$#:$1"'		1:bar 1:bar 0:  #  4
+	check3	'set -- ${X+}; echo "$#:$1"'		0: 0: 0:	#  7
+	check3	'set -- ${X+""}; echo "$#:$1"'		1: 1: 0:	# 10
+	check3	'set -- "${X+}"; echo "$#:$1"'		1: 1: 1:	# 13
+	check3	'set -- "${X+bar}"; echo "$#:$1"'	1:bar 1:bar 1:	# 16
+
+	check3	'set -- ${X+a b c}; echo "$#:$1"'	3:a 3:a 0:	# 19
+	check3	'set -- ${X+"a b c"}; echo "$#:$1"'	'1:a b c' '1:a b c' 0:
+	check3	'set -- "${X+a b c}"; echo "$#:$1"'	'1:a b c' '1:a b c' 1:
+	check3	'set -- ${X+a b\ c}; echo "$#:$1"'	2:a 2:a 0:	# 28
+	check3	'set -- ${X+"a b" c}; echo "$#:$1"'	'2:a b' '2:a b' 0:
+
+	check3	'printf %s "" ${X+}'			''  ''  ''	# 34
+	check3	'printf %s ""${X+bar}'			bar bar ''	# 37
+
+	check3	'Y=bar; printf %s ${X+x}${Y+y}'		xy  xy  y	# 40
+	check3	'Y=bar; printf %s ""${X+${Y+z}}'	z   z   ''	# 43
+	check3	'Y=; printf %s ""${X+${Y+z}}'		z   z   ''	# 46
+	check3	'unset Y; printf %s ""${X+${Y+z}}'	''  ''  ''	# 49
+	check3	'Y=1; printf %s a ${X+"${Y+z}"}'	az  az	a	# 52
+
+	check3	'printf %s ${X+}x}'			x}  x}  x}	# 55
+	check3	'printf %s ${X+}}'			 }   }   }	# 58
+	check3	'printf %s "" ${X+"}"x}'		}x  }x  ''	# 61
+	check3	'printf %s "" ${X+\}x}'			}x  }x  ''	# 64
+	check3	'printf %s "${X+\}x}"'			}x  }x  ''	# 67
+	check3	'printf %s "${X+\}}"'			 }  }   ''	# 70
+
+	check3	'set -- ${X:+bar}; echo "$#:$1"'	1:bar 0: 0:	# 73
+	check3	'set -- ${X:+}; echo "$#:$1"'		0: 0: 0:	# 76
+	check3	'set -- ${X:+""}; echo "$#:$1"'		1: 0: 0:	# 79
+	check3	'set -- "${X:+}"; echo "$#:$1"'		1: 1: 1:	# 80
+	check3	'set -- "${X:+bar}"; echo "$#:$1"'	1:bar 1: 1:	# 83
+
+	check3	'set -- ${X:+a b c}; echo "$#:$1"'	3:a 0: 0:	# 86
+	check3	'set -- ${X:+"a b c"}; echo "$#:$1"'	'1:a b c' 0: 0:	# 89
+	check3	'set -- "${X:+a b c}"; echo "$#:$1"'	'1:a b c' 1: 1:	# 92
+	check3	'set -- ${X:+a b\ c}; echo "$#:$1"'	2:a 0: 0:	# 95
+	check3	'set -- ${X:+"a b" c}; echo "$#:$1"'	'2:a b' 0: 0:	# 98
+
+	check3	'printf %s "" ${X:+}'			''  ''  ''	#101
+	check3	'printf %s ""${X:+bar}'			bar ''  ''	#104
+
+	check3	'Y=bar; printf %s ${X:+x}${Y:+y}'	xy  y   y	#107
+	check3	'Y=bar; printf %s ""${X:+${Y:+z}}'	z   ''  ''	#110
+	check3	'Y=; printf %s ""${X:+${Y+z}}'		z   ''  ''	#113
+	check3	'Y=; printf %s ""${X:+${Y:+z}}'		''  ''  ''	#116
+	check3	'unset Y; printf %s ""${X:+${Y:+z}}'	''  ''  ''	#119
+	check3	'Y=1; printf %s a ${X:+"${Y:+z}"}'	az  a	a	#122
+
+	check3	'printf %s ${X:+}x}'			x}  x}  x}	#125
+	check3	'printf %s ${X:+}}'			 }   }   }	#128
+	check3	'printf %s "" ${X:+"}"x}'		}x  ''  ''	#131
+	check3	'printf %s "" ${X:+\}x}'		}x  ''  ''	#134
+	check3	'printf %s "${X:+\}x}"'			}x  ''  ''	#137
+	check3	'printf %s "${X:+\}}"'			 }  ''  ''	#140
+
+	results
+}
+
+atf_test_case default
+default_head() {
+	atf_set descr 'Test various possibilities for ${var-xxx}'
+}
+default_body() {
+	reset default
+
+	check3	'set -- ${X-bar}; echo "$#:$1"'		1:foo 0: 1:bar	#  3
+	check3	'set -- ${X-}; echo "$#:$1"'		1:foo 0: 0:	#  6
+	check3	'set -- ${X-""}; echo "$#:$1"'		1:foo 0: 1:	#  9
+	check3	'set -- "${X-}"; echo "$#:$1"'		1:foo 1: 1:	# 12
+	check3	'set -- "${X-bar}"; echo "$#:$1"'	1:foo 1: 1:bar	# 15
+
+	check3	'set -- ${X-a b c}; echo "$#:$1"'	1:foo 0: 3:a	# 18
+	check3	'set -- ${X-"a b c"}; echo "$#:$1"'	1:foo 0: '1:a b c' #21
+	check3	'set -- "${X-a b c}"; echo "$#:$1"'	1:foo 1: '1:a b c' #24
+	check3	'set -- ${X-a b\ c}; echo "$#:$1"'	1:foo 0: 2:a	# 27
+	check3	'set -- ${X-"a b" c}; echo "$#:$1"'	1:foo 0: '2:a b'   #30
+
+	check3	'printf %s "" ${X-}'			foo '' ''	# 33
+	check3	'printf %s ""${X-bar}'			foo '' bar	# 36
+
+	check3	'Y=bar; printf %s ${X-x}${Y-y}'		foobar bar xbar	# 39
+	check3	'Y=bar; printf %s ""${X-${Y-z}}'	foo '' bar	# 42
+	check3	'Y=; printf %s ""${X-${Y-z}}'		foo '' ''	# 45
+	check3	'unset Y; printf %s ""${X-${Y-z}}'	foo '' z	# 48
+	check3	'Y=1; printf %s a ${X-"${Y-z}"}'	afoo a a1	# 51
+
+	check3	'printf %s ${X-}x}'			foox} x} x}	# 54
+	check3	'printf %s ${X-}}'			 foo}  }  }	# 57
+	check3	'printf %s ${X-{}}'			 foo}  } {}	# 60
+	check3	'printf %s "" ${X-"}"x}'		foo ''  }x	# 63
+	check3	'printf %s "" ${X-\}x}'			foo ''  }x	# 66
+	check3	'printf %s "${X-\}x}"'			foo ''  }x	# 69
+	check3	'printf %s "${X-\}}"'			foo ''  }	# 72
+
+	check3	'set -- ${X:-bar}; echo "$#:$1"'	1:foo 1:bar 1:bar  #75
+	check3	'set -- ${X:-}; echo "$#:$1"'		1:foo 0: 0:	# 78
+	check3	'set -- ${X:-""}; echo "$#:$1"'		1:foo 1: 1:	# 81
+	check3	'set -- "${X:-}"; echo "$#:$1"'		1:foo 1: 1:	# 84
+	check3	'set -- "${X:-bar}"; echo "$#:$1"'	1:foo 1:bar 1:bar  #87
+
+	check3	'set -- ${X:-a b c}; echo "$#:$1"'	1:foo 3:a 3:a	# 90
+	check3	'set -- ${X:-"a b c"}; echo "$#:$1"' 1:foo '1:a b c' '1:a b c'
+	check3	'set -- "${X:-a b c}"; echo "$#:$1"' 1:foo '1:a b c' '1:a b c'
+	check3	'set -- ${X:-a b\ c}; echo "$#:$1"'	1:foo 2:a 2:a	# 99
+	check3	'set -- ${X:-"a b" c}; echo "$#:$1"'	1:foo '2:a b' '2:a b'
+
+	check3	'printf %s "" ${X:-}'			foo ''  ''	#105
+	check3	'printf %s ""${X:-bar}'			foo bar bar	#108
+
+	check3	'Y=bar; printf %s ${X:-x}${Y:-y}'	foobar xbar xbar #111
+	check3	'Y=bar; printf %s ""${X:-${Y:-z}}'	foo  bar bar	#114
+	check3	'Y=; printf %s ""${X:-${Y-z}}'		foo  ''  ''	#117
+	check3	'Y=; printf %s ""${X:-${Y:-z}}'		foo  z   z	#120
+	check3	'unset Y; printf %s ""${X:-${Y:-z}}'	foo  z   z	#123
+	check3	'Y=1; printf %s a ${X:-"${Y:-z}"}'	afoo a1	 a1	#126
+
+	check3	'printf %s ${X:-}x}'			foox} x}  x}	#129
+	check3	'printf %s ${X:-}}'			 foo}  }   }	#132
+	check3	'printf %s ${X:-{}}'			 foo} {}  {}	#135
+	check3	'printf %s "" ${X:-"}"x}'		 foo  }x  }x	#138
+	check3	'printf %s "" ${X:-\}x}'		 foo  }x  }x	#141
+	check3	'printf %s "${X:-\}x}"'			 foo  }x  }x	#144
+	check3	'printf %s "${X:-\}}"'			 foo  }   }	#147
+
+	results
+}
+
+atf_test_case assign
+assign_head() {
+	atf_set descr 'Test various possibilities for ${var=xxx}'
+}
+assign_body() {
+	reset assign
+
+	check3	'set -- ${X=bar}; echo "$#:$1"'		1:foo 0: 1:bar	#  3
+	check3	'set -- ${X=}; echo "$#:$1"'		1:foo 0: 0:	#  6
+	check3	'set -- ${X=""}; echo "$#:$1"'		1:foo 0: 0:	#  9
+	check3	'set -- "${X=}"; echo "$#:$1"'		1:foo 1: 1:	# 12
+	check3	'set -- "${X=bar}"; echo "$#:$1"'	1:foo 1: 1:bar	# 15
+
+	check3	'set -- ${X=a b c}; echo "$#:$1"'	1:foo 0: 3:a	# 18
+	check3	'set -- ${X="a b c"}; echo "$#:$1"'	1:foo 0: 3:a	# 21
+	check3	'set -- "${X=a b c}"; echo "$#:$1"'	1:foo 1: '1:a b c' #24
+	check3	'set -- ${X=a b\ c}; echo "$#:$1"'	1:foo 0: 3:a	# 27
+	check3	'set -- ${X="a b" c}; echo "$#:$1"'	1:foo 0: 3:a	# 30
+
+	check3	'printf %s "" ${X=}'			foo '' ''	# 33
+	check3	'printf %s ""${X=bar}'			foo '' bar	# 36
+
+	check3	'Y=bar; printf %s ${X=x}${Y=y}'		foobar bar xbar	# 39
+	check3	'Y=bar; printf %s ""${X=${Y=z}}'	foo '' bar	# 42
+	check3	'Y=; printf %s ""${X=${Y=z}}'		foo '' ''	# 45
+	check3	'unset Y; printf %s ""${X=${Y=z}}'	foo '' z	# 48
+	check3	'Y=1; printf %s a ${X="${Y=z}"}'	afoo a a1	# 51
+
+	check3	'printf %s ${X=}x}'			foox} x} x}	# 54
+	check3	'printf %s ${X=}}'			 foo}  }  }	# 57
+	check3	'printf %s ${X={}}'			 foo}  } {}	# 60
+	check3	'printf %s "" ${X="}"x}'		foo ''  }x	# 63
+	check3	'printf %s "" ${X=\}x}'			foo ''  }x	# 66
+	check3	'printf %s "${X=\}x}"'			foo ''  }x	# 69
+	check3	'printf %s "${X=\}}"'			foo ''  }	# 72
+
+	check3	'set -- ${X=a b c}; echo "$#:$1:$X"'  1:foo:foo 0:: '3:a:a b c'
+	check3	'set -- ${X="a b c"}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c'
+	check3	'set -- "${X=a b c}"; echo "$#:$1:$X"' \
+						1:foo:foo 1:: '1:a b c:a b c'
+	check3	'set -- ${X=a b\ c}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c'
+	check3	'set -- ${X="a b" c}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c'
+
+	check3	'printf %s ${X=}x}; printf :%s "${X-U}"' foox}:foo x}: x}: #90
+	check3	'printf %s ${X=}}; printf :%s "${X-U}"'  foo}:foo }:  }:   #93
+	check3	'printf %s ${X={}}; printf :%s "${X-U}"' foo}:foo }: {}:{  #96
+
+	check3	'set -- ${X:=bar}; echo "$#:$1"'	1:foo 1:bar 1:bar # 99	
+	check3	'set -- ${X:=}; echo "$#:$1"'		1:foo 0: 0:	#102
+	check3	'set -- ${X:=""}; echo "$#:$1"'		1:foo 0: 0:	#105
+	check3	'set -- "${X:=}"; echo "$#:$1"'		1:foo 1: 1:	#108
+	check3	'set -- "${X:=bar}"; echo "$#:$1"'	1:foo 1:bar 1:bar #111
+
+	check3	'set -- ${X:=a b c}; echo "$#:$1"'	1:foo 3:a 3:a	#114
+	check3	'set -- ${X:="a b c"}; echo "$#:$1"' 1:foo 3:a 3:a	#117
+	check3	'set -- "${X:=a b c}"; echo "$#:$1"' 1:foo '1:a b c' '1:a b c'
+	check3	'set -- ${X:=a b\ c}; echo "$#:$1"'	1:foo 3:a 3:a	#123
+	check3	'set -- ${X:="a b" c}; echo "$#:$1"'	1:foo 3:a 3:a	#126
+
+	check3	'printf %s "" ${X:=}'			foo ''  ''	#129
+	check3	'printf %s ""${X:=bar}'			foo bar bar	#132
+
+	check3	'Y=bar; printf %s ${X:=x}${Y:=y}'	foobar xbar xbar #135
+	check3	'Y=bar; printf %s ""${X:=${Y:=z}}'	foo  bar bar	#138
+	check3	'Y=; printf %s ""${X:=${Y=z}}'		foo  ''  ''	#141
+	check3	'Y=; printf %s ""${X:=${Y:=z}}'		foo  z   z	#144
+	check3	'unset Y; printf %s ""${X:=${Y:=z}}'	foo  z   z	#147
+	check3	'Y=1; printf %s a ${X:="${Y:=z}"}'	afoo a1	 a1	#150
+
+	check3	'printf %s ${X:=}x}'			foox} x}  x}	#153
+	check3	'printf %s ${X:=}}'			 foo}  }   }	#156
+	check3	'printf %s ${X:={}}'			 foo} {}  {}	#159
+	check3	'printf %s "" ${X:="}"x}'		 foo  }x  }x	#162
+	check3	'printf %s "" ${X:=\}x}'		 foo  }x  }x	#165
+	check3	'printf %s "${X:=\}x}"'			 foo  }x  }x	#168
+	check3	'printf %s "${X:=\}}"'			 foo  }   }	#171
+
+	check3	'set -- ${X:=a b c}; echo "$#:$1:$X"' \
+				1:foo:foo '3:a:a b c' '3:a:a b c'	#174
+	check3	'set -- ${X:="a b c"}; echo "$#:$1:$X"' \
+				1:foo:foo '3:a:a b c' '3:a:a b c'	#177
+	check3	'set -- "${X:=a b c}"; echo "$#:$1:$X"' \
+				1:foo:foo '1:a b c:a b c' '1:a b c:a b c' #180
+	check3	'set -- ${X:=a b\ c}; echo "$#:$1:$X"' \
+				1:foo:foo '3:a:a b c' '3:a:a b c'	#183
+	check3	'set -- ${X:="a b" c}; echo "$#:$1:$X"' \
+				1:foo:foo '3:a:a b c' '3:a:a b c'	#186
+
+	check3	'printf %s ${X:=}x}; printf :%s "${X-U}"' foox}:foo x}: x}:
+	check3	'printf %s ${X:=}}; printf :%s "${X-U}"'  foo}:foo }:  }:
+	check3	'printf %s ${X:=\}}; printf :%s "${X-U}"' foo:foo }:}  }:}
+	check3	'printf %s ${X:={}}; printf :%s "${X-U}"' foo}:foo {}:{ {}:{
+									#198
+
+	results
+}
+
+atf_test_case error
+error_head() {
+	atf_set descr 'Test various possibilities for ${var?xxx}'
+}
+error_body() {
+	reset error
+
+	check 'X=foo; printf %s ${X?X is not set}'	foo	0	#1
+	check 'X=; printf %s ${X?X is not set}'		''	0	#2
+	check 'unset X; printf %s ${X?X is not set}'	''	2	#3
+
+	check 'X=foo; printf %s ${X?}'			foo	0	#4
+	check 'X=; printf %s ${X?}'			''	0	#5
+	check 'unset X; printf %s ${X?}'		''	2	#6
+
+	check 'X=foo; printf %s ${X:?X is not set}'	foo	0	#7
+	check 'X=; printf %s ${X:?X is not set}'	''	2	#8
+	check 'unset X; printf %s ${X:?X is not set}'	''	2	#9
+
+	check 'X=foo; printf %s ${X:?}'			foo	0	#10
+	check 'X=; printf %s ${X:?}'			''	2	#11
+	check 'unset X; printf %s ${X:?}'		''	2	#12
+
+	results
+}
+
 atf_init_test_cases() {
 	# Listed here in the order ATF runs them, not the order from above
 
+	atf_add_test_case alternative
 	atf_add_test_case arithmetic
+	atf_add_test_case assign
+	atf_add_test_case default
 	atf_add_test_case dollar_at
 	atf_add_test_case dollar_at_empty_and_conditional
 	atf_add_test_case dollar_at_in_field_split_context
@@ -1348,6 +1621,7 @@ atf_init_test_cases() {
 	atf_add_test_case dollar_star_in_word_empty_ifs
 	atf_add_test_case dollar_star_with_empty_ifs
 	atf_add_test_case embedded_nl
+	atf_add_test_case error
 	atf_add_test_case iteration_on_null_parameter
 	atf_add_test_case iteration_on_quoted_null_parameter
 	atf_add_test_case iteration_on_null_or_null_parameter

Reply via email to