mirror of
				git://git.code.sf.net/p/cdesktopenv/code
				synced 2025-03-09 15:50:02 +00:00 
			
		
		
		
	Fix another test/[ corner case bug; add --posix test script
This fixes another corner case bug in the horror show that is the test/[ comand. Reproducer: $ ksh --posix -c 'test X -a -n' ksh: test: argument expected Every other shell returns 0 (success) as, POSIXly, this is a test for the strings 'X' and '-n' both being non-empty, combined with the binary -a (logical and) operator. Instead, '-n' was taken as a unary primary operator with a missing argument, which is incorrect. POSIX reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html > 3 arguments: > * If $2 is a binary primary, perform the binary test of $1 and $3. src/cmd/ksh93/bltins/test.c: - e3(): If the final argument begins with a dash, always treat it as a test for a non-empty string, therefore return true. Do not limit this to "new flags" only. src/cmd/ksh93/tests/posix.sh: - Added. These are tests for every aspect of the POSIX mode.
This commit is contained in:
		
							parent
							
								
									9e2a8c6925
								
							
						
					
					
						commit
						fd28da31da
					
				
					 6 changed files with 232 additions and 34 deletions
				
			
		
							
								
								
									
										4
									
								
								NEWS
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								NEWS
									
										
									
									
									
								
							|  | @ -3,6 +3,10 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0 | |||
| 
 | ||||
| Any uppercase BUG_* names are modernish shell bug IDs. | ||||
| 
 | ||||
| 2022-03-10: | ||||
| 
 | ||||
| - Fixed another corner case bug in the 'test'/'[' command. | ||||
| 
 | ||||
| 2022-03-05: | ||||
| 
 | ||||
| - The 'enum' command can now create more than one type per invocation. | ||||
|  |  | |||
|  | @ -334,14 +334,8 @@ static int e3(struct test *tp) | |||
| 	if(*arg=='-' && arg[2]==0) | ||||
| 	{ | ||||
| 		op = arg[1]; | ||||
| 		if(!cp) | ||||
| 		{ | ||||
| 			/* for backward compatibility with new flags */ | ||||
| 			if(op==0 || !strchr(test_opchars+10,op)) | ||||
| 				return(1); | ||||
| 			errormsg(SH_DICT,ERROR_exit(2),e_argument); | ||||
| 			UNREACHABLE(); | ||||
| 		} | ||||
| 		if(!cp)					/* no further argument: */ | ||||
| 			return(1);			/* treat as nonempty string instead of unary op, so return true */ | ||||
| 		if(strchr(test_opchars,op)) | ||||
| 			return(test_unop(op,cp)); | ||||
| 	} | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ const Shtable_t shtab_testops[] = | |||
| }; | ||||
| 
 | ||||
| const char sh_opttest[] = | ||||
| "[-1c?\n@(#)$Id: test (ksh 93u+m) 2021-11-14 $\n]" | ||||
| "[-1c?\n@(#)$Id: test (ksh 93u+m) 2022-03-10 $\n]" | ||||
| "[--catalog?" SH_DICT "]" | ||||
| "[+NAME?test, [ - evaluate expression]" | ||||
| "[+DESCRIPTION?\btest\b evaluates expressions and returns its result using the " | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ | |||
| 
 | ||||
| #define SH_RELEASE_FORK	"93u+m"		/* only change if you develop a new ksh93 fork */ | ||||
| #define SH_RELEASE_SVER	"1.0.0-beta.2"	/* semantic version number: https://semver.org */ | ||||
| #define SH_RELEASE_DATE	"2022-03-05"	/* must be in this format for $((.sh.version)) */ | ||||
| #define SH_RELEASE_DATE	"2022-03-10"	/* must be in this format for $((.sh.version)) */ | ||||
| #define SH_RELEASE_CPYR	"(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK | ||||
| 
 | ||||
| /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ | ||||
|  |  | |||
|  | @ -358,19 +358,6 @@ test ! ! '' 2> /dev/null && err_exit 'test ! ! "" should return non-zero' | |||
| x=10 | ||||
| ([[ x -eq 10 ]]) 2> /dev/null || err_exit 'x -eq 10 fails in [[...]] with x=10' | ||||
| 
 | ||||
| # ====== | ||||
| # The POSIX mode should disable the ancient 'test -t' compatibility hack. | ||||
| if	[[ -o ?posix ]] | ||||
| then	# need 'eval' in first test, as it's a parser hack, and ksh normally parses ahead of execution | ||||
| 	(set -o posix; eval '[ -t ] && test -t') >/dev/null \ | ||||
| 	|| err_exit "posix mode fails to disable 'test -t' compat hack" | ||||
| 	# the compound command variant of the hack is in the 'test' builtin itself; no 'eval' needed | ||||
| 	expect=$'*: argument expected\n*: argument expected' | ||||
| 	actual=$(set -o posix; [ -n X -a -t ] 2>&1; test -n X -a -t 2>&1) | ||||
| 	[[ $actual == $expect ]] || err_exit "posix mode fails to disable 'test -t' compat hack (compound expression)" \ | ||||
| 		"(expected output matching $(printf %q "$expect"), got $(printf %q "$actual"))" | ||||
| fi | ||||
| 
 | ||||
| # ====== | ||||
| # POSIX specifies that on error, test builtin should always return status > 1 | ||||
| expect=$': test: 123x: arithmetic syntax error\nExit status: 2' | ||||
|  | @ -426,17 +413,6 @@ foo=10 | |||
| && [ ! ! -n x -a ! ! ! ! -n x -a ! ! ! ! ! ! -n x ] \ | ||||
| || err_exit '! does not negate ! in [ ... ]' | ||||
| 
 | ||||
| # ====== | ||||
| # https://github.com/ksh93/ksh/issues/330 | ||||
| if	(set -o posix) 2>/dev/null | ||||
| then	set -o posix -o trackall | ||||
| 	test ! -a "" && err_exit "POSIX test/[: binary -a operator does not work with '!' as left-hand expression" | ||||
| 	test \( -a \) 2>/dev/null || err_exit "POSIX test/[: binary -a operator does not work with '(' as left-hand expression" | ||||
| 	test ! -o trackall || err_exit "POSIX test/[: binary -o operator does not work with '!' as left-hand expression" | ||||
| 	test \( -o \) 2>/dev/null || err_exit "POSIX test/[: binary -o operator does not work with '(' as left-hand expression" | ||||
| 	set +o posix | ||||
| fi | ||||
| 
 | ||||
| # ====== | ||||
| # test should support '<' as well as '>'; before 2021-11-13, ksh supported | ||||
| # only '>' due to '<' being missorted in shtab_testops[] in data/testops.c | ||||
|  |  | |||
							
								
								
									
										224
									
								
								src/cmd/ksh93/tests/posix.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								src/cmd/ksh93/tests/posix.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,224 @@ | |||
| ######################################################################## | ||||
| #                                                                      # | ||||
| #              This file is part of the ksh 93u+m package              # | ||||
| #          Copyright (c) 2020-2022 Contributors to ksh 93u+m           # | ||||
| #                    <https://github.com/ksh93/ksh>                    # | ||||
| #                      and is licensed under the                       # | ||||
| #                 Eclipse Public License, Version 1.0                  # | ||||
| #                                                                      # | ||||
| #                A copy of the License is available at                 # | ||||
| #          http://www.eclipse.org/org/documents/epl-v10.html           # | ||||
| #         (with md5 checksum b35adb5213ca9657e911e9befb180842)         # | ||||
| #                                                                      # | ||||
| #                  Martijn Dekker <martijn@inlv.org>                   # | ||||
| #                                                                      # | ||||
| ######################################################################## | ||||
| 
 | ||||
| . "${SHTESTS_COMMON:-${0%/*}/_common}" | ||||
| 
 | ||||
| # Tests for the --posix compatibility mode. The comments are quotes from the manual page. | ||||
| # Some of these tests need to use 'eval' to avoid the parser parsing ahead and/or to work with shcomp. | ||||
| 
 | ||||
| set -o noglob | ||||
| typeset -xi testint=123 | ||||
| 
 | ||||
| if ! (set -o posix) 2>/dev/null | ||||
| then | ||||
| 	warning "$SHELL does not have a posix option -- skipping tests" | ||||
| 	exit 0 | ||||
| fi | ||||
| command set --noposix 2>/dev/null && [[ ! -o posix ]] || err_exit "set --noposix does not work" | ||||
| command set --posix 2>/dev/null && [[ -o posix ]] || err_exit "set --posix does not work" | ||||
| ((Errors)) && exit "$Errors"  # no sense in continuing if the above fail | ||||
| 
 | ||||
| # The --posix option enables the POSIX standard mode for maximum compatibility with other compliant shells. At the | ||||
| # moment that the posix option is turned on, it also turns on letoctal and turns off -B/braceexpand; the reverse | ||||
| # is done when posix is turned back off. (These options can still be controlled independently in between.) | ||||
| let "017 == 15" || err_exit "--posix does not turn on letoctal" | ||||
| (set --noletoctal; let "017 == 17") || err_exit "--posix does not allow independent control of letoctal" | ||||
| (set --noposix; let "017 == 17") || err_exit "--noposix does not turn off letoctal" | ||||
| (set --noposix; set --letoctal; let "017 == 15") || err_exit "--noposix does not allow independent control of letoctal" | ||||
| (set {1..4}; let "$# == 1") || err_exit "--posix does not turn off braceexpand" | ||||
| if ((SHOPT_BRACEPAT)); then | ||||
| 	(set -B; eval 'set {1..4}'; let "$# == 4") || err_exit "--posix does not allow independent control of braceexpand" | ||||
| 	(set --noposix; eval 'set {1..4}'; let "$# == 4") || err_exit "--noposix does not turn on braceexpand" | ||||
| fi | ||||
| (set --noposix; ((SHOPT_BRACEPAT)) && set +B; eval 'set {1..4}'; let "$# == 1") || err_exit "--noposix does not allow independent control of braceexpand" | ||||
| 
 | ||||
| # Furthermore, the posix option is automatically turned on upon invocation if the shell is invoked as sh or rsh, | ||||
| # or if -o posix or --posix is specified on the shell invocation command line, or when executing scripts | ||||
| # without a #! path with this option active in the invoking shell. | ||||
| # In that case, the invoked shell will not set the preset aliases even if interactive, and will not import | ||||
| # type attributes for variables (such as integer or left/right justify) from the environment. | ||||
| set --noposix | ||||
| ln -s "$SHELL" sh | ||||
| ln -s "$SHELL" rsh | ||||
| script=$'[[ -o posix ]]\necho $?\ntypeset -p testint' | ||||
| exp=$'0\ntypeset -x testint=123' | ||||
| got=$(./sh -c "$script") | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invocation as sh" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| got=$(./rsh -c "$script") | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invocation as rsh" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| echo "$script" >script | ||||
| chmod +x script | ||||
| set --posix | ||||
| ./script >|out | ||||
| got=$(<out) | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from posix shell (direct)" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| 
 | ||||
| : <<\DISABLED  # TODO: these do not pass yet, unless SHOPT_SPAWN is disabled. | ||||
| (./script) >|out | ||||
| got=$(<out) | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from posix shell (subshell)" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| got=$(./script) | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from posix shell (comsub)" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| DISABLED | ||||
| 
 | ||||
| got=$("$SHELL" --posix -c "$(<script)") | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking -c script from posix shell" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| set --noposix | ||||
| exp=$'1\ntypeset -x -i testint=123' | ||||
| got=$(./script) | ||||
| [[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from noposix shell" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| set --posix | ||||
| 
 | ||||
| # In addition, while on, the posix option: | ||||
| # | ||||
| # disables exporting variable type attributes to the environment for other ksh processes to import; | ||||
| exp='typeset -x testint=123' | ||||
| got=$("$SHELL" -c 'typeset -p testint') | ||||
| [[ $got == "$exp" ]] || err_exit "variable attributes incorrectly exported in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| set --noposix | ||||
| exp='typeset -x -i testint=123' | ||||
| got=$("$SHELL" -c 'typeset -p testint') | ||||
| [[ $got == "$exp" ]] || err_exit "variable attributes not exported in --noposix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| set --posix | ||||
| 
 | ||||
| # disables the special handling of repeated isspace class characters in the IFS variable; | ||||
| IFS=$'x\t\ty' val=$'\tun\t\tduo\ttres\t' | ||||
| got=$(set $val; echo "$#") | ||||
| exp=3 | ||||
| [[ $got == "$exp" ]] || err_exit "repeated IFS whitespace char (posix): incorrect number of fields" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| got=$(set --noposix; set $val; echo "$#") | ||||
| exp=5 | ||||
| [[ $got == "$exp" ]] || err_exit "repeated IFS whitespace char (noposix): incorrect number of fields" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| IFS=$' \t\n' # default | ||||
| 
 | ||||
| # causes file descriptors > 2 to be left open when invoking another program; | ||||
| exp='ok' | ||||
| got=$(redirect 3>&1; "$SHELL" -c 'echo ok >&3' 2>/dev/null) | ||||
| [[ $got == "$exp" ]] || err_exit "file descriptor 3 not left open in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| exp='' | ||||
| got=$(set --noposix; redirect 3>&1; "$SHELL" -c 'echo ok >&3' 2>/dev/null) | ||||
| [[ $got == "$exp" ]] || err_exit "file descriptor 3 left open in --noposix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| 
 | ||||
| # disables the &> redirection shorthand; | ||||
| exp='' | ||||
| (set --posix; eval 'echo output &>out') >/dev/null | ||||
| got=$(<out) | ||||
| [[ $got == "$exp" ]] || err_exit "&> redirection shorthand not disabled in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| (set --noposix; eval 'echo output &>out') >/dev/null | ||||
| exp='output' | ||||
| got=$(<out) | ||||
| [[ $got == "$exp" ]] || err_exit "&> redirection shorthand disabled in --noposix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| 
 | ||||
| # disables fast filescan loops of type 'while inputredirection; do list; done'; | ||||
| printf '%s\n' "un duo tres" >out | ||||
| set -- | ||||
| exp='0' | ||||
| got=$(while <out; do echo "$#" "$@"; break; done) | ||||
| [[ $got == "$exp" ]] || err_exit "filescan loop not disabled in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| if ((SHOPT_FILESCAN)) | ||||
| then | ||||
| 	exp='3 un duo tres' | ||||
| 	got=$(set --noposix; while <out; do echo "$#" "$@"; break; done) | ||||
| 	[[ $got == "$exp" ]] || err_exit "filescan loop disabled in --noposix mode" \ | ||||
| 		"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| fi | ||||
| 
 | ||||
| # makes the <> redirection operator default to redirecting standard input if no file descriptor number precedes it; | ||||
| : >|out | ||||
| (set --posix; eval 'echo foo <>out') >/dev/null | ||||
| exp='' | ||||
| got=$(<out) | ||||
| [[ $got == "$exp" ]] || err_exit "<> redirects standard output in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| : >|out | ||||
| (set --noposix; eval 'echo foo <>out') >/dev/null | ||||
| exp='foo' | ||||
| got=$(<out) | ||||
| [[ $got == "$exp" ]] || err_exit "<> does not redirect standard output in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| 
 | ||||
| # disables the special floating point constants Inf and NaN in arithmetic evaluation so that, e.g., $((inf)) | ||||
| # and $((nan)) refer to the variables by those names; | ||||
| inf=1 nan=2 | ||||
| let "inf==1 && nan==2" || err_exit "inf and nan are not variables in arithmetic expressions in --posix mode" | ||||
| (set --noposix; let "inf==1 || nan==2") && err_exit "inf and nan are variables in arithmetic expressions in --noposix mode" | ||||
| 
 | ||||
| # enables the recognition of a leading zero as introducing an octal number in all arithmetic evaluation | ||||
| # contexts, except in the let built-in while letoctal is off; | ||||
| let "017 == 15" || err_exit "leading octal zero not recognised in 'let' in --posix" | ||||
| (set --noletoctal; let "017 == 17") || err_exit "leading octal zero erroneously recognised in --posix --noletoctal" | ||||
| (set --noposix; let "017 == 17") || err_exit "leading octal zero erroneously recognised in --noposix" | ||||
| (set --noposix --letoctal; let "017 == 15") || err_exit "leading octal zero not recognised in --noposix --letoctal (1)" | ||||
| (set --noposix; set --letoctal; let "017 == 15") || err_exit "leading octal zero not recognised in --noposix --letoctal (2)" | ||||
| 
 | ||||
| # stops the . command (but not source) from looking up functions defined with the function syntax; | ||||
| echo 'echo SCRIPT' >scrunction | ||||
| function scrunction { echo FUNCTION; } | ||||
| got=$(PATH=.:$PATH; . scrunction) | ||||
| exp='SCRIPT' | ||||
| [[ $got == "$exp" ]] || err_exit "'.' finds ksh function in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| got=$(set --noposix; PATH=.:$PATH; . scrunction) | ||||
| exp='FUNCTION' | ||||
| [[ $got == "$exp" ]] || err_exit "'.' does not find ksh function in --noposix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| got=$(PATH=.:$PATH; source scrunction) | ||||
| [[ $got == "$exp" ]] || err_exit "'source' does not find ksh function in --posix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| got=$(set --noposix; PATH=.:$PATH; source scrunction) | ||||
| [[ $got == "$exp" ]] || err_exit "'source' does not find ksh function in --noposix mode" \ | ||||
| 	"(expected $(printf %q "$exp"), got $(printf %q "$got"))" | ||||
| 
 | ||||
| # changes the test/[ built-in command to make its deprecated expr1 -a expr2 and expr1 -o expr2 operators work | ||||
| # even if expr1 equals "!" or "(" (which means the nonstandard unary -a file and -o option operators cannot | ||||
| # be directly negated using ! or wrapped in parentheses); | ||||
| # https://github.com/ksh93/ksh/issues/330 | ||||
| test ! -a "" && err_exit "POSIX test/[: binary -a operator does not work with '!' as left-hand expression" | ||||
| test \( -a \) 2>/dev/null || err_exit "POSIX test/[: binary -a operator does not work with '(' as left-hand expression" | ||||
| (set --trackall; test ! -o trackall) || err_exit "POSIX test/[: binary -o operator does not work with '!' as left-hand expression" | ||||
| (set --noposix --trackall; test ! -o trackall) && err_exit "ksh test/[: unary -o operator does not work with '!' negator" | ||||
| test \( -o \) 2>/dev/null || err_exit "POSIX test/[: binary -o operator does not work with '(' as left-hand expression" | ||||
| 
 | ||||
| # disables a hack that makes test -t ([ -t ]) equivalent to test -t 1 ([ -t 1 ]). | ||||
| # ...simple 'test -t' is hacked in the parser (so we need 'eval')... | ||||
| eval 'test -t' >/dev/null 2>&1 || err_exit "'test -t' does not test for nonemptiness of string '-t' in --posix mode" | ||||
| eval '[ -t ]' >/dev/null 2>&1 || err_exit "'[ -t ]' does not test for nonemptiness of string '-t' in --posix mode" | ||||
| (set --noposix; eval 'test -t') >/dev/null 2>&1 && err_exit "'test -t' is not equivalent to 'test -t 1' in --noposix mode" | ||||
| (set --noposix; eval '[ -t ]') >/dev/null 2>&1 && err_exit "'[ -t ]' is not equivalent to '[ -t 1 ]' in --noposix mode" | ||||
| # ...whereas complex expressions with bare '-t' are hacked in the test builtin itself | ||||
| test X -a -t >/dev/null 2>&1 || err_exit "'test X -a -t' does not test for nonemptiness of string '-t' in --posix mode" | ||||
| [ X -a -t ] >/dev/null 2>&1 || err_exit "'[ X -a -t ]' does not test for nonemptiness of string '-t' in --posix mode" | ||||
| (set --noposix; test X -a -t) >/dev/null 2>&1 && err_exit "'test X -a -t' is not equivalent to 'test X -a -t 1' in --noposix mode" | ||||
| (set --noposix; [ X -a -t ]) >/dev/null 2>&1 && err_exit "'[ X -a -t ]' is not equivalent to '[ X -a -t 1 ]' in --noposix mode" | ||||
| 
 | ||||
| # ====== | ||||
| exit $((Errors<125?Errors:125)) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue