rewrite funcs.c:c_test(), i.e. test(1) and [(1), to follow POSIX and XSI
in the cases where they are defined unambiguously; bug reported by Jilles Tjoelker in <20111129232526.GC14357@stack.nl> due to a report by Stefano Lattarini on bug-autoconf in the ambiguous case, I stick to traditional pdksh behaviour, which means test ! a = b vs. test a = b and test ! a -o b vs. test a -o b behave different from each other (in the second case, the NOT operator binds strong; POSIX demands a reduction to 3 arguments and negating that result in the first case), so we're at two known not-ok in the FreeBSD® testsuite. (81 and 82 in regress.sh,v 1.3)
This commit is contained in:
		
							
								
								
									
										7
									
								
								check.t
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								check.t
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| # $MirOS: src/bin/mksh/check.t,v 1.496 2011/11/26 00:45:18 tg Exp $ | # $MirOS: src/bin/mksh/check.t,v 1.497 2011/11/30 21:34:10 tg Exp $ | ||||||
| # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ | # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ | ||||||
| # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ | # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ | ||||||
| # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ | # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ | ||||||
| @@ -23,9 +23,12 @@ | |||||||
| #- | #- | ||||||
| # You may also want to test IFS with the script at | # You may also want to test IFS with the script at | ||||||
| # http://www.research.att.com/~gsf/public/ifs.sh | # http://www.research.att.com/~gsf/public/ifs.sh | ||||||
|  | # | ||||||
|  | # More testsuites at: | ||||||
|  | # http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD | ||||||
|  |  | ||||||
| expected-stdout: | expected-stdout: | ||||||
| 	@(#)MIRBSD KSH R40 2011/11/25 | 	@(#)MIRBSD KSH R40 2011/11/30 | ||||||
| description: | description: | ||||||
| 	Check version of shell. | 	Check version of shell. | ||||||
| stdin: | stdin: | ||||||
|   | |||||||
							
								
								
									
										143
									
								
								funcs.c
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								funcs.c
									
									
									
									
									
								
							| @@ -38,7 +38,7 @@ | |||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.199 2011/11/19 17:42:24 tg Exp $"); | __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.200 2011/11/30 21:34:12 tg Exp $"); | ||||||
|  |  | ||||||
| #if HAVE_KILLPG | #if HAVE_KILLPG | ||||||
| /* | /* | ||||||
| @@ -2724,8 +2724,10 @@ c_mknod(const char **wp) | |||||||
| int | int | ||||||
| c_test(const char **wp) | c_test(const char **wp) | ||||||
| { | { | ||||||
| 	int argc, res; | 	int argc, rv, invert = 0; | ||||||
| 	Test_env te; | 	Test_env te; | ||||||
|  | 	Test_op op; | ||||||
|  | 	const char *lhs, **swp; | ||||||
|  |  | ||||||
| 	te.flags = 0; | 	te.flags = 0; | ||||||
| 	te.isa = ptest_isa; | 	te.isa = ptest_isa; | ||||||
| @@ -2747,63 +2749,96 @@ c_test(const char **wp) | |||||||
| 	te.wp_end = wp + argc; | 	te.wp_end = wp + argc; | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * Handle the special cases from POSIX.2, section 4.62.4. | 	 * Attempt to conform to POSIX special cases. This is pretty | ||||||
| 	 * Implementation of all the rules isn't necessary since | 	 * dumb code straight-forward from the 2008 spec, but unless | ||||||
| 	 * our parser does the right thing for the omitted steps. | 	 * the old pdksh code doesn't live from so many assumptions. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (argc <= 5) { | 	switch (argc - 1) { | ||||||
| 		const char **owp = wp, **owpend = te.wp_end; | 	case 0: | ||||||
| 		int invert = 0; | 		return (1); | ||||||
| 		Test_op op; | 	case 1: | ||||||
| 		const char *opnd1, *opnd2; |  ptest_one: | ||||||
|  | 		op = TO_STNZE; | ||||||
| 		if (argc >= 2 && ((*te.isa)(&te, TM_OPAREN))) { | 		goto ptest_unary; | ||||||
| 			te.pos.wp = te.wp_end - 1; | 	case 2: | ||||||
| 			if ((*te.isa)(&te, TM_CPAREN)) { |  ptest_two: | ||||||
| 				argc -= 2; | 		if ((*te.isa)(&te, TM_NOT)) { | ||||||
| 				te.wp_end--; | 			++invert; | ||||||
| 				te.pos.wp = owp + 2; | 			goto ptest_one; | ||||||
| 			} else { |  | ||||||
| 				te.pos.wp = owp + 1; |  | ||||||
| 				te.wp_end = owpend; |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  | 		if ((op = (*te.isa)(&te, TM_UNOP))) { | ||||||
| 		while (--argc >= 0) { |  ptest_unary: | ||||||
| 			if ((*te.isa)(&te, TM_END)) | 			rv = (*te.eval)(&te, op, | ||||||
| 				return (!0); | 			    (*te.getopnd)(&te, op, true), NULL, true); | ||||||
| 			if (argc == 3) { |  ptest_out: | ||||||
| 				opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); | 			return ((invert & 1) ? rv : !rv); | ||||||
| 				if ((op = (*te.isa)(&te, TM_BINOP))) { |  | ||||||
| 					opnd2 = (*te.getopnd)(&te, op, 1); |  | ||||||
| 					res = (*te.eval)(&te, op, opnd1, |  | ||||||
| 					    opnd2, 1); |  | ||||||
| 					if (te.flags & TEF_ERROR) |  | ||||||
| 						return (T_ERR_EXIT); |  | ||||||
| 					if (invert & 1) |  | ||||||
| 						res = !res; |  | ||||||
| 					return (!res); |  | ||||||
| 				} |  | ||||||
| 				/* back up to opnd1 */ |  | ||||||
| 				te.pos.wp--; |  | ||||||
| 			} |  | ||||||
| 			if (argc == 1) { |  | ||||||
| 				opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); |  | ||||||
| 				res = (*te.eval)(&te, TO_STNZE, opnd1, |  | ||||||
| 				    NULL, 1); |  | ||||||
| 				if (invert & 1) |  | ||||||
| 					res = !res; |  | ||||||
| 				return (!res); |  | ||||||
| 			} |  | ||||||
| 			if ((*te.isa)(&te, TM_NOT)) { |  | ||||||
| 				invert++; |  | ||||||
| 			} else |  | ||||||
| 				break; |  | ||||||
| 		} | 		} | ||||||
| 		te.pos.wp = owp + 1; | 		/* let the parser deal with anything else */ | ||||||
| 		te.wp_end = owpend; | 		break; | ||||||
|  | 	case 3: | ||||||
|  |  ptest_three: | ||||||
|  | 		swp = te.pos.wp; | ||||||
|  | 		/* skip lhs, without evaluation */ | ||||||
|  | 		(*te.getopnd)(&te, TO_NONOP, false); | ||||||
|  | 		if ((op = (*te.isa)(&te, TM_BINOP))) { | ||||||
|  | 			const char *rhs; | ||||||
|  |  | ||||||
|  | 			/* read rhs, with evaluation */ | ||||||
|  | 			rhs = (*te.getopnd)(&te, op, true); | ||||||
|  | 			/* back up to lhs */ | ||||||
|  | 			te.pos.wp = swp; | ||||||
|  | 			/* re-read lhs, with evaluation */ | ||||||
|  | 			lhs = (*te.getopnd)(&te, op, true); | ||||||
|  | 			/* finally run the test of lhs op rhs */ | ||||||
|  | 			rv = (*te.eval)(&te, op, lhs, rhs, true); | ||||||
|  | 			goto ptest_out; | ||||||
|  | 		} | ||||||
|  | 		/* back up to lhs */ | ||||||
|  | 		te.pos.wp = swp; | ||||||
|  | 		if ((*te.isa)(&te, TM_NOT)) { | ||||||
|  | 			++invert; | ||||||
|  | 			goto ptest_two; | ||||||
|  | 		} | ||||||
|  | 		if ((*te.isa)(&te, TM_OPAREN)) { | ||||||
|  | 			swp = te.pos.wp; | ||||||
|  | 			/* skip operand, without evaluation */ | ||||||
|  | 			(*te.getopnd)(&te, TO_NONOP, false); | ||||||
|  | 			/* check for closing parenthesis */ | ||||||
|  | 			op = (*te.isa)(&te, TM_CPAREN); | ||||||
|  | 			/* back up to operand */ | ||||||
|  | 			te.pos.wp = swp; | ||||||
|  | 			/* if there was a closing paren, handle it */ | ||||||
|  | 			if (op) | ||||||
|  | 				goto ptest_one; | ||||||
|  | 			/* backing up is done before calling the parser */ | ||||||
|  | 		} | ||||||
|  | 		/* let the parser deal with it */ | ||||||
|  | 		break; | ||||||
|  | 	case 4: | ||||||
|  | 		if ((*te.isa)(&te, TM_NOT)) { | ||||||
|  | 			++invert; | ||||||
|  | 			goto ptest_three; | ||||||
|  | 		} | ||||||
|  | 		if ((*te.isa)(&te, TM_OPAREN)) { | ||||||
|  | 			swp = te.pos.wp; | ||||||
|  | 			/* skip two operands, without evaluation */ | ||||||
|  | 			(*te.getopnd)(&te, TO_NONOP, false); | ||||||
|  | 			(*te.getopnd)(&te, TO_NONOP, false); | ||||||
|  | 			/* check for closing parenthesis */ | ||||||
|  | 			op = (*te.isa)(&te, TM_CPAREN); | ||||||
|  | 			/* back up to first operand */ | ||||||
|  | 			te.pos.wp = swp; | ||||||
|  | 			/* if there was a closing paren, handle it */ | ||||||
|  | 			if (op) | ||||||
|  | 				goto ptest_two; | ||||||
|  | 			/* backing up is done before calling the parser */ | ||||||
|  | 		} | ||||||
|  | 		/* defer this to the parser */ | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	/* "The results are unspecified." */ | ||||||
|  | 	te.pos.wp = wp + 1; | ||||||
| 	return (test_parse(&te)); | 	return (test_parse(&te)); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								mksh.1
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								mksh.1
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| .\" $MirOS: src/bin/mksh/mksh.1,v 1.278 2011/11/11 22:14:02 tg Exp $ | .\" $MirOS: src/bin/mksh/mksh.1,v 1.279 2011/11/30 21:34:14 tg Exp $ | ||||||
| .\" $OpenBSD: ksh.1,v 1.141 2011/09/03 22:59:08 jmc Exp $ | .\" $OpenBSD: ksh.1,v 1.141 2011/09/03 22:59:08 jmc Exp $ | ||||||
| .\"- | .\"- | ||||||
| .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, | .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, | ||||||
| @@ -72,7 +72,7 @@ | |||||||
| .\" with -mandoc, it might implement .Mx itself, but we want to | .\" with -mandoc, it might implement .Mx itself, but we want to | ||||||
| .\" use our own definition. And .Dd must come *first*, always. | .\" use our own definition. And .Dd must come *first*, always. | ||||||
| .\" | .\" | ||||||
| .Dd $Mdocdate: November 11 2011 $ | .Dd $Mdocdate: November 30 2011 $ | ||||||
| .\" | .\" | ||||||
| .\" Check which macro package we use | .\" Check which macro package we use | ||||||
| .\" | .\" | ||||||
| @@ -3549,7 +3549,7 @@ The file type may be | |||||||
| (character type device), | (character type device), | ||||||
| or | or | ||||||
| .Cm p | .Cm p | ||||||
| (named pipe). | .Pq named pipe , Tn FIFO . | ||||||
| The file created may be modified according to its | The file created may be modified according to its | ||||||
| .Ar mode | .Ar mode | ||||||
| (via the | (via the | ||||||
| @@ -4296,7 +4296,8 @@ instead of | |||||||
| .Ql xtrace . | .Ql xtrace . | ||||||
| .It Fl p Ar file | .It Fl p Ar file | ||||||
| .Ar file | .Ar file | ||||||
| is a named pipe. | is a named pipe | ||||||
|  | .Pq Tn FIFO . | ||||||
| .It Fl r Ar file | .It Fl r Ar file | ||||||
| .Ar file | .Ar file | ||||||
| exists and is readable. | exists and is readable. | ||||||
| @@ -4394,22 +4395,22 @@ a mathematical term or the name of an integer variable: | |||||||
| x=1; [ "x" \-eq 1 ]	evaluates to true | x=1; [ "x" \-eq 1 ]	evaluates to true | ||||||
| .Ed | .Ed | ||||||
| .Pp | .Pp | ||||||
| Note that some special rules are applied (courtesy of POSIX) | Note that some special rules are applied (courtesy of | ||||||
| if the number of | .Px | ||||||
| arguments to | ) if the number of arguments to | ||||||
| .Ic test | .Ic test | ||||||
| or | or inside the brackets | ||||||
| .Ic \&[ ... \&] | .Ic \&[ ... \&] | ||||||
| is less than five: if leading | is less than five: if leading | ||||||
| .Ql \&! | .Ql \&! | ||||||
| arguments can be stripped such that only one argument remains then a string | arguments can be stripped such that only one to three arguments remain, | ||||||
| length test is performed (again, even if the argument is a unary operator); if | then the lowered comparison is executed; (thanks to XSI) parentheses | ||||||
| leading | .Ic \e( ... \e) | ||||||
| .Ql \&! | lower four- and three-argument forms to two- and one-argument forms, | ||||||
| arguments can be stripped such that three arguments remain and the second | respectively; three-argument forms ultimately prefer binary operations, | ||||||
| argument is a binary operator, then the binary operation is performed (even | followed by negation and parenthesis lowering; two- and four-argument forms | ||||||
| if the first argument is a unary operator, including an unstripped | prefer negation followed by parenthesis; the one-argument form always implies | ||||||
| .Ql \&! ) . | .Fl n . | ||||||
| .Pp | .Pp | ||||||
| .Sy Note : | .Sy Note : | ||||||
| A common mistake is to use | A common mistake is to use | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								sh.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								sh.h
									
									
									
									
									
								
							| @@ -151,9 +151,9 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef EXTERN | #ifdef EXTERN | ||||||
| __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.505 2011/11/26 00:45:21 tg Exp $"); | __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.506 2011/11/30 21:34:15 tg Exp $"); | ||||||
| #endif | #endif | ||||||
| #define MKSH_VERSION "R40 2011/11/25" | #define MKSH_VERSION "R40 2011/11/30" | ||||||
|  |  | ||||||
| #ifndef MKSH_INCLUDES_ONLY | #ifndef MKSH_INCLUDES_ONLY | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user