summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorjmmv <jmmv@FreeBSD.org>2014-03-09 17:04:31 +0000
committerjmmv <jmmv@FreeBSD.org>2014-03-09 17:04:31 +0000
commit80efbcbbcda6a5c59a21cb9628b2fd0573697dfe (patch)
tree2eed051c6d4bc448ea3f0f646cd5dbe45f87974c /bin
parentf3010cfb7d9dbb4553d0cfa508ec09190a32fc7a (diff)
downloadFreeBSD-src-80efbcbbcda6a5c59a21cb9628b2fd0573697dfe.zip
FreeBSD-src-80efbcbbcda6a5c59a21cb9628b2fd0573697dfe.tar.gz
Sync sh(1) in stable/10 to head.
This is a MFC of all the commits listed below. My original goal of this change was to only merge the move of the tests from tools/regression/bin/ into the new layout (which include tests for sh(1) and other tools as well). However, doing so is tricky due to the ongoing work in sh(1) and, especially, the many changes to its tests since stable/10 was first branched. Merging everything is the simplest way to achieve this goal and, as a bonus point, we get various fixes and miscellaneous improvements into the branch. Per jilles' suggestion, I'm avoiding the merge of a couple of changes (r256850 and r257506) that required depending kernel changes. I'm also avoiding very recent changes that have not had a long enough time to be validated in current. This is "make tinderbox" clean. r256735 sh: Remove one syscall when waiting for a foreground job. r257399 sh: Allow trapping SIGINT/SIGQUIT after ignore because of '&'. r257504 sh: Reorder union node to reduce its size on 64-bit platforms. r257920 sh: Add a test case for would-be assignments that are not due to quoting. r257929 sh: Properly quote alias output from command -v. r258489 sh: Add tests for the </dev/null implicit in a background command. r258533 sh: Add more tests for the </dev/null implicit in a background command. r258535 sh: Make <&0 disable the </dev/null implicit in a background command. r258776 sh: Prefer memcpy() to strcpy() in most cases. Remove the scopy macro. r259047 sh: Split set -x output into a separate function. r259210 Migrate tools/regression/bin/ tests to the new layout. r259844 sh: Remove an unused variable. r259846 sh: Initialize OPTIND=1 even if it came from the environment. r259874 sh: Simplify code related to PPID variable. r259946 sh: Don't check input for non-whitespace if history is disabled. r260246 sh(1): Discourage use of -e. r260506 Run the sh(1) and test(1) tests as unprivileged. r260586 Mark the bin/pax tests as requiring perl. r260634 Use TAP_TESTS_PERL to register the legacy_test in bin/pax. r260635 Replace hand-crafted Kyuafiles with automatic generation. r260654 sh: Remove SIGWINCH handler and just check for resize before every read. r261121 sh: Add test for nested alias. r261125 sh: Solve the alias recursion problem in a less hackish way. r261141 sh: Do not depend on parse/execute split in new alias test. r261160 sh: Add tests for alias names after another alias. r261192 sh: Allow aliases to force alias substitution on the following word. r262533 sh: Make expari() static. r262565 sh: Do not corrupt internal representation if LINENO inner expansion fails. r262697 sh: Simplify expari(). Reviewed by: jilles
Diffstat (limited to 'bin')
-rw-r--r--bin/Makefile4
-rw-r--r--bin/date/Makefile6
-rw-r--r--bin/date/tests/Makefile9
-rw-r--r--bin/date/tests/legacy_test.sh91
-rw-r--r--bin/mv/Makefile6
-rw-r--r--bin/mv/tests/Makefile9
-rw-r--r--bin/mv/tests/legacy_test.sh296
-rw-r--r--bin/pax/Makefile6
-rw-r--r--bin/pax/tests/Makefile9
-rw-r--r--bin/pax/tests/legacy_test.pl89
-rw-r--r--bin/sh/Makefile7
-rw-r--r--bin/sh/alias.c44
-rw-r--r--bin/sh/cd.c12
-rw-r--r--bin/sh/eval.c75
-rw-r--r--bin/sh/exec.c22
-rw-r--r--bin/sh/expand.c111
-rw-r--r--bin/sh/expand.h1
-rw-r--r--bin/sh/input.c37
-rw-r--r--bin/sh/jobs.c3
-rw-r--r--bin/sh/memalloc.c6
-rw-r--r--bin/sh/mystring.c5
-rw-r--r--bin/sh/mystring.h1
-rw-r--r--bin/sh/nodetypes6
-rw-r--r--bin/sh/parser.c6
-rw-r--r--bin/sh/parser.h1
-rw-r--r--bin/sh/redir.c9
-rw-r--r--bin/sh/sh.124
-rw-r--r--bin/sh/show.c4
-rw-r--r--bin/sh/tests/Makefile18
-rw-r--r--bin/sh/tests/builtins/Makefile148
-rw-r--r--bin/sh/tests/builtins/alias.09
-rw-r--r--bin/sh/tests/builtins/alias.0.stdout4
-rw-r--r--bin/sh/tests/builtins/alias.13
-rw-r--r--bin/sh/tests/builtins/alias.1.stderr1
-rw-r--r--bin/sh/tests/builtins/alias3.012
-rw-r--r--bin/sh/tests/builtins/alias3.0.stdout4
-rw-r--r--bin/sh/tests/builtins/alias4.04
-rw-r--r--bin/sh/tests/builtins/break1.016
-rw-r--r--bin/sh/tests/builtins/break2.012
-rw-r--r--bin/sh/tests/builtins/break2.0.stdout1
-rw-r--r--bin/sh/tests/builtins/break3.015
-rw-r--r--bin/sh/tests/builtins/break4.47
-rw-r--r--bin/sh/tests/builtins/break5.412
-rw-r--r--bin/sh/tests/builtins/builtin1.031
-rw-r--r--bin/sh/tests/builtins/case1.013
-rw-r--r--bin/sh/tests/builtins/case10.016
-rw-r--r--bin/sh/tests/builtins/case11.06
-rw-r--r--bin/sh/tests/builtins/case12.06
-rw-r--r--bin/sh/tests/builtins/case13.012
-rw-r--r--bin/sh/tests/builtins/case14.05
-rw-r--r--bin/sh/tests/builtins/case15.05
-rw-r--r--bin/sh/tests/builtins/case16.07
-rw-r--r--bin/sh/tests/builtins/case17.03
-rw-r--r--bin/sh/tests/builtins/case18.07
-rw-r--r--bin/sh/tests/builtins/case19.07
-rw-r--r--bin/sh/tests/builtins/case2.0106
-rw-r--r--bin/sh/tests/builtins/case3.095
-rw-r--r--bin/sh/tests/builtins/case4.06
-rw-r--r--bin/sh/tests/builtins/case5.057
-rw-r--r--bin/sh/tests/builtins/case6.052
-rw-r--r--bin/sh/tests/builtins/case7.024
-rw-r--r--bin/sh/tests/builtins/case8.032
-rw-r--r--bin/sh/tests/builtins/case9.039
-rw-r--r--bin/sh/tests/builtins/cd1.030
-rw-r--r--bin/sh/tests/builtins/cd2.016
-rw-r--r--bin/sh/tests/builtins/cd3.021
-rw-r--r--bin/sh/tests/builtins/cd4.038
-rw-r--r--bin/sh/tests/builtins/cd5.023
-rw-r--r--bin/sh/tests/builtins/cd6.010
-rw-r--r--bin/sh/tests/builtins/cd7.015
-rw-r--r--bin/sh/tests/builtins/cd8.026
-rw-r--r--bin/sh/tests/builtins/command1.05
-rw-r--r--bin/sh/tests/builtins/command10.014
-rw-r--r--bin/sh/tests/builtins/command11.014
-rw-r--r--bin/sh/tests/builtins/command12.07
-rw-r--r--bin/sh/tests/builtins/command2.03
-rw-r--r--bin/sh/tests/builtins/command3.014
-rw-r--r--bin/sh/tests/builtins/command3.0.stdout7
-rw-r--r--bin/sh/tests/builtins/command4.02
-rw-r--r--bin/sh/tests/builtins/command5.015
-rw-r--r--bin/sh/tests/builtins/command5.0.stdout8
-rw-r--r--bin/sh/tests/builtins/command6.022
-rw-r--r--bin/sh/tests/builtins/command6.0.stdout7
-rw-r--r--bin/sh/tests/builtins/command7.034
-rw-r--r--bin/sh/tests/builtins/command8.045
-rw-r--r--bin/sh/tests/builtins/command9.014
-rw-r--r--bin/sh/tests/builtins/dot1.021
-rw-r--r--bin/sh/tests/builtins/dot2.021
-rw-r--r--bin/sh/tests/builtins/dot3.010
-rw-r--r--bin/sh/tests/builtins/dot4.012
-rw-r--r--bin/sh/tests/builtins/eval1.09
-rw-r--r--bin/sh/tests/builtins/eval2.07
-rw-r--r--bin/sh/tests/builtins/eval3.09
-rw-r--r--bin/sh/tests/builtins/eval4.05
-rw-r--r--bin/sh/tests/builtins/eval5.04
-rw-r--r--bin/sh/tests/builtins/eval6.05
-rw-r--r--bin/sh/tests/builtins/exec1.025
-rw-r--r--bin/sh/tests/builtins/exec2.025
-rw-r--r--bin/sh/tests/builtins/exit1.06
-rw-r--r--bin/sh/tests/builtins/exit2.87
-rw-r--r--bin/sh/tests/builtins/exit3.05
-rw-r--r--bin/sh/tests/builtins/export1.03
-rw-r--r--bin/sh/tests/builtins/fc1.027
-rw-r--r--bin/sh/tests/builtins/fc2.034
-rw-r--r--bin/sh/tests/builtins/for1.04
-rw-r--r--bin/sh/tests/builtins/for2.09
-rw-r--r--bin/sh/tests/builtins/for3.08
-rw-r--r--bin/sh/tests/builtins/getopts1.025
-rw-r--r--bin/sh/tests/builtins/getopts1.0.stdout8
-rw-r--r--bin/sh/tests/builtins/getopts2.06
-rw-r--r--bin/sh/tests/builtins/getopts2.0.stdout1
-rw-r--r--bin/sh/tests/builtins/hash1.05
-rw-r--r--bin/sh/tests/builtins/hash1.0.stdout1
-rw-r--r--bin/sh/tests/builtins/hash2.04
-rw-r--r--bin/sh/tests/builtins/hash2.0.stdout1
-rw-r--r--bin/sh/tests/builtins/hash3.03
-rw-r--r--bin/sh/tests/builtins/hash3.0.stdout2
-rw-r--r--bin/sh/tests/builtins/hash4.06
-rw-r--r--bin/sh/tests/builtins/jobid1.07
-rw-r--r--bin/sh/tests/builtins/jobid2.09
-rw-r--r--bin/sh/tests/builtins/lineno.016
-rw-r--r--bin/sh/tests/builtins/lineno.0.stdout9
-rw-r--r--bin/sh/tests/builtins/lineno2.010
-rw-r--r--bin/sh/tests/builtins/local1.013
-rw-r--r--bin/sh/tests/builtins/local2.017
-rw-r--r--bin/sh/tests/builtins/local3.026
-rw-r--r--bin/sh/tests/builtins/local4.012
-rw-r--r--bin/sh/tests/builtins/locale1.0134
-rw-r--r--bin/sh/tests/builtins/printf1.03
-rw-r--r--bin/sh/tests/builtins/printf2.03
-rw-r--r--bin/sh/tests/builtins/printf3.05
-rw-r--r--bin/sh/tests/builtins/printf4.05
-rw-r--r--bin/sh/tests/builtins/read1.026
-rw-r--r--bin/sh/tests/builtins/read1.0.stdout20
-rw-r--r--bin/sh/tests/builtins/read2.031
-rw-r--r--bin/sh/tests/builtins/read3.011
-rw-r--r--bin/sh/tests/builtins/read3.0.stdout9
-rw-r--r--bin/sh/tests/builtins/read4.010
-rw-r--r--bin/sh/tests/builtins/read4.0.stdout8
-rw-r--r--bin/sh/tests/builtins/read5.032
-rw-r--r--bin/sh/tests/builtins/read6.05
-rw-r--r--bin/sh/tests/builtins/read7.05
-rw-r--r--bin/sh/tests/builtins/return1.07
-rw-r--r--bin/sh/tests/builtins/return2.17
-rw-r--r--bin/sh/tests/builtins/return3.13
-rw-r--r--bin/sh/tests/builtins/return4.016
-rw-r--r--bin/sh/tests/builtins/return5.017
-rw-r--r--bin/sh/tests/builtins/return6.43
-rw-r--r--bin/sh/tests/builtins/return7.46
-rw-r--r--bin/sh/tests/builtins/return8.013
-rw-r--r--bin/sh/tests/builtins/set1.032
-rw-r--r--bin/sh/tests/builtins/set2.03
-rw-r--r--bin/sh/tests/builtins/trap1.022
-rw-r--r--bin/sh/tests/builtins/trap10.06
-rw-r--r--bin/sh/tests/builtins/trap11.08
-rw-r--r--bin/sh/tests/builtins/trap12.010
-rw-r--r--bin/sh/tests/builtins/trap13.08
-rw-r--r--bin/sh/tests/builtins/trap14.010
-rw-r--r--bin/sh/tests/builtins/trap2.052
-rw-r--r--bin/sh/tests/builtins/trap3.011
-rw-r--r--bin/sh/tests/builtins/trap4.017
-rw-r--r--bin/sh/tests/builtins/trap5.019
-rw-r--r--bin/sh/tests/builtins/trap6.09
-rw-r--r--bin/sh/tests/builtins/trap7.03
-rw-r--r--bin/sh/tests/builtins/trap8.07
-rw-r--r--bin/sh/tests/builtins/trap9.03
-rw-r--r--bin/sh/tests/builtins/type1.08
-rw-r--r--bin/sh/tests/builtins/type1.0.stderr4
-rw-r--r--bin/sh/tests/builtins/type2.026
-rw-r--r--bin/sh/tests/builtins/type3.03
-rw-r--r--bin/sh/tests/builtins/unalias.021
-rw-r--r--bin/sh/tests/builtins/var-assign.055
-rw-r--r--bin/sh/tests/builtins/var-assign2.055
-rw-r--r--bin/sh/tests/builtins/wait1.023
-rw-r--r--bin/sh/tests/builtins/wait10.05
-rw-r--r--bin/sh/tests/builtins/wait2.015
-rw-r--r--bin/sh/tests/builtins/wait3.021
-rw-r--r--bin/sh/tests/builtins/wait4.012
-rw-r--r--bin/sh/tests/builtins/wait5.012
-rw-r--r--bin/sh/tests/builtins/wait6.03
-rw-r--r--bin/sh/tests/builtins/wait7.04
-rw-r--r--bin/sh/tests/builtins/wait8.07
-rw-r--r--bin/sh/tests/builtins/wait9.1273
-rw-r--r--bin/sh/tests/errors/Makefile30
-rw-r--r--bin/sh/tests/errors/assignment-error1.030
-rw-r--r--bin/sh/tests/errors/assignment-error2.08
-rw-r--r--bin/sh/tests/errors/backquote-error1.04
-rw-r--r--bin/sh/tests/errors/backquote-error2.07
-rw-r--r--bin/sh/tests/errors/bad-binary1.12612
-rw-r--r--bin/sh/tests/errors/bad-keyword1.04
-rw-r--r--bin/sh/tests/errors/bad-parm-exp1.07
-rw-r--r--bin/sh/tests/errors/bad-parm-exp2.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp2.2.stderr1
-rw-r--r--bin/sh/tests/errors/bad-parm-exp3.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp3.2.stderr1
-rw-r--r--bin/sh/tests/errors/bad-parm-exp4.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp4.2.stderr1
-rw-r--r--bin/sh/tests/errors/bad-parm-exp5.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp5.2.stderr1
-rw-r--r--bin/sh/tests/errors/bad-parm-exp6.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp6.2.stderr1
-rw-r--r--bin/sh/tests/errors/option-error.046
-rw-r--r--bin/sh/tests/errors/redirection-error.053
-rw-r--r--bin/sh/tests/errors/redirection-error2.24
-rw-r--r--bin/sh/tests/errors/redirection-error3.054
-rw-r--r--bin/sh/tests/errors/redirection-error4.07
-rw-r--r--bin/sh/tests/errors/redirection-error5.05
-rw-r--r--bin/sh/tests/errors/redirection-error6.012
-rw-r--r--bin/sh/tests/errors/redirection-error7.07
-rw-r--r--bin/sh/tests/errors/write-error1.03
-rw-r--r--bin/sh/tests/execution/Makefile53
-rw-r--r--bin/sh/tests/execution/bg1.03
-rw-r--r--bin/sh/tests/execution/bg10.04
-rw-r--r--bin/sh/tests/execution/bg10.0.stdout1
-rw-r--r--bin/sh/tests/execution/bg2.05
-rw-r--r--bin/sh/tests/execution/bg3.05
-rw-r--r--bin/sh/tests/execution/bg4.06
-rw-r--r--bin/sh/tests/execution/bg5.04
-rw-r--r--bin/sh/tests/execution/bg6.04
-rw-r--r--bin/sh/tests/execution/bg6.0.stdout1
-rw-r--r--bin/sh/tests/execution/bg7.05
-rw-r--r--bin/sh/tests/execution/bg8.05
-rw-r--r--bin/sh/tests/execution/bg9.05
-rw-r--r--bin/sh/tests/execution/fork1.010
-rw-r--r--bin/sh/tests/execution/fork2.09
-rw-r--r--bin/sh/tests/execution/fork3.04
-rw-r--r--bin/sh/tests/execution/func1.04
-rw-r--r--bin/sh/tests/execution/func2.012
-rw-r--r--bin/sh/tests/execution/func3.07
-rw-r--r--bin/sh/tests/execution/hash1.012
-rw-r--r--bin/sh/tests/execution/int-cmd1.03
-rw-r--r--bin/sh/tests/execution/killed1.08
-rw-r--r--bin/sh/tests/execution/killed2.010
-rw-r--r--bin/sh/tests/execution/not1.04
-rw-r--r--bin/sh/tests/execution/not2.06
-rw-r--r--bin/sh/tests/execution/path1.015
-rw-r--r--bin/sh/tests/execution/redir1.027
-rw-r--r--bin/sh/tests/execution/redir2.029
-rw-r--r--bin/sh/tests/execution/redir3.03
-rw-r--r--bin/sh/tests/execution/redir4.04
-rw-r--r--bin/sh/tests/execution/redir5.03
-rw-r--r--bin/sh/tests/execution/redir6.021
-rw-r--r--bin/sh/tests/execution/redir7.021
-rw-r--r--bin/sh/tests/execution/set-n1.07
-rw-r--r--bin/sh/tests/execution/set-n2.05
-rw-r--r--bin/sh/tests/execution/set-n3.04
-rw-r--r--bin/sh/tests/execution/set-n4.03
-rw-r--r--bin/sh/tests/execution/set-x1.08
-rw-r--r--bin/sh/tests/execution/set-x2.09
-rw-r--r--bin/sh/tests/execution/set-x3.09
-rw-r--r--bin/sh/tests/execution/shellproc1.011
-rw-r--r--bin/sh/tests/execution/subshell1.06
-rw-r--r--bin/sh/tests/execution/subshell1.0.stdout2
-rw-r--r--bin/sh/tests/execution/subshell2.010
-rw-r--r--bin/sh/tests/execution/subshell3.04
-rw-r--r--bin/sh/tests/execution/subshell4.03
-rw-r--r--bin/sh/tests/execution/unknown1.029
-rw-r--r--bin/sh/tests/execution/var-assign1.03
-rw-r--r--bin/sh/tests/expansion/Makefile86
-rw-r--r--bin/sh/tests/expansion/arith1.030
-rw-r--r--bin/sh/tests/expansion/arith10.035
-rw-r--r--bin/sh/tests/expansion/arith11.012
-rw-r--r--bin/sh/tests/expansion/arith12.04
-rw-r--r--bin/sh/tests/expansion/arith13.06
-rw-r--r--bin/sh/tests/expansion/arith2.077
-rw-r--r--bin/sh/tests/expansion/arith3.014
-rw-r--r--bin/sh/tests/expansion/arith4.020
-rw-r--r--bin/sh/tests/expansion/arith5.017
-rw-r--r--bin/sh/tests/expansion/arith6.016
-rw-r--r--bin/sh/tests/expansion/arith7.012
-rw-r--r--bin/sh/tests/expansion/arith8.04
-rw-r--r--bin/sh/tests/expansion/arith9.020
-rw-r--r--bin/sh/tests/expansion/assign1.037
-rw-r--r--bin/sh/tests/expansion/cmdsubst1.048
-rw-r--r--bin/sh/tests/expansion/cmdsubst10.051
-rw-r--r--bin/sh/tests/expansion/cmdsubst11.05
-rw-r--r--bin/sh/tests/expansion/cmdsubst12.06
-rw-r--r--bin/sh/tests/expansion/cmdsubst13.012
-rw-r--r--bin/sh/tests/expansion/cmdsubst14.05
-rw-r--r--bin/sh/tests/expansion/cmdsubst15.05
-rw-r--r--bin/sh/tests/expansion/cmdsubst16.05
-rw-r--r--bin/sh/tests/expansion/cmdsubst17.05
-rw-r--r--bin/sh/tests/expansion/cmdsubst2.043
-rw-r--r--bin/sh/tests/expansion/cmdsubst3.023
-rw-r--r--bin/sh/tests/expansion/cmdsubst4.04
-rw-r--r--bin/sh/tests/expansion/cmdsubst5.05
-rw-r--r--bin/sh/tests/expansion/cmdsubst6.053
-rw-r--r--bin/sh/tests/expansion/cmdsubst7.031
-rw-r--r--bin/sh/tests/expansion/cmdsubst8.017
-rw-r--r--bin/sh/tests/expansion/cmdsubst9.011
-rw-r--r--bin/sh/tests/expansion/export1.013
-rw-r--r--bin/sh/tests/expansion/export2.024
-rw-r--r--bin/sh/tests/expansion/export3.030
-rw-r--r--bin/sh/tests/expansion/heredoc1.025
-rw-r--r--bin/sh/tests/expansion/heredoc2.015
-rw-r--r--bin/sh/tests/expansion/ifs1.035
-rw-r--r--bin/sh/tests/expansion/ifs2.024
-rw-r--r--bin/sh/tests/expansion/ifs3.021
-rw-r--r--bin/sh/tests/expansion/ifs4.039
-rw-r--r--bin/sh/tests/expansion/length1.012
-rw-r--r--bin/sh/tests/expansion/length2.04
-rw-r--r--bin/sh/tests/expansion/length3.010
-rw-r--r--bin/sh/tests/expansion/length4.011
-rw-r--r--bin/sh/tests/expansion/length5.027
-rw-r--r--bin/sh/tests/expansion/length6.08
-rw-r--r--bin/sh/tests/expansion/length7.014
-rw-r--r--bin/sh/tests/expansion/length8.014
-rw-r--r--bin/sh/tests/expansion/local1.028
-rw-r--r--bin/sh/tests/expansion/local2.034
-rw-r--r--bin/sh/tests/expansion/pathname1.061
-rw-r--r--bin/sh/tests/expansion/pathname2.031
-rw-r--r--bin/sh/tests/expansion/pathname3.029
-rw-r--r--bin/sh/tests/expansion/pathname4.028
-rw-r--r--bin/sh/tests/expansion/plus-minus1.076
-rw-r--r--bin/sh/tests/expansion/plus-minus2.04
-rw-r--r--bin/sh/tests/expansion/plus-minus3.044
-rw-r--r--bin/sh/tests/expansion/plus-minus4.038
-rw-r--r--bin/sh/tests/expansion/plus-minus5.031
-rw-r--r--bin/sh/tests/expansion/plus-minus6.034
-rw-r--r--bin/sh/tests/expansion/plus-minus7.026
-rw-r--r--bin/sh/tests/expansion/plus-minus8.05
-rw-r--r--bin/sh/tests/expansion/question1.022
-rw-r--r--bin/sh/tests/expansion/readonly1.07
-rw-r--r--bin/sh/tests/expansion/set-u1.029
-rw-r--r--bin/sh/tests/expansion/set-u2.012
-rw-r--r--bin/sh/tests/expansion/set-u3.06
-rw-r--r--bin/sh/tests/expansion/tilde1.056
-rw-r--r--bin/sh/tests/expansion/tilde2.090
-rw-r--r--bin/sh/tests/expansion/trim1.085
-rw-r--r--bin/sh/tests/expansion/trim2.055
-rw-r--r--bin/sh/tests/expansion/trim3.046
-rw-r--r--bin/sh/tests/expansion/trim4.015
-rw-r--r--bin/sh/tests/expansion/trim5.028
-rw-r--r--bin/sh/tests/expansion/trim6.022
-rw-r--r--bin/sh/tests/expansion/trim7.016
-rw-r--r--bin/sh/tests/expansion/trim8.075
-rw-r--r--bin/sh/tests/legacy_test.sh46
-rw-r--r--bin/sh/tests/parameters/Makefile19
-rw-r--r--bin/sh/tests/parameters/env1.011
-rw-r--r--bin/sh/tests/parameters/exitstatus1.09
-rw-r--r--bin/sh/tests/parameters/mail1.015
-rw-r--r--bin/sh/tests/parameters/mail2.015
-rw-r--r--bin/sh/tests/parameters/optind1.03
-rw-r--r--bin/sh/tests/parameters/optind2.03
-rw-r--r--bin/sh/tests/parameters/positional1.013
-rw-r--r--bin/sh/tests/parameters/positional2.065
-rw-r--r--bin/sh/tests/parameters/pwd1.011
-rw-r--r--bin/sh/tests/parameters/pwd2.024
-rw-r--r--bin/sh/tests/parser/Makefile64
-rw-r--r--bin/sh/tests/parser/alias1.05
-rw-r--r--bin/sh/tests/parser/alias10.09
-rw-r--r--bin/sh/tests/parser/alias11.06
-rw-r--r--bin/sh/tests/parser/alias12.06
-rw-r--r--bin/sh/tests/parser/alias13.06
-rw-r--r--bin/sh/tests/parser/alias14.06
-rw-r--r--bin/sh/tests/parser/alias15.012
-rw-r--r--bin/sh/tests/parser/alias15.0.stdout4
-rw-r--r--bin/sh/tests/parser/alias2.06
-rw-r--r--bin/sh/tests/parser/alias3.06
-rw-r--r--bin/sh/tests/parser/alias4.05
-rw-r--r--bin/sh/tests/parser/alias5.05
-rw-r--r--bin/sh/tests/parser/alias6.06
-rw-r--r--bin/sh/tests/parser/alias7.04
-rw-r--r--bin/sh/tests/parser/alias8.04
-rw-r--r--bin/sh/tests/parser/alias9.06
-rw-r--r--bin/sh/tests/parser/and-pipe-not.02
-rw-r--r--bin/sh/tests/parser/case1.014
-rw-r--r--bin/sh/tests/parser/case2.032
-rw-r--r--bin/sh/tests/parser/dollar-quote1.012
-rw-r--r--bin/sh/tests/parser/dollar-quote10.010
-rw-r--r--bin/sh/tests/parser/dollar-quote11.08
-rw-r--r--bin/sh/tests/parser/dollar-quote2.05
-rw-r--r--bin/sh/tests/parser/dollar-quote3.022
-rw-r--r--bin/sh/tests/parser/dollar-quote4.019
-rw-r--r--bin/sh/tests/parser/dollar-quote5.012
-rw-r--r--bin/sh/tests/parser/dollar-quote6.05
-rw-r--r--bin/sh/tests/parser/dollar-quote7.06
-rw-r--r--bin/sh/tests/parser/dollar-quote8.011
-rw-r--r--bin/sh/tests/parser/dollar-quote9.08
-rw-r--r--bin/sh/tests/parser/empty-braces1.07
-rw-r--r--bin/sh/tests/parser/empty-cmd1.03
-rw-r--r--bin/sh/tests/parser/for1.029
-rw-r--r--bin/sh/tests/parser/for2.015
-rw-r--r--bin/sh/tests/parser/func1.025
-rw-r--r--bin/sh/tests/parser/func2.06
-rw-r--r--bin/sh/tests/parser/func3.06
-rw-r--r--bin/sh/tests/parser/heredoc1.085
-rw-r--r--bin/sh/tests/parser/heredoc10.049
-rw-r--r--bin/sh/tests/parser/heredoc11.026
-rw-r--r--bin/sh/tests/parser/heredoc2.039
-rw-r--r--bin/sh/tests/parser/heredoc3.07
-rw-r--r--bin/sh/tests/parser/heredoc4.044
-rw-r--r--bin/sh/tests/parser/heredoc5.056
-rw-r--r--bin/sh/tests/parser/heredoc6.05
-rw-r--r--bin/sh/tests/parser/heredoc7.019
-rw-r--r--bin/sh/tests/parser/heredoc8.020
-rw-r--r--bin/sh/tests/parser/heredoc9.058
-rw-r--r--bin/sh/tests/parser/no-space1.018
-rw-r--r--bin/sh/tests/parser/no-space2.07
-rw-r--r--bin/sh/tests/parser/only-redir1.03
-rw-r--r--bin/sh/tests/parser/only-redir2.02
-rw-r--r--bin/sh/tests/parser/only-redir3.02
-rw-r--r--bin/sh/tests/parser/only-redir4.02
-rw-r--r--bin/sh/tests/parser/pipe-not1.03
-rw-r--r--bin/sh/tests/parser/var-assign1.019
-rw-r--r--bin/sh/tests/set-e/Makefile44
-rw-r--r--bin/sh/tests/set-e/and1.03
-rw-r--r--bin/sh/tests/set-e/and2.14
-rw-r--r--bin/sh/tests/set-e/and3.04
-rw-r--r--bin/sh/tests/set-e/and4.04
-rw-r--r--bin/sh/tests/set-e/background1.03
-rw-r--r--bin/sh/tests/set-e/cmd1.03
-rw-r--r--bin/sh/tests/set-e/cmd2.14
-rw-r--r--bin/sh/tests/set-e/elif1.07
-rw-r--r--bin/sh/tests/set-e/elif2.07
-rw-r--r--bin/sh/tests/set-e/eval1.03
-rw-r--r--bin/sh/tests/set-e/eval2.14
-rw-r--r--bin/sh/tests/set-e/for1.09
-rw-r--r--bin/sh/tests/set-e/func1.07
-rw-r--r--bin/sh/tests/set-e/func2.17
-rw-r--r--bin/sh/tests/set-e/if1.05
-rw-r--r--bin/sh/tests/set-e/if2.07
-rw-r--r--bin/sh/tests/set-e/if3.05
-rw-r--r--bin/sh/tests/set-e/not1.04
-rw-r--r--bin/sh/tests/set-e/not2.04
-rw-r--r--bin/sh/tests/set-e/or1.03
-rw-r--r--bin/sh/tests/set-e/or2.03
-rw-r--r--bin/sh/tests/set-e/or3.14
-rw-r--r--bin/sh/tests/set-e/pipe1.14
-rw-r--r--bin/sh/tests/set-e/pipe2.03
-rw-r--r--bin/sh/tests/set-e/return1.011
-rw-r--r--bin/sh/tests/set-e/semi1.14
-rw-r--r--bin/sh/tests/set-e/semi2.14
-rw-r--r--bin/sh/tests/set-e/subshell1.03
-rw-r--r--bin/sh/tests/set-e/subshell2.14
-rw-r--r--bin/sh/tests/set-e/until1.05
-rw-r--r--bin/sh/tests/set-e/until2.05
-rw-r--r--bin/sh/tests/set-e/until3.09
-rw-r--r--bin/sh/tests/set-e/while1.05
-rw-r--r--bin/sh/tests/set-e/while2.05
-rw-r--r--bin/sh/tests/set-e/while3.09
-rw-r--r--bin/sh/trap.c19
-rw-r--r--bin/sh/trap.h1
-rw-r--r--bin/sh/var.c24
-rw-r--r--bin/sh/var.h1
-rw-r--r--bin/test/Makefile6
-rw-r--r--bin/test/tests/Makefile15
-rw-r--r--bin/test/tests/legacy_test.sh196
-rw-r--r--bin/tests/Makefile10
449 files changed, 7596 insertions, 219 deletions
diff --git a/bin/Makefile b/bin/Makefile
index 7b6a4ab..e4bc281 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -52,6 +52,10 @@ SUBDIR+= rmail
SUBDIR+= csh
.endif
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.arch.inc.mk>
SUBDIR:= ${SUBDIR:O}
diff --git a/bin/date/Makefile b/bin/date/Makefile
index 4f5c3fe..6da5848 100644
--- a/bin/date/Makefile
+++ b/bin/date/Makefile
@@ -1,7 +1,13 @@
# @(#)Makefile 8.1 (Berkeley) 5/31/93
# $FreeBSD$
+.include <bsd.own.mk>
+
PROG= date
SRCS= date.c netdate.c vary.c
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
diff --git a/bin/date/tests/Makefile b/bin/date/tests/Makefile
new file mode 100644
index 0000000..540008b
--- /dev/null
+++ b/bin/date/tests/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin/date
+
+TAP_TESTS_SH= legacy_test
+
+.include <tap.test.mk>
diff --git a/bin/date/tests/legacy_test.sh b/bin/date/tests/legacy_test.sh
new file mode 100644
index 0000000..981bdd0
--- /dev/null
+++ b/bin/date/tests/legacy_test.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+#
+# Regression tests for date(1)
+#
+# Submitted by Edwin Groothuis <edwin@FreeBSD.org>
+#
+# $FreeBSD$
+#
+
+#
+# These two date/times have been chosen carefully, they
+# create both the single digit and double/multidigit version of
+# the values.
+#
+# To create a new one, make sure you are using the UTC timezone!
+#
+
+TEST1=3222243 # 1970-02-07 07:04:03
+TEST2=1005600000 # 2001-11-12 21:11:12
+
+export LC_ALL=C
+export TZ=UTC
+count=0
+
+check()
+{
+ S=$1
+ A1=$2
+ A2=$3
+
+ count=`expr ${count} + 1`
+
+ if [ -z "${A2}" ]; then A2=${A1}; fi
+
+ R=`date -r ${TEST1} +%${S}`
+ if [ "${R}" = "${A1}" ]; then
+ echo "ok ${S}{t1}"
+ else
+ echo "no ok ${S}{t1} - (got ${R}, expected ${A1})"
+ fi
+
+ R=`date -r ${TEST2} +%${S}`
+ if [ "${R}" = "${A2}" ]; then
+ echo "ok ${S}{t2}"
+ else
+ echo "no ok ${S}{t2} - (got ${R}, expected ${A2})"
+ fi
+}
+
+echo "1..78"
+
+check A Saturday Monday
+check a Sat Mon
+check B February November
+check b Feb Nov
+check C 19 20
+check c "Sat Feb 7 07:04:03 1970" "Mon Nov 12 21:20:00 2001"
+check D 02/07/70 11/12/01
+check d 07 12
+check e " 7" 12
+check F "1970-02-07" "2001-11-12"
+check G 1970 2001
+check g 70 01
+check H 07 21
+check h Feb Nov
+check I 07 09
+check j 038 316
+check k " 7" 21
+check l " 7" " 9"
+check M 04 20
+check m 02 11
+check p AM PM
+check R 07:04 21:20
+check r "07:04:03 AM" "09:20:00 PM"
+check S 03 00
+check s ${TEST1} ${TEST2}
+check U 05 45
+check u 6 1
+check V 06 46
+check v " 7-Feb-1970" "12-Nov-2001"
+check W 05 46
+check w 6 1
+check X "07:04:03" "21:20:00"
+check x "02/07/70" "11/12/01"
+check Y 1970 2001
+check y 70 01
+check Z UTC UTC
+check z +0000 +0000
+check % % %
+check + "Sat Feb 7 07:04:03 UTC 1970" "Mon Nov 12 21:20:00 UTC 2001"
diff --git a/bin/mv/Makefile b/bin/mv/Makefile
index 8405782..ad8cc4f 100644
--- a/bin/mv/Makefile
+++ b/bin/mv/Makefile
@@ -1,6 +1,12 @@
# @(#)Makefile 8.2 (Berkeley) 4/2/94
# $FreeBSD$
+.include <bsd.own.mk>
+
PROG= mv
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
diff --git a/bin/mv/tests/Makefile b/bin/mv/tests/Makefile
new file mode 100644
index 0000000..051a3b6
--- /dev/null
+++ b/bin/mv/tests/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin/mv
+
+TAP_TESTS_SH= legacy_test
+
+.include <tap.test.mk>
diff --git a/bin/mv/tests/legacy_test.sh b/bin/mv/tests/legacy_test.sh
new file mode 100644
index 0000000..d0a5e83
--- /dev/null
+++ b/bin/mv/tests/legacy_test.sh
@@ -0,0 +1,296 @@
+#!/bin/sh
+# $FreeBSD$
+
+# A directory in a device different from that where the tests are run
+TMPDIR=/tmp/regress.$$
+COUNT=0
+
+# Begin an individual test
+begin()
+{
+ COUNT=`expr $COUNT + 1`
+ OK=1
+ if [ -z "$FS" ]
+ then
+ NAME="$1"
+ else
+ NAME="$1 (cross device)"
+ fi
+ rm -rf testdir $TMPDIR/testdir
+ mkdir -p testdir $TMPDIR/testdir
+ cd testdir
+}
+
+# End an individual test
+end()
+{
+ if [ $OK = 1 ]
+ then
+ printf 'ok '
+ else
+ printf 'not ok '
+ fi
+ echo "$COUNT - $NAME"
+ cd ..
+ rm -rf testdir $TMPDIR/testdir
+}
+
+# Make a file that can later be verified
+mkf()
+{
+ CN=`basename $1`
+ echo "$CN-$CN" >$1
+}
+
+# Verify that the file specified is correct
+ckf()
+{
+ if [ -f $2 ] && echo "$1-$1" | diff - $2 >/dev/null
+ then
+ ok
+ else
+ notok
+ fi
+}
+
+# Make a fifo that can later be verified
+mkp()
+{
+ mkfifo $1
+}
+
+# Verify that the file specified is correct
+ckp()
+{
+ if [ -p $2 ]
+ then
+ ok
+ else
+ notok
+ fi
+}
+
+# Make a directory that can later be verified
+mkd()
+{
+ CN=`basename $1`
+ mkdir -p $1/"$CN-$CN"
+}
+
+# Verify that the directory specified is correct
+ckd()
+{
+ if [ -d $2/$1-$1 ]
+ then
+ ok
+ else
+ notok
+ fi
+}
+
+# Verify that the specified file does not exist
+# (is not there)
+cknt()
+{
+ if [ -r $1 ]
+ then
+ notok
+ else
+ ok
+ fi
+}
+
+# A part of a test succeeds
+ok()
+{
+ :
+}
+
+# A part of a test fails
+notok()
+{
+ OK=0
+}
+
+# Verify that the exit code passed is for unsuccessful termination
+ckfail()
+{
+ if [ $1 -gt 0 ]
+ then
+ ok
+ else
+ notok
+ fi
+}
+
+# Verify that the exit code passed is for successful termination
+ckok()
+{
+ if [ $1 -eq 0 ]
+ then
+ ok
+ else
+ notok
+ fi
+}
+
+# Run all tests locally and across devices
+echo 1..32
+for FS in '' $TMPDIR/testdir/
+do
+ begin 'Rename file'
+ mkf fa
+ mv fa ${FS}fb
+ ckok $?
+ ckf fa ${FS}fb
+ cknt fa
+ end
+
+ begin 'Move files into directory'
+ mkf fa
+ mkf fb
+ mkdir -p ${FS}1/2/3
+ mv fa fb ${FS}1/2/3
+ ckok $?
+ ckf fa ${FS}1/2/3/fa
+ ckf fb ${FS}1/2/3/fb
+ cknt fa
+ cknt fb
+ end
+
+ begin 'Move file from directory to file'
+ mkdir -p 1/2/3
+ mkf 1/2/3/fa
+ mv 1/2/3/fa ${FS}fb
+ ckok $?
+ ckf fa ${FS}fb
+ cknt 1/2/3/fa
+ end
+
+ begin 'Move file from directory to existing file'
+ mkdir -p 1/2/3
+ mkf 1/2/3/fa
+ :> ${FS}fb
+ mv 1/2/3/fa ${FS}fb
+ ckok $?
+ ckf fa ${FS}fb
+ cknt 1/2/3/fa
+ end
+
+ begin 'Move file from directory to existing directory'
+ mkdir -p 1/2/3
+ mkf 1/2/3/fa
+ mkdir -p ${FS}db/fa
+ # Should fail per POSIX step 3a:
+ # Destination path is a file of type directory and
+ # source_file is not a file of type directory
+ mv 1/2/3/fa ${FS}db 2>/dev/null
+ ckfail $?
+ ckf fa 1/2/3/fa
+ end
+
+ begin 'Move file from directory to directory'
+ mkdir -p da1/da2/da3
+ mkdir -p ${FS}db1/db2/db3
+ mkf da1/da2/da3/fa
+ mv da1/da2/da3/fa ${FS}db1/db2/db3/fb
+ ckok $?
+ ckf fa ${FS}db1/db2/db3/fb
+ cknt da1/da2/da3/fa
+ end
+
+ begin 'Rename directory'
+ mkd da
+ mv da ${FS}db
+ ckok $?
+ ckd da ${FS}db
+ cknt da
+ end
+
+ begin 'Move directory to directory name'
+ mkd da1/da2/da3/da
+ mkdir -p ${FS}db1/db2/db3
+ mv da1/da2/da3/da ${FS}db1/db2/db3/db
+ ckok $?
+ ckd da ${FS}db1/db2/db3/db
+ cknt da1/da2/da3/da
+ end
+
+ begin 'Move directory to directory'
+ mkd da1/da2/da3/da
+ mkdir -p ${FS}db1/db2/db3
+ mv da1/da2/da3/da ${FS}db1/db2/db3
+ ckok $?
+ ckd da ${FS}db1/db2/db3/da
+ cknt da1/da2/da3/da
+ end
+
+ begin 'Move directory to existing empty directory'
+ mkd da1/da2/da3/da
+ mkdir -p ${FS}db1/db2/db3/da
+ mv da1/da2/da3/da ${FS}db1/db2/db3
+ ckok $?
+ ckd da ${FS}db1/db2/db3/da
+ cknt da1/da2/da3/da
+ end
+
+ begin 'Move directory to existing non-empty directory'
+ mkd da1/da2/da3/da
+ mkdir -p ${FS}db1/db2/db3/da/full
+ # Should fail (per the semantics of rename(2))
+ mv da1/da2/da3/da ${FS}db1/db2/db3 2>/dev/null
+ ckfail $?
+ ckd da da1/da2/da3/da
+ end
+
+ begin 'Move directory to existing file'
+ mkd da1/da2/da3/da
+ mkdir -p ${FS}db1/db2/db3
+ :> ${FS}db1/db2/db3/da
+ # Should fail per POSIX step 3b:
+ # Destination path is a file not of type directory
+ # and source_file is a file of type directory
+ mv da1/da2/da3/da ${FS}db1/db2/db3/da 2>/dev/null
+ ckfail $?
+ ckd da da1/da2/da3/da
+ end
+
+ begin 'Rename fifo'
+ mkp fa
+ mv fa ${FS}fb
+ ckok $?
+ ckp fa ${FS}fb
+ cknt fa
+ end
+
+ begin 'Move fifos into directory'
+ mkp fa
+ mkp fb
+ mkdir -p ${FS}1/2/3
+ mv fa fb ${FS}1/2/3
+ ckok $?
+ ckp fa ${FS}1/2/3/fa
+ ckp fb ${FS}1/2/3/fb
+ cknt fa
+ cknt fb
+ end
+
+ begin 'Move fifo from directory to fifo'
+ mkdir -p 1/2/3
+ mkp 1/2/3/fa
+ mv 1/2/3/fa ${FS}fb
+ ckok $?
+ ckp fa ${FS}fb
+ cknt 1/2/3/fa
+ end
+
+ begin 'Move fifo from directory to directory'
+ mkdir -p da1/da2/da3
+ mkdir -p ${FS}db1/db2/db3
+ mkp da1/da2/da3/fa
+ mv da1/da2/da3/fa ${FS}db1/db2/db3/fb
+ ckok $?
+ ckp fa ${FS}db1/db2/db3/fb
+ cknt da1/da2/da3/fa
+ end
+done
diff --git a/bin/pax/Makefile b/bin/pax/Makefile
index 9e8d4e1..95f18bb 100644
--- a/bin/pax/Makefile
+++ b/bin/pax/Makefile
@@ -1,6 +1,8 @@
# @(#)Makefile 8.1 (Berkeley) 5/31/93
# $FreeBSD$
+.include <bsd.own.mk>
+
# To install on versions prior to BSD 4.4 the following may have to be
# defined with CFLAGS +=
#
@@ -30,4 +32,8 @@ SRCS= ar_io.c ar_subs.c buf_subs.c cache.c cpio.c file_subs.c ftree.c \
gen_subs.c getoldopt.c options.c pat_rep.c pax.c sel_subs.c \
tables.c tar.c tty_subs.c
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
diff --git a/bin/pax/tests/Makefile b/bin/pax/tests/Makefile
new file mode 100644
index 0000000..f4f81ee
--- /dev/null
+++ b/bin/pax/tests/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin/pax
+
+TAP_TESTS_PERL= legacy_test
+
+.include <tap.test.mk>
diff --git a/bin/pax/tests/legacy_test.pl b/bin/pax/tests/legacy_test.pl
new file mode 100644
index 0000000..dabba42
--- /dev/null
+++ b/bin/pax/tests/legacy_test.pl
@@ -0,0 +1,89 @@
+# $FreeBSD$
+
+use strict;
+use warnings;
+
+use Test::More tests => 6;
+use File::Path qw(rmtree mkpath);
+use Cwd;
+
+my $n = 0;
+sub create_file {
+ my $fn = shift;
+
+ $n++;
+ (my $dir = $fn) =~ s,/[^/]+$,,;
+ mkpath $dir;
+ open my $fd, ">", $fn or die "$fn: $!";
+ print $fd "file $n\n";
+}
+
+
+ustar_pathnames: { SKIP: {
+ # Prove that pax breaks up ustar pathnames properly
+
+ my $top = getcwd . "/ustar-pathnames-1";
+ skip "Current path is too long", 6 if length $top > 92;
+ rmtree $top;
+ my $subdir = "x" . "x" x (92 - length $top);
+ my $work94 = "$top/$subdir";
+ mkpath $work94; # $work is 94 characters long
+
+ my $x49 = "x" x 49;
+ my $x50 = "x" x 50;
+ my $x60 = "x" x 60;
+ my $x95 = "x" x 95;
+
+ my @paths = (
+ "$work94/x099", # 99 chars
+ "$work94/xx100", # 100 chars
+ "$work94/xxx101", # 101 chars
+ "$work94/$x49/${x50}x199", # 199 chars
+ "$work94/$x49/${x50}xx200", # 200 chars
+ "$work94/$x49/${x50}xxx201", # 201 chars
+ "$work94/$x60/${x95}254", # 254 chars
+ "$work94/$x60/${x95}x255", # 255 chars
+ );
+
+ my @l = map { length } @paths;
+
+ my $n = 0;
+ create_file $_ for @paths;
+ system "pax -wf ustar.ok $work94";
+ ok($? == 0, "Wrote 'ustar.ok' containing files with lengths @l");
+
+ (my $orig = $top) =~ s,1$,2,;
+ rmtree $orig;
+ rename $top, $orig;
+
+ system "pax -rf ustar.ok";
+ ok($? == 0, "Restored 'ustar.ok' containing files with lengths @l");
+
+ system "diff -ru $orig $top";
+ ok($? == 0, "Restored files are identical");
+
+ rmtree $top;
+ rename $orig, $top;
+
+ # 256 chars (with components < 100 chars) should not work
+ push @paths, "$work94/x$x60/${x95}x256"; # 256 chars
+ push @l, length $paths[-1];
+ create_file $paths[-1];
+ system "pax -wf ustar.fail1 $work94";
+ ok($?, "Failed to write 'ustar.fail1' containing files with lengths @l");
+
+ # Components with 100 chars shouldn't work
+ unlink $paths[-1];
+ $paths[-1] = "$work94/${x95}xc100"; # 100 char filename
+ $l[-1] = length $paths[-1];
+ create_file $paths[-1];
+ system "pax -wf ustar.fail2 $work94";
+ ok($?, "Failed to write 'ustar.fail2' with a 100 char filename");
+
+ unlink $paths[-1];
+ $paths[-1] = "$work94/${x95}xc100/x"; # 100 char component
+ $l[-1] = length $paths[-1];
+ create_file $paths[-1];
+ system "pax -wf ustar.fail3 $work94";
+ ok($?, "Failed to write 'ustar.fail3' with a 100 char component");
+}}
diff --git a/bin/sh/Makefile b/bin/sh/Makefile
index 00d34fa..e1ce5fe 100644
--- a/bin/sh/Makefile
+++ b/bin/sh/Makefile
@@ -1,6 +1,8 @@
# @(#)Makefile 8.4 (Berkeley) 5/5/95
# $FreeBSD$
+.include <bsd.own.mk>
+
PROG= sh
INSTALLFLAGS= -S
SHSRCS= alias.c arith_yacc.c arith_yylex.c cd.c echo.c error.c eval.c \
@@ -59,7 +61,8 @@ syntax.c syntax.h: mksyntax
token.h: mktokens
sh ${.CURDIR}/mktokens
-regress:
- cd ${.CURDIR}/../../tools/regression/bin/sh && ${MAKE} SH=${.OBJDIR}/sh
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
.include <bsd.prog.mk>
diff --git a/bin/sh/alias.c b/bin/sh/alias.c
index 044c869..9ebdcb5 100644
--- a/bin/sh/alias.c
+++ b/bin/sh/alias.c
@@ -68,18 +68,7 @@ setalias(const char *name, const char *val)
if (equal(name, ap->name)) {
INTOFF;
ckfree(ap->val);
- /* See HACK below. */
-#ifdef notyet
ap->val = savestr(val);
-#else
- {
- size_t len = strlen(val);
- ap->val = ckmalloc(len + 2);
- memcpy(ap->val, val, len);
- ap->val[len] = ' ';
- ap->val[len+1] = '\0';
- }
-#endif
INTON;
return;
}
@@ -88,34 +77,7 @@ setalias(const char *name, const char *val)
INTOFF;
ap = ckmalloc(sizeof (struct alias));
ap->name = savestr(name);
- /*
- * XXX - HACK: in order that the parser will not finish reading the
- * alias value off the input before processing the next alias, we
- * dummy up an extra space at the end of the alias. This is a crock
- * and should be re-thought. The idea (if you feel inclined to help)
- * is to avoid alias recursions. The mechanism used is: when
- * expanding an alias, the value of the alias is pushed back on the
- * input as a string and a pointer to the alias is stored with the
- * string. The alias is marked as being in use. When the input
- * routine finishes reading the string, it marks the alias not
- * in use. The problem is synchronization with the parser. Since
- * it reads ahead, the alias is marked not in use before the
- * resulting token(s) is next checked for further alias sub. The
- * H A C K is that we add a little fluff after the alias value
- * so that the string will not be exhausted. This is a good
- * idea ------- ***NOT***
- */
-#ifdef notyet
ap->val = savestr(val);
-#else /* hack */
- {
- size_t len = strlen(val);
- ap->val = ckmalloc(len + 2);
- memcpy(ap->val, val, len);
- ap->val[len] = ' '; /* fluff */
- ap->val[len+1] = '\0';
- }
-#endif
ap->flag = 0;
ap->next = *app;
*app = ap;
@@ -207,14 +169,8 @@ comparealiases(const void *p1, const void *p2)
static void
printalias(const struct alias *a)
{
- char *p;
-
out1fmt("%s=", a->name);
- /* Don't print the space added above. */
- p = a->val + strlen(a->val) - 1;
- *p = '\0';
out1qstr(a->val);
- *p = ' ';
out1c('\n');
}
diff --git a/bin/sh/cd.c b/bin/sh/cd.c
index fa6f492..7720fad 100644
--- a/bin/sh/cd.c
+++ b/bin/sh/cd.c
@@ -182,6 +182,7 @@ cdlogical(char *dest)
struct stat statb;
int first;
int badstat;
+ size_t len;
/*
* Check each component of the path. If we find a symlink or
@@ -189,8 +190,9 @@ cdlogical(char *dest)
* next time we get the value of the current directory.
*/
badstat = 0;
- cdcomppath = stalloc(strlen(dest) + 1);
- scopy(dest, cdcomppath);
+ len = strlen(dest);
+ cdcomppath = stalloc(len + 1);
+ memcpy(cdcomppath, dest, len + 1);
STARTSTACKSTR(p);
if (*dest == '/') {
STPUTC('/', p);
@@ -275,6 +277,7 @@ findcwd(char *dir)
{
char *new;
char *p;
+ size_t len;
/*
* If our argument is NULL, we don't know the current directory
@@ -283,8 +286,9 @@ findcwd(char *dir)
*/
if (dir == NULL || curdir == NULL)
return getpwd2();
- cdcomppath = stalloc(strlen(dir) + 1);
- scopy(dir, cdcomppath);
+ len = strlen(dir);
+ cdcomppath = stalloc(len + 1);
+ memcpy(cdcomppath, dir, len + 1);
STARTSTACKSTR(new);
if (*dir != '/') {
STPUTS(curdir, new);
diff --git a/bin/sh/eval.c b/bin/sh/eval.c
index b8f76d5..4f7559e 100644
--- a/bin/sh/eval.c
+++ b/bin/sh/eval.c
@@ -750,6 +750,45 @@ isdeclarationcmd(struct narg *arg)
(have_command || !isfunc("local"))));
}
+static void
+xtracecommand(struct arglist *varlist, struct arglist *arglist)
+{
+ struct strlist *sp;
+ char sep = 0;
+ const char *p, *ps4;
+
+ ps4 = expandstr(ps4val());
+ out2str(ps4 != NULL ? ps4 : ps4val());
+ for (sp = varlist->list ; sp ; sp = sp->next) {
+ if (sep != 0)
+ out2c(' ');
+ p = strchr(sp->text, '=');
+ if (p != NULL) {
+ p++;
+ outbin(sp->text, p - sp->text, out2);
+ out2qstr(p);
+ } else
+ out2qstr(sp->text);
+ sep = ' ';
+ }
+ for (sp = arglist->list ; sp ; sp = sp->next) {
+ if (sep != 0)
+ out2c(' ');
+ /* Disambiguate command looking like assignment. */
+ if (sp == arglist->list &&
+ strchr(sp->text, '=') != NULL &&
+ strchr(sp->text, '\'') == NULL) {
+ out2c('\'');
+ out2str(sp->text);
+ out2c('\'');
+ } else
+ out2qstr(sp->text);
+ sep = ' ';
+ }
+ out2c('\n');
+ flushout(&errout);
+}
+
/*
* Check if a builtin can safely be executed in the same process,
* even though it should be in a subshell (command substitution).
@@ -847,40 +886,8 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
argv -= argc;
/* Print the command if xflag is set. */
- if (xflag) {
- char sep = 0;
- const char *p, *ps4;
- ps4 = expandstr(ps4val());
- out2str(ps4 != NULL ? ps4 : ps4val());
- for (sp = varlist.list ; sp ; sp = sp->next) {
- if (sep != 0)
- out2c(' ');
- p = strchr(sp->text, '=');
- if (p != NULL) {
- p++;
- outbin(sp->text, p - sp->text, out2);
- out2qstr(p);
- } else
- out2qstr(sp->text);
- sep = ' ';
- }
- for (sp = arglist.list ; sp ; sp = sp->next) {
- if (sep != 0)
- out2c(' ');
- /* Disambiguate command looking like assignment. */
- if (sp == arglist.list &&
- strchr(sp->text, '=') != NULL &&
- strchr(sp->text, '\'') == NULL) {
- out2c('\'');
- out2str(sp->text);
- out2c('\'');
- } else
- out2qstr(sp->text);
- sep = ' ';
- }
- out2c('\n');
- flushout(&errout);
- }
+ if (xflag)
+ xtracecommand(&varlist, &arglist);
/* Now locate the command. */
if (argc == 0) {
diff --git a/bin/sh/exec.c b/bin/sh/exec.c
index 9f8e029..5f30ec6 100644
--- a/bin/sh/exec.c
+++ b/bin/sh/exec.c
@@ -187,14 +187,15 @@ padvance(const char **path, const char *name)
{
const char *p, *start;
char *q;
- size_t len;
+ size_t len, namelen;
if (*path == NULL)
return NULL;
start = *path;
for (p = start; *p && *p != ':' && *p != '%'; p++)
; /* nothing */
- len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
+ namelen = strlen(name);
+ len = p - start + namelen + 2; /* "2" is for '/' and '\0' */
STARTSTACKSTR(q);
CHECKSTRSPACE(len, q);
if (p != start) {
@@ -202,7 +203,7 @@ padvance(const char **path, const char *name)
q += p - start;
*q++ = '/';
}
- strcpy(q, name);
+ memcpy(q, name, namelen + 1);
pathopt = NULL;
if (*p == '%') {
pathopt = ++p;
@@ -527,6 +528,7 @@ cmdlookup(const char *name, int add)
const char *p;
struct tblentry *cmdp;
struct tblentry **pp;
+ size_t len;
p = name;
hashval = *p << 4;
@@ -541,11 +543,11 @@ cmdlookup(const char *name, int add)
}
if (add && cmdp == NULL) {
INTOFF;
- cmdp = *pp = ckmalloc(sizeof (struct tblentry)
- + strlen(name) + 1);
+ len = strlen(name);
+ cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
cmdp->next = NULL;
cmdp->cmdtype = CMDUNKNOWN;
- strcpy(cmdp->cmdname, name);
+ memcpy(cmdp->cmdname, name, len + 1);
INTON;
}
lastcmdentry = pp;
@@ -672,9 +674,11 @@ typecmd_impl(int argc, char **argv, int cmd, const char *path)
/* Then look at the aliases */
if ((ap = lookupalias(argv[i], 1)) != NULL) {
- if (cmd == TYPECMD_SMALLV)
- out1fmt("alias %s='%s'\n", argv[i], ap->val);
- else
+ if (cmd == TYPECMD_SMALLV) {
+ out1fmt("alias %s=", argv[i]);
+ out1qstr(ap->val);
+ outcslow('\n', out1);
+ } else
out1fmt("%s is an alias for %s\n", argv[i],
ap->val);
continue;
diff --git a/bin/sh/expand.c b/bin/sh/expand.c
index 414bfa7..cad0116 100644
--- a/bin/sh/expand.c
+++ b/bin/sh/expand.c
@@ -100,6 +100,7 @@ static struct arglist exparg; /* holds expanded arg list */
static void argstr(char *, int);
static char *exptilde(char *, int);
+static char *expari(char *);
static void expbackq(union node *, int, int);
static int subevalvar(char *, char *, int, int, int, int, int);
static char *evalvar(char *, int);
@@ -206,7 +207,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
/*
* Perform parameter expansion, command substitution and arithmetic
* expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
- * Processing ends at a CTLENDVAR character as well as '\0'.
+ * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
* This is used to expand word in ${var+word} etc.
* If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
* characters to allow for further processing.
@@ -231,6 +232,7 @@ argstr(char *p, int flag)
switch (c = *p++) {
case '\0':
case CTLENDVAR:
+ case CTLENDARI:
goto breakloop;
case CTLQUOTEMARK:
lit_quoted = 1;
@@ -261,8 +263,8 @@ argstr(char *p, int flag)
expbackq(argbackq->n, c & CTLQUOTE, flag);
argbackq = argbackq->next;
break;
- case CTLENDARI:
- expari(flag);
+ case CTLARI:
+ p = expari(p);
break;
case ':':
case '=':
@@ -387,59 +389,56 @@ removerecordregions(int endoff)
}
/*
- * Expand arithmetic expression. Backup to start of expression,
- * evaluate, place result in (backed up) result, adjust string position.
+ * Expand arithmetic expression.
+ * Note that flag is not required as digits never require CTLESC characters.
*/
-void
-expari(int flag)
+static char *
+expari(char *p)
{
- char *p, *q, *start;
+ char *q, *start;
arith_t result;
int begoff;
- int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
int quoted;
+ int c;
+ int nesting;
+ int adj;
- /*
- * This routine is slightly over-complicated for
- * efficiency. First we make sure there is
- * enough space for the result, which may be bigger
- * than the expression. Next we
- * scan backwards looking for the start of arithmetic. If the
- * next previous character is a CTLESC character, then we
- * have to rescan starting from the beginning since CTLESC
- * characters have to be processed left to right.
- */
- CHECKSTRSPACE(DIGITS(result) - 2, expdest);
- USTPUTC('\0', expdest);
- start = stackblock();
- p = expdest - 2;
- while (p >= start && *p != CTLARI)
- --p;
- if (p < start || *p != CTLARI)
- error("missing CTLARI (shouldn't happen)");
- if (p > start && *(p - 1) == CTLESC)
- for (p = start; *p != CTLARI; p++)
- if (*p == CTLESC)
- p++;
-
- if (p[1] == '"')
- quoted=1;
- else
- quoted=0;
- begoff = p - start;
+ quoted = *p++ == '"';
+ begoff = expdest - stackblock();
+ argstr(p, 0);
removerecordregions(begoff);
- if (quotes)
- rmescapes(p+2);
+ STPUTC('\0', expdest);
+ start = stackblock() + begoff;
+
q = grabstackstr(expdest);
- result = arith(p+2);
+ result = arith(start);
ungrabstackstr(q, expdest);
- fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
- while (*p++)
- ;
- if (quoted == 0)
- recordregion(begoff, p - 1 - start, 0);
- result = expdest - p + 1;
- STADJUST(-result, expdest);
+
+ start = stackblock() + begoff;
+ adj = start - expdest;
+ STADJUST(adj, expdest);
+
+ CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest);
+ fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
+ adj = strlen(expdest);
+ STADJUST(adj, expdest);
+ if (!quoted)
+ recordregion(begoff, expdest - stackblock(), 0);
+ nesting = 1;
+ while (nesting > 0) {
+ c = *p++;
+ if (c == CTLESC)
+ p++;
+ else if (c == CTLARI)
+ nesting++;
+ else if (c == CTLENDARI)
+ nesting--;
+ else if (c == CTLVAR)
+ p++; /* ignore variable substitution byte */
+ else if (c == '\0')
+ return p - 1;
+ }
+ return p;
}
@@ -671,10 +670,8 @@ evalvar(char *p, int flag)
again: /* jump here after setting a variable with ${var=text} */
if (varflags & VSLINENO) {
set = 1;
- special = 0;
- val = var;
- p[-1] = '\0'; /* temporarily overwrite '=' to have \0
- terminated string */
+ special = 1;
+ val = NULL;
} else if (special) {
set = varisset(var, varflags & VSNUL);
val = NULL;
@@ -703,7 +700,10 @@ again: /* jump here after setting a variable with ${var=text} */
if (set && subtype != VSPLUS) {
/* insert the value of the variable */
if (special) {
- varvalue(var, varflags & VSQUOTE, subtype, flag);
+ if (varflags & VSLINENO)
+ STPUTBIN(var, p - var - 1, expdest);
+ else
+ varvalue(var, varflags & VSQUOTE, subtype, flag);
if (subtype == VSLENGTH) {
varlenb = expdest - stackblock() - startloc;
varlen = varlenb;
@@ -815,7 +815,6 @@ record:
default:
abort();
}
- p[-1] = '='; /* recover overwritten '=' */
if (subtype != VSNORMAL) { /* skip to end of alternative */
int nesting = 1;
@@ -1307,9 +1306,11 @@ addfname(char *name)
{
char *p;
struct strlist *sp;
+ size_t len;
- p = stalloc(strlen(name) + 1);
- scopy(name, p);
+ len = strlen(name);
+ p = stalloc(len + 1);
+ memcpy(p, name, len + 1);
sp = (struct strlist *)stalloc(sizeof *sp);
sp->text = p;
*exparg.lastp = sp;
diff --git a/bin/sh/expand.h b/bin/sh/expand.h
index 8d44321..7495a63 100644
--- a/bin/sh/expand.h
+++ b/bin/sh/expand.h
@@ -58,6 +58,5 @@ struct arglist {
union node;
void expandarg(union node *, struct arglist *, int);
-void expari(int);
void rmescapes(char *);
int casematch(union node *, const char *);
diff --git a/bin/sh/input.c b/bin/sh/input.c
index e46095d..05cc1eb 100644
--- a/bin/sh/input.c
+++ b/bin/sh/input.c
@@ -162,20 +162,16 @@ preadfd(void)
int nr;
parsenextc = parsefile->buf;
-#ifndef NO_HISTORY
- if (el != NULL && gotwinch) {
- gotwinch = 0;
- el_resize(el);
- }
-#endif
retry:
#ifndef NO_HISTORY
if (parsefile->fd == 0 && el) {
static const char *rl_cp;
static int el_len;
- if (rl_cp == NULL)
+ if (rl_cp == NULL) {
+ el_resize(el);
rl_cp = el_gets(el, &el_len);
+ }
if (rl_cp == NULL)
nr = el_len == 0 ? 0 : -1;
else {
@@ -228,10 +224,16 @@ preadbuffer(void)
{
char *p, *q;
int more;
- int something;
char savec;
- if (parsefile->strpush) {
+ while (parsefile->strpush) {
+ /*
+ * Add a space to the end of an alias to ensure that the
+ * alias remains in use while parsing its last word.
+ * This avoids alias recursions.
+ */
+ if (parsenleft == -1 && parsefile->strpush->ap != NULL)
+ return ' ';
popstring();
if (--parsenleft >= 0)
return (*parsenextc++);
@@ -252,24 +254,18 @@ again:
q = p = parsefile->buf + (parsenextc - parsefile->buf);
/* delete nul characters */
- something = 0;
for (more = 1; more;) {
switch (*p) {
case '\0':
p++; /* Skip nul */
goto check;
- case '\t':
- case ' ':
- break;
-
case '\n':
parsenleft = q - parsenextc;
more = 0; /* Stop processing here */
break;
default:
- something = 1;
break;
}
@@ -288,7 +284,8 @@ check:
*q = '\0';
#ifndef NO_HISTORY
- if (parsefile->fd == 0 && hist && something) {
+ if (parsefile->fd == 0 && hist &&
+ parsenextc[strspn(parsenextc, " \t\n")] != '\0') {
HistEvent he;
INTOFF;
history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD,
@@ -370,12 +367,16 @@ popstring(void)
struct strpush *sp = parsefile->strpush;
INTOFF;
+ if (sp->ap) {
+ if (parsenextc != sp->ap->val &&
+ (parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
+ forcealias();
+ sp->ap->flag &= ~ALIASINUSE;
+ }
parsenextc = sp->prevstring;
parsenleft = sp->prevnleft;
parselleft = sp->prevlleft;
/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/
- if (sp->ap)
- sp->ap->flag &= ~ALIASINUSE;
parsefile->strpush = sp->prev;
if (sp != &(parsefile->basestrpush))
ckfree(sp);
diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c
index bbb954a..1c0c6bd 100644
--- a/bin/sh/jobs.c
+++ b/bin/sh/jobs.c
@@ -978,7 +978,6 @@ int
waitforjob(struct job *jp, int *origstatus)
{
#if JOBS
- pid_t mypgrp = getpgrp();
int propagate_int = jp->jobctl && jp->foreground;
#endif
int status;
@@ -992,7 +991,7 @@ waitforjob(struct job *jp, int *origstatus)
dotrap();
#if JOBS
if (jp->jobctl) {
- if (tcsetpgrp(ttyfd, mypgrp) < 0)
+ if (tcsetpgrp(ttyfd, rootpid) < 0)
error("tcsetpgrp failed, errno=%d\n", errno);
}
if (jp->state == JOBSTOPPED)
diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c
index cb330d0..119f12e 100644
--- a/bin/sh/memalloc.c
+++ b/bin/sh/memalloc.c
@@ -98,9 +98,11 @@ char *
savestr(const char *s)
{
char *p;
+ size_t len;
- p = ckmalloc(strlen(s) + 1);
- scopy(s, p);
+ len = strlen(s);
+ p = ckmalloc(len + 1);
+ memcpy(p, s, len + 1);
return p;
}
diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c
index 67617ec..de7e9b6 100644
--- a/bin/sh/mystring.c
+++ b/bin/sh/mystring.c
@@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$");
* String functions.
*
* equal(s1, s2) Return true if strings are equal.
- * scopy(from, to) Copy a string.
* number(s) Convert a string of digits to an integer.
* is_number(s) Return true if s is a string of digits.
*/
@@ -60,10 +59,6 @@ char nullstr[1]; /* zero length string */
* equal - #defined in mystring.h
*/
-/*
- * scopy - #defined in mystring.h
- */
-
/*
* prefix -- see if pfx is a prefix of string.
diff --git a/bin/sh/mystring.h b/bin/sh/mystring.h
index 5a6e3d8..87b76c8 100644
--- a/bin/sh/mystring.h
+++ b/bin/sh/mystring.h
@@ -40,4 +40,3 @@ int number(const char *);
int is_number(const char *);
#define equal(s1, s2) (strcmp(s1, s2) == 0)
-#define scopy(s1, s2) ((void)strcpy(s2, s1))
diff --git a/bin/sh/nodetypes b/bin/sh/nodetypes
index 5e70c64..603c777 100644
--- a/bin/sh/nodetypes
+++ b/bin/sh/nodetypes
@@ -118,16 +118,16 @@ NFROMTO nfile # fd<> fname
NAPPEND nfile # fd>> fname
NCLOBBER nfile # fd>| fname
type int
- next nodeptr # next redirection in list
fd int # file descriptor being redirected
+ next nodeptr # next redirection in list
fname nodeptr # file name, in a NARG node
expfname temp char *expfname # actual file name
NTOFD ndup # fd<&dupfd
NFROMFD ndup # fd>&dupfd
type int
- next nodeptr # next redirection in list
fd int # file descriptor being redirected
+ next nodeptr # next redirection in list
dupfd int # file descriptor to duplicate
vname nodeptr # file name if fd>&$var
@@ -135,8 +135,8 @@ NFROMFD ndup # fd>&dupfd
NHERE nhere # fd<<\!
NXHERE nhere # fd<<!
type int
- next nodeptr # next redirection in list
fd int # file descriptor being redirected
+ next nodeptr # next redirection in list
doc nodeptr # input to command (NARG node)
expdoc temp char *expdoc # actual document (for NXHERE)
diff --git a/bin/sh/parser.c b/bin/sh/parser.c
index a0def64..e6e9f82 100644
--- a/bin/sh/parser.c
+++ b/bin/sh/parser.c
@@ -683,6 +683,12 @@ makebinary(int type, union node *n1, union node *n2)
}
void
+forcealias(void)
+{
+ checkkwd |= CHKALIAS;
+}
+
+void
fixredir(union node *n, const char *text, int err)
{
TRACE(("Fix redir %s %d\n", text, err));
diff --git a/bin/sh/parser.h b/bin/sh/parser.h
index d500d2f..5982594 100644
--- a/bin/sh/parser.h
+++ b/bin/sh/parser.h
@@ -76,6 +76,7 @@ extern const char *const parsekwd[];
union node *parsecmd(int);
+void forcealias(void);
void fixredir(union node *, const char *, int);
int goodname(const char *);
int isassignment(const char *);
diff --git a/bin/sh/redir.c b/bin/sh/redir.c
index 9325545..79728a0 100644
--- a/bin/sh/redir.c
+++ b/bin/sh/redir.c
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
struct redirtab {
struct redirtab *next;
int renamed[10];
+ int fd0_redirected;
};
@@ -109,11 +110,14 @@ redirect(union node *redir, int flags)
sv = ckmalloc(sizeof (struct redirtab));
for (i = 0 ; i < 10 ; i++)
sv->renamed[i] = EMPTY;
+ sv->fd0_redirected = fd0_redirected;
sv->next = redirlist;
redirlist = sv;
}
for (n = redir ; n ; n = n->nfile.next) {
fd = n->nfile.fd;
+ if (fd == 0)
+ fd0_redirected = 1;
if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
n->ndup.dupfd == fd)
continue; /* redirect from/to same file descriptor */
@@ -134,8 +138,6 @@ redirect(union node *redir, int flags)
sv->renamed[fd] = i;
INTON;
}
- if (fd == 0)
- fd0_redirected++;
openredirect(n, memory);
}
if (memory[1])
@@ -303,8 +305,6 @@ popredir(void)
for (i = 0 ; i < 10 ; i++) {
if (rp->renamed[i] != EMPTY) {
- if (i == 0)
- fd0_redirected--;
if (rp->renamed[i] >= 0) {
dup2(rp->renamed[i], i);
close(rp->renamed[i]);
@@ -314,6 +314,7 @@ popredir(void)
}
}
INTOFF;
+ fd0_redirected = rp->fd0_redirected;
redirlist = rp->next;
ckfree(rp);
INTON;
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index ff29be6..b5916a8 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -32,7 +32,7 @@
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
.\" $FreeBSD$
.\"
-.Dd June 14, 2013
+.Dd January 26, 2014
.Dt SH 1
.Os
.Sh NAME
@@ -235,10 +235,16 @@ or
.Dq Li ||
operator; or if the command is a pipeline preceded by the
.Ic !\&
-operator.
+keyword.
If a shell function is executed and its exit status is explicitly
tested, all commands of the function are considered to be tested as
well.
+.Pp
+It is recommended to check for failures explicitly
+instead of relying on
+.Fl e
+because it tends to behave in unexpected ways,
+particularly in larger scripts.
.It Fl f Li noglob
Disable pathname expansion.
.It Fl h Li trackall
@@ -527,6 +533,20 @@ would become
.Pp
.Dl "ls -F foobar"
.Pp
+Aliases are also recognized after an alias
+whose value ends with a space or tab.
+For example, if there is also an alias called
+.Dq Li nohup
+with the value
+.Dq Li "nohup " ,
+then the input
+.Pp
+.Dl "nohup lf foobar"
+.Pp
+would become
+.Pp
+.Dl "nohup ls -F foobar"
+.Pp
Aliases provide a convenient way for naive users to
create shorthands for commands without having to learn how
to create functions with arguments.
diff --git a/bin/sh/show.c b/bin/sh/show.c
index 7cb3547..db18ae9 100644
--- a/bin/sh/show.c
+++ b/bin/sh/show.c
@@ -390,11 +390,11 @@ opentrace(void)
else
p = "/tmp";
}
- scopy(p, s);
+ strcpy(s, p);
strcat(s, "/trace");
}
#else
- scopy("./trace", s);
+ strcpy(s, "./trace");
#endif /* not_this_way */
if ((tracefile = fopen(s, "a")) == NULL) {
fprintf(stderr, "Can't open %s: %s\n", s, strerror(errno));
diff --git a/bin/sh/tests/Makefile b/bin/sh/tests/Makefile
new file mode 100644
index 0000000..f6ddb8a
--- /dev/null
+++ b/bin/sh/tests/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin/sh
+
+TAP_TESTS_SH= legacy_test
+TAP_TESTS_SH_SED_legacy_test= -e 's,__SH__,/bin/sh,g'
+# Some tests in here are silently not run when the tests are executed as
+# root. Explicitly tell Kyua to drop privileges.
+#
+# TODO(jmmv): Kyua needs to do this by default, not only when explicitly
+# requested. See https://code.google.com/p/kyua/issues/detail?id=6
+TEST_METADATA.legacy_test+= required_user="unprivileged"
+
+SUBDIR+= builtins errors execution expansion parameters parser set-e
+
+.include <tap.test.mk>
diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile
new file mode 100644
index 0000000..6f60f84
--- /dev/null
+++ b/bin/sh/tests/builtins/Makefile
@@ -0,0 +1,148 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/builtins
+KYUAFILE= no
+
+FILES= alias.0 alias.0.stdout
+FILES+= alias.1 alias.1.stderr
+FILES+= alias3.0 alias3.0.stdout
+FILES+= alias4.0
+FILES+= break1.0
+FILES+= break2.0 break2.0.stdout
+FILES+= break3.0
+FILES+= break4.4
+FILES+= break5.4
+FILES+= builtin1.0
+FILES+= case1.0
+FILES+= case2.0
+FILES+= case3.0
+FILES+= case4.0
+FILES+= case5.0
+FILES+= case6.0
+FILES+= case7.0
+FILES+= case8.0
+FILES+= case9.0
+FILES+= case10.0
+FILES+= case11.0
+FILES+= case12.0
+FILES+= case13.0
+FILES+= case14.0
+FILES+= case15.0
+FILES+= case16.0
+FILES+= case17.0
+FILES+= case18.0
+FILES+= case19.0
+FILES+= cd1.0
+FILES+= cd2.0
+FILES+= cd3.0
+FILES+= cd4.0
+FILES+= cd5.0
+FILES+= cd6.0
+FILES+= cd7.0
+FILES+= cd8.0
+FILES+= command1.0
+FILES+= command2.0
+FILES+= command3.0
+FILES+= command3.0.stdout
+FILES+= command4.0
+FILES+= command5.0
+FILES+= command5.0.stdout
+FILES+= command6.0
+FILES+= command6.0.stdout
+FILES+= command7.0
+FILES+= command8.0
+FILES+= command9.0
+FILES+= command10.0
+FILES+= command11.0
+FILES+= command12.0
+FILES+= dot1.0
+FILES+= dot2.0
+FILES+= dot3.0
+FILES+= dot4.0
+FILES+= eval1.0
+FILES+= eval2.0
+FILES+= eval3.0
+FILES+= eval4.0
+FILES+= eval5.0
+FILES+= eval6.0
+FILES+= exec1.0
+FILES+= exec2.0
+FILES+= exit1.0
+FILES+= exit2.8
+FILES+= exit3.0
+FILES+= export1.0
+FILES+= fc1.0
+FILES+= fc2.0
+FILES+= for1.0
+FILES+= for2.0
+FILES+= for3.0
+FILES+= getopts1.0 getopts1.0.stdout
+FILES+= getopts2.0 getopts2.0.stdout
+FILES+= hash1.0 hash1.0.stdout
+FILES+= hash2.0 hash2.0.stdout
+FILES+= hash3.0 hash3.0.stdout
+FILES+= hash4.0
+FILES+= jobid1.0
+FILES+= jobid2.0
+FILES+= lineno.0 lineno.0.stdout
+FILES+= lineno2.0
+FILES+= local1.0
+FILES+= local2.0
+FILES+= local3.0
+FILES+= local4.0
+FILES+= locale1.0
+FILES+= printf1.0
+FILES+= printf2.0
+FILES+= printf3.0
+FILES+= printf4.0
+FILES+= read1.0 read1.0.stdout
+FILES+= read2.0
+FILES+= read3.0 read3.0.stdout
+FILES+= read4.0 read4.0.stdout
+FILES+= read5.0
+FILES+= read6.0
+FILES+= read7.0
+FILES+= return1.0
+FILES+= return2.1
+FILES+= return3.1
+FILES+= return4.0
+FILES+= return5.0
+FILES+= return6.4
+FILES+= return7.4
+FILES+= return8.0
+FILES+= set1.0
+FILES+= set2.0
+FILES+= trap1.0
+FILES+= trap10.0
+FILES+= trap11.0
+FILES+= trap12.0
+FILES+= trap13.0
+FILES+= trap14.0
+FILES+= trap2.0
+FILES+= trap3.0
+FILES+= trap4.0
+FILES+= trap5.0
+FILES+= trap6.0
+FILES+= trap7.0
+FILES+= trap8.0
+FILES+= trap9.0
+FILES+= type1.0 type1.0.stderr
+FILES+= type2.0
+FILES+= type3.0
+FILES+= unalias.0
+FILES+= var-assign.0
+FILES+= var-assign2.0
+FILES+= wait1.0
+FILES+= wait2.0
+FILES+= wait3.0
+FILES+= wait4.0
+FILES+= wait5.0
+FILES+= wait6.0
+FILES+= wait7.0
+FILES+= wait8.0
+FILES+= wait9.127
+FILES+= wait10.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/builtins/alias.0 b/bin/sh/tests/builtins/alias.0
new file mode 100644
index 0000000..d9b2796
--- /dev/null
+++ b/bin/sh/tests/builtins/alias.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+set -e
+
+unalias -a
+alias foo=bar
+alias bar=
+alias quux="1 2 3"
+alias
+alias foo
diff --git a/bin/sh/tests/builtins/alias.0.stdout b/bin/sh/tests/builtins/alias.0.stdout
new file mode 100644
index 0000000..52efaf0
--- /dev/null
+++ b/bin/sh/tests/builtins/alias.0.stdout
@@ -0,0 +1,4 @@
+bar=''
+foo=bar
+quux='1 2 3'
+foo=bar
diff --git a/bin/sh/tests/builtins/alias.1 b/bin/sh/tests/builtins/alias.1
new file mode 100644
index 0000000..31403dc
--- /dev/null
+++ b/bin/sh/tests/builtins/alias.1
@@ -0,0 +1,3 @@
+# $FreeBSD$
+unalias -a
+alias foo
diff --git a/bin/sh/tests/builtins/alias.1.stderr b/bin/sh/tests/builtins/alias.1.stderr
new file mode 100644
index 0000000..c9f4011
--- /dev/null
+++ b/bin/sh/tests/builtins/alias.1.stderr
@@ -0,0 +1 @@
+alias: foo: not found
diff --git a/bin/sh/tests/builtins/alias3.0 b/bin/sh/tests/builtins/alias3.0
new file mode 100644
index 0000000..fe65e31f
--- /dev/null
+++ b/bin/sh/tests/builtins/alias3.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+set -e
+
+unalias -a
+alias foo=bar
+alias bar=
+alias quux="1 2 3"
+alias foo=bar
+alias bar=
+alias quux="1 2 3"
+alias
+alias foo
diff --git a/bin/sh/tests/builtins/alias3.0.stdout b/bin/sh/tests/builtins/alias3.0.stdout
new file mode 100644
index 0000000..52efaf0
--- /dev/null
+++ b/bin/sh/tests/builtins/alias3.0.stdout
@@ -0,0 +1,4 @@
+bar=''
+foo=bar
+quux='1 2 3'
+foo=bar
diff --git a/bin/sh/tests/builtins/alias4.0 b/bin/sh/tests/builtins/alias4.0
new file mode 100644
index 0000000..3d5efec
--- /dev/null
+++ b/bin/sh/tests/builtins/alias4.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+unalias -a
+alias --
diff --git a/bin/sh/tests/builtins/break1.0 b/bin/sh/tests/builtins/break1.0
new file mode 100644
index 0000000..ba0cbb4
--- /dev/null
+++ b/bin/sh/tests/builtins/break1.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+if [ "$1" != nested ]; then
+ while :; do
+ set -- nested
+ . "$0"
+ echo bad2
+ exit 2
+ done
+ exit 0
+fi
+# To trigger the bug, the following commands must be at the top level,
+# with newlines in between.
+break
+echo bad1
+exit 1
diff --git a/bin/sh/tests/builtins/break2.0 b/bin/sh/tests/builtins/break2.0
new file mode 100644
index 0000000..ff52dd3
--- /dev/null
+++ b/bin/sh/tests/builtins/break2.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+# It is not immediately obvious that this should work, and someone probably
+# relies on it.
+
+while :; do
+ trap 'break' USR1
+ kill -USR1 $$
+ echo bad
+ exit 1
+done
+echo good
diff --git a/bin/sh/tests/builtins/break2.0.stdout b/bin/sh/tests/builtins/break2.0.stdout
new file mode 100644
index 0000000..12799cc
--- /dev/null
+++ b/bin/sh/tests/builtins/break2.0.stdout
@@ -0,0 +1 @@
+good
diff --git a/bin/sh/tests/builtins/break3.0 b/bin/sh/tests/builtins/break3.0
new file mode 100644
index 0000000..10a5ca8
--- /dev/null
+++ b/bin/sh/tests/builtins/break3.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+# We accept this and people might rely on it.
+# However, various other shells do not accept it.
+
+f() {
+ break
+ echo bad1
+}
+
+while :; do
+ f
+ echo bad2
+ exit 2
+done
diff --git a/bin/sh/tests/builtins/break4.4 b/bin/sh/tests/builtins/break4.4
new file mode 100644
index 0000000..d52ff52
--- /dev/null
+++ b/bin/sh/tests/builtins/break4.4
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# Although this is not specified by POSIX, some configure scripts (gawk 4.1.0)
+# appear to depend on it.
+
+break
+exit 4
diff --git a/bin/sh/tests/builtins/break5.4 b/bin/sh/tests/builtins/break5.4
new file mode 100644
index 0000000..7df8e18
--- /dev/null
+++ b/bin/sh/tests/builtins/break5.4
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+# Although this is not specified by POSIX, some configure scripts (gawk 4.1.0)
+# appear to depend on it.
+# In some uncommitted code, the subshell environment corrupted the outer
+# shell environment's state.
+
+(for i in a b c; do
+ exit 3
+done)
+break
+exit 4
diff --git a/bin/sh/tests/builtins/builtin1.0 b/bin/sh/tests/builtins/builtin1.0
new file mode 100644
index 0000000..b608385
--- /dev/null
+++ b/bin/sh/tests/builtins/builtin1.0
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+builtin : || echo "Bad return code at $LINENO"
+builtin true || echo "Bad return code at $LINENO"
+builtin ls 2>/dev/null && echo "Bad return code at $LINENO"
+check '"$(builtin pwd)" = "$(pwd)"'
+check '-z "$(builtin :)"'
+check '-z "$(builtin true)"'
+check '-z "$( (builtin nosuchtool) 2>/dev/null)"'
+check '-z "$(builtin nosuchtool 2>/dev/null)"'
+check '-z "$(builtin nosuchtool 2>/dev/null; :)"'
+check '-z "$( (builtin ls) 2>/dev/null)"'
+check '-z "$(builtin ls 2>/dev/null)"'
+check '-z "$(builtin ls 2>/dev/null; :)"'
+check '-n "$( (builtin nosuchtool) 2>&1)"'
+check '-n "$(builtin nosuchtool 2>&1)"'
+check '-n "$(builtin nosuchtool 2>&1; :)"'
+check '-n "$( (builtin ls) 2>&1)"'
+check '-n "$(builtin ls 2>&1)"'
+check '-n "$(builtin ls 2>&1; :)"'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/case1.0 b/bin/sh/tests/builtins/case1.0
new file mode 100644
index 0000000..860fc67
--- /dev/null
+++ b/bin/sh/tests/builtins/case1.0
@@ -0,0 +1,13 @@
+#$FreeBSD$
+f()
+{
+ false
+ case $1 in
+ foo) true ;;
+ bar) false ;;
+ esac
+}
+
+f foo || exit 1
+f bar && exit 1
+f quux || exit 1
diff --git a/bin/sh/tests/builtins/case10.0 b/bin/sh/tests/builtins/case10.0
new file mode 100644
index 0000000..a627b5c
--- /dev/null
+++ b/bin/sh/tests/builtins/case10.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+case ! in
+[\!!]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case ! in
+['!'!]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case ! in
+["!"!]) ;;
+*) echo Failed at $LINENO ;;
+esac
diff --git a/bin/sh/tests/builtins/case11.0 b/bin/sh/tests/builtins/case11.0
new file mode 100644
index 0000000..0e66e11
--- /dev/null
+++ b/bin/sh/tests/builtins/case11.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+false
+case x in
+*)
+esac
diff --git a/bin/sh/tests/builtins/case12.0 b/bin/sh/tests/builtins/case12.0
new file mode 100644
index 0000000..2a442ba
--- /dev/null
+++ b/bin/sh/tests/builtins/case12.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+false
+case x in
+y)
+esac
diff --git a/bin/sh/tests/builtins/case13.0 b/bin/sh/tests/builtins/case13.0
new file mode 100644
index 0000000..78f4e9b
--- /dev/null
+++ b/bin/sh/tests/builtins/case13.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+case ^ in
+[\^^]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case s in
+[\^^]) echo Failed at $LINENO ;;
+[s\]]) ;;
+*) echo Failed at $LINENO ;;
+esac
diff --git a/bin/sh/tests/builtins/case14.0 b/bin/sh/tests/builtins/case14.0
new file mode 100644
index 0000000..0338e8a
--- /dev/null
+++ b/bin/sh/tests/builtins/case14.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+case `false` in
+no) exit 3 ;;
+esac
diff --git a/bin/sh/tests/builtins/case15.0 b/bin/sh/tests/builtins/case15.0
new file mode 100644
index 0000000..09b0e11
--- /dev/null
+++ b/bin/sh/tests/builtins/case15.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+case x in
+`false`) exit 3 ;;
+esac
diff --git a/bin/sh/tests/builtins/case16.0 b/bin/sh/tests/builtins/case16.0
new file mode 100644
index 0000000..2430302
--- /dev/null
+++ b/bin/sh/tests/builtins/case16.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+f() { return 42; }
+f
+case x in
+x) [ $? = 42 ] ;;
+esac
diff --git a/bin/sh/tests/builtins/case17.0 b/bin/sh/tests/builtins/case17.0
new file mode 100644
index 0000000..ed1d25f
--- /dev/null
+++ b/bin/sh/tests/builtins/case17.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+! case x in x) false ;& y) esac
diff --git a/bin/sh/tests/builtins/case18.0 b/bin/sh/tests/builtins/case18.0
new file mode 100644
index 0000000..470253f
--- /dev/null
+++ b/bin/sh/tests/builtins/case18.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+case x$(false) in
+x) ;&
+y) [ $? != 0 ] ;;
+z) false ;;
+esac
diff --git a/bin/sh/tests/builtins/case19.0 b/bin/sh/tests/builtins/case19.0
new file mode 100644
index 0000000..215066a
--- /dev/null
+++ b/bin/sh/tests/builtins/case19.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+[ "`case x in
+x) false ;&
+y) ;&
+z) echo $? ;;
+esac`" != 0 ]
diff --git a/bin/sh/tests/builtins/case2.0 b/bin/sh/tests/builtins/case2.0
new file mode 100644
index 0000000..e319148
--- /dev/null
+++ b/bin/sh/tests/builtins/case2.0
@@ -0,0 +1,106 @@
+# Generated by ./test-fnmatch -s 1, do not edit.
+# $FreeBSD$
+failures=
+failed() { printf '%s\n' "Failed: $1 '$2' '$3'"; failures=x$failures; }
+testmatch() { eval "case \$2 in ''$1) ;; *) failed testmatch \"\$@\";; esac"; }
+testnomatch() { eval "case \$2 in ''$1) failed testnomatch \"\$@\";; esac"; }
+testmatch '' ''
+testmatch 'a' 'a'
+testnomatch 'a' 'b'
+testnomatch 'a' 'A'
+testmatch '*' 'a'
+testmatch '*' 'aa'
+testmatch '*a' 'a'
+testnomatch '*a' 'b'
+testnomatch '*a*' 'b'
+testmatch '*a*b*' 'ab'
+testmatch '*a*b*' 'qaqbq'
+testmatch '*a*bb*' 'qaqbqbbq'
+testmatch '*a*bc*' 'qaqbqbcq'
+testmatch '*a*bb*' 'qaqbqbb'
+testmatch '*a*bc*' 'qaqbqbc'
+testmatch '*a*bb' 'qaqbqbb'
+testmatch '*a*bc' 'qaqbqbc'
+testnomatch '*a*bb' 'qaqbqbbq'
+testnomatch '*a*bc' 'qaqbqbcq'
+testnomatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaa'
+testmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaa'
+testmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaaa'
+testnomatch '.*.*.*.*.*.*.*.*.*.*' '.........'
+testmatch '.*.*.*.*.*.*.*.*.*.*' '..........'
+testmatch '.*.*.*.*.*.*.*.*.*.*' '...........'
+testnomatch '*?*?*?*?*?*?*?*?*?*?*' '123456789'
+testnomatch '??????????*' '123456789'
+testnomatch '*??????????' '123456789'
+testmatch '*?*?*?*?*?*?*?*?*?*?*' '1234567890'
+testmatch '??????????*' '1234567890'
+testmatch '*??????????' '1234567890'
+testmatch '*?*?*?*?*?*?*?*?*?*?*' '12345678901'
+testmatch '??????????*' '12345678901'
+testmatch '*??????????' '12345678901'
+testmatch '[x]' 'x'
+testmatch '[*]' '*'
+testmatch '[?]' '?'
+testmatch '[' '['
+testmatch '[[]' '['
+testnomatch '[[]' 'x'
+testnomatch '[*]' ''
+testnomatch '[*]' 'x'
+testnomatch '[?]' 'x'
+testmatch '*[*]*' 'foo*foo'
+testnomatch '*[*]*' 'foo'
+testmatch '[0-9]' '0'
+testmatch '[0-9]' '5'
+testmatch '[0-9]' '9'
+testnomatch '[0-9]' '/'
+testnomatch '[0-9]' ':'
+testnomatch '[0-9]' '*'
+testnomatch '[!0-9]' '0'
+testnomatch '[!0-9]' '5'
+testnomatch '[!0-9]' '9'
+testmatch '[!0-9]' '/'
+testmatch '[!0-9]' ':'
+testmatch '[!0-9]' '*'
+testmatch '*[0-9]' 'a0'
+testmatch '*[0-9]' 'a5'
+testmatch '*[0-9]' 'a9'
+testnomatch '*[0-9]' 'a/'
+testnomatch '*[0-9]' 'a:'
+testnomatch '*[0-9]' 'a*'
+testnomatch '*[!0-9]' 'a0'
+testnomatch '*[!0-9]' 'a5'
+testnomatch '*[!0-9]' 'a9'
+testmatch '*[!0-9]' 'a/'
+testmatch '*[!0-9]' 'a:'
+testmatch '*[!0-9]' 'a*'
+testmatch '*[0-9]' 'a00'
+testmatch '*[0-9]' 'a55'
+testmatch '*[0-9]' 'a99'
+testmatch '*[0-9]' 'a0a0'
+testmatch '*[0-9]' 'a5a5'
+testmatch '*[0-9]' 'a9a9'
+testmatch '\*' '*'
+testmatch '\?' '?'
+testmatch '\[x]' '[x]'
+testmatch '\[' '['
+testmatch '\\' '\'
+testmatch '*\**' 'foo*foo'
+testnomatch '*\**' 'foo'
+testmatch '*\\*' 'foo\foo'
+testnomatch '*\\*' 'foo'
+testmatch '\(' '('
+testmatch '\a' 'a'
+testnomatch '\*' 'a'
+testnomatch '\?' 'a'
+testnomatch '\*' '\*'
+testnomatch '\?' '\?'
+testnomatch '\[x]' '\[x]'
+testnomatch '\[x]' '\x'
+testnomatch '\[' '\['
+testnomatch '\(' '\('
+testnomatch '\a' '\a'
+testmatch '.*' '.'
+testmatch '.*' '..'
+testmatch '.*' '.a'
+testmatch 'a*' 'a.'
+[ -z "$failures" ]
diff --git a/bin/sh/tests/builtins/case3.0 b/bin/sh/tests/builtins/case3.0
new file mode 100644
index 0000000..42e53d6
--- /dev/null
+++ b/bin/sh/tests/builtins/case3.0
@@ -0,0 +1,95 @@
+# Generated by ./test-fnmatch -s 2, do not edit.
+# $FreeBSD$
+failures=
+failed() { printf '%s\n' "Failed: $1 '$2' '$3'"; failures=x$failures; }
+# We do not treat a backslash specially in this case,
+# but this is not the case in all shells.
+netestmatch() { case $2 in $1) ;; *) failed netestmatch "$@";; esac; }
+netestnomatch() { case $2 in $1) failed netestnomatch "$@";; esac; }
+netestmatch '' ''
+netestmatch 'a' 'a'
+netestnomatch 'a' 'b'
+netestnomatch 'a' 'A'
+netestmatch '*' 'a'
+netestmatch '*' 'aa'
+netestmatch '*a' 'a'
+netestnomatch '*a' 'b'
+netestnomatch '*a*' 'b'
+netestmatch '*a*b*' 'ab'
+netestmatch '*a*b*' 'qaqbq'
+netestmatch '*a*bb*' 'qaqbqbbq'
+netestmatch '*a*bc*' 'qaqbqbcq'
+netestmatch '*a*bb*' 'qaqbqbb'
+netestmatch '*a*bc*' 'qaqbqbc'
+netestmatch '*a*bb' 'qaqbqbb'
+netestmatch '*a*bc' 'qaqbqbc'
+netestnomatch '*a*bb' 'qaqbqbbq'
+netestnomatch '*a*bc' 'qaqbqbcq'
+netestnomatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaa'
+netestmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaa'
+netestmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaaa'
+netestnomatch '.*.*.*.*.*.*.*.*.*.*' '.........'
+netestmatch '.*.*.*.*.*.*.*.*.*.*' '..........'
+netestmatch '.*.*.*.*.*.*.*.*.*.*' '...........'
+netestnomatch '*?*?*?*?*?*?*?*?*?*?*' '123456789'
+netestnomatch '??????????*' '123456789'
+netestnomatch '*??????????' '123456789'
+netestmatch '*?*?*?*?*?*?*?*?*?*?*' '1234567890'
+netestmatch '??????????*' '1234567890'
+netestmatch '*??????????' '1234567890'
+netestmatch '*?*?*?*?*?*?*?*?*?*?*' '12345678901'
+netestmatch '??????????*' '12345678901'
+netestmatch '*??????????' '12345678901'
+netestmatch '[x]' 'x'
+netestmatch '[*]' '*'
+netestmatch '[?]' '?'
+netestmatch '[' '['
+netestmatch '[[]' '['
+netestnomatch '[[]' 'x'
+netestnomatch '[*]' ''
+netestnomatch '[*]' 'x'
+netestnomatch '[?]' 'x'
+netestmatch '*[*]*' 'foo*foo'
+netestnomatch '*[*]*' 'foo'
+netestmatch '[0-9]' '0'
+netestmatch '[0-9]' '5'
+netestmatch '[0-9]' '9'
+netestnomatch '[0-9]' '/'
+netestnomatch '[0-9]' ':'
+netestnomatch '[0-9]' '*'
+netestnomatch '[!0-9]' '0'
+netestnomatch '[!0-9]' '5'
+netestnomatch '[!0-9]' '9'
+netestmatch '[!0-9]' '/'
+netestmatch '[!0-9]' ':'
+netestmatch '[!0-9]' '*'
+netestmatch '*[0-9]' 'a0'
+netestmatch '*[0-9]' 'a5'
+netestmatch '*[0-9]' 'a9'
+netestnomatch '*[0-9]' 'a/'
+netestnomatch '*[0-9]' 'a:'
+netestnomatch '*[0-9]' 'a*'
+netestnomatch '*[!0-9]' 'a0'
+netestnomatch '*[!0-9]' 'a5'
+netestnomatch '*[!0-9]' 'a9'
+netestmatch '*[!0-9]' 'a/'
+netestmatch '*[!0-9]' 'a:'
+netestmatch '*[!0-9]' 'a*'
+netestmatch '*[0-9]' 'a00'
+netestmatch '*[0-9]' 'a55'
+netestmatch '*[0-9]' 'a99'
+netestmatch '*[0-9]' 'a0a0'
+netestmatch '*[0-9]' 'a5a5'
+netestmatch '*[0-9]' 'a9a9'
+netestmatch '\*' '\*'
+netestmatch '\?' '\?'
+netestmatch '\' '\'
+netestnomatch '\\' '\'
+netestmatch '\\' '\\'
+netestmatch '*\*' 'foo\foo'
+netestnomatch '*\*' 'foo'
+netestmatch '.*' '.'
+netestmatch '.*' '..'
+netestmatch '.*' '.a'
+netestmatch 'a*' 'a.'
+[ -z "$failures" ]
diff --git a/bin/sh/tests/builtins/case4.0 b/bin/sh/tests/builtins/case4.0
new file mode 100644
index 0000000..18219f5
--- /dev/null
+++ b/bin/sh/tests/builtins/case4.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+set -- "*"
+case x in
+"$1") echo failed ;;
+esac
diff --git a/bin/sh/tests/builtins/case5.0 b/bin/sh/tests/builtins/case5.0
new file mode 100644
index 0000000..8c6db5a
--- /dev/null
+++ b/bin/sh/tests/builtins/case5.0
@@ -0,0 +1,57 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.UTF-8
+export LC_CTYPE
+
+c1=e
+# a umlaut
+c2=$(printf '\303\244')
+# euro sign
+c3=$(printf '\342\202\254')
+# some sort of 't' outside BMP
+c4=$(printf '\360\235\225\245')
+
+ok=0
+case $c1$c2$c3$c4 in
+*) ok=1 ;;
+esac
+if [ $ok = 0 ]; then
+ echo wrong at $LINENO
+ exit 3
+fi
+
+case $c1$c2$c3$c4 in
+$c1$c2$c3$c4) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+"$c1$c2$c3$c4") ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+????) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1.$c2.$c3.$c4 in
+?.?.?.?) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+[!a][!b][!c][!d]) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+[$c1][$c2][$c3][$c4]) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+["$c1"]["$c2"]["$c3"]["$c4"]) ;;
+*) echo wrong at $LINENO ;;
+esac
diff --git a/bin/sh/tests/builtins/case6.0 b/bin/sh/tests/builtins/case6.0
new file mode 100644
index 0000000..8d79183
--- /dev/null
+++ b/bin/sh/tests/builtins/case6.0
@@ -0,0 +1,52 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=de_DE.ISO8859-1
+export LC_CTYPE
+
+c1=e
+# o umlaut
+c2=$(printf '\366')
+# non-break space
+c3=$(printf '\240')
+c4=$(printf '\240')
+# $c2$c3$c4 form one utf-8 character
+
+ok=0
+case $c1$c2$c3$c4 in
+*) ok=1 ;;
+esac
+if [ $ok = 0 ]; then
+ echo wrong at $LINENO
+ exit 3
+fi
+
+case $c1$c2$c3$c4 in
+$c1$c2$c3$c4) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+"$c1$c2$c3$c4") ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+????) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+[!$c2][!b][!c][!d]) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+[$c1][$c2][$c3][$c4]) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2$c3$c4 in
+["$c1"]["$c2"]["$c3"]["$c4"]) ;;
+*) echo wrong at $LINENO ;;
+esac
diff --git a/bin/sh/tests/builtins/case7.0 b/bin/sh/tests/builtins/case7.0
new file mode 100644
index 0000000..96b9de6
--- /dev/null
+++ b/bin/sh/tests/builtins/case7.0
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+# Character ranges in a locale other than the POSIX locale, not specified
+# by POSIX.
+
+unset LC_ALL
+LC_CTYPE=de_DE.ISO8859-1
+export LC_CTYPE
+LC_COLLATE=de_DE.ISO8859-1
+export LC_COLLATE
+
+c1=e
+# o umlaut
+c2=$(printf '\366')
+
+case $c1$c2 in
+[a-z][a-z]) ;;
+*) echo wrong at $LINENO ;;
+esac
+
+case $c1$c2 in
+[a-f][n-p]) ;;
+*) echo wrong at $LINENO ;;
+esac
diff --git a/bin/sh/tests/builtins/case8.0 b/bin/sh/tests/builtins/case8.0
new file mode 100644
index 0000000..8d9f8b6
--- /dev/null
+++ b/bin/sh/tests/builtins/case8.0
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+case aZ_ in
+[[:alpha:]_][[:upper:]_][[:alpha:]_]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case ' ' in
+[[:alpha:][:digit:]]) echo Failed at $LINENO ;;
+[![:alpha:][:digit:]]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case '.X.' in
+*[[:lower:]]*) echo Failed at $LINENO ;;
+*[[:upper:]]*) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case ' ' in
+[![:print:]]) echo Failed at $LINENO ;;
+[![:alnum:][:punct:]]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case '
+' in
+[[:print:]]) echo Failed at $LINENO ;;
+['
+'[:digit:]]) ;;
+*) echo Failed at $LINENO ;;
+esac
diff --git a/bin/sh/tests/builtins/case9.0 b/bin/sh/tests/builtins/case9.0
new file mode 100644
index 0000000..476caec
--- /dev/null
+++ b/bin/sh/tests/builtins/case9.0
@@ -0,0 +1,39 @@
+# $FreeBSD$
+
+errors=0
+
+f() {
+ result=
+ case $1 in
+ a) result=${result}a ;;
+ b) result=${result}b ;&
+ c) result=${result}c ;&
+ d) result=${result}d ;;
+ e) result=${result}e ;&
+ esac
+}
+
+check() {
+ f "$1"
+ if [ "$result" != "$2" ]; then
+ printf "For %s, expected %s got %s\n" "$1" "$2" "$result"
+ errors=$((errors + 1))
+ fi
+}
+
+check '' ''
+check a a
+check b bcd
+check c cd
+check d d
+check e e
+
+if ! (case 1 in
+ 1) false ;&
+ 2) true ;;
+esac) then
+ echo "Subshell bad"
+ errors=$((errors + 1))
+fi
+
+exit $((errors != 0))
diff --git a/bin/sh/tests/builtins/cd1.0 b/bin/sh/tests/builtins/cd1.0
new file mode 100644
index 0000000..bc5108e
--- /dev/null
+++ b/bin/sh/tests/builtins/cd1.0
@@ -0,0 +1,30 @@
+# $FreeBSD$
+set -e
+
+P=${TMPDIR:-/tmp}
+cd $P
+T=$(mktemp -d sh-test.XXXXXX)
+
+chmod 0 $T
+if [ `id -u` -ne 0 ]; then
+ # Root can always cd, regardless of directory permissions.
+ cd -L $T 2>/dev/null && exit 1
+ [ "$PWD" = "$P" ]
+ [ "$(pwd)" = "$P" ]
+ cd -P $T 2>/dev/null && exit 1
+ [ "$PWD" = "$P" ]
+ [ "$(pwd)" = "$P" ]
+fi
+
+chmod 755 $T
+cd $T
+mkdir -p 1/2/3
+ln -s 1/2 link1
+ln -s 2/3 1/link2
+(cd -L 1/../1 && [ "$(pwd -L)" = "$P/$T/1" ])
+(cd -L link1 && [ "$(pwd -L)" = "$P/$T/link1" ])
+(cd -L link1 && [ "$(pwd -P)" = "$P/$T/1/2" ])
+(cd -P link1 && [ "$(pwd -L)" = "$P/$T/1/2" ])
+(cd -P link1 && [ "$(pwd -P)" = "$P/$T/1/2" ])
+
+rm -rf ${P}/${T}
diff --git a/bin/sh/tests/builtins/cd2.0 b/bin/sh/tests/builtins/cd2.0
new file mode 100644
index 0000000..f2b6416
--- /dev/null
+++ b/bin/sh/tests/builtins/cd2.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+set -e
+
+L=$(getconf PATH_MAX / 2>/dev/null) || L=4096
+[ "$L" -lt 100000 ] 2>/dev/null || L=4096
+L=$((L+100))
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf ${T}' 0
+cd $T
+D=$T
+while [ ${#D} -lt $L ]; do
+ mkdir veryverylongdirectoryname
+ cd veryverylongdirectoryname
+ D=$D/veryverylongdirectoryname
+done
+[ $(pwd | wc -c) -eq $((${#D} + 1)) ] # +\n
diff --git a/bin/sh/tests/builtins/cd3.0 b/bin/sh/tests/builtins/cd3.0
new file mode 100644
index 0000000..7729c54
--- /dev/null
+++ b/bin/sh/tests/builtins/cd3.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+# If fully successful, cd -Pe must be like cd -P.
+
+set -e
+
+cd "${TMPDIR:-/tmp}"
+cd -Pe /
+[ "$PWD" = / ]
+[ "$(pwd)" = / ]
+cd "${TMPDIR:-/tmp}"
+cd -eP /
+[ "$PWD" = / ]
+[ "$(pwd)" = / ]
+
+set +e
+
+# If cd -Pe cannot chdir, the exit status must be greater than 1.
+
+v=$( (cd -Pe /var/empty/nonexistent) 2>&1 >/dev/null)
+[ $? -gt 1 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/builtins/cd4.0 b/bin/sh/tests/builtins/cd4.0
new file mode 100644
index 0000000..df3a9a4
--- /dev/null
+++ b/bin/sh/tests/builtins/cd4.0
@@ -0,0 +1,38 @@
+# $FreeBSD$
+
+# This test assumes that whatever mechanism cd -P uses to determine the
+# pathname to the current directory if it is longer than PATH_MAX requires
+# read permission on all parent directories. It also works if this
+# requirement always applies.
+
+set -e
+L=$(getconf PATH_MAX / 2>/dev/null) || L=4096
+[ "$L" -lt 100000 ] 2>/dev/null || L=4096
+L=$((L+100))
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'chmod u+r ${T}; rm -rf ${T}' 0
+cd -Pe $T
+D=$(pwd)
+chmod u-r "$D"
+if [ -r "$D" ]; then
+ # Running as root, cannot test.
+ exit 0
+fi
+set +e
+while [ ${#D} -lt $L ]; do
+ mkdir veryverylongdirectoryname || exit
+ cd -Pe veryverylongdirectoryname 2>/dev/null
+ r=$?
+ [ $r -gt 1 ] && exit $r
+ if [ $r -eq 1 ]; then
+ # Verify that the directory was changed correctly.
+ cd -Pe .. || exit
+ [ "$(pwd)" = "$D" ] || exit
+ # Verify that omitting -e results in success.
+ cd -P veryverylongdirectoryname 2>/dev/null || exit
+ exit 0
+ fi
+ D=$D/veryverylongdirectoryname
+done
+echo "cd -Pe never returned 1"
+exit 0
diff --git a/bin/sh/tests/builtins/cd5.0 b/bin/sh/tests/builtins/cd5.0
new file mode 100644
index 0000000..3dff604
--- /dev/null
+++ b/bin/sh/tests/builtins/cd5.0
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+set -e
+T=$(mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXX")
+trap 'rm -rf "$T"' 0
+
+cd -P "$T"
+D=$(pwd)
+
+mkdir a a/1 b b/1 b/2
+
+CDPATH=$D/a:
+# Basic test.
+cd 1 >/dev/null
+[ "$(pwd)" = "$D/a/1" ]
+# Test that the current directory is not checked before CDPATH.
+cd "$D/b"
+cd 1 >/dev/null
+[ "$(pwd)" = "$D/a/1" ]
+# Test not using a CDPATH entry.
+cd "$D/b"
+cd 2
+[ "$(pwd)" = "$D/b/2" ]
diff --git a/bin/sh/tests/builtins/cd6.0 b/bin/sh/tests/builtins/cd6.0
new file mode 100644
index 0000000..083a061
--- /dev/null
+++ b/bin/sh/tests/builtins/cd6.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+set -e
+cd -P /bin
+d=$PWD
+CDPATH=/:
+cd -P .
+[ "$d" = "$PWD" ]
+cd -P ./
+[ "$d" = "$PWD" ]
diff --git a/bin/sh/tests/builtins/cd7.0 b/bin/sh/tests/builtins/cd7.0
new file mode 100644
index 0000000..9adda86
--- /dev/null
+++ b/bin/sh/tests/builtins/cd7.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+set -e
+cd /usr/bin
+[ "$PWD" = /usr/bin ]
+CDPATH=/:
+cd .
+[ "$PWD" = /usr/bin ]
+cd ./
+[ "$PWD" = /usr/bin ]
+cd ..
+[ "$PWD" = /usr ]
+cd /usr/bin
+cd ../
+[ "$PWD" = /usr ]
diff --git a/bin/sh/tests/builtins/cd8.0 b/bin/sh/tests/builtins/cd8.0
new file mode 100644
index 0000000..a68f77f
--- /dev/null
+++ b/bin/sh/tests/builtins/cd8.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+# The exact wording of the error message is not standardized, but giving
+# a description of the errno is useful.
+
+LC_ALL=C
+export LC_ALL
+r=0
+
+t() {
+ exec 3>&1
+ errmsg=`cd "$1" 2>&1 >&3 3>&-`
+ exec 3>&-
+ case $errmsg in
+ *[Nn]ot\ a\ directory*)
+ ;;
+ *)
+ printf "Wrong error message for %s: %s\n" "$1" "$errmsg"
+ r=3
+ ;;
+ esac
+}
+
+t /dev/tty
+t /dev/tty/x
+exit $r
diff --git a/bin/sh/tests/builtins/command1.0 b/bin/sh/tests/builtins/command1.0
new file mode 100644
index 0000000..fd0afdf
--- /dev/null
+++ b/bin/sh/tests/builtins/command1.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+true() {
+ false
+}
+command true
diff --git a/bin/sh/tests/builtins/command10.0 b/bin/sh/tests/builtins/command10.0
new file mode 100644
index 0000000..2c1adf1
--- /dev/null
+++ b/bin/sh/tests/builtins/command10.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$(f() { shift x; }; { command eval f 2>/dev/null; } >/dev/null; echo hi)" = hi'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/command11.0 b/bin/sh/tests/builtins/command11.0
new file mode 100644
index 0000000..10c8647
--- /dev/null
+++ b/bin/sh/tests/builtins/command11.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$({ command eval \{ shift x\; \} 2\>/dev/null; } >/dev/null; echo hi)" = hi'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/command12.0 b/bin/sh/tests/builtins/command12.0
new file mode 100644
index 0000000..f981db3
--- /dev/null
+++ b/bin/sh/tests/builtins/command12.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+alias aa=echo\ \'\"\'
+cmd=$(command -v aa)
+alias aa=echo\ bad
+eval "$cmd"
+[ "$(eval aa)" = \" ]
diff --git a/bin/sh/tests/builtins/command2.0 b/bin/sh/tests/builtins/command2.0
new file mode 100644
index 0000000..ff7b5f2
--- /dev/null
+++ b/bin/sh/tests/builtins/command2.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+PATH=
+command -p cat < /dev/null
diff --git a/bin/sh/tests/builtins/command3.0 b/bin/sh/tests/builtins/command3.0
new file mode 100644
index 0000000..9d4ae89
--- /dev/null
+++ b/bin/sh/tests/builtins/command3.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+command -v ls
+command -v true
+command -v /bin/ls
+
+fun() {
+ :
+}
+command -v fun
+command -v break
+command -v if
+
+alias foo=bar
+command -v foo
diff --git a/bin/sh/tests/builtins/command3.0.stdout b/bin/sh/tests/builtins/command3.0.stdout
new file mode 100644
index 0000000..67b8691
--- /dev/null
+++ b/bin/sh/tests/builtins/command3.0.stdout
@@ -0,0 +1,7 @@
+/bin/ls
+true
+/bin/ls
+fun
+break
+if
+alias foo=bar
diff --git a/bin/sh/tests/builtins/command4.0 b/bin/sh/tests/builtins/command4.0
new file mode 100644
index 0000000..3e49613
--- /dev/null
+++ b/bin/sh/tests/builtins/command4.0
@@ -0,0 +1,2 @@
+# $FreeBSD$
+! command -v nonexisting
diff --git a/bin/sh/tests/builtins/command5.0 b/bin/sh/tests/builtins/command5.0
new file mode 100644
index 0000000..13b3fe1
--- /dev/null
+++ b/bin/sh/tests/builtins/command5.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+command -V ls
+command -V true
+command -V /bin/ls
+
+fun() {
+ :
+}
+command -V fun
+command -V break
+command -V if
+command -V {
+
+alias foo=bar
+command -V foo
diff --git a/bin/sh/tests/builtins/command5.0.stdout b/bin/sh/tests/builtins/command5.0.stdout
new file mode 100644
index 0000000..523f7b2
--- /dev/null
+++ b/bin/sh/tests/builtins/command5.0.stdout
@@ -0,0 +1,8 @@
+ls is /bin/ls
+true is a shell builtin
+/bin/ls is /bin/ls
+fun is a shell function
+break is a special shell builtin
+if is a shell keyword
+{ is a shell keyword
+foo is an alias for bar
diff --git a/bin/sh/tests/builtins/command6.0 b/bin/sh/tests/builtins/command6.0
new file mode 100644
index 0000000..5b63bfe
--- /dev/null
+++ b/bin/sh/tests/builtins/command6.0
@@ -0,0 +1,22 @@
+# $FreeBSD$
+PATH=/var/empty
+case $(command -pV ls) in
+*/var/empty/ls*)
+ echo "Failed: \$(command -pV ls) should not match */var/empty/ls*" ;;
+"ls is"*" "/*/ls) ;;
+*)
+ echo "Failed: \$(command -pV ls) match \"ls is\"*\" \"/*/ls" ;;
+esac
+command -pV true
+command -pV /bin/ls
+
+fun() {
+ :
+}
+command -pV fun
+command -pV break
+command -pV if
+command -pV {
+
+alias foo=bar
+command -pV foo
diff --git a/bin/sh/tests/builtins/command6.0.stdout b/bin/sh/tests/builtins/command6.0.stdout
new file mode 100644
index 0000000..3180207
--- /dev/null
+++ b/bin/sh/tests/builtins/command6.0.stdout
@@ -0,0 +1,7 @@
+true is a shell builtin
+/bin/ls is /bin/ls
+fun is a shell function
+break is a special shell builtin
+if is a shell keyword
+{ is a shell keyword
+foo is an alias for bar
diff --git a/bin/sh/tests/builtins/command7.0 b/bin/sh/tests/builtins/command7.0
new file mode 100644
index 0000000..fc652f2
--- /dev/null
+++ b/bin/sh/tests/builtins/command7.0
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$(PATH=/libexec command -V ld-elf.so.1)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"'
+check '"$(PATH=/libexec command -V ld-elf.so.1; :)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"'
+check '"$(PATH=/libexec command -pv ld-elf.so.1)" = ""'
+check '"$(PATH=/libexec command -pv ld-elf.so.1; :)" = ""'
+
+PATH=/libexec:$PATH
+
+check '"$(command -V ld-elf.so.1)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"'
+check '"$(command -V ld-elf.so.1; :)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"'
+check '"$(command -pv ld-elf.so.1)" = ""'
+check '"$(command -pv ld-elf.so.1; :)" = ""'
+
+PATH=/libexec
+
+check '"$(command -v ls)" = ""'
+case $(command -pv ls) in
+/*/ls) ;;
+*)
+ echo "Failed: \$(command -pv ls) match /*/ls"
+ : $((failures += 1)) ;;
+esac
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/command8.0 b/bin/sh/tests/builtins/command8.0
new file mode 100644
index 0000000..9e3a2b6
--- /dev/null
+++ b/bin/sh/tests/builtins/command8.0
@@ -0,0 +1,45 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+ :,\
+ continue,\
+ . /dev/null,\
+ eval,\
+ exec,\
+ export -p,\
+ readonly -p,\
+ set,\
+ shift 0,\
+ times,\
+ trap,\
+ unset foo"
+
+set -e
+
+# Check that special builtins can be executed via "command".
+
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "v=:; while \$v; do v=false; command ${cmd}; done" >/dev/null
+done
+
+while :; do
+ command break
+ echo Error on line $LINENO
+done
+
+set p q r
+command shift 2
+if [ $# -ne 1 ]; then
+ echo Error on line $LINENO
+fi
+
+(
+ command exec >/dev/null
+ echo Error on line $LINENO
+)
+
+set +e
+! command shift 2 2>/dev/null
diff --git a/bin/sh/tests/builtins/command9.0 b/bin/sh/tests/builtins/command9.0
new file mode 100644
index 0000000..212e52a
--- /dev/null
+++ b/bin/sh/tests/builtins/command9.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$({ command eval shift x 2>/dev/null; } >/dev/null; echo hi)" = hi'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/dot1.0 b/bin/sh/tests/builtins/dot1.0
new file mode 100644
index 0000000..43eab0d
--- /dev/null
+++ b/bin/sh/tests/builtins/dot1.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) || exit
+trap 'rm -rf $T' 0
+cd $T || exit 3
+unset x
+echo 'x=2' >testscript
+. ./testscript
+[ "$x" = 2 ] || failure $LINENO
+cd / || exit 3
+x=1
+PATH=$T:$PATH . testscript
+[ "$x" = 2 ] || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/dot2.0 b/bin/sh/tests/builtins/dot2.0
new file mode 100644
index 0000000..ed6379b
--- /dev/null
+++ b/bin/sh/tests/builtins/dot2.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) || exit
+trap 'rm -rf $T' 0
+cd $T || exit 3
+unset x
+echo 'x=2' >testscript
+. -- ./testscript
+[ "$x" = 2 ] || failure $LINENO
+cd / || exit 3
+x=1
+PATH=$T:$PATH . -- testscript
+[ "$x" = 2 ] || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/dot3.0 b/bin/sh/tests/builtins/dot3.0
new file mode 100644
index 0000000..b337f0f
--- /dev/null
+++ b/bin/sh/tests/builtins/dot3.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+# . should return 0 if no command was executed.
+
+if false; then
+ exit 3
+else
+ . /dev/null
+ exit $?
+fi
diff --git a/bin/sh/tests/builtins/dot4.0 b/bin/sh/tests/builtins/dot4.0
new file mode 100644
index 0000000..b898131
--- /dev/null
+++ b/bin/sh/tests/builtins/dot4.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+v=abcd
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+r=$( (
+ trap 'exit 0' 0
+ . "$v"
+) 2>&1 >/dev/null) && [ -n "$r" ]
diff --git a/bin/sh/tests/builtins/eval1.0 b/bin/sh/tests/builtins/eval1.0
new file mode 100644
index 0000000..04606a4
--- /dev/null
+++ b/bin/sh/tests/builtins/eval1.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+set -e
+
+eval
+eval "" ""
+eval "true"
+! eval "false
+
+"
diff --git a/bin/sh/tests/builtins/eval2.0 b/bin/sh/tests/builtins/eval2.0
new file mode 100644
index 0000000..bf06b6e
--- /dev/null
+++ b/bin/sh/tests/builtins/eval2.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+eval '
+false
+
+' && exit 1
+exit 0
diff --git a/bin/sh/tests/builtins/eval3.0 b/bin/sh/tests/builtins/eval3.0
new file mode 100644
index 0000000..dfb8357
--- /dev/null
+++ b/bin/sh/tests/builtins/eval3.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+eval 'false;' && exit 1
+eval 'true;' || exit 1
+eval 'false;
+' && exit 1
+eval 'true;
+' || exit 1
+exit 0
diff --git a/bin/sh/tests/builtins/eval4.0 b/bin/sh/tests/builtins/eval4.0
new file mode 100644
index 0000000..67da2f5
--- /dev/null
+++ b/bin/sh/tests/builtins/eval4.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# eval should preserve $? from command substitutions when starting
+# the parsed command.
+[ $(eval 'echo $?' $(false)) = 1 ]
diff --git a/bin/sh/tests/builtins/eval5.0 b/bin/sh/tests/builtins/eval5.0
new file mode 100644
index 0000000..3e86de9
--- /dev/null
+++ b/bin/sh/tests/builtins/eval5.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+# eval should return 0 if no command was executed.
+eval $(false)
diff --git a/bin/sh/tests/builtins/eval6.0 b/bin/sh/tests/builtins/eval6.0
new file mode 100644
index 0000000..6752bb6
--- /dev/null
+++ b/bin/sh/tests/builtins/eval6.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# eval should preserve $? from command substitutions when starting
+# the parsed command.
+[ $(false; eval 'echo $?' $(:)) = 0 ]
diff --git a/bin/sh/tests/builtins/exec1.0 b/bin/sh/tests/builtins/exec1.0
new file mode 100644
index 0000000..dd30a4c
--- /dev/null
+++ b/bin/sh/tests/builtins/exec1.0
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+(
+ exec >/dev/null
+ echo bad
+)
+[ $? = 0 ] || failure $LINENO
+(
+ exec ${SH} -c 'exit 42'
+ echo bad
+)
+[ $? = 42 ] || failure $LINENO
+(
+ exec /var/empty/nosuch
+ echo bad
+) 2>/dev/null
+[ $? = 127 ] || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/exec2.0 b/bin/sh/tests/builtins/exec2.0
new file mode 100644
index 0000000..3dcb6c4
--- /dev/null
+++ b/bin/sh/tests/builtins/exec2.0
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+(
+ exec -- >/dev/null
+ echo bad
+)
+[ $? = 0 ] || failure $LINENO
+(
+ exec -- ${SH} -c 'exit 42'
+ echo bad
+)
+[ $? = 42 ] || failure $LINENO
+(
+ exec -- /var/empty/nosuch
+ echo bad
+) 2>/dev/null
+[ $? = 127 ] || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/exit1.0 b/bin/sh/tests/builtins/exit1.0
new file mode 100644
index 0000000..496d448
--- /dev/null
+++ b/bin/sh/tests/builtins/exit1.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+# exit with an argument should overwrite the exit status in an EXIT trap.
+
+trap 'true; exit $?' 0
+false
diff --git a/bin/sh/tests/builtins/exit2.8 b/bin/sh/tests/builtins/exit2.8
new file mode 100644
index 0000000..124c32e
--- /dev/null
+++ b/bin/sh/tests/builtins/exit2.8
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# exit without arguments is the same as exit $? outside a trap.
+
+trap 'true; true' 0
+(exit 8)
+exit
diff --git a/bin/sh/tests/builtins/exit3.0 b/bin/sh/tests/builtins/exit3.0
new file mode 100644
index 0000000..80655ac
--- /dev/null
+++ b/bin/sh/tests/builtins/exit3.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# exit without arguments differs from exit $? in an EXIT trap.
+
+trap 'false; exit' 0
diff --git a/bin/sh/tests/builtins/export1.0 b/bin/sh/tests/builtins/export1.0
new file mode 100644
index 0000000..7b08c9d
--- /dev/null
+++ b/bin/sh/tests/builtins/export1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+env @badness=1 ${SH} -c 'v=`export -p`; eval "$v"'
diff --git a/bin/sh/tests/builtins/fc1.0 b/bin/sh/tests/builtins/fc1.0
new file mode 100644
index 0000000..ab7a387
--- /dev/null
+++ b/bin/sh/tests/builtins/fc1.0
@@ -0,0 +1,27 @@
+# $FreeBSD$
+set -e
+trap 'echo Broken pipe -- test failed' PIPE
+
+P=${TMPDIR:-/tmp}
+cd $P
+T=$(mktemp -d sh-test.XXXXXX)
+cd $T
+
+mkfifo input output error
+HISTFILE=/dev/null ${SH} +m -i <input >output 2>error &
+{
+ # Syntax error
+ echo ')' >&3
+ # Read error message, shell will read new input now
+ read dummy <&5
+ # Execute bad command again
+ echo 'fc -e true' >&3
+ # Verify that the shell is still running
+ echo 'echo continued' >&3 || rc=3
+ echo 'exit' >&3 || rc=3
+ read line <&4 && [ "$line" = continued ] && : ${rc:=0}
+} 3>input 4<output 5<error
+
+rm input output error
+rmdir ${P}/${T}
+exit ${rc:-3}
diff --git a/bin/sh/tests/builtins/fc2.0 b/bin/sh/tests/builtins/fc2.0
new file mode 100644
index 0000000..7eb92ac
--- /dev/null
+++ b/bin/sh/tests/builtins/fc2.0
@@ -0,0 +1,34 @@
+# $FreeBSD$
+set -e
+trap 'echo Broken pipe -- test failed' PIPE
+
+P=${TMPDIR:-/tmp}
+cd $P
+T=$(mktemp -d sh-test.XXXXXX)
+cd $T
+
+mkfifo input output error
+HISTFILE=/dev/null ${SH} +m -i <input >output 2>error &
+exec 3>input
+{
+ # Command not found, containing slash
+ echo '/var/empty/nonexistent' >&3
+ # Read error message, shell will read new input now
+ read dummy <&5
+ # Execute bad command again
+ echo 'fc -e true; echo continued' >&3
+ read dummy <&5
+ read line <&4 && [ "$line" = continued ] && : ${rc:=0}
+ exec 3>&-
+ # Old sh duplicates itself after the fc, producing another line
+ # of output.
+ if read line <&4; then
+ echo "Extraneous output: $line"
+ rc=1
+ fi
+} 4<output 5<error
+exec 3>&-
+
+rm input output error
+rmdir ${P}/${T}
+exit ${rc:-3}
diff --git a/bin/sh/tests/builtins/for1.0 b/bin/sh/tests/builtins/for1.0
new file mode 100644
index 0000000..cd55e2c
--- /dev/null
+++ b/bin/sh/tests/builtins/for1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+false
+for i in `false`; do exit 3; done
diff --git a/bin/sh/tests/builtins/for2.0 b/bin/sh/tests/builtins/for2.0
new file mode 100644
index 0000000..48c22ce
--- /dev/null
+++ b/bin/sh/tests/builtins/for2.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+r=x
+f() { return 42; }
+f
+for i in x; do
+ r=$?
+done
+[ "$r" = 42 ]
diff --git a/bin/sh/tests/builtins/for3.0 b/bin/sh/tests/builtins/for3.0
new file mode 100644
index 0000000..cc37238
--- /dev/null
+++ b/bin/sh/tests/builtins/for3.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+r=x
+f() { return 42; }
+for i in x`f`; do
+ r=$?
+done
+[ "$r" = 42 ]
diff --git a/bin/sh/tests/builtins/getopts1.0 b/bin/sh/tests/builtins/getopts1.0
new file mode 100644
index 0000000..af31d55
--- /dev/null
+++ b/bin/sh/tests/builtins/getopts1.0
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+printf -- '-1-\n'
+set -- -abc
+getopts "ab:" OPTION
+echo ${OPTION}
+
+# In this case 'getopts' should realize that we have not provided the
+# required argument for "-b".
+# Note that Solaris 10's (UNIX 03) /usr/xpg4/bin/sh, /bin/sh, and /bin/ksh;
+# ksh93 20090505; pdksh 5.2.14p2; mksh R39c; bash 4.1 PL7; and zsh 4.3.10.
+# all recognize that "b" is missing its argument on the *first* iteration
+# of 'getopts' and do not produce the "a" in $OPTION.
+printf -- '-2-\n'
+set -- -ab
+getopts "ab:" OPTION
+echo ${OPTION}
+getopts "ab:" OPTION
+echo ${OPTION}
+
+# The 'shift' is aimed at causing an error.
+printf -- '-3-\n'
+shift 1
+getopts "ab:" OPTION
+echo ${OPTION}
diff --git a/bin/sh/tests/builtins/getopts1.0.stdout b/bin/sh/tests/builtins/getopts1.0.stdout
new file mode 100644
index 0000000..a0a347e
--- /dev/null
+++ b/bin/sh/tests/builtins/getopts1.0.stdout
@@ -0,0 +1,8 @@
+-1-
+a
+-2-
+a
+No arg for -b option
+?
+-3-
+?
diff --git a/bin/sh/tests/builtins/getopts2.0 b/bin/sh/tests/builtins/getopts2.0
new file mode 100644
index 0000000..5a5dbe2
--- /dev/null
+++ b/bin/sh/tests/builtins/getopts2.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+set - -ax
+getopts ax option
+set -C
+getopts ax option
+echo $option
diff --git a/bin/sh/tests/builtins/getopts2.0.stdout b/bin/sh/tests/builtins/getopts2.0.stdout
new file mode 100644
index 0000000..587be6b
--- /dev/null
+++ b/bin/sh/tests/builtins/getopts2.0.stdout
@@ -0,0 +1 @@
+x
diff --git a/bin/sh/tests/builtins/hash1.0 b/bin/sh/tests/builtins/hash1.0
new file mode 100644
index 0000000..45cc300
--- /dev/null
+++ b/bin/sh/tests/builtins/hash1.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+cat /dev/null
+hash
+hash -r
+hash
diff --git a/bin/sh/tests/builtins/hash1.0.stdout b/bin/sh/tests/builtins/hash1.0.stdout
new file mode 100644
index 0000000..3afc3e7
--- /dev/null
+++ b/bin/sh/tests/builtins/hash1.0.stdout
@@ -0,0 +1 @@
+/bin/cat
diff --git a/bin/sh/tests/builtins/hash2.0 b/bin/sh/tests/builtins/hash2.0
new file mode 100644
index 0000000..e5cd21b
--- /dev/null
+++ b/bin/sh/tests/builtins/hash2.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+hash
+hash cat
+hash
diff --git a/bin/sh/tests/builtins/hash2.0.stdout b/bin/sh/tests/builtins/hash2.0.stdout
new file mode 100644
index 0000000..3afc3e7
--- /dev/null
+++ b/bin/sh/tests/builtins/hash2.0.stdout
@@ -0,0 +1 @@
+/bin/cat
diff --git a/bin/sh/tests/builtins/hash3.0 b/bin/sh/tests/builtins/hash3.0
new file mode 100644
index 0000000..eade0b3
--- /dev/null
+++ b/bin/sh/tests/builtins/hash3.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+hash -v cat
+hash
diff --git a/bin/sh/tests/builtins/hash3.0.stdout b/bin/sh/tests/builtins/hash3.0.stdout
new file mode 100644
index 0000000..a34864c
--- /dev/null
+++ b/bin/sh/tests/builtins/hash3.0.stdout
@@ -0,0 +1,2 @@
+/bin/cat
+/bin/cat
diff --git a/bin/sh/tests/builtins/hash4.0 b/bin/sh/tests/builtins/hash4.0
new file mode 100644
index 0000000..dec584c
--- /dev/null
+++ b/bin/sh/tests/builtins/hash4.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+exec 3>&1
+m=`hash nosuchtool 2>&1 >&3`
+r=$?
+[ "$r" != 0 ] && [ -n "$m" ]
diff --git a/bin/sh/tests/builtins/jobid1.0 b/bin/sh/tests/builtins/jobid1.0
new file mode 100644
index 0000000..483fda2
--- /dev/null
+++ b/bin/sh/tests/builtins/jobid1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+# Non-standard builtin.
+
+: &
+p1=$!
+p2=$(jobid)
+[ "${p1:?}" = "${p2:?}" ]
diff --git a/bin/sh/tests/builtins/jobid2.0 b/bin/sh/tests/builtins/jobid2.0
new file mode 100644
index 0000000..101831a
--- /dev/null
+++ b/bin/sh/tests/builtins/jobid2.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+: &
+p1=$(jobid)
+p2=$(jobid --)
+p3=$(jobid %+)
+p4=$(jobid -- %+)
+[ "${p1:?}" = "${p2:?}" ] && [ "${p2:?}" = "${p3:?}" ] &&
+[ "${p3:?}" = "${p4:?}" ] && [ "${p4:?}" = "${p1:?}" ]
diff --git a/bin/sh/tests/builtins/lineno.0 b/bin/sh/tests/builtins/lineno.0
new file mode 100644
index 0000000..c9311f8
--- /dev/null
+++ b/bin/sh/tests/builtins/lineno.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+echo $LINENO
+echo $LINENO
+
+f() {
+ echo $LINENO
+ echo $LINENO
+}
+
+f
+
+echo ${LINENO:-foo}
+echo ${LINENO=foo}
+echo ${LINENO:+foo}
+echo ${LINENO+foo}
+echo ${#LINENO}
diff --git a/bin/sh/tests/builtins/lineno.0.stdout b/bin/sh/tests/builtins/lineno.0.stdout
new file mode 100644
index 0000000..82583a9
--- /dev/null
+++ b/bin/sh/tests/builtins/lineno.0.stdout
@@ -0,0 +1,9 @@
+2
+3
+2
+3
+12
+13
+foo
+foo
+2
diff --git a/bin/sh/tests/builtins/lineno2.0 b/bin/sh/tests/builtins/lineno2.0
new file mode 100644
index 0000000..ddbd104
--- /dev/null
+++ b/bin/sh/tests/builtins/lineno2.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+f() {
+ : ${LINENO+${x?}}
+}
+
+unset -v x
+command eval f 2>/dev/null && exit 3
+x=1
+f
diff --git a/bin/sh/tests/builtins/local1.0 b/bin/sh/tests/builtins/local1.0
new file mode 100644
index 0000000..b28837ec
--- /dev/null
+++ b/bin/sh/tests/builtins/local1.0
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# A commonly used but non-POSIX builtin.
+
+f() {
+ local x
+ x=2
+ [ "$x" = 2 ]
+}
+x=1
+f || exit 3
+[ "$x" = 1 ] || exit 3
+f || exit 3
+[ "$x" = 1 ] || exit 3
diff --git a/bin/sh/tests/builtins/local2.0 b/bin/sh/tests/builtins/local2.0
new file mode 100644
index 0000000..cc8c10f
--- /dev/null
+++ b/bin/sh/tests/builtins/local2.0
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+f() {
+ local -
+ set -a
+ case $- in
+ *a*) : ;;
+ *) echo In-function \$- bad
+ esac
+}
+case $- in
+*a*) echo Initial \$- bad
+esac
+f
+case $- in
+*a*) echo Final \$- bad
+esac
diff --git a/bin/sh/tests/builtins/local3.0 b/bin/sh/tests/builtins/local3.0
new file mode 100644
index 0000000..39ee370
--- /dev/null
+++ b/bin/sh/tests/builtins/local3.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+f() {
+ local "$@"
+ set -a
+ x=7
+ case $- in
+ *a*) : ;;
+ *) echo In-function \$- bad
+ esac
+ [ "$x" = 7 ] || echo In-function \$x bad
+}
+x=1
+case $- in
+*a*) echo Initial \$- bad
+esac
+f x -
+case $- in
+*a*) echo Intermediate \$- bad
+esac
+[ "$x" = 1 ] || echo Intermediate \$x bad
+f - x
+case $- in
+*a*) echo Final \$- bad
+esac
+[ "$x" = 1 ] || echo Final \$x bad
diff --git a/bin/sh/tests/builtins/local4.0 b/bin/sh/tests/builtins/local4.0
new file mode 100644
index 0000000..3955aaa
--- /dev/null
+++ b/bin/sh/tests/builtins/local4.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+f() {
+ local -- x
+ x=2
+ [ "$x" = 2 ]
+}
+x=1
+f || exit 3
+[ "$x" = 1 ] || exit 3
+f || exit 3
+[ "$x" = 1 ] || exit 3
diff --git a/bin/sh/tests/builtins/locale1.0 b/bin/sh/tests/builtins/locale1.0
new file mode 100644
index 0000000..90b1094
--- /dev/null
+++ b/bin/sh/tests/builtins/locale1.0
@@ -0,0 +1,134 @@
+# $FreeBSD$
+# Note: this test depends on strerror() using locale.
+
+failures=0
+
+check() {
+ if ! eval "[ $1 ]"; then
+ echo "Failed: $1 at $2"
+ : $((failures += 1))
+ fi
+}
+
+unset LANG LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME LC_MESSAGES
+unset LANGUAGE
+
+msgeng="No such file or directory"
+msgdut="Bestand of map niet gevonden"
+
+# Verify C locale error message.
+case $(command . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+# Various locale variables that should not affect the message.
+case $(LC_ALL=C command . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_ALL=C LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_ALL=C LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_CTYPE=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+# Verify Dutch message.
+case $(export LANG=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(export LC_MESSAGES=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(export LC_ALL=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_ALL=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+# Verify that command assignments do not set the locale persistently.
+case $(command . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in
+ *"$msgdut"*"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in
+ *"$msgdut"*"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(LC_ALL=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in
+ *"$msgdut"*"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+# Check special builtin; add colon invocation to avoid depending on certain fix.
+case $(LC_ALL=nl_NL.ISO8859-1 . /var/empty/foo 2>&1; :) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+# Assignments on special builtins are exported to that builtin; the export
+# is not persistent.
+case $(LC_ALL=nl_NL.ISO8859-1 . /dev/null; . /var/empty/foo 2>&1) in
+ *"$msgeng"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+case $(export LC_ALL; LC_ALL=nl_NL.ISO8859-1 . /dev/null; . /var/empty/foo 2>&1) in
+ *"$msgdut"*) ok=1 ;;
+ *) ok=0 ;;
+esac
+check '$ok -eq 1' $LINENO
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/printf1.0 b/bin/sh/tests/builtins/printf1.0
new file mode 100644
index 0000000..99a82d0
--- /dev/null
+++ b/bin/sh/tests/builtins/printf1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+[ "$(printf '%c\0%s%d' x '\' 010 | tr '\0' Z)" = 'xZ\8' ]
diff --git a/bin/sh/tests/builtins/printf2.0 b/bin/sh/tests/builtins/printf2.0
new file mode 100644
index 0000000..7763d6f
--- /dev/null
+++ b/bin/sh/tests/builtins/printf2.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+[ "$(printf '%cZ%s%d' x '\' 010)" = 'xZ\8' ]
diff --git a/bin/sh/tests/builtins/printf3.0 b/bin/sh/tests/builtins/printf3.0
new file mode 100644
index 0000000..0e7ea85
--- /dev/null
+++ b/bin/sh/tests/builtins/printf3.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+set -e
+v=$(! printf "%d" @wrong 2>/dev/null)
+[ "$v" = "0" ]
diff --git a/bin/sh/tests/builtins/printf4.0 b/bin/sh/tests/builtins/printf4.0
new file mode 100644
index 0000000..2dd3e72
--- /dev/null
+++ b/bin/sh/tests/builtins/printf4.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+set -e
+v=$(! printf "%d" 4wrong 2>/dev/null)
+[ "$v" = "4" ]
diff --git a/bin/sh/tests/builtins/read1.0 b/bin/sh/tests/builtins/read1.0
new file mode 100644
index 0000000..06a68fa
--- /dev/null
+++ b/bin/sh/tests/builtins/read1.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+set -e
+
+echo "1 2 3" | { read a; echo "x${a}x"; }
+echo "1 2 3" | { read a b; echo "x${a}x${b}x"; }
+echo "1 2 3" | { read a b c; echo "x${a}x${b}x${c}x"; }
+echo "1 2 3" | { read a b c d; echo "x${a}x${b}x${c}x${d}x"; }
+
+echo " 1 2 3 " | { read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 2 3 " | { unset IFS; read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 2 3 " | { IFS=$(printf ' \t\n') read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 2 3 " | { IFS= read a b; echo "x${a}x${b}x"; }
+
+echo " 1,2 3 " | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; }
+echo ", 2 ,3" | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 ,,3" | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 , , 3" | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 ,2 3," | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 ,2 3,," | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; }
+
+echo " 1,2 3 " | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; }
+echo ", 2 ,3" | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 ,,3" | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 , , 3" | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 ,2 3," | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; }
+echo " 1 ,2 3,," | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; }
diff --git a/bin/sh/tests/builtins/read1.0.stdout b/bin/sh/tests/builtins/read1.0.stdout
new file mode 100644
index 0000000..dbcb1af
--- /dev/null
+++ b/bin/sh/tests/builtins/read1.0.stdout
@@ -0,0 +1,20 @@
+x1 2 3x
+x1x2 3x
+x1x2x3x
+x1x2x3xx
+x1x2x3x
+x1x2x3x
+x1x2x3x
+x 1 2 3 xx
+x1x2x3x
+xx2x3x
+x1xx3x
+x1xx3x
+x1x2x3x
+x1x2x3,,x
+x1x2x3x
+xx2x3x
+x1xx3x
+x1xx3x
+x1x2x3x
+x1x2x3,,x
diff --git a/bin/sh/tests/builtins/read2.0 b/bin/sh/tests/builtins/read2.0
new file mode 100644
index 0000000..fc74511
--- /dev/null
+++ b/bin/sh/tests/builtins/read2.0
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+set -e
+{
+ echo 1
+ echo two
+ echo three
+} | {
+ read x
+ [ "$x" = 1 ]
+ (read x
+ [ "$x" = two ])
+ read x
+ [ "$x" = three ]
+}
+
+T=`mktemp sh-test.XXXXXX`
+trap 'rm -f "$T"' 0
+{
+ echo 1
+ echo two
+ echo three
+} >$T
+{
+ read x
+ [ "$x" = 1 ]
+ (read x
+ [ "$x" = two ])
+ read x
+ [ "$x" = three ]
+} <$T
diff --git a/bin/sh/tests/builtins/read3.0 b/bin/sh/tests/builtins/read3.0
new file mode 100644
index 0000000..c6ae9c1
--- /dev/null
+++ b/bin/sh/tests/builtins/read3.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+printf '%s\n' 'a\ b c' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' 'a b\ c' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' 'a\:b:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' 'a:b\:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\:a' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\\ a' | { read -r a b; printf '%s\n' "x${a}x${b}x"; }
diff --git a/bin/sh/tests/builtins/read3.0.stdout b/bin/sh/tests/builtins/read3.0.stdout
new file mode 100644
index 0000000..8ed98ca
--- /dev/null
+++ b/bin/sh/tests/builtins/read3.0.stdout
@@ -0,0 +1,9 @@
+xa bxcx
+xaxb cx
+xa:bxcx
+xaxb:cx
+x axx
+x:axx
+x\xx
+x\ axx
+x\\\xax
diff --git a/bin/sh/tests/builtins/read4.0 b/bin/sh/tests/builtins/read4.0
new file mode 100644
index 0000000..7204a35
--- /dev/null
+++ b/bin/sh/tests/builtins/read4.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+printf '%s\n' '\a\ b c' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\a b\ c' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\a\:b:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\a:b\:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\:a' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; }
+printf '%s\n' '\\\:a' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; }
diff --git a/bin/sh/tests/builtins/read4.0.stdout b/bin/sh/tests/builtins/read4.0.stdout
new file mode 100644
index 0000000..a8747a4
--- /dev/null
+++ b/bin/sh/tests/builtins/read4.0.stdout
@@ -0,0 +1,8 @@
+xa bxcx
+xaxb cx
+xa:bxcx
+xaxb:cx
+x\xax
+x\xax
+x\ axx
+x\:axx
diff --git a/bin/sh/tests/builtins/read5.0 b/bin/sh/tests/builtins/read5.0
new file mode 100644
index 0000000..7d83391
--- /dev/null
+++ b/bin/sh/tests/builtins/read5.0
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.ISO8859-1
+export LC_CTYPE
+
+# Note: the first and last characters are not whitespace.
+# Exclude backslash and newline.
+bad1=`printf %03o \'\\\\`
+bad2=`printf %03o \''
+'`
+e=
+for i in 0 1 2 3; do
+ for j in 0 1 2 3 4 5 6 7; do
+ for k in 0 1 2 3 4 5 6 7; do
+ case $i$j$k in
+ 000|$bad1|$bad2) continue ;;
+ esac
+ e="$e\\$i$j$k"
+ done
+ done
+done
+e=`printf "$e"`
+[ "${#e}" = 253 ] || echo length bad
+
+r1=`printf '%s\n' "$e" | { read -r x; printf '%s' "$x"; }`
+[ "$r1" = "$e" ] || echo "read with -r bad"
+r2=`printf '%s\n' "$e" | { read x; printf '%s' "$x"; }`
+[ "$r2" = "$e" ] || echo "read without -r bad 1"
+IFS=
+r3=`printf '%s\n' "$e" | { read x; printf '%s' "$x"; }`
+[ "$r3" = "$e" ] || echo "read without -r bad 2"
diff --git a/bin/sh/tests/builtins/read6.0 b/bin/sh/tests/builtins/read6.0
new file mode 100644
index 0000000..2168e10
--- /dev/null
+++ b/bin/sh/tests/builtins/read6.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+: | read x
+r=$?
+[ "$r" = 1 ]
diff --git a/bin/sh/tests/builtins/read7.0 b/bin/sh/tests/builtins/read7.0
new file mode 100644
index 0000000..e78f887
--- /dev/null
+++ b/bin/sh/tests/builtins/read7.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+{ errmsg=`read x <&- 2>&1 >&3`; } 3>&1
+r=$?
+[ "$r" -ge 2 ] && [ "$r" -le 128 ] && [ -n "$errmsg" ]
diff --git a/bin/sh/tests/builtins/return1.0 b/bin/sh/tests/builtins/return1.0
new file mode 100644
index 0000000..787e892
--- /dev/null
+++ b/bin/sh/tests/builtins/return1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+f() {
+ return 0
+ exit 1
+}
+
+f
diff --git a/bin/sh/tests/builtins/return2.1 b/bin/sh/tests/builtins/return2.1
new file mode 100644
index 0000000..0ef8171
--- /dev/null
+++ b/bin/sh/tests/builtins/return2.1
@@ -0,0 +1,7 @@
+# $FreeBSD$
+f() {
+ true && return 1
+ return 0
+}
+
+f
diff --git a/bin/sh/tests/builtins/return3.1 b/bin/sh/tests/builtins/return3.1
new file mode 100644
index 0000000..605ec68
--- /dev/null
+++ b/bin/sh/tests/builtins/return3.1
@@ -0,0 +1,3 @@
+# $FreeBSD$
+return 1
+exit 0
diff --git a/bin/sh/tests/builtins/return4.0 b/bin/sh/tests/builtins/return4.0
new file mode 100644
index 0000000..be5582b
--- /dev/null
+++ b/bin/sh/tests/builtins/return4.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) || exit
+trap 'rm -rf $T' 0
+cd $T || exit 3
+echo 'return 42; exit 4' >testscript
+. ./testscript
+[ "$?" = 42 ] || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/return5.0 b/bin/sh/tests/builtins/return5.0
new file mode 100644
index 0000000..6e4b7bd
--- /dev/null
+++ b/bin/sh/tests/builtins/return5.0
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+if [ "$1" != nested ]; then
+ f() {
+ set -- nested
+ . "$0"
+ # Allow return to return from the function or the dot script.
+ return 4
+ }
+ f
+ exit $(($? ^ 4))
+fi
+# To trigger the bug, the following commands must be at the top level,
+# with newlines in between.
+return 4
+echo bad
+exit 1
diff --git a/bin/sh/tests/builtins/return6.4 b/bin/sh/tests/builtins/return6.4
new file mode 100644
index 0000000..e4d8e0d
--- /dev/null
+++ b/bin/sh/tests/builtins/return6.4
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+while return 4; do exit 3; done
diff --git a/bin/sh/tests/builtins/return7.4 b/bin/sh/tests/builtins/return7.4
new file mode 100644
index 0000000..2047373
--- /dev/null
+++ b/bin/sh/tests/builtins/return7.4
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+f() {
+ while return 4; do exit 3; done
+}
+f
diff --git a/bin/sh/tests/builtins/return8.0 b/bin/sh/tests/builtins/return8.0
new file mode 100644
index 0000000..f00e859
--- /dev/null
+++ b/bin/sh/tests/builtins/return8.0
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+if [ "$1" = nested ]; then
+ return 17
+fi
+
+f() {
+ set -- nested
+ . "$0"
+ return $(($? ^ 1))
+}
+f
+exit $(($? ^ 16))
diff --git a/bin/sh/tests/builtins/set1.0 b/bin/sh/tests/builtins/set1.0
new file mode 100644
index 0000000..fc39fad
--- /dev/null
+++ b/bin/sh/tests/builtins/set1.0
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+set +C
+set +f
+set -e
+
+settings=$(set +o)
+set -C
+set -f
+set +e
+case $- in
+*C*) ;;
+*) echo missing C ;;
+esac
+case $- in
+*f*) ;;
+*) echo missing C ;;
+esac
+case $- in
+*e*) echo bad e ;;
+esac
+eval "$settings"
+case $- in
+*C*) echo bad C ;;
+esac
+case $- in
+*f*) echo bad f ;;
+esac
+case $- in
+*e*) ;;
+*) echo missing e ;;
+esac
diff --git a/bin/sh/tests/builtins/set2.0 b/bin/sh/tests/builtins/set2.0
new file mode 100644
index 0000000..ad13eab
--- /dev/null
+++ b/bin/sh/tests/builtins/set2.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+! env @badness=1 ${SH} -c 'v=`set`; eval "$v"' 2>&1 | grep @badness
diff --git a/bin/sh/tests/builtins/trap1.0 b/bin/sh/tests/builtins/trap1.0
new file mode 100644
index 0000000..313f6a3
--- /dev/null
+++ b/bin/sh/tests/builtins/trap1.0
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+test "$(trap 'echo trapped' EXIT; :)" = trapped || exit 1
+
+test "$(trap 'echo trapped' EXIT; /usr/bin/true)" = trapped || exit 1
+
+result=$(${SH} -c 'trap "echo trapped" EXIT; /usr/bin/false')
+test $? -eq 1 || exit 1
+test "$result" = trapped || exit 1
+
+result=$(${SH} -c 'trap "echo trapped" EXIT; exec /usr/bin/false')
+test $? -eq 1 || exit 1
+test -z "$result" || exit 1
+
+result=0
+trap 'result=$((result+1))' INT
+kill -INT $$
+test "$result" -eq 1 || exit 1
+(kill -INT $$)
+test "$result" -eq 2 || exit 1
+
+exit 0
diff --git a/bin/sh/tests/builtins/trap10.0 b/bin/sh/tests/builtins/trap10.0
new file mode 100644
index 0000000..fa0e35d
--- /dev/null
+++ b/bin/sh/tests/builtins/trap10.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+# Check that the return statement will not break the EXIT trap, ie. all
+# trap commands are executed before the script exits.
+
+test "$(trap 'printf trap; echo ped' EXIT; f() { return; }; f)" = trapped || exit 1
diff --git a/bin/sh/tests/builtins/trap11.0 b/bin/sh/tests/builtins/trap11.0
new file mode 100644
index 0000000..cfeea9e
--- /dev/null
+++ b/bin/sh/tests/builtins/trap11.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+# Check that the return statement will not break the USR1 trap, ie. all
+# trap commands are executed before the script resumes.
+
+result=$(${SH} -c 'trap "printf trap; echo ped" USR1; f() { return $(kill -USR1 $$); }; f')
+test $? -eq 0 || exit 1
+test "$result" = trapped || exit 1
diff --git a/bin/sh/tests/builtins/trap12.0 b/bin/sh/tests/builtins/trap12.0
new file mode 100644
index 0000000..8c62ffd
--- /dev/null
+++ b/bin/sh/tests/builtins/trap12.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+f() {
+ trap 'return 42' USR1
+ kill -USR1 $$
+ return 3
+}
+f
+r=$?
+[ "$r" = 42 ]
diff --git a/bin/sh/tests/builtins/trap13.0 b/bin/sh/tests/builtins/trap13.0
new file mode 100644
index 0000000..d90eb08
--- /dev/null
+++ b/bin/sh/tests/builtins/trap13.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+{
+ trap 'exit 0' INT
+ ${SH} -c 'kill -INT $PPID'
+ exit 3
+} &
+wait $!
diff --git a/bin/sh/tests/builtins/trap14.0 b/bin/sh/tests/builtins/trap14.0
new file mode 100644
index 0000000..97cce8d
--- /dev/null
+++ b/bin/sh/tests/builtins/trap14.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+{
+ trap - INT
+ ${SH} -c 'kill -INT $PPID' &
+ wait
+} &
+wait $!
+r=$?
+[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = INT ]
diff --git a/bin/sh/tests/builtins/trap2.0 b/bin/sh/tests/builtins/trap2.0
new file mode 100644
index 0000000..a05287a
--- /dev/null
+++ b/bin/sh/tests/builtins/trap2.0
@@ -0,0 +1,52 @@
+# $FreeBSD$
+# This is really a test for outqstr(), which is readily accessible via trap.
+
+runtest()
+{
+ teststring=$1
+ trap -- "$teststring" USR1
+ traps=$(trap)
+ if [ "$teststring" != "-" ] && [ -z "$traps" ]; then
+ # One possible reading of POSIX requires the above to return an
+ # empty string because backquote commands are executed in a
+ # subshell and subshells shall reset traps. However, an example
+ # in the normative description of the trap builtin shows the
+ # same usage as here, it is useful and our /bin/sh allows it.
+ echo '$(trap) is broken'
+ exit 1
+ fi
+ trap - USR1
+ eval "$traps"
+ traps2=$(trap)
+ if [ "$traps" != "$traps2" ]; then
+ echo "Mismatch for '$teststring'"
+ exit 1
+ fi
+}
+
+runtest 'echo'
+runtest 'echo hi'
+runtest "'echo' 'hi'"
+runtest '"echo" $PATH'
+runtest '\echo "$PATH"'
+runtest ' 0'
+runtest '0 '
+runtest ' 1'
+runtest '1 '
+i=1
+while [ $i -le 127 ]; do
+ c=$(printf \\"$(printf %o $i)")
+ if [ $i -lt 48 ] || [ $i -gt 57 ]; then
+ runtest "$c"
+ fi
+ runtest " $c$c"
+ runtest "a$c"
+ i=$((i+1))
+done
+IFS=,
+runtest ' '
+runtest ','
+unset IFS
+runtest ' '
+
+exit 0
diff --git a/bin/sh/tests/builtins/trap3.0 b/bin/sh/tests/builtins/trap3.0
new file mode 100644
index 0000000..8160729
--- /dev/null
+++ b/bin/sh/tests/builtins/trap3.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+{
+ trap '' garbage && exit 3
+ trap - garbage && exit 3
+ trap true garbage && exit 3
+ trap '' 99999 && exit 3
+ trap - 99999 && exit 3
+ trap true 99999 && exit 3
+} 2>/dev/null
+exit 0
diff --git a/bin/sh/tests/builtins/trap4.0 b/bin/sh/tests/builtins/trap4.0
new file mode 100644
index 0000000..7f2080e
--- /dev/null
+++ b/bin/sh/tests/builtins/trap4.0
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf $T' 0
+cd $T || exit 3
+mkfifo fifo1
+
+v=$(
+ exec 3>&1
+ : <fifo1 &
+ {
+ wait $!
+ trap 'trap "" PIPE; echo trapped >&3 2>/dev/null' PIPE
+ echo x 2>/dev/null
+ } >fifo1
+)
+test "$v" = trapped
diff --git a/bin/sh/tests/builtins/trap5.0 b/bin/sh/tests/builtins/trap5.0
new file mode 100644
index 0000000..56e0fb1
--- /dev/null
+++ b/bin/sh/tests/builtins/trap5.0
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+set -e
+trap - USR1
+initial=$(trap)
+trap -- -l USR1
+added=$(trap)
+[ -n "$added" ]
+trap - USR1
+second=$(trap)
+[ "$initial" = "$second" ]
+eval "$added"
+added2=$(trap)
+added3=$(trap --)
+[ "$added" = "$added2" ]
+[ "$added2" = "$added3" ]
+trap -- - USR1
+third=$(trap)
+[ "$initial" = "$third" ]
diff --git a/bin/sh/tests/builtins/trap6.0 b/bin/sh/tests/builtins/trap6.0
new file mode 100644
index 0000000..bd2bf7e
--- /dev/null
+++ b/bin/sh/tests/builtins/trap6.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+v=$(
+ ${SH} -c 'trap "echo ok; exit" USR1; kill -USR1 $$' &
+ # Suppress possible message about exit on signal
+ wait $! >/dev/null 2>&1
+)
+r=$(kill -l $?)
+[ "$v" = "ok" ] && { [ "$r" = "USR1" ] || [ "$r" = "usr1" ]; }
diff --git a/bin/sh/tests/builtins/trap7.0 b/bin/sh/tests/builtins/trap7.0
new file mode 100644
index 0000000..35529b8
--- /dev/null
+++ b/bin/sh/tests/builtins/trap7.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+[ "$(trap 'echo trapped' EXIT)" = trapped ]
diff --git a/bin/sh/tests/builtins/trap8.0 b/bin/sh/tests/builtins/trap8.0
new file mode 100644
index 0000000..cdce976
--- /dev/null
+++ b/bin/sh/tests/builtins/trap8.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# I am not sure if POSIX requires the shell to continue processing
+# further trap names in the same trap command after an invalid one.
+
+test -n "$(trap true garbage TERM 2>/dev/null || trap)" || exit 3
+exit 0
diff --git a/bin/sh/tests/builtins/trap9.0 b/bin/sh/tests/builtins/trap9.0
new file mode 100644
index 0000000..0f584ec
--- /dev/null
+++ b/bin/sh/tests/builtins/trap9.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+test "$(trap 'printf trap; echo ped' EXIT; f() { :; }; f)" = trapped || exit 1
diff --git a/bin/sh/tests/builtins/type1.0 b/bin/sh/tests/builtins/type1.0
new file mode 100644
index 0000000..c5e4564
--- /dev/null
+++ b/bin/sh/tests/builtins/type1.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+command -v not-here && exit 1
+command -v /not-here && exit 1
+command -V not-here && exit 1
+command -V /not-here && exit 1
+type not-here && exit 1
+type /not-here && exit 1
+exit 0
diff --git a/bin/sh/tests/builtins/type1.0.stderr b/bin/sh/tests/builtins/type1.0.stderr
new file mode 100644
index 0000000..7853418
--- /dev/null
+++ b/bin/sh/tests/builtins/type1.0.stderr
@@ -0,0 +1,4 @@
+not-here: not found
+/not-here: No such file or directory
+not-here: not found
+/not-here: No such file or directory
diff --git a/bin/sh/tests/builtins/type2.0 b/bin/sh/tests/builtins/type2.0
new file mode 100644
index 0000000..fe44d95
--- /dev/null
+++ b/bin/sh/tests/builtins/type2.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "$*"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check 'PATH=/libexec type ld-elf.so.1 >/dev/null'
+check '! PATH=/libexec type ls 2>/dev/null'
+
+PATH=/libexec:$PATH
+
+check 'type ld-elf.so.1 >/dev/null'
+
+PATH=/libexec
+
+check 'type ld-elf.so.1 >/dev/null'
+check '! type ls 2>/dev/null'
+check 'PATH=/bin type ls >/dev/null'
+check '! PATH=/bin type ld-elf.so.1 2>/dev/null'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/builtins/type3.0 b/bin/sh/tests/builtins/type3.0
new file mode 100644
index 0000000..87cccdd
--- /dev/null
+++ b/bin/sh/tests/builtins/type3.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+[ "$(type type)" = "$(type -- type)" ]
diff --git a/bin/sh/tests/builtins/unalias.0 b/bin/sh/tests/builtins/unalias.0
new file mode 100644
index 0000000..34d8d6e
--- /dev/null
+++ b/bin/sh/tests/builtins/unalias.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+set -e
+
+alias false=true
+false
+unalias false
+false && exit 1
+unalias false && exit 1
+
+alias a1=foo a2=bar
+unalias a1 a2
+unalias a1 && exit 1
+unalias a2 && exit 1
+alias a2=bar
+unalias a1 a2 && exit 1
+
+alias a1=foo a2=bar
+unalias -a
+unalias a1 && exit 1
+unalias a2 && exit 1
+exit 0
diff --git a/bin/sh/tests/builtins/var-assign.0 b/bin/sh/tests/builtins/var-assign.0
new file mode 100644
index 0000000..ace39c0
--- /dev/null
+++ b/bin/sh/tests/builtins/var-assign.0
@@ -0,0 +1,55 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+ :,\
+ continue,\
+ . /dev/null,
+ eval,
+ exec,
+ export -p,
+ readonly -p,
+ set,
+ shift 0,
+ times,
+ trap,
+ unset foo"
+
+UTILS="alias,\
+ bg,\
+ bind,\
+ cd,\
+ command echo,\
+ echo,\
+ false,\
+ fc -l,\
+ fg,\
+ getopts a var,\
+ hash,\
+ jobs,\
+ printf a,\
+ pwd,\
+ read var < /dev/null,\
+ test,\
+ true,\
+ type ls,\
+ ulimit,\
+ umask,\
+ unalias -a,\
+ wait"
+
+set -e
+
+# For special built-ins variable assignments affect the shell environment.
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "VAR=1; VAR=0 ${cmd}; exit \${VAR}" >/dev/null 2>&1
+done
+
+# For other built-ins and utilites they do not.
+set -- ${UTILS}
+for cmd in "$@"
+do
+ ${SH} -c "VAR=0; VAR=1 ${cmd}; exit \${VAR}" >/dev/null 2>&1
+done
diff --git a/bin/sh/tests/builtins/var-assign2.0 b/bin/sh/tests/builtins/var-assign2.0
new file mode 100644
index 0000000..eafec89
--- /dev/null
+++ b/bin/sh/tests/builtins/var-assign2.0
@@ -0,0 +1,55 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+ :,\
+ continue,\
+ . /dev/null,\
+ eval,\
+ exec,\
+ export -p,\
+ readonly -p,\
+ set,\
+ shift 0,\
+ times,\
+ trap,\
+ unset foo"
+
+UTILS="alias,\
+ bg,\
+ bind,\
+ cd,\
+ command echo,\
+ echo,\
+ false,\
+ fc -l,\
+ fg,\
+ getopts a var,\
+ hash,\
+ jobs,\
+ printf a,\
+ pwd,\
+ read var < /dev/null,\
+ test,\
+ true,\
+ type ls,\
+ ulimit,\
+ umask,\
+ unalias -a,\
+ wait"
+
+set -e
+
+# With 'command', variable assignments do not affect the shell environment.
+
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1
+done
+
+set -- ${UTILS}
+for cmd in "$@"
+do
+ ${SH} -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1
+done
diff --git a/bin/sh/tests/builtins/wait1.0 b/bin/sh/tests/builtins/wait1.0
new file mode 100644
index 0000000..1ca8530
--- /dev/null
+++ b/bin/sh/tests/builtins/wait1.0
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+exit 4 & p4=$!
+exit 8 & p8=$!
+wait $p4
+[ $? = 4 ] || failure $LINENO
+wait $p8
+[ $? = 8 ] || failure $LINENO
+
+exit 3 & p3=$!
+exit 7 & p7=$!
+wait $p7
+[ $? = 7 ] || failure $LINENO
+wait $p3
+[ $? = 3 ] || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/wait10.0 b/bin/sh/tests/builtins/wait10.0
new file mode 100644
index 0000000..864fc78
--- /dev/null
+++ b/bin/sh/tests/builtins/wait10.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+# Init cannot be a child of the shell.
+exit 49 & p49=$!
+wait 1 "$p49"
+[ "$?" = 49 ]
diff --git a/bin/sh/tests/builtins/wait2.0 b/bin/sh/tests/builtins/wait2.0
new file mode 100644
index 0000000..e61455c
--- /dev/null
+++ b/bin/sh/tests/builtins/wait2.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+for i in 1 2 3 4 5 6 7 8 9 10; do
+ exit $i &
+done
+wait || failure $LINENO
+wait || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/wait3.0 b/bin/sh/tests/builtins/wait3.0
new file mode 100644
index 0000000..1ed5299
--- /dev/null
+++ b/bin/sh/tests/builtins/wait3.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+failures=
+failure() {
+ echo "Error at line $1" >&2
+ failures=x$failures
+}
+
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf $T' 0
+cd $T || exit 3
+mkfifo fifo1
+for i in 1 2 3 4 5 6 7 8 9 10; do
+ exit $i 4<fifo1 &
+done
+exec 3>fifo1
+wait || failure $LINENO
+(${SH} -c echo >&3) 2>/dev/null && failure $LINENO
+wait || failure $LINENO
+
+test -z "$failures"
diff --git a/bin/sh/tests/builtins/wait4.0 b/bin/sh/tests/builtins/wait4.0
new file mode 100644
index 0000000..7935131
--- /dev/null
+++ b/bin/sh/tests/builtins/wait4.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX`
+trap 'rm -rf $T' 0
+cd $T || exit 3
+mkfifo fifo1
+trapped=
+trap trapped=1 QUIT
+{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 &
+wait $! <fifo1
+r=$?
+[ "$r" -gt 128 ] && [ -n "$trapped" ]
diff --git a/bin/sh/tests/builtins/wait5.0 b/bin/sh/tests/builtins/wait5.0
new file mode 100644
index 0000000..6874bf6
--- /dev/null
+++ b/bin/sh/tests/builtins/wait5.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX`
+trap 'rm -rf $T' 0
+cd $T || exit 3
+mkfifo fifo1
+trapped=
+trap trapped=1 QUIT
+{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 &
+wait <fifo1
+r=$?
+[ "$r" -gt 128 ] && [ -n "$trapped" ]
diff --git a/bin/sh/tests/builtins/wait6.0 b/bin/sh/tests/builtins/wait6.0
new file mode 100644
index 0000000..20e3c68
--- /dev/null
+++ b/bin/sh/tests/builtins/wait6.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+wait --
diff --git a/bin/sh/tests/builtins/wait7.0 b/bin/sh/tests/builtins/wait7.0
new file mode 100644
index 0000000..0fb092f
--- /dev/null
+++ b/bin/sh/tests/builtins/wait7.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+: &
+wait -- $!
diff --git a/bin/sh/tests/builtins/wait8.0 b/bin/sh/tests/builtins/wait8.0
new file mode 100644
index 0000000..b59ff59
--- /dev/null
+++ b/bin/sh/tests/builtins/wait8.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+exit 44 & p44=$!
+exit 45 & p45=$!
+exit 7 & p7=$!
+wait "$p44" "$p7" "$p45"
+[ "$?" = 45 ]
diff --git a/bin/sh/tests/builtins/wait9.127 b/bin/sh/tests/builtins/wait9.127
new file mode 100644
index 0000000..661f275
--- /dev/null
+++ b/bin/sh/tests/builtins/wait9.127
@@ -0,0 +1,3 @@
+# $FreeBSD$
+# Init cannot be a child of the shell.
+wait 1
diff --git a/bin/sh/tests/errors/Makefile b/bin/sh/tests/errors/Makefile
new file mode 100644
index 0000000..9f8b0f2
--- /dev/null
+++ b/bin/sh/tests/errors/Makefile
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/errors
+KYUAFILE= no
+
+FILES= assignment-error1.0
+FILES+= assignment-error2.0
+FILES+= backquote-error1.0
+FILES+= backquote-error2.0
+FILES+= bad-binary1.126
+FILES+= bad-keyword1.0
+FILES+= bad-parm-exp1.0
+FILES+= bad-parm-exp2.2 bad-parm-exp2.2.stderr
+FILES+= bad-parm-exp3.2 bad-parm-exp3.2.stderr
+FILES+= bad-parm-exp4.2 bad-parm-exp4.2.stderr
+FILES+= bad-parm-exp5.2 bad-parm-exp5.2.stderr
+FILES+= bad-parm-exp6.2 bad-parm-exp6.2.stderr
+FILES+= option-error.0
+FILES+= redirection-error.0
+FILES+= redirection-error2.2
+FILES+= redirection-error3.0
+FILES+= redirection-error4.0
+FILES+= redirection-error5.0
+FILES+= redirection-error6.0
+FILES+= redirection-error7.0
+FILES+= write-error1.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/errors/assignment-error1.0 b/bin/sh/tests/errors/assignment-error1.0
new file mode 100644
index 0000000..00eaed9
--- /dev/null
+++ b/bin/sh/tests/errors/assignment-error1.0
@@ -0,0 +1,30 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+ :,\
+ continue,\
+ . /dev/null,\
+ eval,\
+ exec,\
+ export -p,\
+ readonly -p,\
+ set,\
+ shift,\
+ times,\
+ trap,\
+ unset foo"
+
+# If there is no command word, the shell must abort on an assignment error.
+${SH} -c "readonly a=0; a=2; exit 0" 2>/dev/null && exit 1
+
+# Special built-in utilities must abort on an assignment error.
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "readonly a=0; a=2 ${cmd}; exit 0" 2>/dev/null && exit 1
+done
+
+# Other utilities must not abort; we currently still execute them.
+${SH} -c 'readonly a=0; a=1 true; exit $a' 2>/dev/null || exit 1
+${SH} -c 'readonly a=0; a=1 command :; exit $a' 2>/dev/null || exit 1
diff --git a/bin/sh/tests/errors/assignment-error2.0 b/bin/sh/tests/errors/assignment-error2.0
new file mode 100644
index 0000000..ff4e629
--- /dev/null
+++ b/bin/sh/tests/errors/assignment-error2.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+set -e
+HOME=/
+readonly HOME
+cd /sbin
+{ HOME=/bin cd; } 2>/dev/null || :
+[ "$(pwd)" != /bin ]
diff --git a/bin/sh/tests/errors/backquote-error1.0 b/bin/sh/tests/errors/backquote-error1.0
new file mode 100644
index 0000000..43e3303
--- /dev/null
+++ b/bin/sh/tests/errors/backquote-error1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+echo 'echo `for` echo ".BAD"CODE.' | ${SH} +m -i 2>&1 | grep -q BADCODE && exit 1
+exit 0
diff --git a/bin/sh/tests/errors/backquote-error2.0 b/bin/sh/tests/errors/backquote-error2.0
new file mode 100644
index 0000000..5b49e2b
--- /dev/null
+++ b/bin/sh/tests/errors/backquote-error2.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+${SH} -c 'echo `echo .BA"DCODE.`
+echo ".BAD"CODE.' 2>&1 | grep -q BADCODE && exit 1
+echo '`"`' | ${SH} -n 2>/dev/null && exit 1
+echo '`'"'"'`' | ${SH} -n 2>/dev/null && exit 1
+exit 0
diff --git a/bin/sh/tests/errors/bad-binary1.126 b/bin/sh/tests/errors/bad-binary1.126
new file mode 100644
index 0000000..d92e9de
--- /dev/null
+++ b/bin/sh/tests/errors/bad-binary1.126
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# Checking for binary "scripts" without magic number is permitted but not
+# required by POSIX. However, it is preferable to getting errors like
+# Syntax error: word unexpected (expecting ")")
+# from trying to execute ELF binaries for the wrong architecture.
+
+T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit
+trap 'rm -rf "${T}"' 0
+printf '\0echo bad\n' >"$T/testshellproc"
+chmod 755 "$T/testshellproc"
+PATH=$T:$PATH
+testshellproc 2>/dev/null
diff --git a/bin/sh/tests/errors/bad-keyword1.0 b/bin/sh/tests/errors/bad-keyword1.0
new file mode 100644
index 0000000..ac01536
--- /dev/null
+++ b/bin/sh/tests/errors/bad-keyword1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+echo ':; fi' | ${SH} -n 2>/dev/null && exit 1
+exit 0
diff --git a/bin/sh/tests/errors/bad-parm-exp1.0 b/bin/sh/tests/errors/bad-parm-exp1.0
new file mode 100644
index 0000000..6e94994
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+false && {
+ ${}
+ ${foo/}
+ ${foo@bar}
+}
+:
diff --git a/bin/sh/tests/errors/bad-parm-exp2.2 b/bin/sh/tests/errors/bad-parm-exp2.2
new file mode 100644
index 0000000..7e13d2b
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp2.2
@@ -0,0 +1,2 @@
+# $FreeBSD$
+${}
diff --git a/bin/sh/tests/errors/bad-parm-exp2.2.stderr b/bin/sh/tests/errors/bad-parm-exp2.2.stderr
new file mode 100644
index 0000000..d027a5a
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp2.2.stderr
@@ -0,0 +1 @@
+./errors/bad-parm-exp2.2: ${}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp3.2 b/bin/sh/tests/errors/bad-parm-exp3.2
new file mode 100644
index 0000000..a5ecba5
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp3.2
@@ -0,0 +1,2 @@
+# $FreeBSD$
+${foo/}
diff --git a/bin/sh/tests/errors/bad-parm-exp3.2.stderr b/bin/sh/tests/errors/bad-parm-exp3.2.stderr
new file mode 100644
index 0000000..ef40251
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp3.2.stderr
@@ -0,0 +1 @@
+./errors/bad-parm-exp3.2: ${foo/}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp4.2 b/bin/sh/tests/errors/bad-parm-exp4.2
new file mode 100644
index 0000000..9eec8d0
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp4.2
@@ -0,0 +1,2 @@
+# $FreeBSD$
+${foo:@abc}
diff --git a/bin/sh/tests/errors/bad-parm-exp4.2.stderr b/bin/sh/tests/errors/bad-parm-exp4.2.stderr
new file mode 100644
index 0000000..89bd80f
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp4.2.stderr
@@ -0,0 +1 @@
+./errors/bad-parm-exp4.2: ${foo:@...}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp5.2 b/bin/sh/tests/errors/bad-parm-exp5.2
new file mode 100644
index 0000000..459281f
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp5.2
@@ -0,0 +1,2 @@
+# $FreeBSD$
+${/}
diff --git a/bin/sh/tests/errors/bad-parm-exp5.2.stderr b/bin/sh/tests/errors/bad-parm-exp5.2.stderr
new file mode 100644
index 0000000..89b1997
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp5.2.stderr
@@ -0,0 +1 @@
+./errors/bad-parm-exp5.2: ${/}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp6.2 b/bin/sh/tests/errors/bad-parm-exp6.2
new file mode 100644
index 0000000..ba51442
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp6.2
@@ -0,0 +1,2 @@
+# $FreeBSD$
+${#foo^}
diff --git a/bin/sh/tests/errors/bad-parm-exp6.2.stderr b/bin/sh/tests/errors/bad-parm-exp6.2.stderr
new file mode 100644
index 0000000..dbf14b5
--- /dev/null
+++ b/bin/sh/tests/errors/bad-parm-exp6.2.stderr
@@ -0,0 +1 @@
+./errors/bad-parm-exp6.2: ${foo...}: Bad substitution
diff --git a/bin/sh/tests/errors/option-error.0 b/bin/sh/tests/errors/option-error.0
new file mode 100644
index 0000000..b4b44c4
--- /dev/null
+++ b/bin/sh/tests/errors/option-error.0
@@ -0,0 +1,46 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break abc,\
+ continue abc,\
+ .,
+ exit abc,
+ export -x,
+ readonly -x,
+ return abc,
+ set -z,
+ shift abc,
+ trap -y,
+ unset -y"
+
+UTILS="alias -y,\
+ cat -z,\
+ cd abc def,\
+ command break abc,\
+ expr 1 +,\
+ fc -z,\
+ getopts,\
+ hash -z,\
+ jobs -z,\
+ printf,\
+ pwd abc,\
+ read,\
+ test abc =,\
+ ulimit -z,\
+ umask -z,\
+ unalias -z,\
+ wait abc"
+
+# Special built-in utilities must abort on an option or operand error.
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "${cmd}; exit 0" 2>/dev/null && exit 1
+done
+
+# Other utilities must not abort.
+set -- ${UTILS}
+for cmd in "$@"
+do
+ ${SH} -c "${cmd}; exit 0" 2>/dev/null || exit 1
+done
diff --git a/bin/sh/tests/errors/redirection-error.0 b/bin/sh/tests/errors/redirection-error.0
new file mode 100644
index 0000000..cb8c0b1
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error.0
@@ -0,0 +1,53 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+ :,\
+ continue,\
+ . /dev/null,
+ eval,
+ exec,
+ export -p,
+ readonly -p,
+ set,
+ shift,
+ times,
+ trap,
+ unset foo"
+
+UTILS="alias,\
+ bg,\
+ bind,\
+ cd,\
+ command echo,\
+ echo,\
+ false,\
+ fc -l,\
+ fg,\
+ getopts a -a,\
+ hash,\
+ jobs,\
+ printf a,\
+ pwd,\
+ read var < /dev/null,\
+ test,\
+ true,\
+ type ls,\
+ ulimit,\
+ umask,\
+ unalias -a,\
+ wait"
+
+# Special built-in utilities must abort on a redirection error.
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "${cmd} > /; exit 0" 2>/dev/null && exit 1
+done
+
+# Other utilities must not abort.
+set -- ${UTILS}
+for cmd in "$@"
+do
+ ${SH} -c "${cmd} > /; exit 0" 2>/dev/null || exit 1
+done
diff --git a/bin/sh/tests/errors/redirection-error2.2 b/bin/sh/tests/errors/redirection-error2.2
new file mode 100644
index 0000000..32bccd8
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error2.2
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+# sh should fail gracefully on this bad redirect
+${SH} -c 'echo 1 >&$a' 2>/dev/null
diff --git a/bin/sh/tests/errors/redirection-error3.0 b/bin/sh/tests/errors/redirection-error3.0
new file mode 100644
index 0000000..8a07d03
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error3.0
@@ -0,0 +1,54 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+ :,\
+ continue,\
+ . /dev/null,\
+ eval,\
+ exec,\
+ export -p,\
+ readonly -p,\
+ set,\
+ shift,\
+ times,\
+ trap,\
+ unset foo"
+
+UTILS="alias,\
+ bg,\
+ bind,\
+ cd,\
+ command echo,\
+ echo,\
+ false,\
+ fc -l,\
+ fg,\
+ getopts a -a,\
+ hash,\
+ jobs,\
+ printf a,\
+ pwd,\
+ read var < /dev/null,\
+ test,\
+ true,\
+ type ls,\
+ ulimit,\
+ umask,\
+ unalias -a,\
+ wait"
+
+# When used with 'command', neither special built-in utilities nor other
+# utilities must abort on a redirection error.
+
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+ ${SH} -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1
+done
+
+set -- ${UTILS}
+for cmd in "$@"
+do
+ ${SH} -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1
+done
diff --git a/bin/sh/tests/errors/redirection-error4.0 b/bin/sh/tests/errors/redirection-error4.0
new file mode 100644
index 0000000..2060974
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error4.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+# A redirection error should not abort the shell if there is no command word.
+exec 2>/dev/null
+</var/empty/x
+</var/empty/x y=2
+y=2 </var/empty/x
+exit 0
diff --git a/bin/sh/tests/errors/redirection-error5.0 b/bin/sh/tests/errors/redirection-error5.0
new file mode 100644
index 0000000..1fcd47e
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error5.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+# A redirection error on a subshell should not abort the shell.
+exec 2>/dev/null
+( echo bad ) </var/empty/x
+exit 0
diff --git a/bin/sh/tests/errors/redirection-error6.0 b/bin/sh/tests/errors/redirection-error6.0
new file mode 100644
index 0000000..17d1109
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error6.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# A redirection error on a compound command should not abort the shell.
+exec 2>/dev/null
+{ echo bad; } </var/empty/x
+if :; then echo bad; fi </var/empty/x
+for i in 1; do echo bad; done </var/empty/x
+i=0
+while [ $i = 0 ]; do echo bad; i=1; done </var/empty/x
+i=0
+until [ $i != 0 ]; do echo bad; i=1; done </var/empty/x
+case i in *) echo bad ;; esac </var/empty/x
+exit 0
diff --git a/bin/sh/tests/errors/redirection-error7.0 b/bin/sh/tests/errors/redirection-error7.0
new file mode 100644
index 0000000..5b20f04
--- /dev/null
+++ b/bin/sh/tests/errors/redirection-error7.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+! dummy=$(
+ exec 3>&1 >&2 2>&3
+ ulimit -n 9
+ exec 9<.
+) && [ -n "$dummy" ]
diff --git a/bin/sh/tests/errors/write-error1.0 b/bin/sh/tests/errors/write-error1.0
new file mode 100644
index 0000000..fcb52e7
--- /dev/null
+++ b/bin/sh/tests/errors/write-error1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+! echo >&- 2>/dev/null
diff --git a/bin/sh/tests/execution/Makefile b/bin/sh/tests/execution/Makefile
new file mode 100644
index 0000000..302d0d8
--- /dev/null
+++ b/bin/sh/tests/execution/Makefile
@@ -0,0 +1,53 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/execution
+KYUAFILE= no
+
+FILES= bg1.0
+FILES+= bg2.0
+FILES+= bg3.0
+FILES+= bg4.0
+FILES+= bg5.0
+FILES+= bg6.0 bg6.0.stdout
+FILES+= bg7.0
+FILES+= bg8.0
+FILES+= bg9.0
+FILES+= bg10.0 bg10.0.stdout
+FILES+= fork1.0
+FILES+= fork2.0
+FILES+= fork3.0
+FILES+= func1.0
+FILES+= func2.0
+FILES+= func3.0
+FILES+= hash1.0
+FILES+= int-cmd1.0
+FILES+= killed1.0
+FILES+= killed2.0
+FILES+= not1.0
+FILES+= not2.0
+FILES+= path1.0
+FILES+= redir1.0
+FILES+= redir2.0
+FILES+= redir3.0
+FILES+= redir4.0
+FILES+= redir5.0
+FILES+= redir6.0
+FILES+= redir7.0
+FILES+= set-n1.0
+FILES+= set-n2.0
+FILES+= set-n3.0
+FILES+= set-n4.0
+FILES+= set-x1.0
+FILES+= set-x2.0
+FILES+= set-x3.0
+FILES+= shellproc1.0
+FILES+= subshell1.0 subshell1.0.stdout
+FILES+= subshell2.0
+FILES+= subshell3.0
+FILES+= subshell4.0
+FILES+= unknown1.0
+FILES+= var-assign1.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/execution/bg1.0 b/bin/sh/tests/execution/bg1.0
new file mode 100644
index 0000000..edb92ae
--- /dev/null
+++ b/bin/sh/tests/execution/bg1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+: `false` &
diff --git a/bin/sh/tests/execution/bg10.0 b/bin/sh/tests/execution/bg10.0
new file mode 100644
index 0000000..44a25dc
--- /dev/null
+++ b/bin/sh/tests/execution/bg10.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+# The redirection overrides the </dev/null implicit in a background command.
+
+echo yes | ${SH} -c '{ cat & wait; } <&0'
diff --git a/bin/sh/tests/execution/bg10.0.stdout b/bin/sh/tests/execution/bg10.0.stdout
new file mode 100644
index 0000000..7cfab5b
--- /dev/null
+++ b/bin/sh/tests/execution/bg10.0.stdout
@@ -0,0 +1 @@
+yes
diff --git a/bin/sh/tests/execution/bg2.0 b/bin/sh/tests/execution/bg2.0
new file mode 100644
index 0000000..2e2fbc5
--- /dev/null
+++ b/bin/sh/tests/execution/bg2.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+f() { return 42; }
+f
+: | : &
diff --git a/bin/sh/tests/execution/bg3.0 b/bin/sh/tests/execution/bg3.0
new file mode 100644
index 0000000..359fc6f
--- /dev/null
+++ b/bin/sh/tests/execution/bg3.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+f() { return 42; }
+f
+(:) &
diff --git a/bin/sh/tests/execution/bg4.0 b/bin/sh/tests/execution/bg4.0
new file mode 100644
index 0000000..25e4f4e
--- /dev/null
+++ b/bin/sh/tests/execution/bg4.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+x=''
+: ${x:=1} &
+wait
+exit ${x:-0}
diff --git a/bin/sh/tests/execution/bg5.0 b/bin/sh/tests/execution/bg5.0
new file mode 100644
index 0000000..cc9ceaa
--- /dev/null
+++ b/bin/sh/tests/execution/bg5.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+# A background command has an implicit </dev/null redirection.
+
+echo bad | ${SH} -c '{ cat & wait; }'
diff --git a/bin/sh/tests/execution/bg6.0 b/bin/sh/tests/execution/bg6.0
new file mode 100644
index 0000000..b0faf9e
--- /dev/null
+++ b/bin/sh/tests/execution/bg6.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+# The redirection overrides the </dev/null implicit in a background command.
+
+echo yes | ${SH} -c '{ cat & wait; } </dev/stdin'
diff --git a/bin/sh/tests/execution/bg6.0.stdout b/bin/sh/tests/execution/bg6.0.stdout
new file mode 100644
index 0000000..7cfab5b
--- /dev/null
+++ b/bin/sh/tests/execution/bg6.0.stdout
@@ -0,0 +1 @@
+yes
diff --git a/bin/sh/tests/execution/bg7.0 b/bin/sh/tests/execution/bg7.0
new file mode 100644
index 0000000..f771edc
--- /dev/null
+++ b/bin/sh/tests/execution/bg7.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+# The redirection does not apply to the background command, and therefore
+# does not override the implicit </dev/null.
+
+echo bad | ${SH} -c '</dev/null; { cat & wait; }'
diff --git a/bin/sh/tests/execution/bg8.0 b/bin/sh/tests/execution/bg8.0
new file mode 100644
index 0000000..33667cb
--- /dev/null
+++ b/bin/sh/tests/execution/bg8.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+# The redirection does not apply to the background command, and therefore
+# does not override the implicit </dev/null.
+
+echo bad | ${SH} -c 'command eval \) </dev/null 2>/dev/null; { cat & wait; }'
diff --git a/bin/sh/tests/execution/bg9.0 b/bin/sh/tests/execution/bg9.0
new file mode 100644
index 0000000..64fde3e
--- /dev/null
+++ b/bin/sh/tests/execution/bg9.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+# The redirection does not apply to the background command, and therefore
+# does not override the implicit </dev/null.
+
+echo bad | ${SH} -c 'command eval eval \\\) \</dev/null 2>/dev/null; { cat & wait; }'
diff --git a/bin/sh/tests/execution/fork1.0 b/bin/sh/tests/execution/fork1.0
new file mode 100644
index 0000000..2eeac79
--- /dev/null
+++ b/bin/sh/tests/execution/fork1.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+shname=${SH%% *}
+shname=${shname##*/}
+
+result=$(${SH} -c 'ps -p $$ -o comm=')
+test "$result" = "ps" || exit 1
+
+result=$(${SH} -c 'ps -p $$ -o comm=; :')
+test "$result" = "$shname" || exit 1
diff --git a/bin/sh/tests/execution/fork2.0 b/bin/sh/tests/execution/fork2.0
new file mode 100644
index 0000000..62a2537
--- /dev/null
+++ b/bin/sh/tests/execution/fork2.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+result=$(${SH} -c '(/bin/sleep 1)& sleep 0.1; ps -p $! -o comm=; kill $!')
+test "$result" = sleep || exit 1
+
+result=$(${SH} -c '{ trap "echo trapped" EXIT; (/usr/bin/true); } & wait')
+test "$result" = trapped || exit 1
+
+exit 0
diff --git a/bin/sh/tests/execution/fork3.0 b/bin/sh/tests/execution/fork3.0
new file mode 100644
index 0000000..3cb678c
--- /dev/null
+++ b/bin/sh/tests/execution/fork3.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+result=$(${SH} -c 'f() { ps -p $$ -o comm=; }; f')
+test "$result" = "ps"
diff --git a/bin/sh/tests/execution/func1.0 b/bin/sh/tests/execution/func1.0
new file mode 100644
index 0000000..29fcc07
--- /dev/null
+++ b/bin/sh/tests/execution/func1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+MALLOC_OPTIONS=J ${SH} -c 'g() { g() { :; }; :; }; g' &&
+MALLOC_OPTIONS=J ${SH} -c 'g() { unset -f g; :; }; g'
diff --git a/bin/sh/tests/execution/func2.0 b/bin/sh/tests/execution/func2.0
new file mode 100644
index 0000000..9830b5e
--- /dev/null
+++ b/bin/sh/tests/execution/func2.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# The empty pairs of braces here are to test that this does not cause a crash.
+
+f() { }
+f
+hash -v f >/dev/null
+f() { { }; }
+f
+hash -v f >/dev/null
+f() { { } }
+f
+hash -v f >/dev/null
diff --git a/bin/sh/tests/execution/func3.0 b/bin/sh/tests/execution/func3.0
new file mode 100644
index 0000000..e0ed581
--- /dev/null
+++ b/bin/sh/tests/execution/func3.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# This may fail when parsing or when defining the function, or the definition
+# may silently do nothing. In no event may the function be executed.
+
+${SH} -c 'unset() { echo overriding function executed, bad; }; v=1; unset v; exit "${v-0}"' 2>/dev/null
+:
diff --git a/bin/sh/tests/execution/hash1.0 b/bin/sh/tests/execution/hash1.0
new file mode 100644
index 0000000..a645c2a
--- /dev/null
+++ b/bin/sh/tests/execution/hash1.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit
+trap 'rm -rf "${T}"' 0
+PATH=$T:$PATH
+ls -ld . >/dev/null
+cat <<EOF >"$T/ls"
+:
+EOF
+chmod 755 "$T/ls"
+PATH=$PATH
+ls -ld .
diff --git a/bin/sh/tests/execution/int-cmd1.0 b/bin/sh/tests/execution/int-cmd1.0
new file mode 100644
index 0000000..a1f097b
--- /dev/null
+++ b/bin/sh/tests/execution/int-cmd1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+! echo echo bad | $SH -ic 'fi' 2>/dev/null
diff --git a/bin/sh/tests/execution/killed1.0 b/bin/sh/tests/execution/killed1.0
new file mode 100644
index 0000000..41d3e05
--- /dev/null
+++ b/bin/sh/tests/execution/killed1.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+# Sometimes the "Killed" message is not flushed soon enough and it
+# is redirected along with the output of a builtin.
+# Do not change the semicolon to a newline as it would hide the bug.
+
+exec 3>&1
+exec >/dev/null 2>&1
+${SH} -c 'kill -9 $$'; : >&3 2>&3
diff --git a/bin/sh/tests/execution/killed2.0 b/bin/sh/tests/execution/killed2.0
new file mode 100644
index 0000000..7ff3fe2
--- /dev/null
+++ b/bin/sh/tests/execution/killed2.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+# Most shells print a message when a foreground job is killed by a signal.
+# POSIX allows this, provided the message is sent to stderr, not stdout.
+# Some trickery is needed to capture the message as redirecting stderr of
+# the command itself does not affect it. The colon command ensures that
+# the subshell forks for ${SH}.
+
+exec 3>&1
+r=`(${SH} -c 'kill $$'; :) 2>&1 >&3`
+[ -n "$r" ]
diff --git a/bin/sh/tests/execution/not1.0 b/bin/sh/tests/execution/not1.0
new file mode 100644
index 0000000..12c6265
--- /dev/null
+++ b/bin/sh/tests/execution/not1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+f() { ! return $1; }
+f 0 && ! f 1
diff --git a/bin/sh/tests/execution/not2.0 b/bin/sh/tests/execution/not2.0
new file mode 100644
index 0000000..1b128d0
--- /dev/null
+++ b/bin/sh/tests/execution/not2.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+while :; do
+ ! break
+ exit 3
+done
diff --git a/bin/sh/tests/execution/path1.0 b/bin/sh/tests/execution/path1.0
new file mode 100644
index 0000000..50829d6
--- /dev/null
+++ b/bin/sh/tests/execution/path1.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Some builtins should not be overridable via PATH.
+
+set -e
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf ${T}' 0
+echo '#!/bin/sh
+echo bad' >"$T/cd"
+chmod 755 "$T/cd"
+cd /bin
+oPATH=$PATH
+PATH=$T:$PATH:%builtin
+cd /
+PATH=$oPATH
+[ "$(pwd)" = / ]
diff --git a/bin/sh/tests/execution/redir1.0 b/bin/sh/tests/execution/redir1.0
new file mode 100644
index 0000000..dd0011f
--- /dev/null
+++ b/bin/sh/tests/execution/redir1.0
@@ -0,0 +1,27 @@
+# $FreeBSD$
+trap ': $((brokenpipe+=1))' PIPE
+
+P=${TMPDIR:-/tmp}
+cd $P
+T=$(mktemp -d sh-test.XXXXXX)
+cd $T
+
+brokenpipe=0
+mkfifo fifo1 fifo2
+read dummy >fifo2 <fifo1 &
+{
+ exec 4>fifo2
+} 3<fifo2 # Formerly, sh would keep fd 3 and a duplicate of it open.
+echo dummy >fifo1
+if [ $brokenpipe -ne 0 ]; then
+ rc=3
+fi
+wait
+echo dummy >&4 2>/dev/null
+if [ $brokenpipe -eq 1 ]; then
+ : ${rc:=0}
+fi
+
+rm fifo1 fifo2
+rmdir ${P}/${T}
+exit ${rc:-3}
diff --git a/bin/sh/tests/execution/redir2.0 b/bin/sh/tests/execution/redir2.0
new file mode 100644
index 0000000..1588105
--- /dev/null
+++ b/bin/sh/tests/execution/redir2.0
@@ -0,0 +1,29 @@
+# $FreeBSD$
+trap ': $((brokenpipe+=1))' PIPE
+
+P=${TMPDIR:-/tmp}
+cd $P
+T=$(mktemp -d sh-test.XXXXXX)
+cd $T
+
+brokenpipe=0
+mkfifo fifo1 fifo2
+{
+ {
+ exec ${SH} -c 'exec <fifo1; read dummy'
+ } 7<&- # fifo2 should be kept open, but not passed to programs
+ true
+} 7<fifo2 &
+
+exec 4>fifo2
+exec 3>fifo1
+echo dummy >&4 2>/dev/null
+if [ $brokenpipe -eq 1 ]; then
+ : ${rc:=0}
+fi
+echo dummy >&3
+wait
+
+rm fifo1 fifo2
+rmdir ${P}/${T}
+exit ${rc:-3}
diff --git a/bin/sh/tests/execution/redir3.0 b/bin/sh/tests/execution/redir3.0
new file mode 100644
index 0000000..d68e450
--- /dev/null
+++ b/bin/sh/tests/execution/redir3.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+3>&- 3>&-
diff --git a/bin/sh/tests/execution/redir4.0 b/bin/sh/tests/execution/redir4.0
new file mode 100644
index 0000000..57054c1
--- /dev/null
+++ b/bin/sh/tests/execution/redir4.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+{ echo bad 0>&3; } 2>/dev/null 3>/dev/null 3>&-
+exit 0
diff --git a/bin/sh/tests/execution/redir5.0 b/bin/sh/tests/execution/redir5.0
new file mode 100644
index 0000000..707ca68
--- /dev/null
+++ b/bin/sh/tests/execution/redir5.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+{ (echo bad) >/dev/null; } </dev/null
diff --git a/bin/sh/tests/execution/redir6.0 b/bin/sh/tests/execution/redir6.0
new file mode 100644
index 0000000..4e3ac0c
--- /dev/null
+++ b/bin/sh/tests/execution/redir6.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ "$2" != "$3" ]; then
+ echo "Failure at $1" >&2
+ failures=$((failures + 1))
+ fi
+}
+
+check $LINENO "$(trap "echo bye" EXIT; : >/dev/null)" bye
+check $LINENO "$(trap "echo bye" EXIT; { :; } >/dev/null)" bye
+check $LINENO "$(trap "echo bye" EXIT; (:) >/dev/null)" bye
+check $LINENO "$(trap "echo bye" EXIT; (: >/dev/null))" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; : >/dev/null')" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; { :; } >/dev/null')" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; (:) >/dev/null')" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; (: >/dev/null)')" bye
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/execution/redir7.0 b/bin/sh/tests/execution/redir7.0
new file mode 100644
index 0000000..2487bcf
--- /dev/null
+++ b/bin/sh/tests/execution/redir7.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ "$2" != "$3" ]; then
+ echo "Failure at $1" >&2
+ failures=$((failures + 1))
+ fi
+}
+
+check $LINENO "$(trap "echo bye" EXIT; f() { :; }; f >/dev/null)" bye
+check $LINENO "$(trap "echo bye" EXIT; f() { :; }; { f; } >/dev/null)" bye
+check $LINENO "$(trap "echo bye" EXIT; f() { :; }; (f) >/dev/null)" bye
+check $LINENO "$(trap "echo bye" EXIT; f() { :; }; (f >/dev/null))" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; f >/dev/null')" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; { f; } >/dev/null')" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; (f) >/dev/null')" bye
+check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; (f >/dev/null)')" bye
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/execution/set-n1.0 b/bin/sh/tests/execution/set-n1.0
new file mode 100644
index 0000000..14c9b93
--- /dev/null
+++ b/bin/sh/tests/execution/set-n1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+v=$( ($SH -n <<'EOF'
+for
+EOF
+) 2>&1 >/dev/null)
+[ $? -ne 0 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/execution/set-n2.0 b/bin/sh/tests/execution/set-n2.0
new file mode 100644
index 0000000..c7f3162
--- /dev/null
+++ b/bin/sh/tests/execution/set-n2.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+$SH -n <<'EOF'
+echo bad
+EOF
diff --git a/bin/sh/tests/execution/set-n3.0 b/bin/sh/tests/execution/set-n3.0
new file mode 100644
index 0000000..24a9159
--- /dev/null
+++ b/bin/sh/tests/execution/set-n3.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+v=$( ($SH -nc 'for') 2>&1 >/dev/null)
+[ $? -ne 0 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/execution/set-n4.0 b/bin/sh/tests/execution/set-n4.0
new file mode 100644
index 0000000..3698508
--- /dev/null
+++ b/bin/sh/tests/execution/set-n4.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+$SH -nc 'echo bad'
diff --git a/bin/sh/tests/execution/set-x1.0 b/bin/sh/tests/execution/set-x1.0
new file mode 100644
index 0000000..7fe1dbf
--- /dev/null
+++ b/bin/sh/tests/execution/set-x1.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+key='must_contain_this'
+{ r=`set -x; { : "$key"; } 2>&1 >/dev/null`; } 2>/dev/null
+case $r in
+*"$key"*) true ;;
+*) false ;;
+esac
diff --git a/bin/sh/tests/execution/set-x2.0 b/bin/sh/tests/execution/set-x2.0
new file mode 100644
index 0000000..56d54e3
--- /dev/null
+++ b/bin/sh/tests/execution/set-x2.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+key='must contain this'
+PS4="$key+ "
+{ r=`set -x; { :; } 2>&1 >/dev/null`; } 2>/dev/null
+case $r in
+*"$key"*) true ;;
+*) false ;;
+esac
diff --git a/bin/sh/tests/execution/set-x3.0 b/bin/sh/tests/execution/set-x3.0
new file mode 100644
index 0000000..1ca57ac
--- /dev/null
+++ b/bin/sh/tests/execution/set-x3.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+key='must contain this'
+PS4='$key+ '
+{ r=`set -x; { :; } 2>&1 >/dev/null`; } 2>/dev/null
+case $r in
+*"$key"*) true ;;
+*) false ;;
+esac
diff --git a/bin/sh/tests/execution/shellproc1.0 b/bin/sh/tests/execution/shellproc1.0
new file mode 100644
index 0000000..1326bc2
--- /dev/null
+++ b/bin/sh/tests/execution/shellproc1.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit
+trap 'rm -rf "${T}"' 0
+cat <<EOF >"$T/testshellproc"
+printf 'this '
+echo is a test
+EOF
+chmod 755 "$T/testshellproc"
+PATH=$T:$PATH
+[ "`testshellproc`" = "this is a test" ]
diff --git a/bin/sh/tests/execution/subshell1.0 b/bin/sh/tests/execution/subshell1.0
new file mode 100644
index 0000000..347806e
--- /dev/null
+++ b/bin/sh/tests/execution/subshell1.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+(eval "cd /
+v=$(printf %0100000d 1)
+echo \${#v}")
+echo end
diff --git a/bin/sh/tests/execution/subshell1.0.stdout b/bin/sh/tests/execution/subshell1.0.stdout
new file mode 100644
index 0000000..8c71af3
--- /dev/null
+++ b/bin/sh/tests/execution/subshell1.0.stdout
@@ -0,0 +1,2 @@
+100000
+end
diff --git a/bin/sh/tests/execution/subshell2.0 b/bin/sh/tests/execution/subshell2.0
new file mode 100644
index 0000000..3216449
--- /dev/null
+++ b/bin/sh/tests/execution/subshell2.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+f() {
+ x=2
+}
+(
+ x=1
+ f
+ [ "$x" = 2 ]
+)
diff --git a/bin/sh/tests/execution/subshell3.0 b/bin/sh/tests/execution/subshell3.0
new file mode 100644
index 0000000..9a87acb
--- /dev/null
+++ b/bin/sh/tests/execution/subshell3.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+(false; exit) && exit 3
+exit 0
diff --git a/bin/sh/tests/execution/subshell4.0 b/bin/sh/tests/execution/subshell4.0
new file mode 100644
index 0000000..b39edb1
--- /dev/null
+++ b/bin/sh/tests/execution/subshell4.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+(eval "set v=1"; false) && echo bad; :
diff --git a/bin/sh/tests/execution/unknown1.0 b/bin/sh/tests/execution/unknown1.0
new file mode 100644
index 0000000..45f541e
--- /dev/null
+++ b/bin/sh/tests/execution/unknown1.0
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+nosuchtool 2>/dev/null
+[ $? -ne 127 ] && exit 1
+/var/empty/nosuchtool 2>/dev/null
+[ $? -ne 127 ] && exit 1
+(nosuchtool) 2>/dev/null
+[ $? -ne 127 ] && exit 1
+(/var/empty/nosuchtool) 2>/dev/null
+[ $? -ne 127 ] && exit 1
+/ 2>/dev/null
+[ $? -ne 126 ] && exit 1
+PATH=/usr bin 2>/dev/null
+[ $? -ne 126 ] && exit 1
+
+dummy=$(nosuchtool 2>/dev/null)
+[ $? -ne 127 ] && exit 1
+dummy=$(/var/empty/nosuchtool 2>/dev/null)
+[ $? -ne 127 ] && exit 1
+dummy=$( (nosuchtool) 2>/dev/null)
+[ $? -ne 127 ] && exit 1
+dummy=$( (/var/empty/nosuchtool) 2>/dev/null)
+[ $? -ne 127 ] && exit 1
+dummy=$(/ 2>/dev/null)
+[ $? -ne 126 ] && exit 1
+dummy=$(PATH=/usr bin 2>/dev/null)
+[ $? -ne 126 ] && exit 1
+
+exit 0
diff --git a/bin/sh/tests/execution/var-assign1.0 b/bin/sh/tests/execution/var-assign1.0
new file mode 100644
index 0000000..26e5424
--- /dev/null
+++ b/bin/sh/tests/execution/var-assign1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+[ "$(HOME=/etc HOME=/ cd && pwd)" = / ]
diff --git a/bin/sh/tests/expansion/Makefile b/bin/sh/tests/expansion/Makefile
new file mode 100644
index 0000000..bd24319
--- /dev/null
+++ b/bin/sh/tests/expansion/Makefile
@@ -0,0 +1,86 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/expansion
+KYUAFILE= no
+
+FILES= arith1.0
+FILES+= arith2.0
+FILES+= arith3.0
+FILES+= arith4.0
+FILES+= arith5.0
+FILES+= arith6.0
+FILES+= arith7.0
+FILES+= arith8.0
+FILES+= arith9.0
+FILES+= arith10.0
+FILES+= arith11.0
+FILES+= arith12.0
+FILES+= arith13.0
+FILES+= assign1.0
+FILES+= cmdsubst1.0
+FILES+= cmdsubst2.0
+FILES+= cmdsubst3.0
+FILES+= cmdsubst4.0
+FILES+= cmdsubst5.0
+FILES+= cmdsubst6.0
+FILES+= cmdsubst7.0
+FILES+= cmdsubst8.0
+FILES+= cmdsubst9.0
+FILES+= cmdsubst10.0
+FILES+= cmdsubst11.0
+FILES+= cmdsubst12.0
+FILES+= cmdsubst13.0
+FILES+= cmdsubst14.0
+FILES+= cmdsubst15.0
+FILES+= cmdsubst16.0
+FILES+= cmdsubst17.0
+FILES+= export1.0
+FILES+= export2.0
+FILES+= export3.0
+FILES+= heredoc1.0
+FILES+= heredoc2.0
+FILES+= ifs1.0
+FILES+= ifs2.0
+FILES+= ifs3.0
+FILES+= ifs4.0
+FILES+= length1.0
+FILES+= length2.0
+FILES+= length3.0
+FILES+= length4.0
+FILES+= length5.0
+FILES+= length6.0
+FILES+= length7.0
+FILES+= length8.0
+FILES+= local1.0
+FILES+= local2.0
+FILES+= pathname1.0
+FILES+= pathname2.0
+FILES+= pathname3.0
+FILES+= pathname4.0
+FILES+= plus-minus1.0
+FILES+= plus-minus2.0
+FILES+= plus-minus3.0
+FILES+= plus-minus4.0
+FILES+= plus-minus5.0
+FILES+= plus-minus6.0
+FILES+= plus-minus7.0
+FILES+= plus-minus8.0
+FILES+= question1.0
+FILES+= readonly1.0
+FILES+= set-u1.0
+FILES+= set-u2.0
+FILES+= set-u3.0
+FILES+= tilde1.0
+FILES+= tilde2.0
+FILES+= trim1.0
+FILES+= trim2.0
+FILES+= trim3.0
+FILES+= trim4.0
+FILES+= trim5.0
+FILES+= trim6.0
+FILES+= trim7.0
+FILES+= trim8.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/expansion/arith1.0 b/bin/sh/tests/expansion/arith1.0
new file mode 100644
index 0000000..118ba22
--- /dev/null
+++ b/bin/sh/tests/expansion/arith1.0
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ $(($1)) != $2 ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $2 actual $(($1))"
+ fi
+}
+
+check "0&&0" 0
+check "1&&0" 0
+check "0&&1" 0
+check "1&&1" 1
+check "2&&2" 1
+check "1&&2" 1
+check "1<<40&&1<<40" 1
+check "1<<40&&4" 1
+
+check "0||0" 0
+check "1||0" 1
+check "0||1" 1
+check "1||1" 1
+check "2||2" 1
+check "1||2" 1
+check "1<<40||1<<40" 1
+check "1<<40||4" 1
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/arith10.0 b/bin/sh/tests/expansion/arith10.0
new file mode 100644
index 0000000..1aaf619
--- /dev/null
+++ b/bin/sh/tests/expansion/arith10.0
@@ -0,0 +1,35 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ $(($1)) != $2 ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $2 actual $(($1))"
+ fi
+}
+
+readonly ro=4
+rw=1
+check "0 && 0 / 0" 0
+check "1 || 0 / 0" 1
+check "0 && (ro = 2)" 0
+check "ro" 4
+check "1 || (ro = -1)" 1
+check "ro" 4
+check "0 && (rw += 1)" 0
+check "rw" 1
+check "1 || (rw += 1)" 1
+check "rw" 1
+check "0 ? 44 / 0 : 51" 51
+check "0 ? ro = 3 : 52" 52
+check "ro" 4
+check "0 ? rw += 1 : 52" 52
+check "rw" 1
+check "1 ? 68 : 30 / 0" 68
+check "2 ? 1 : (ro += 2)" 1
+check "ro" 4
+check "4 ? 1 : (rw += 1)" 1
+check "rw" 1
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/arith11.0 b/bin/sh/tests/expansion/arith11.0
new file mode 100644
index 0000000..6bc7369
--- /dev/null
+++ b/bin/sh/tests/expansion/arith11.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# Try to divide the smallest integer by -1.
+# On amd64 this causes SIGFPE, so make sure the shell checks.
+
+# Calculate the minimum possible value, assuming two's complement and
+# a certain interpretation of overflow when shifting left.
+minint=1
+while [ $((minint <<= 1)) -gt 0 ]; do
+ :
+done
+v=$( eval ': $((minint / -1))' 2>&1 >/dev/null)
+[ $? -ne 0 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/expansion/arith12.0 b/bin/sh/tests/expansion/arith12.0
new file mode 100644
index 0000000..cb7da3b
--- /dev/null
+++ b/bin/sh/tests/expansion/arith12.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+_x=4 y_=5 z_z=6
+[ "$((_x*100+y_*10+z_z))" = 456 ]
diff --git a/bin/sh/tests/expansion/arith13.0 b/bin/sh/tests/expansion/arith13.0
new file mode 100644
index 0000000..207e488
--- /dev/null
+++ b/bin/sh/tests/expansion/arith13.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+# Pre-increment and pre-decrement in arithmetic expansion are not in POSIX.
+# Require either an error or a correct implementation.
+
+! (eval 'x=4; [ $((++x)) != 5 ] || [ $x != 5 ]') 2>/dev/null &&
+! (eval 'x=2; [ $((--x)) != 1 ] || [ $x != 1 ]') 2>/dev/null
diff --git a/bin/sh/tests/expansion/arith2.0 b/bin/sh/tests/expansion/arith2.0
new file mode 100644
index 0000000..95b48a0
--- /dev/null
+++ b/bin/sh/tests/expansion/arith2.0
@@ -0,0 +1,77 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ $(($1)) != $2 ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $2 actual $(($1))"
+ fi
+}
+
+# variables
+unset v
+check "v=2" 2
+check "v" 2
+check "$(($v))" 2
+check "v+=1" 3
+check "v" 3
+
+# constants
+check "4611686018427387904" 4611686018427387904
+check "0x4000000000000000" 4611686018427387904
+check "0400000000000000000000" 4611686018427387904
+check "0x4Ab0000000000000" 5381801554707742720
+check "010" 8
+
+# try out all operators
+v=42
+check "!v" 0
+check "!!v" 1
+check "!0" 1
+check "~0" -1
+check "~(-1)" 0
+check "-0" 0
+check "-v" -42
+check "v*v" 1764
+check "v/2" 21
+check "v%10" 2
+check "v+v" 84
+check "v-4" 38
+check "v<<1" 84
+check "v>>1" 21
+check "v<43" 1
+check "v>42" 0
+check "v<=43" 1
+check "v>=43" 0
+check "v==41" 0
+check "v!=42" 0
+check "v&3" 2
+check "v^3" 41
+check "v|3" 43
+check "v>=40&&v<=44" 1
+check "v<40||v>44" 0
+check "(v=42)&&(v+=1)==43" 1
+check "v" 43
+check "(v=42)&&(v-=1)==41" 1
+check "v" 41
+check "(v=42)&&(v*=2)==84" 1
+check "v" 84
+check "(v=42)&&(v/=10)==4" 1
+check "v" 4
+check "(v=42)&&(v%=10)==2" 1
+check "v" 2
+check "(v=42)&&(v<<=1)==84" 1
+check "v" 84
+check "(v=42)&&(v>>=2)==10" 1
+check "v" 10
+check "(v=42)&&(v&=32)==32" 1
+check "v" 32
+check "(v=42)&&(v^=32)==10" 1
+check "v" 10
+check "(v=42)&&(v|=32)==42" 1
+check "v" 42
+
+# missing: ternary
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/arith3.0 b/bin/sh/tests/expansion/arith3.0
new file mode 100644
index 0000000..b69159d
--- /dev/null
+++ b/bin/sh/tests/expansion/arith3.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ $(($1)) != $2 ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $2 actual $(($1))"
+ fi
+}
+
+check "1 << 1 + 1 | 1" 5
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/arith4.0 b/bin/sh/tests/expansion/arith4.0
new file mode 100644
index 0000000..610dad8
--- /dev/null
+++ b/bin/sh/tests/expansion/arith4.0
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ $(($1)) != $2 ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $2 actual $(($1))"
+ fi
+}
+
+check '20 / 2 / 2' 5
+check '20 - 2 - 2' 16
+unset a b c d
+check "a = b = c = d = 1" 1
+check "a == 1 && b == 1 && c == 1 && d == 1" 1
+check "a += b += c += d" 4
+check "a == 4 && b == 3 && c == 2 && d == 1" 1
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/arith5.0 b/bin/sh/tests/expansion/arith5.0
new file mode 100644
index 0000000..d0f2331
--- /dev/null
+++ b/bin/sh/tests/expansion/arith5.0
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ "$2" != "$3" ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $3 actual $2"
+ fi
+}
+
+unset a
+check '$((1+${a:-$((7+2))}))' "$((1+${a:-$((7+2))}))" 10
+check '$((1+${a:=$((2+2))}))' "$((1+${a:=$((2+2))}))" 5
+check '$a' "$a" 4
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/arith6.0 b/bin/sh/tests/expansion/arith6.0
new file mode 100644
index 0000000..fc4589c
--- /dev/null
+++ b/bin/sh/tests/expansion/arith6.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+v1=1\ +\ 1
+v2=D
+v3=C123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+f() { v4="$*"; }
+
+while [ ${#v2} -lt 1250 ]; do
+ eval $v2=$((3+${#v2})) $v3=$((4-${#v2}))
+ eval f $(($v2+ $v1 +$v3))
+ if [ $v4 -ne 9 ]; then
+ echo bad: $v4 -ne 9 at ${#v2}
+ fi
+ v2=x$v2
+ v3=y$v3
+done
diff --git a/bin/sh/tests/expansion/arith7.0 b/bin/sh/tests/expansion/arith7.0
new file mode 100644
index 0000000..5aada2b
--- /dev/null
+++ b/bin/sh/tests/expansion/arith7.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+v=1+
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+[ "$(cat <<EOF
+$(($v 1))
+EOF
+)" = 1025 ]
diff --git a/bin/sh/tests/expansion/arith8.0 b/bin/sh/tests/expansion/arith8.0
new file mode 100644
index 0000000..2d03e50
--- /dev/null
+++ b/bin/sh/tests/expansion/arith8.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+v=$( (eval ': $((08))') 2>&1 >/dev/null)
+[ $? -ne 0 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/expansion/arith9.0 b/bin/sh/tests/expansion/arith9.0
new file mode 100644
index 0000000..cc8b597
--- /dev/null
+++ b/bin/sh/tests/expansion/arith9.0
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if [ $(($1)) != $2 ]; then
+ failures=$((failures+1))
+ echo "For $1, expected $2 actual $(($1))"
+ fi
+}
+
+check "0 ? 44 : 51" 51
+check "1 ? 68 : 30" 68
+check "2 ? 1 : -5" 1
+check "0 ? 4 : 0 ? 5 : 6" 6
+check "0 ? 4 : 1 ? 5 : 6" 5
+check "1 ? 4 : 0 ? 5 : 6" 4
+check "1 ? 4 : 1 ? 5 : 6" 4
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/assign1.0 b/bin/sh/tests/expansion/assign1.0
new file mode 100644
index 0000000..d4fa772
--- /dev/null
+++ b/bin/sh/tests/expansion/assign1.0
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'v=; set -- ${v=a b} $v' '0|'
+testcase 'unset v; set -- ${v=a b} $v' '4|a|b|a|b'
+testcase 'v=; set -- ${v:=a b} $v' '4|a|b|a|b'
+testcase 'v=; set -- "${v:=a b}" "$v"' '2|a b|a b'
+# expect sensible behaviour, although it disagrees with POSIX
+testcase 'v=; set -- ${v:=a\ b} $v' '4|a|b|a|b'
+testcase 'v=; set -- ${v:=$p} $v' '2|/etc/|/etc/'
+testcase 'v=; set -- "${v:=$p}" "$v"' '2|/et[c]/|/et[c]/'
+testcase 'v=; set -- "${v:=a\ b}" "$v"' '2|a\ b|a\ b'
+testcase 'v=; set -- ${v:="$p"} $v' '2|/etc/|/etc/'
+# whether $p is quoted or not shouldn't really matter
+testcase 'v=; set -- "${v:="$p"}" "$v"' '2|/et[c]/|/et[c]/'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/cmdsubst1.0 b/bin/sh/tests/expansion/cmdsubst1.0
new file mode 100644
index 0000000..515c7da
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst1.0
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$(echo abcde)" = "abcde"'
+check '"$(echo abcde; :)" = "abcde"'
+
+check '"$(printf abcde)" = "abcde"'
+check '"$(printf abcde; :)" = "abcde"'
+
+# regular
+check '-n "$(umask)"'
+check '-n "$(umask; :)"'
+check '-n "$(umask 2>&1)"'
+check '-n "$(umask 2>&1; :)"'
+
+# special
+check '-n "$(times)"'
+check '-n "$(times; :)"'
+check '-n "$(times 2>&1)"'
+check '-n "$(times 2>&1; :)"'
+
+# regular
+check '".$(umask -@ 2>&1)." = ".umask: Illegal option -@."'
+check '".$(umask -@ 2>&1; :)." = ".umask: Illegal option -@."'
+check '".$({ umask -@; } 2>&1)." = ".umask: Illegal option -@."'
+
+# special
+check '".$(shift xyz 2>&1)." = ".shift: Illegal number: xyz."'
+check '".$(shift xyz 2>&1; :)." = ".shift: Illegal number: xyz."'
+check '".$({ shift xyz; } 2>&1)." = ".shift: Illegal number: xyz."'
+
+v=1
+check '-z "$(v=2 :)"'
+check '"$v" = 1'
+check '-z "$(v=3)"'
+check '"$v" = 1'
+check '"$(v=4 eval echo \$v)" = 4'
+check '"$v" = 1'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/expansion/cmdsubst10.0 b/bin/sh/tests/expansion/cmdsubst10.0
new file mode 100644
index 0000000..7cf17a3
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst10.0
@@ -0,0 +1,51 @@
+# $FreeBSD$
+
+a1=$(alias)
+: $(alias testalias=abcd)
+a2=$(alias)
+[ "$a1" = "$a2" ] || echo Error at line $LINENO
+
+alias testalias2=abcd
+a1=$(alias)
+: $(unalias testalias2)
+a2=$(alias)
+[ "$a1" = "$a2" ] || echo Error at line $LINENO
+
+[ "$(command -V pwd)" = "$(command -V pwd; exit $?)" ] || echo Error at line $LINENO
+
+v=1
+: $(export v=2)
+[ "$v" = 1 ] || echo Error at line $LINENO
+
+rotest=1
+: $(readonly rotest=2)
+[ "$rotest" = 1 ] || echo Error at line $LINENO
+
+set +u
+: $(set -u)
+case $- in
+*u*) echo Error at line $LINENO ;;
+esac
+set +u
+
+set +u
+: $(set -o nounset)
+case $- in
+*u*) echo Error at line $LINENO ;;
+esac
+set +u
+
+set +u
+: $(command set -u)
+case $- in
+*u*) echo Error at line $LINENO ;;
+esac
+set +u
+
+umask 77
+u1=$(umask)
+: $(umask 022)
+u2=$(umask)
+[ "$u1" = "$u2" ] || echo Error at line $LINENO
+
+dummy=$(exit 3); [ $? -eq 3 ] || echo Error at line $LINENO
diff --git a/bin/sh/tests/expansion/cmdsubst11.0 b/bin/sh/tests/expansion/cmdsubst11.0
new file mode 100644
index 0000000..f1af547
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst11.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# Not required by POSIX but useful for efficiency.
+
+[ $$ = $(eval '${SH} -c echo\ \$PPID') ]
diff --git a/bin/sh/tests/expansion/cmdsubst12.0 b/bin/sh/tests/expansion/cmdsubst12.0
new file mode 100644
index 0000000..50394db
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst12.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+f() {
+ echo x$(printf foo >&2)y
+}
+[ "$(f 2>&1)" = "fooxy" ]
diff --git a/bin/sh/tests/expansion/cmdsubst13.0 b/bin/sh/tests/expansion/cmdsubst13.0
new file mode 100644
index 0000000..7fdc5b2
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst13.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+x=1 y=2
+[ "$(
+ case $((x+=1)) in
+ ($((y+=1))) echo bad1 ;;
+ ($((y-1))) echo $x.$y ;;
+ ($((y=2))) echo bad2 ;;
+ (*) echo bad3 ;;
+ esac
+)" = "2.3" ] || echo "Error at $LINENO"
+[ "$x.$y" = "1.2" ] || echo "Error at $LINENO"
diff --git a/bin/sh/tests/expansion/cmdsubst14.0 b/bin/sh/tests/expansion/cmdsubst14.0
new file mode 100644
index 0000000..bdbbb82
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst14.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+! v=`false
+
+`
diff --git a/bin/sh/tests/expansion/cmdsubst15.0 b/bin/sh/tests/expansion/cmdsubst15.0
new file mode 100644
index 0000000..31d85d4
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst15.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+! v=`false;
+
+`
diff --git a/bin/sh/tests/expansion/cmdsubst16.0 b/bin/sh/tests/expansion/cmdsubst16.0
new file mode 100644
index 0000000..71df562
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst16.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+f() { return 3; }
+f
+[ `echo $?` = 3 ]
diff --git a/bin/sh/tests/expansion/cmdsubst17.0 b/bin/sh/tests/expansion/cmdsubst17.0
new file mode 100644
index 0000000..8c29e83
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst17.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+f() { return 3; }
+f
+[ `echo $?; :` = 3 ]
diff --git a/bin/sh/tests/expansion/cmdsubst2.0 b/bin/sh/tests/expansion/cmdsubst2.0
new file mode 100644
index 0000000..b86776e
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst2.0
@@ -0,0 +1,43 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '`echo /et[c]/` = "/etc/"'
+check '`printf /var/empty%s /et[c]/` = "/var/empty/etc/"'
+check '"`echo /et[c]/`" = "/etc/"'
+check '`echo "/et[c]/"` = "/etc/"'
+check '`printf /var/empty%s "/et[c]/"` = "/var/empty/et[c]/"'
+check '`printf /var/empty/%s \"/et[c]/\"` = "/var/empty/\"/et[c]/\""'
+check '"`echo \"/et[c]/\"`" = "/et[c]/"'
+check '"`echo "/et[c]/"`" = "/et[c]/"'
+check '`echo $$` = $$'
+check '"`echo $$`" = $$'
+check '`echo \$\$` = $$'
+check '"`echo \$\$`" = $$'
+
+# Command substitutions consisting of a single builtin may be treated
+# differently.
+check '`:; echo /et[c]/` = "/etc/"'
+check '`:; printf /var/empty%s /et[c]/` = "/var/empty/etc/"'
+check '"`:; echo /et[c]/`" = "/etc/"'
+check '`:; echo "/et[c]/"` = "/etc/"'
+check '`:; printf /var/empty%s "/et[c]/"` = "/var/empty/et[c]/"'
+check '`:; printf /var/empty/%s \"/et[c]/\"` = "/var/empty/\"/et[c]/\""'
+check '"`:; echo \"/et[c]/\"`" = "/et[c]/"'
+check '"`:; echo "/et[c]/"`" = "/et[c]/"'
+check '`:; echo $$` = $$'
+check '"`:; echo $$`" = $$'
+check '`:; echo \$\$` = $$'
+check '"`:; echo \$\$`" = $$'
+
+check '`set -f; echo /et[c]/` = "/etc/"'
+check '"`set -f; echo /et[c]/`" = "/et[c]/"'
+
+exit $((failures > 0))
diff --git a/bin/sh/tests/expansion/cmdsubst3.0 b/bin/sh/tests/expansion/cmdsubst3.0
new file mode 100644
index 0000000..abb6b22
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst3.0
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+unset LC_ALL
+export LC_CTYPE=en_US.ISO8859-1
+
+e=
+for i in 0 1 2 3; do
+ for j in 0 1 2 3 4 5 6 7; do
+ for k in 0 1 2 3 4 5 6 7; do
+ case $i$j$k in
+ 000) continue ;;
+ esac
+ e="$e\n\\$i$j$k"
+ done
+ done
+done
+e1=$(printf "$e")
+e2="$(printf "$e")"
+[ "${#e1}" = 510 ] || echo length bad
+[ "$e1" = "$e2" ] || echo e1 != e2
+[ "$e1" = "$(printf "$e")" ] || echo quoted bad
+IFS=
+[ "$e1" = $(printf "$e") ] || echo unquoted bad
diff --git a/bin/sh/tests/expansion/cmdsubst4.0 b/bin/sh/tests/expansion/cmdsubst4.0
new file mode 100644
index 0000000..ee1ce73
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst4.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+exec 2>/dev/null
+! y=$(: </var/empty/nonexistent)
diff --git a/bin/sh/tests/expansion/cmdsubst5.0 b/bin/sh/tests/expansion/cmdsubst5.0
new file mode 100644
index 0000000..afca371
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst5.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+unset v
+exec 2>/dev/null
+! y=$(: ${v?})
diff --git a/bin/sh/tests/expansion/cmdsubst6.0 b/bin/sh/tests/expansion/cmdsubst6.0
new file mode 100644
index 0000000..6586f33
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst6.0
@@ -0,0 +1,53 @@
+# $FreeBSD$
+# This tests if the cmdsubst optimization is still used if possible.
+
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+
+ unset v
+ eval "pid=\$(dummy=$code echo \$(\$SH -c echo\ \\\$PPID))"
+
+ if [ "$pid" = "$$" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "Failure for $code"
+ fi
+}
+
+unset v
+w=1
+testcase '$w'
+testcase '1${w+1}'
+testcase '1${w-1}'
+testcase '1${v+1}'
+testcase '1${v-1}'
+testcase '1${w:+1}'
+testcase '1${w:-1}'
+testcase '1${v:+1}'
+testcase '1${v:-1}'
+testcase '${w?}'
+testcase '${w:?}'
+testcase '${w#x}'
+testcase '${w##x}'
+testcase '${w%x}'
+testcase '${w%%x}'
+
+testcase '$((w))'
+testcase '$(((w+4)*2/3))'
+testcase '$((w==1))'
+testcase '$((w>=0 && w<=5 && w!=2))'
+testcase '$((${#w}))'
+testcase '$((${#IFS}))'
+testcase '$((${#w}>=1))'
+testcase '$(($$))'
+testcase '$(($#))'
+testcase '$(($?))'
+
+testcase '$(: $((w=4)))'
+testcase '$(: ${v=2})'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/cmdsubst7.0 b/bin/sh/tests/expansion/cmdsubst7.0
new file mode 100644
index 0000000..dbd1aec
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst7.0
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+
+ unset v
+ eval ": \$($code)"
+
+ if [ "${v:+bad}" = "" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "Failure for $code"
+ fi
+}
+
+testcase ': ${v=0}'
+testcase ': ${v:=0}'
+testcase ': $((v=1))'
+testcase ': $((v+=1))'
+w='v=1'
+testcase ': $(($w))'
+testcase ': $((${$+v=1}))'
+testcase ': $((v${$+=1}))'
+testcase ': $((v $(echo =) 1))'
+testcase ': $(($(echo $w)))'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/cmdsubst8.0 b/bin/sh/tests/expansion/cmdsubst8.0
new file mode 100644
index 0000000..52adaea
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst8.0
@@ -0,0 +1,17 @@
+# $FreeBSD$
+# Not required by POSIX (although referenced in a non-normative section),
+# but possibly useful.
+
+: hi there &
+p=$!
+q=$(jobs -l $p)
+
+# Change tabs to spaces.
+set -f
+set -- $q
+r="$*"
+
+case $r in
+*" $p "*) ;;
+*) echo Pid missing; exit 3 ;;
+esac
diff --git a/bin/sh/tests/expansion/cmdsubst9.0 b/bin/sh/tests/expansion/cmdsubst9.0
new file mode 100644
index 0000000..0b1f81f
--- /dev/null
+++ b/bin/sh/tests/expansion/cmdsubst9.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+set -e
+
+cd /
+dummy=$(cd /bin)
+[ "$(pwd)" = / ]
+
+v=1
+dummy=$(eval v=2)
+[ "$v" = 1 ]
diff --git a/bin/sh/tests/expansion/export1.0 b/bin/sh/tests/expansion/export1.0
new file mode 100644
index 0000000..4ee3ef4
--- /dev/null
+++ b/bin/sh/tests/expansion/export1.0
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+w='@ vv=6'
+
+v=0 vv=0
+export \v=$w
+[ "$v" = "@" ] || echo "Expected @ got $v"
+[ "$vv" = "6" ] || echo "Expected 6 got $vv"
+
+HOME=/known/value
+
+export \v=~
+[ "$v" = \~ ] || echo "Expected ~ got $v"
diff --git a/bin/sh/tests/expansion/export2.0 b/bin/sh/tests/expansion/export2.0
new file mode 100644
index 0000000..57f64e7
--- /dev/null
+++ b/bin/sh/tests/expansion/export2.0
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+w='@ @'
+check() {
+ [ "$v" = "$w" ] || echo "Expected $w got $v"
+}
+
+export v=$w
+check
+
+HOME=/known/value
+check() {
+ [ "$v" = ~ ] || echo "Expected $HOME got $v"
+}
+
+export v=~
+check
+
+check() {
+ [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+}
+
+export v=x:~
+check
diff --git a/bin/sh/tests/expansion/export3.0 b/bin/sh/tests/expansion/export3.0
new file mode 100644
index 0000000..a1a0e66
--- /dev/null
+++ b/bin/sh/tests/expansion/export3.0
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+w='@ @'
+check() {
+ [ "$v" = "$w" ] || echo "Expected $w got $v"
+}
+
+command export v=$w
+check
+command command export v=$w
+check
+
+HOME=/known/value
+check() {
+ [ "$v" = ~ ] || echo "Expected $HOME got $v"
+}
+
+command export v=~
+check
+command command export v=~
+check
+
+check() {
+ [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+}
+
+command export v=x:~
+check
+command command export v=x:~
+check
diff --git a/bin/sh/tests/expansion/heredoc1.0 b/bin/sh/tests/expansion/heredoc1.0
new file mode 100644
index 0000000..a67b2da
--- /dev/null
+++ b/bin/sh/tests/expansion/heredoc1.0
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+f() { return $1; }
+
+[ `f 42; { cat; } <<EOF
+$?
+EOF
+` = 42 ] || echo compound command bad
+
+[ `f 42; (cat) <<EOF
+$?
+EOF
+` = 42 ] || echo subshell bad
+
+long=`printf %08192d 0`
+
+[ `f 42; { cat; } <<EOF
+$long.$?
+EOF
+` = $long.42 ] || echo long compound command bad
+
+[ `f 42; (cat) <<EOF
+$long.$?
+EOF
+` = $long.42 ] || echo long subshell bad
diff --git a/bin/sh/tests/expansion/heredoc2.0 b/bin/sh/tests/expansion/heredoc2.0
new file mode 100644
index 0000000..2551432
--- /dev/null
+++ b/bin/sh/tests/expansion/heredoc2.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+f() { return $1; }
+
+[ `f 42; cat <<EOF
+$?
+EOF
+` = 42 ] || echo simple command bad
+
+long=`printf %08192d 0`
+
+[ `f 42; cat <<EOF
+$long.$?
+EOF
+` = $long.42 ] || echo long simple command bad
diff --git a/bin/sh/tests/expansion/ifs1.0 b/bin/sh/tests/expansion/ifs1.0
new file mode 100644
index 0000000..e7f53c7
--- /dev/null
+++ b/bin/sh/tests/expansion/ifs1.0
@@ -0,0 +1,35 @@
+# $FreeBSD$
+
+c=: e= s=' '
+failures=''
+ok=''
+
+check_result() {
+ if [ "x$2" = "x$3" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $1, expected $3 actual $2"
+ fi
+}
+
+IFS='
+'
+set -- a ''
+set -- "$@"
+check_result 'set -- "$@"' "($#)($1)($2)" "(2)(a)()"
+
+set -- a ''
+set -- "$@"$e
+check_result 'set -- "$@"$e' "($#)($1)($2)" "(2)(a)()"
+
+set -- a ''
+set -- "$@"$s
+check_result 'set -- "$@"$s' "($#)($1)($2)" "(2)(a)()"
+
+IFS="$c"
+set -- a ''
+set -- "$@"$c
+check_result 'set -- "$@"$c' "($#)($1)($2)" "(2)(a)()"
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/ifs2.0 b/bin/sh/tests/expansion/ifs2.0
new file mode 100644
index 0000000..e91b867
--- /dev/null
+++ b/bin/sh/tests/expansion/ifs2.0
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+failures=0
+i=1
+set -f
+while [ "$i" -le 127 ]; do
+ # A different byte still in the range 1..127.
+ i2=$((i^2+(i==2)))
+ # Add a character to work around command substitution's removal of
+ # final newlines, then remove it again.
+ c=$(printf \\"$(printf %o@ "$i")")
+ c=${c%@}
+ c2=$(printf \\"$(printf %o@ "$i2")")
+ c2=${c2%@}
+ IFS=$c
+ set -- $c2$c$c2$c$c2
+ if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] ||
+ [ "$3" != "$c2" ]; then
+ echo "Bad results for separator $i (word $i2)" >&2
+ : $((failures += 1))
+ fi
+ i=$((i+1))
+done
+exit $((failures > 0))
diff --git a/bin/sh/tests/expansion/ifs3.0 b/bin/sh/tests/expansion/ifs3.0
new file mode 100644
index 0000000..0569b57
--- /dev/null
+++ b/bin/sh/tests/expansion/ifs3.0
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+failures=0
+unset LC_ALL
+export LC_CTYPE=en_US.ISO8859-1
+i=128
+set -f
+while [ "$i" -le 255 ]; do
+ i2=$((i^2))
+ c=$(printf \\"$(printf %o "$i")")
+ c2=$(printf \\"$(printf %o "$i2")")
+ IFS=$c
+ set -- $c2$c$c2$c$c2
+ if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] ||
+ [ "$3" != "$c2" ]; then
+ echo "Bad results for separator $i (word $i2)" >&2
+ : $((failures += 1))
+ fi
+ i=$((i+1))
+done
+exit $((failures > 0))
diff --git a/bin/sh/tests/expansion/ifs4.0 b/bin/sh/tests/expansion/ifs4.0
new file mode 100644
index 0000000..5b896a2
--- /dev/null
+++ b/bin/sh/tests/expansion/ifs4.0
@@ -0,0 +1,39 @@
+# $FreeBSD$
+
+c=: e= s=' '
+failures=''
+ok=''
+
+check_result() {
+ if [ "x$2" = "x$3" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $1, expected $3 actual $2"
+ fi
+}
+
+IFS='
+'
+set -- a b '' c
+set -- $@
+check_result 'set -- $@' "($#)($1)($2)($3)($4)" "(3)(a)(b)(c)()"
+
+IFS=''
+set -- a b '' c
+set -- $@
+check_result 'set -- $@' "($#)($1)($2)($3)($4)" "(3)(a)(b)(c)()"
+
+set -- a b '' c
+set -- $*
+check_result 'set -- $*' "($#)($1)($2)($3)($4)" "(3)(a)(b)(c)()"
+
+set -- a b '' c
+set -- "$@"
+check_result 'set -- "$@"' "($#)($1)($2)($3)($4)" "(4)(a)(b)()(c)"
+
+set -- a b '' c
+set -- "$*"
+check_result 'set -- "$*"' "($#)($1)($2)($3)($4)" "(1)(abc)()()()"
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/length1.0 b/bin/sh/tests/expansion/length1.0
new file mode 100644
index 0000000..2aaebf9
--- /dev/null
+++ b/bin/sh/tests/expansion/length1.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+v=abcd
+[ "${#v}" = 4 ] || echo '${#v} wrong'
+v=$$
+[ "${#$}" = "${#v}" ] || echo '${#$} wrong'
+[ "${#!}" = 0 ] || echo '${#!} wrong'
+set -- 01 2 3 4 5 6 7 8 9 10 11 12 0013
+[ "${#1}" = 2 ] || echo '${#1} wrong'
+[ "${#13}" = 4 ] || echo '${#13} wrong'
+v=$0
+[ "${#0}" = "${#v}" ] || echo '${#0} wrong'
diff --git a/bin/sh/tests/expansion/length2.0 b/bin/sh/tests/expansion/length2.0
new file mode 100644
index 0000000..d749b51
--- /dev/null
+++ b/bin/sh/tests/expansion/length2.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+v=$-
+[ "${#-}" = "${#v}" ] || echo '${#-} wrong'
diff --git a/bin/sh/tests/expansion/length3.0 b/bin/sh/tests/expansion/length3.0
new file mode 100644
index 0000000..2093eed
--- /dev/null
+++ b/bin/sh/tests/expansion/length3.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+set -- 1 2 3 4 5 6 7 8 9 10 11 12 13
+[ "$#" = 13 ] || echo '$# wrong'
+[ "${#}" = 13 ] || echo '${#} wrong'
+[ "${##}" = 2 ] || echo '${##} wrong'
+set --
+[ "$#" = 0 ] || echo '$# wrong'
+[ "${#}" = 0 ] || echo '${#} wrong'
+[ "${##}" = 1 ] || echo '${##} wrong'
diff --git a/bin/sh/tests/expansion/length4.0 b/bin/sh/tests/expansion/length4.0
new file mode 100644
index 0000000..5348be5
--- /dev/null
+++ b/bin/sh/tests/expansion/length4.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+# The construct ${#?} is ambiguous in POSIX.1-2008: it could be the length
+# of $? or it could be $# giving an error in the (impossible) case that it
+# is not set.
+# We use the former interpretation; it seems more useful.
+
+:
+[ "${#?}" = 1 ] || echo '${#?} wrong'
+(exit 42)
+[ "${#?}" = 2 ] || echo '${#?} wrong'
diff --git a/bin/sh/tests/expansion/length5.0 b/bin/sh/tests/expansion/length5.0
new file mode 100644
index 0000000..322ca16
--- /dev/null
+++ b/bin/sh/tests/expansion/length5.0
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.ISO8859-1
+export LC_CTYPE
+
+e=
+for i in 0 1 2 3; do
+ for j in 0 1 2 3 4 5 6 7; do
+ for k in 0 1 2 3 4 5 6 7; do
+ case $i$j$k in
+ 000) continue ;;
+ esac
+ e="$e\\$i$j$k"
+ done
+ done
+done
+ee=`printf "$e"`
+[ ${#ee} = 255 ] || echo bad 1
+[ "${#ee}" = 255 ] || echo bad 2
+[ $((${#ee})) = 255 ] || echo bad 3
+[ "$((${#ee}))" = 255 ] || echo bad 4
+set -- "$ee"
+[ ${#1} = 255 ] || echo bad 5
+[ "${#1}" = 255 ] || echo bad 6
+[ $((${#1})) = 255 ] || echo bad 7
+[ "$((${#1}))" = 255 ] || echo bad 8
diff --git a/bin/sh/tests/expansion/length6.0 b/bin/sh/tests/expansion/length6.0
new file mode 100644
index 0000000..6b78309
--- /dev/null
+++ b/bin/sh/tests/expansion/length6.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+x='!@#$%^&*()[]'
+[ ${#x} = 12 ] || echo bad 1
+[ "${#x}" = 12 ] || echo bad 2
+IFS=2
+[ ${#x} = 1 ] || echo bad 3
+[ "${#x}" = 12 ] || echo bad 4
diff --git a/bin/sh/tests/expansion/length7.0 b/bin/sh/tests/expansion/length7.0
new file mode 100644
index 0000000..b79b116
--- /dev/null
+++ b/bin/sh/tests/expansion/length7.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.UTF-8
+export LC_CTYPE
+
+# a umlaut
+s=$(printf '\303\244')
+# euro sign
+s=$s$(printf '\342\202\254')
+# some sort of 't' outside BMP
+s=$s$(printf '\360\235\225\245')
+set -- "$s"
+[ ${#s} = 3 ] && [ ${#1} = 3 ]
diff --git a/bin/sh/tests/expansion/length8.0 b/bin/sh/tests/expansion/length8.0
new file mode 100644
index 0000000..3cc6c15
--- /dev/null
+++ b/bin/sh/tests/expansion/length8.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.ISO8859-1
+export LC_CTYPE
+
+# a umlaut
+s=$(printf '\303\244')
+# euro sign
+s=$s$(printf '\342\202\254')
+# some sort of 't' outside BMP
+s=$s$(printf '\360\235\225\245')
+set -- "$s"
+[ ${#s} = 9 ] && [ ${#1} = 9 ]
diff --git a/bin/sh/tests/expansion/local1.0 b/bin/sh/tests/expansion/local1.0
new file mode 100644
index 0000000..3477835
--- /dev/null
+++ b/bin/sh/tests/expansion/local1.0
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+run_test() {
+ w='@ @'
+ check() {
+ [ "$v" = "$w" ] || echo "Expected $w got $v"
+ }
+
+ local v=$w
+ check
+
+ HOME=/known/value
+ check() {
+ [ "$v" = ~ ] || echo "Expected $HOME got $v"
+ }
+
+ local v=~
+ check
+
+ check() {
+ [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+ }
+
+ local v=x:~
+ check
+}
+
+run_test
diff --git a/bin/sh/tests/expansion/local2.0 b/bin/sh/tests/expansion/local2.0
new file mode 100644
index 0000000..1984290
--- /dev/null
+++ b/bin/sh/tests/expansion/local2.0
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+run_test() {
+ w='@ @'
+ check() {
+ [ "$v" = "$w" ] || echo "Expected $w got $v"
+ }
+
+ command local v=$w
+ check
+ command command local v=$w
+ check
+
+ HOME=/known/value
+ check() {
+ [ "$v" = ~ ] || echo "Expected $HOME got $v"
+ }
+
+ command local v=~
+ check
+ command command local v=~
+ check
+
+ check() {
+ [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+ }
+
+ command local v=x:~
+ check
+ command command local v=x:~
+ check
+}
+
+run_test
diff --git a/bin/sh/tests/expansion/pathname1.0 b/bin/sh/tests/expansion/pathname1.0
new file mode 100644
index 0000000..8c6b01c
--- /dev/null
+++ b/bin/sh/tests/expansion/pathname1.0
@@ -0,0 +1,61 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ testcase=$1
+ expect=$2
+ eval "set -- $testcase"
+ actual="$*"
+ if [ "$actual" != "$expect" ]; then
+ failures=$((failures+1))
+ printf '%s\n' "For $testcase, expected $expect actual $actual"
+ fi
+}
+
+set -e
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf $T' 0
+cd -P $T
+
+mkdir testdir testdir2 'testdir/*' 'testdir/?' testdir/a testdir/b testdir2/b
+mkdir testdir2/.c
+touch testf 'testdir/*/1' 'testdir/?/1' testdir/a/1 testdir/b/1 testdir2/b/.a
+
+check '' ''
+check 'testdir/b' 'testdir/b'
+check 'testdir/c' 'testdir/c'
+check '\*' '*'
+check '\?' '?'
+check '*' 'testdir testdir2 testf'
+check '*""' 'testdir testdir2 testf'
+check '""*' 'testdir testdir2 testf'
+check '*/' 'testdir/ testdir2/'
+check 'testdir*/a' 'testdir/a'
+check 'testdir*/b' 'testdir/b testdir2/b'
+check '*/.c' 'testdir2/.c'
+check 'testdir2/*' 'testdir2/b'
+check 'testdir2/b/*' 'testdir2/b/*'
+check 'testdir/*' 'testdir/* testdir/? testdir/a testdir/b'
+check 'testdir/*/1' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1'
+check '"testdir/"*/1' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1'
+check 'testdir/\*/*' 'testdir/*/1'
+check 'testdir/\?/*' 'testdir/?/1'
+check 'testdir/"?"/*' 'testdir/?/1'
+check '"testdir"/"?"/*' 'testdir/?/1'
+check '"testdir"/"?"*/*' 'testdir/?/1'
+check '"testdir"/*"?"/*' 'testdir/?/1'
+check '"testdir/?"*/*' 'testdir/?/1'
+check 'testdir/\*/' 'testdir/*/'
+check 'testdir/\?/' 'testdir/?/'
+check 'testdir/"?"/' 'testdir/?/'
+check '"testdir"/"?"/' 'testdir/?/'
+check '"testdir"/"?"*/' 'testdir/?/'
+check '"testdir"/*"?"/' 'testdir/?/'
+check '"testdir/?"*/' 'testdir/?/'
+check 'testdir/[*]/' 'testdir/*/'
+check 'testdir/[?]/' 'testdir/?/'
+check 'testdir/[*?]/' 'testdir/*/ testdir/?/'
+check '[tz]estdir/[*]/' 'testdir/*/'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/pathname2.0 b/bin/sh/tests/expansion/pathname2.0
new file mode 100644
index 0000000..8a884ff
--- /dev/null
+++ b/bin/sh/tests/expansion/pathname2.0
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ testcase=$1
+ expect=$2
+ eval "set -- $testcase"
+ actual="$*"
+ if [ "$actual" != "$expect" ]; then
+ failures=$((failures+1))
+ printf '%s\n' "For $testcase, expected $expect actual $actual"
+ fi
+}
+
+set -e
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf $T' 0
+cd -P $T
+
+mkdir testdir testdir2 'testdir/*' 'testdir/?' testdir/a testdir/b testdir2/b
+mkdir testdir2/.c
+touch testf 'testdir/*/1' 'testdir/?/1' testdir/a/1 testdir/b/1 testdir2/b/.a
+
+check '*\/' 'testdir/ testdir2/'
+check '"testdir/"*"/1"' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1'
+check '"testdir/"*"/"*' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1'
+check '"testdir/"*\/*' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1'
+check '"testdir"*"/"*"/"*' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/pathname3.0 b/bin/sh/tests/expansion/pathname3.0
new file mode 100644
index 0000000..d1672e0
--- /dev/null
+++ b/bin/sh/tests/expansion/pathname3.0
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+v=12345678
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+v=$v$v$v$v
+# 8192 bytes
+v=${v##???}
+[ /*/$v = "/*/$v" ] || exit 1
+
+s=////
+s=$s$s$s$s
+s=$s$s$s$s
+s=$s$s$s$s
+s=$s$s$s$s
+# 1024 bytes
+s=${s##??????????}
+[ /var/empt[y]/$s/$v = "/var/empt[y]/$s/$v" ] || exit 2
+while [ ${#s} -lt 1034 ]; do
+ set -- /.${s}et[c]
+ [ ${#s} -gt 1018 ] || [ "$1" = /.${s}etc ] || exit 3
+ set -- /.${s}et[c]/
+ [ ${#s} -gt 1017 ] || [ "$1" = /.${s}etc/ ] || exit 4
+ set -- /.${s}et[c]/.
+ [ ${#s} -gt 1016 ] || [ "$1" = /.${s}etc/. ] || exit 5
+ s=$s/
+done
diff --git a/bin/sh/tests/expansion/pathname4.0 b/bin/sh/tests/expansion/pathname4.0
new file mode 100644
index 0000000..18269c4
--- /dev/null
+++ b/bin/sh/tests/expansion/pathname4.0
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ testcase=$1
+ expect=$2
+ eval "set -- $testcase"
+ actual="$*"
+ if [ "$actual" != "$expect" ]; then
+ failures=$((failures+1))
+ printf '%s\n' "For $testcase, expected $expect actual $actual"
+ fi
+}
+
+set -e
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf $T' 0
+cd -P $T
+
+mkdir !!a
+touch !!a/fff
+
+chmod u-r .
+check '!!a/ff*' '!!a/fff'
+chmod u+r .
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/expansion/plus-minus1.0 b/bin/sh/tests/expansion/plus-minus1.0
new file mode 100644
index 0000000..9a6a53a
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus1.0
@@ -0,0 +1,76 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- a b' '2|a|b'
+testcase 'set --' '0|'
+testcase 'set -- ${e}' '0|'
+testcase 'set -- "${e}"' '1|'
+
+testcase 'set -- $p' '1|/etc/'
+testcase 'set -- "$p"' '1|/et[c]/'
+testcase 'set -- ${s+$p}' '1|/etc/'
+testcase 'set -- "${s+$p}"' '1|/et[c]/'
+testcase 'set -- ${s+"$p"}' '1|/et[c]/'
+# Dquotes in dquotes is undefined for Bourne shell operators
+#testcase 'set -- "${s+"$p"}"' '1|/et[c]/'
+testcase 'set -- ${e:-$p}' '1|/etc/'
+testcase 'set -- "${e:-$p}"' '1|/et[c]/'
+testcase 'set -- ${e:-"$p"}' '1|/et[c]/'
+# Dquotes in dquotes is undefined for Bourne shell operators
+#testcase 'set -- "${e:-"$p"}"' '1|/et[c]/'
+testcase 'set -- ${e:+"$e"}' '0|'
+testcase 'set -- ${e:+$w"$e"}' '0|'
+testcase 'set -- ${w:+"$w"}' '1|a b c'
+testcase 'set -- ${w:+$w"$w"}' '3|a|b|ca b c'
+
+testcase 'set -- "${s+a b}"' '1|a b'
+testcase 'set -- "${e:-a b}"' '1|a b'
+testcase 'set -- ${e:-\}}' '1|}'
+testcase 'set -- ${e:+{}}' '1|}'
+testcase 'set -- "${e:+{}}"' '1|}'
+
+testcase 'set -- ${e+x}${e+x}' '1|xx'
+testcase 'set -- "${e+x}"${e+x}' '1|xx'
+testcase 'set -- ${e+x}"${e+x}"' '1|xx'
+testcase 'set -- "${e+x}${e+x}"' '1|xx'
+testcase 'set -- "${e+x}""${e+x}"' '1|xx'
+
+testcase 'set -- ${e:-${e:-$p}}' '1|/etc/'
+testcase 'set -- "${e:-${e:-$p}}"' '1|/et[c]/'
+testcase 'set -- ${e:-"${e:-$p}"}' '1|/et[c]/'
+testcase 'set -- ${e:-${e:-"$p"}}' '1|/et[c]/'
+testcase 'set -- ${e:-${e:-${e:-$w}}}' '3|a|b|c'
+testcase 'set -- ${e:-${e:-${e:-"$w"}}}' '1|a b c'
+testcase 'set -- ${e:-${e:-"${e:-$w}"}}' '1|a b c'
+testcase 'set -- ${e:-"${e:-${e:-$w}}"}' '1|a b c'
+testcase 'set -- "${e:-${e:-${e:-$w}}}"' '1|a b c'
+
+testcase 'shift $#; set -- ${1+"$@"}' '0|'
+testcase 'set -- ""; set -- ${1+"$@"}' '1|'
+testcase 'set -- "" a; set -- ${1+"$@"}' '2||a'
+testcase 'set -- a ""; set -- ${1+"$@"}' '2|a|'
+testcase 'set -- a b; set -- ${1+"$@"}' '2|a|b'
+testcase 'set -- a\ b; set -- ${1+"$@"}' '1|a b'
+testcase 'set -- " " ""; set -- ${1+"$@"}' '2| |'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/plus-minus2.0 b/bin/sh/tests/expansion/plus-minus2.0
new file mode 100644
index 0000000..f5a8752
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus2.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+e=
+test "${e:-\}}" = '}'
diff --git a/bin/sh/tests/expansion/plus-minus3.0 b/bin/sh/tests/expansion/plus-minus3.0
new file mode 100644
index 0000000..49fcdc2
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus3.0
@@ -0,0 +1,44 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+# We follow original ash behaviour for quoted ${var+-=?} expansions:
+# a double-quote in one switches back to unquoted state.
+# This allows expanding a variable as a single word if it is set
+# and substituting multiple words otherwise.
+# It is also close to the Bourne and Korn shells.
+# POSIX leaves this undefined, and various other shells treat
+# such double-quotes as introducing a second level of quoting
+# which does not do much except quoting close braces.
+
+testcase 'set -- "${p+"/et[c]/"}"' '1|/etc/'
+testcase 'set -- "${p-"/et[c]/"}"' '1|/et[c]/'
+testcase 'set -- "${p+"$p"}"' '1|/etc/'
+testcase 'set -- "${p-"$p"}"' '1|/et[c]/'
+testcase 'set -- "${p+"""/et[c]/"}"' '1|/etc/'
+testcase 'set -- "${p-"""/et[c]/"}"' '1|/et[c]/'
+testcase 'set -- "${p+"""$p"}"' '1|/etc/'
+testcase 'set -- "${p-"""$p"}"' '1|/et[c]/'
+testcase 'set -- "${p+"\@"}"' '1|@'
+testcase 'set -- "${p+"'\''/et[c]/'\''"}"' '1|/et[c]/'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/plus-minus4.0 b/bin/sh/tests/expansion/plus-minus4.0
new file mode 100644
index 0000000..66dea38
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus4.0
@@ -0,0 +1,38 @@
+# $FreeBSD$
+
+# These may be a bit unclear in the POSIX spec or the proposed revisions,
+# and conflict with bash's interpretation, but I think ksh93's interpretation
+# makes most sense. In particular, it makes no sense to me that single-quotes
+# must match but are not removed.
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- ${e:-'"'"'}'"'"'}' '1|}'
+testcase "set -- \${e:-\\'}" "1|'"
+testcase "set -- \${e:-\\'\\'}" "1|''"
+testcase "set -- \"\${e:-'}\"" "1|'"
+testcase "set -- \"\${e:-'}'}\"" "1|''}"
+testcase "set -- \"\${e:-''}\"" "1|''"
+testcase 'set -- ${e:-\a}' '1|a'
+testcase 'set -- "${e:-\a}"' '1|\a'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/plus-minus5.0 b/bin/sh/tests/expansion/plus-minus5.0
new file mode 100644
index 0000000..0b25e53
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus5.0
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- ${e:-"{x}"}' '1|{x}'
+testcase 'set -- "${e:-"{x}"}"' '1|{x}'
+testcase 'set -- ${h+"{x}"}' '1|{x}'
+testcase 'set -- "${h+"{x}"}"' '1|{x}'
+testcase 'set -- ${h:-"{x}"}' '1|##'
+testcase 'set -- "${h:-"{x}"}"' '1|##'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/plus-minus6.0 b/bin/sh/tests/expansion/plus-minus6.0
new file mode 100644
index 0000000..bc66805
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus6.0
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+failures=0
+unset LC_ALL
+export LC_CTYPE=en_US.ISO8859-1
+nl='
+'
+i=1
+set -f
+while [ "$i" -le 255 ]; do
+ # A different byte still in the range 1..255.
+ i2=$((i^2+(i==2)))
+ # Add a character to work around command substitution's removal of
+ # final newlines, then remove it again.
+ c=$(printf \\"$(printf %o@ "$i")")
+ c=${c%@}
+ c2=$(printf \\"$(printf %o@ "$i2")")
+ c2=${c2%@}
+ case $c in
+ [\'$nl'$}();&|\"`']) c=M
+ esac
+ case $c2 in
+ [\'$nl'$}();&|\"`']) c2=N
+ esac
+ IFS=$c
+ command eval "set -- \${\$+$c2$c$c2$c$c2}"
+ if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] ||
+ [ "$3" != "$c2" ]; then
+ echo "Bad results for separator $i (word $i2)" >&2
+ : $((failures += 1))
+ fi
+ i=$((i+1))
+done
+exit $((failures > 0))
diff --git a/bin/sh/tests/expansion/plus-minus7.0 b/bin/sh/tests/expansion/plus-minus7.0
new file mode 100644
index 0000000..9e81f58
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus7.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+e= s='foo'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- ${s+a b}' '2|a|b'
+testcase 'set -- ${e:-a b}' '2|a|b'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/plus-minus8.0 b/bin/sh/tests/expansion/plus-minus8.0
new file mode 100644
index 0000000..beba009
--- /dev/null
+++ b/bin/sh/tests/expansion/plus-minus8.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+set -- 1 2 3 4 5 6 7 8 9 10 11 12 13
+[ "${#+hi}" = hi ] || echo '${#+hi} wrong'
+[ "${#-hi}" = 13 ] || echo '${#-hi} wrong'
diff --git a/bin/sh/tests/expansion/question1.0 b/bin/sh/tests/expansion/question1.0
new file mode 100644
index 0000000..663c68d
--- /dev/null
+++ b/bin/sh/tests/expansion/question1.0
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+x=a\ b
+[ "$x" = "${x?}" ] || exit 1
+set -- ${x?}
+{ [ "$#" = 2 ] && [ "$1" = a ] && [ "$2" = b ]; } || exit 1
+unset x
+(echo ${x?abcdefg}) 2>&1 | grep -q abcdefg || exit 1
+${SH} -c 'unset foo; echo ${foo?}' 2>/dev/null && exit 1
+${SH} -c 'foo=; echo ${foo:?}' 2>/dev/null && exit 1
+${SH} -c 'foo=; echo ${foo?}' >/dev/null || exit 1
+${SH} -c 'foo=1; echo ${foo:?}' >/dev/null || exit 1
+${SH} -c 'echo ${!?}' 2>/dev/null && exit 1
+${SH} -c ':& echo ${!?}' >/dev/null || exit 1
+${SH} -c 'echo ${#?}' >/dev/null || exit 1
+${SH} -c 'echo ${*?}' 2>/dev/null && exit 1
+${SH} -c 'echo ${*?}' ${SH} x >/dev/null || exit 1
+${SH} -c 'echo ${1?}' 2>/dev/null && exit 1
+${SH} -c 'echo ${1?}' ${SH} x >/dev/null || exit 1
+${SH} -c 'echo ${2?}' ${SH} x 2>/dev/null && exit 1
+${SH} -c 'echo ${2?}' ${SH} x y >/dev/null || exit 1
+exit 0
diff --git a/bin/sh/tests/expansion/readonly1.0 b/bin/sh/tests/expansion/readonly1.0
new file mode 100644
index 0000000..5ad0e14
--- /dev/null
+++ b/bin/sh/tests/expansion/readonly1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+w='@ @'
+
+v=0 HOME=/known/value
+readonly v=~:~/:$w
+[ "$v" = "$HOME:$HOME/:$w" ] || echo "Expected $HOME/:$w got $v"
diff --git a/bin/sh/tests/expansion/set-u1.0 b/bin/sh/tests/expansion/set-u1.0
new file mode 100644
index 0000000..763eb7d
--- /dev/null
+++ b/bin/sh/tests/expansion/set-u1.0
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+${SH} -uc 'unset foo; echo $foo' 2>/dev/null && exit 1
+${SH} -uc 'foo=; echo $foo' >/dev/null || exit 1
+${SH} -uc 'foo=1; echo $foo' >/dev/null || exit 1
+# -/+/= are unaffected by set -u
+${SH} -uc 'unset foo; echo ${foo-}' >/dev/null || exit 1
+${SH} -uc 'unset foo; echo ${foo+}' >/dev/null || exit 1
+${SH} -uc 'unset foo; echo ${foo=}' >/dev/null || exit 1
+# length/trimming are affected
+${SH} -uc 'unset foo; echo ${#foo}' 2>/dev/null && exit 1
+${SH} -uc 'foo=; echo ${#foo}' >/dev/null || exit 1
+${SH} -uc 'unset foo; echo ${foo#?}' 2>/dev/null && exit 1
+${SH} -uc 'foo=1; echo ${foo#?}' >/dev/null || exit 1
+${SH} -uc 'unset foo; echo ${foo##?}' 2>/dev/null && exit 1
+${SH} -uc 'foo=1; echo ${foo##?}' >/dev/null || exit 1
+${SH} -uc 'unset foo; echo ${foo%?}' 2>/dev/null && exit 1
+${SH} -uc 'foo=1; echo ${foo%?}' >/dev/null || exit 1
+${SH} -uc 'unset foo; echo ${foo%%?}' 2>/dev/null && exit 1
+${SH} -uc 'foo=1; echo ${foo%%?}' >/dev/null || exit 1
+
+${SH} -uc 'echo $!' 2>/dev/null && exit 1
+${SH} -uc ':& echo $!' >/dev/null || exit 1
+${SH} -uc 'echo $#' >/dev/null || exit 1
+${SH} -uc 'echo $1' 2>/dev/null && exit 1
+${SH} -uc 'echo $1' ${SH} x >/dev/null || exit 1
+${SH} -uc 'echo $2' ${SH} x 2>/dev/null && exit 1
+${SH} -uc 'echo $2' ${SH} x y >/dev/null || exit 1
+exit 0
diff --git a/bin/sh/tests/expansion/set-u2.0 b/bin/sh/tests/expansion/set-u2.0
new file mode 100644
index 0000000..f81aa62
--- /dev/null
+++ b/bin/sh/tests/expansion/set-u2.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+set -u
+: $* $@ "$@" "$*"
+set -- x
+: $* $@ "$@" "$*"
+shift $#
+: $* $@ "$@" "$*"
+set -- y
+set --
+: $* $@ "$@" "$*"
+exit 0
diff --git a/bin/sh/tests/expansion/set-u3.0 b/bin/sh/tests/expansion/set-u3.0
new file mode 100644
index 0000000..7f199b4
--- /dev/null
+++ b/bin/sh/tests/expansion/set-u3.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+set -u
+unset x
+v=$( (eval ': $((x))') 2>&1 >/dev/null)
+[ $? -ne 0 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/expansion/tilde1.0 b/bin/sh/tests/expansion/tilde1.0
new file mode 100644
index 0000000..7d8581b
--- /dev/null
+++ b/bin/sh/tests/expansion/tilde1.0
@@ -0,0 +1,56 @@
+# $FreeBSD$
+
+HOME=/tmp
+roothome=~root
+if [ "$roothome" = "~root" ]; then
+ echo "~root is not expanded!"
+ exit 2
+fi
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- ~' '1|/tmp'
+testcase 'set -- ~/foo' '1|/tmp/foo'
+testcase 'set -- x~' '1|x~'
+testcase 'set -- ~root' "1|$roothome"
+h=~
+testcase 'set -- "$h"' '1|/tmp'
+ooIFS=$IFS
+IFS=m
+testcase 'set -- ~' '1|/tmp'
+testcase 'set -- ~/foo' '1|/tmp/foo'
+testcase 'set -- $h' '2|/t|p'
+IFS=$ooIFS
+t=\~
+testcase 'set -- $t' '1|~'
+r=$(cat <<EOF
+~
+EOF
+)
+testcase 'set -- $r' '1|~'
+r=$(cat <<EOF
+${t+~}
+EOF
+)
+testcase 'set -- $r' '1|~'
+r=$(cat <<EOF
+${t+~/.}
+EOF
+)
+testcase 'set -- $r' '1|~/.'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/tilde2.0 b/bin/sh/tests/expansion/tilde2.0
new file mode 100644
index 0000000..4f8ed9b
--- /dev/null
+++ b/bin/sh/tests/expansion/tilde2.0
@@ -0,0 +1,90 @@
+# $FreeBSD$
+
+HOME=/tmp
+roothome=~root
+if [ "$roothome" = "~root" ]; then
+ echo "~root is not expanded!"
+ exit 2
+fi
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- ${$+~}' '1|/tmp'
+testcase 'set -- ${$+~/}' '1|/tmp/'
+testcase 'set -- ${$+~/foo}' '1|/tmp/foo'
+testcase 'set -- ${$+x~}' '1|x~'
+testcase 'set -- ${$+~root}' "1|$roothome"
+testcase 'set -- ${$+"~"}' '1|~'
+testcase 'set -- ${$+"~/"}' '1|~/'
+testcase 'set -- ${$+"~/foo"}' '1|~/foo'
+testcase 'set -- ${$+"x~"}' '1|x~'
+testcase 'set -- ${$+"~root"}' "1|~root"
+testcase 'set -- "${$+~}"' '1|~'
+testcase 'set -- "${$+~/}"' '1|~/'
+testcase 'set -- "${$+~/foo}"' '1|~/foo'
+testcase 'set -- "${$+x~}"' '1|x~'
+testcase 'set -- "${$+~root}"' "1|~root"
+testcase 'set -- ${HOME#~}' '0|'
+h=~
+testcase 'set -- "$h"' '1|/tmp'
+f=~/foo
+testcase 'set -- "$f"' '1|/tmp/foo'
+testcase 'set -- ${f#~}' '1|/foo'
+testcase 'set -- ${f#~/}' '1|foo'
+
+ooIFS=$IFS
+IFS=m
+testcase 'set -- ${$+~}' '1|/tmp'
+testcase 'set -- ${$+~/foo}' '1|/tmp/foo'
+testcase 'set -- ${$+$h}' '2|/t|p'
+testcase 'set -- ${HOME#~}' '0|'
+IFS=$ooIFS
+
+t=\~
+testcase 'set -- ${$+$t}' '1|~'
+r=$(cat <<EOF
+${HOME#~}
+EOF
+)
+testcase 'set -- $r' '0|'
+r=$(cat <<EOF
+${HOME#'~'}
+EOF
+)
+testcase 'set -- $r' '1|/tmp'
+r=$(cat <<EOF
+${t#'~'}
+EOF
+)
+testcase 'set -- $r' '0|'
+r=$(cat <<EOF
+${roothome#~root}
+EOF
+)
+testcase 'set -- $r' '0|'
+r=$(cat <<EOF
+${f#~}
+EOF
+)
+testcase 'set -- $r' '1|/foo'
+r=$(cat <<EOF
+${f#~/}
+EOF
+)
+testcase 'set -- $r' '1|foo'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/trim1.0 b/bin/sh/tests/expansion/trim1.0
new file mode 100644
index 0000000..b548e52
--- /dev/null
+++ b/bin/sh/tests/expansion/trim1.0
@@ -0,0 +1,85 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- ${t%t}' '1|texttex'
+testcase 'set -- "${t%t}"' '1|texttex'
+testcase 'set -- ${t%e*}' '1|textt'
+testcase 'set -- "${t%e*}"' '1|textt'
+testcase 'set -- ${t%%e*}' '1|t'
+testcase 'set -- "${t%%e*}"' '1|t'
+testcase 'set -- ${t%%*}' '0|'
+testcase 'set -- "${t%%*}"' '1|'
+testcase 'set -- ${t#t}' '1|exttext'
+testcase 'set -- "${t#t}"' '1|exttext'
+testcase 'set -- ${t#*x}' '1|ttext'
+testcase 'set -- "${t#*x}"' '1|ttext'
+testcase 'set -- ${t##*x}' '1|t'
+testcase 'set -- "${t##*x}"' '1|t'
+testcase 'set -- ${t##*}' '0|'
+testcase 'set -- "${t##*}"' '1|'
+testcase 'set -- ${t%e$a}' '1|textt'
+
+set -f
+testcase 'set -- ${s%[?]*}' '1|ast*que'
+testcase 'set -- "${s%[?]*}"' '1|ast*que'
+testcase 'set -- ${s%[*]*}' '1|ast'
+testcase 'set -- "${s%[*]*}"' '1|ast'
+set +f
+
+testcase 'set -- $b' '1|{{(#)}}'
+testcase 'set -- ${b%\}}' '1|{{(#)}'
+testcase 'set -- ${b#{}' '1|{(#)}}'
+testcase 'set -- "${b#{}"' '1|{(#)}}'
+# Parentheses are special in ksh, check that they can be escaped
+testcase 'set -- ${b%\)*}' '1|{{(#'
+testcase 'set -- ${b#{}' '1|{(#)}}'
+testcase 'set -- $h' '1|##'
+testcase 'set -- ${h#\#}' '1|#'
+testcase 'set -- ${h###}' '1|#'
+testcase 'set -- "${h###}"' '1|#'
+testcase 'set -- ${h%#}' '1|#'
+testcase 'set -- "${h%#}"' '1|#'
+
+set -f
+testcase 'set -- ${s%"${s#?}"}' '1|a'
+testcase 'set -- ${s%"${s#????}"}' '1|ast*'
+testcase 'set -- ${s%"${s#????????}"}' '1|ast*que?'
+testcase 'set -- ${s#"${s%?}"}' '1|n'
+testcase 'set -- ${s#"${s%????}"}' '1|?non'
+testcase 'set -- ${s#"${s%????????}"}' '1|*que?non'
+set +f
+testcase 'set -- "${s%"${s#?}"}"' '1|a'
+testcase 'set -- "${s%"${s#????}"}"' '1|ast*'
+testcase 'set -- "${s%"${s#????????}"}"' '1|ast*que?'
+testcase 'set -- "${s#"${s%?}"}"' '1|n'
+testcase 'set -- "${s#"${s%????}"}"' '1|?non'
+testcase 'set -- "${s#"${s%????????}"}"' '1|*que?non'
+testcase 'set -- ${p#${p}}' '1|/etc/'
+testcase 'set -- "${p#${p}}"' '1|/et[c]/'
+testcase 'set -- ${p#*[[]}' '1|c]/'
+testcase 'set -- "${p#*[[]}"' '1|c]/'
+testcase 'set -- ${p#*\[}' '1|c]/'
+testcase 'set -- ${p#*"["}' '1|c]/'
+testcase 'set -- "${p#*"["}"' '1|c]/'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/trim2.0 b/bin/sh/tests/expansion/trim2.0
new file mode 100644
index 0000000..619ef65
--- /dev/null
+++ b/bin/sh/tests/expansion/trim2.0
@@ -0,0 +1,55 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+set -f
+testcase 'set -- $s' '1|ast*que?non'
+testcase 'set -- ${s%\?*}' '1|ast*que'
+testcase 'set -- "${s%\?*}"' '1|ast*que'
+testcase 'set -- ${s%\**}' '1|ast'
+testcase 'set -- "${s%\**}"' '1|ast'
+testcase 'set -- ${s%"$q"*}' '1|ast*que'
+testcase 'set -- "${s%"$q"*}"' '1|ast*que'
+testcase 'set -- ${s%"$a"*}' '1|ast'
+testcase 'set -- "${s%"$a"*}"' '1|ast'
+testcase 'set -- ${s%"$q"$a}' '1|ast*que'
+testcase 'set -- "${s%"$q"$a}"' '1|ast*que'
+testcase 'set -- ${s%"$a"$a}' '1|ast'
+testcase 'set -- "${s%"$a"$a}"' '1|ast'
+set +f
+
+testcase 'set -- "${b%\}}"' '1|{{(#)}'
+# Parentheses are special in ksh, check that they can be escaped
+testcase 'set -- "${b%\)*}"' '1|{{(#'
+testcase 'set -- "${h#\#}"' '1|#'
+
+testcase 'set -- ${p%"${p#?}"}' '1|/'
+testcase 'set -- ${p%"${p#??????}"}' '1|/etc'
+testcase 'set -- ${p%"${p#???????}"}' '1|/etc/'
+testcase 'set -- "${p%"${p#?}"}"' '1|/'
+testcase 'set -- "${p%"${p#??????}"}"' '1|/et[c]'
+testcase 'set -- "${p%"${p#???????}"}"' '1|/et[c]/'
+testcase 'set -- ${p#"${p}"}' '0|'
+testcase 'set -- "${p#"${p}"}"' '1|'
+testcase 'set -- "${p#*\[}"' '1|c]/'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/trim3.0 b/bin/sh/tests/expansion/trim3.0
new file mode 100644
index 0000000..b89a041
--- /dev/null
+++ b/bin/sh/tests/expansion/trim3.0
@@ -0,0 +1,46 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##' c='\\\\'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+# This doesn't make much sense, but it fails in dash so I'm adding it here:
+testcase 'set -- "${w%${w#???}}"' '1|a b'
+
+testcase 'set -- ${p#/et[}' '1|c]/'
+testcase 'set -- "${p#/et[}"' '1|c]/'
+testcase 'set -- "${p%${p#????}}"' '1|/et['
+
+testcase 'set -- ${b%'\'}\''}' '1|{{(#)}'
+
+testcase 'set -- ${c#\\}' '1|\\\'
+testcase 'set -- ${c#\\\\}' '1|\\'
+testcase 'set -- ${c#\\\\\\}' '1|\'
+testcase 'set -- ${c#\\\\\\\\}' '0|'
+testcase 'set -- "${c#\\}"' '1|\\\'
+testcase 'set -- "${c#\\\\}"' '1|\\'
+testcase 'set -- "${c#\\\\\\}"' '1|\'
+testcase 'set -- "${c#\\\\\\\\}"' '1|'
+testcase 'set -- "${c#"$c"}"' '1|'
+testcase 'set -- ${c#"$c"}' '0|'
+testcase 'set -- "${c%"$c"}"' '1|'
+testcase 'set -- ${c%"$c"}' '0|'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/trim4.0 b/bin/sh/tests/expansion/trim4.0
new file mode 100644
index 0000000..1000bd3
--- /dev/null
+++ b/bin/sh/tests/expansion/trim4.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+v1=/homes/SOME_USER
+v2=
+v3=C123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+
+# Trigger bug in VSTRIMRIGHT processing STADJUST() call in expand.c:subevalvar()
+while [ ${#v2} -lt 2000 ]; do
+ v4="${v2} ${v1%/*} $v3"
+ if [ ${#v4} -ne $((${#v2} + ${#v3} + 8)) ]; then
+ echo bad: ${#v4} -ne $((${#v2} + ${#v3} + 8))
+ fi
+ v2=x$v2
+ v3=y$v3
+done
diff --git a/bin/sh/tests/expansion/trim5.0 b/bin/sh/tests/expansion/trim5.0
new file mode 100644
index 0000000..937ec9a
--- /dev/null
+++ b/bin/sh/tests/expansion/trim5.0
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}'
+h='##'
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- "${b%'\'}\''}"' '1|{{(#)}'
+testcase 'set -- ${b%"}"}' '1|{{(#)}'
+testcase 'set -- "${b%"}"}"' '1|{{(#)}'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/expansion/trim6.0 b/bin/sh/tests/expansion/trim6.0
new file mode 100644
index 0000000..3f753c4
--- /dev/null
+++ b/bin/sh/tests/expansion/trim6.0
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+e=
+for i in 0 1 2 3; do
+ for j in 0 1 2 3 4 5 6 7; do
+ for k in 0 1 2 3 4 5 6 7; do
+ case $i$j$k in
+ 000) continue ;;
+ esac
+ e="$e\\$i$j$k"
+ done
+ done
+done
+e=$(printf "$e")
+v=@$e@$e@
+y=${v##*"$e"}
+yq="${v##*"$e"}"
+[ "$y" = @ ] || echo "error when unquoted in non-splitting context"
+[ "$yq" = @ ] || echo "error when quoted in non-splitting context"
+[ "${v##*"$e"}" = @ ] || echo "error when quoted in splitting context"
+IFS=
+[ ${v##*"$e"} = @ ] || echo "error when unquoted in splitting context"
diff --git a/bin/sh/tests/expansion/trim7.0 b/bin/sh/tests/expansion/trim7.0
new file mode 100644
index 0000000..352bdea
--- /dev/null
+++ b/bin/sh/tests/expansion/trim7.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+set -- 1 2 3 4 5 6 7 8 9 10 11 12 13
+[ "${##1}" = 3 ] || echo '${##1} wrong'
+[ "${###1}" = 3 ] || echo '${###1} wrong'
+[ "${###}" = 13 ] || echo '${###} wrong'
+[ "${#%3}" = 1 ] || echo '${#%3} wrong'
+[ "${#%%3}" = 1 ] || echo '${#%%3} wrong'
+[ "${#%%}" = 13 ] || echo '${#%%} wrong'
+set --
+[ "${##0}" = "" ] || echo '${##0} wrong'
+[ "${###0}" = "" ] || echo '${###0} wrong'
+[ "${###}" = 0 ] || echo '${###} wrong'
+[ "${#%0}" = "" ] || echo '${#%0} wrong'
+[ "${#%%0}" = "" ] || echo '${#%%0} wrong'
+[ "${#%%}" = 0 ] || echo '${#%%} wrong'
diff --git a/bin/sh/tests/expansion/trim8.0 b/bin/sh/tests/expansion/trim8.0
new file mode 100644
index 0000000..f7272f3
--- /dev/null
+++ b/bin/sh/tests/expansion/trim8.0
@@ -0,0 +1,75 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.UTF-8
+export LC_CTYPE
+
+c1=e
+# a umlaut
+c2=$(printf '\303\244')
+# euro sign
+c3=$(printf '\342\202\254')
+# some sort of 't' outside BMP
+c4=$(printf '\360\235\225\245')
+
+s=$c1$c2$c3$c4
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- "$s"' "1|$s"
+testcase 'set -- "${s#$c2}"' "1|$s"
+testcase 'set -- "${s#*}"' "1|$s"
+testcase 'set -- "${s#$c1}"' "1|$c2$c3$c4"
+testcase 'set -- "${s#$c1$c2}"' "1|$c3$c4"
+testcase 'set -- "${s#$c1$c2$c3}"' "1|$c4"
+testcase 'set -- "${s#$c1$c2$c3$c4}"' "1|"
+testcase 'set -- "${s#?}"' "1|$c2$c3$c4"
+testcase 'set -- "${s#??}"' "1|$c3$c4"
+testcase 'set -- "${s#???}"' "1|$c4"
+testcase 'set -- "${s#????}"' "1|"
+testcase 'set -- "${s#*$c3}"' "1|$c4"
+testcase 'set -- "${s%$c4}"' "1|$c1$c2$c3"
+testcase 'set -- "${s%$c3$c4}"' "1|$c1$c2"
+testcase 'set -- "${s%$c2$c3$c4}"' "1|$c1"
+testcase 'set -- "${s%$c1$c2$c3$c4}"' "1|"
+testcase 'set -- "${s%?}"' "1|$c1$c2$c3"
+testcase 'set -- "${s%??}"' "1|$c1$c2"
+testcase 'set -- "${s%???}"' "1|$c1"
+testcase 'set -- "${s%????}"' "1|"
+testcase 'set -- "${s%$c2*}"' "1|$c1"
+testcase 'set -- "${s##$c2}"' "1|$s"
+testcase 'set -- "${s##*}"' "1|"
+testcase 'set -- "${s##$c1}"' "1|$c2$c3$c4"
+testcase 'set -- "${s##$c1$c2}"' "1|$c3$c4"
+testcase 'set -- "${s##$c1$c2$c3}"' "1|$c4"
+testcase 'set -- "${s##$c1$c2$c3$c4}"' "1|"
+testcase 'set -- "${s##?}"' "1|$c2$c3$c4"
+testcase 'set -- "${s##??}"' "1|$c3$c4"
+testcase 'set -- "${s##???}"' "1|$c4"
+testcase 'set -- "${s##????}"' "1|"
+testcase 'set -- "${s##*$c3}"' "1|$c4"
+testcase 'set -- "${s%%$c4}"' "1|$c1$c2$c3"
+testcase 'set -- "${s%%$c3$c4}"' "1|$c1$c2"
+testcase 'set -- "${s%%$c2$c3$c4}"' "1|$c1"
+testcase 'set -- "${s%%$c1$c2$c3$c4}"' "1|"
+testcase 'set -- "${s%%?}"' "1|$c1$c2$c3"
+testcase 'set -- "${s%%??}"' "1|$c1$c2"
+testcase 'set -- "${s%%???}"' "1|$c1"
+testcase 'set -- "${s%%????}"' "1|"
+testcase 'set -- "${s%%$c2*}"' "1|$c1"
+
+test "x$failures" = x
diff --git a/bin/sh/tests/legacy_test.sh b/bin/sh/tests/legacy_test.sh
new file mode 100644
index 0000000..d43f5dd
--- /dev/null
+++ b/bin/sh/tests/legacy_test.sh
@@ -0,0 +1,46 @@
+# $FreeBSD$
+
+: ${SH:="__SH__"}
+export SH
+
+# TODO(jmmv): The Kyua TAP interface should be passing us the value of
+# "srcdir" as an environment variable, just as it does with the ATF
+# interface in the form of a configuration variable. For now, just try
+# to guess this.
+: ${TESTS_DATA:=$(dirname ${0})}
+
+COUNTER=1
+
+do_test() {
+ c=${COUNTER}
+ COUNTER=$((COUNTER+1))
+ ${SH} $1 > tmp.stdout 2> tmp.stderr
+ if [ $? -ne $2 ]; then
+ echo "not ok ${c} - ${1} # wrong exit status"
+ rm tmp.stdout tmp.stderr
+ return
+ fi
+ sed -I '' -e "s|^${TESTS_DATA}|.|" tmp.stderr
+ for i in stdout stderr; do
+ if [ -f ${1}.${i} ]; then
+ if ! cmp -s tmp.${i} ${1}.${i}; then
+ echo "not ok ${c} - ${1} # wrong output on ${i}"
+ rm tmp.stdout tmp.stderr
+ return
+ fi
+ elif [ -s tmp.${i} ]; then
+ echo "not ok ${c} - ${1} # wrong output on ${i}"
+ rm tmp.stdout tmp.stderr
+ return
+ fi
+ done
+ echo "ok ${c} - ${1}"
+ rm tmp.stdout tmp.stderr
+}
+
+TESTS=$(find -Es ${TESTS_DATA} -regex ".*\.[0-9]+")
+printf "1..%d\n" $(echo ${TESTS} | wc -w)
+
+for i in ${TESTS} ; do
+ do_test ${i} ${i##*.}
+done
diff --git a/bin/sh/tests/parameters/Makefile b/bin/sh/tests/parameters/Makefile
new file mode 100644
index 0000000..fafc059
--- /dev/null
+++ b/bin/sh/tests/parameters/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/parameters
+KYUAFILE= no
+
+FILES= env1.0
+FILES+= exitstatus1.0
+FILES+= mail1.0
+FILES+= mail2.0
+FILES+= optind1.0
+FILES+= optind2.0
+FILES+= positional1.0
+FILES+= positional2.0
+FILES+= pwd1.0
+FILES+= pwd2.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/parameters/env1.0 b/bin/sh/tests/parameters/env1.0
new file mode 100644
index 0000000..c0d4a2c
--- /dev/null
+++ b/bin/sh/tests/parameters/env1.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+export key='must contain this'
+unset x
+r=$(ENV="\${x?\$key}" ${SH} -i +m 2>&1 >/dev/null <<\EOF
+exit 0
+EOF
+) && case $r in
+*"$key"*) true ;;
+*) false ;;
+esac
diff --git a/bin/sh/tests/parameters/exitstatus1.0 b/bin/sh/tests/parameters/exitstatus1.0
new file mode 100644
index 0000000..696823d
--- /dev/null
+++ b/bin/sh/tests/parameters/exitstatus1.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+f() {
+ [ $? = $1 ] || exit 1
+}
+
+true
+f 0
+false
+f 1
diff --git a/bin/sh/tests/parameters/mail1.0 b/bin/sh/tests/parameters/mail1.0
new file mode 100644
index 0000000..5791a5a
--- /dev/null
+++ b/bin/sh/tests/parameters/mail1.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Test that a non-interactive shell does not access $MAIL.
+
+goodfile=/var/empty/sh-test-goodfile
+mailfile=/var/empty/sh-test-mailfile
+T=$(mktemp sh-test.XXXXXX) || exit
+MAIL=$mailfile ktrace -i -f "$T" ${SH} -c "[ -s $goodfile ]" 2>/dev/null
+if ! grep -q $goodfile "$T"; then
+ # ktrace problem
+ rc=0
+elif ! grep -q $mailfile "$T"; then
+ rc=0
+fi
+rm "$T"
+exit ${rc:-3}
diff --git a/bin/sh/tests/parameters/mail2.0 b/bin/sh/tests/parameters/mail2.0
new file mode 100644
index 0000000..343c99d
--- /dev/null
+++ b/bin/sh/tests/parameters/mail2.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Test that an interactive shell accesses $MAIL.
+
+goodfile=/var/empty/sh-test-goodfile
+mailfile=/var/empty/sh-test-mailfile
+T=$(mktemp sh-test.XXXXXX) || exit
+ENV=$goodfile MAIL=$mailfile ktrace -i -f "$T" ${SH} +m -i </dev/null >/dev/null 2>&1
+if ! grep -q $goodfile "$T"; then
+ # ktrace problem
+ rc=0
+elif grep -q $mailfile "$T"; then
+ rc=0
+fi
+rm "$T"
+exit ${rc:-3}
diff --git a/bin/sh/tests/parameters/optind1.0 b/bin/sh/tests/parameters/optind1.0
new file mode 100644
index 0000000..33e0288
--- /dev/null
+++ b/bin/sh/tests/parameters/optind1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+unset OPTIND && [ -z "$OPTIND" ]
diff --git a/bin/sh/tests/parameters/optind2.0 b/bin/sh/tests/parameters/optind2.0
new file mode 100644
index 0000000..a7689f6
--- /dev/null
+++ b/bin/sh/tests/parameters/optind2.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+[ "$(OPTIND=42 ${SH} -c 'printf %s "$OPTIND"')" = 1 ]
diff --git a/bin/sh/tests/parameters/positional1.0 b/bin/sh/tests/parameters/positional1.0
new file mode 100644
index 0000000..67d1951
--- /dev/null
+++ b/bin/sh/tests/parameters/positional1.0
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+set -- a b c d e f g h i j
+[ "$1" = a ] || echo "error at line $LINENO"
+[ "${1}" = a ] || echo "error at line $LINENO"
+[ "${1-foo}" = a ] || echo "error at line $LINENO"
+[ "${1+foo}" = foo ] || echo "error at line $LINENO"
+[ "$1+foo" = a+foo ] || echo "error at line $LINENO"
+[ "$10" = a0 ] || echo "error at line $LINENO"
+[ "$100" = a00 ] || echo "error at line $LINENO"
+[ "${10}" = j ] || echo "error at line $LINENO"
+[ "${10-foo}" = j ] || echo "error at line $LINENO"
+[ "${100-foo}" = foo ] || echo "error at line $LINENO"
diff --git a/bin/sh/tests/parameters/positional2.0 b/bin/sh/tests/parameters/positional2.0
new file mode 100644
index 0000000..fcec2a4
--- /dev/null
+++ b/bin/sh/tests/parameters/positional2.0
@@ -0,0 +1,65 @@
+# $FreeBSD$
+
+failures=''
+ok=''
+
+testcase() {
+ code="$1"
+ expected="$2"
+ oIFS="$IFS"
+ eval "$code"
+ IFS='|'
+ result="$#|$*"
+ IFS="$oIFS"
+ if [ "x$result" = "x$expected" ]; then
+ ok=x$ok
+ else
+ failures=x$failures
+ echo "For $code, expected $expected actual $result"
+ fi
+}
+
+testcase 'set -- a b; set -- p$@q' '2|pa|bq'
+testcase 'set -- a b; set -- $@q' '2|a|bq'
+testcase 'set -- a b; set -- p$@' '2|pa|b'
+testcase 'set -- a b; set -- p$@q' '2|pa|bq'
+testcase 'set -- a b; set -- $@q' '2|a|bq'
+testcase 'set -- a b; set -- p$@' '2|pa|b'
+testcase 'set -- a b; set -- p$*q' '2|pa|bq'
+testcase 'set -- a b; set -- $*q' '2|a|bq'
+testcase 'set -- a b; set -- p$*' '2|pa|b'
+testcase 'set -- a b; set -- p$*q' '2|pa|bq'
+testcase 'set -- a b; set -- $*q' '2|a|bq'
+testcase 'set -- a b; set -- p$*' '2|pa|b'
+testcase 'set -- a b; set -- "p$@q"' '2|pa|bq'
+testcase 'set -- a b; set -- "$@q"' '2|a|bq'
+testcase 'set -- a b; set -- "p$@"' '2|pa|b'
+testcase 'set -- a b; set -- p"$@"q' '2|pa|bq'
+testcase 'set -- a b; set -- "$@"q' '2|a|bq'
+testcase 'set -- a b; set -- p"$@"' '2|pa|b'
+testcase 'set -- "" a b; set -- "p$@q"' '3|p|a|bq'
+testcase 'set -- "" a b; set -- "$@q"' '3||a|bq'
+testcase 'set -- "" a b; set -- "p$@"' '3|p|a|b'
+testcase 'set -- "" a b; set -- p"$@"q' '3|p|a|bq'
+testcase 'set -- "" a b; set -- "$@"q' '3||a|bq'
+testcase 'set -- "" a b; set -- p"$@"' '3|p|a|b'
+testcase 'set -- a; set -- p$@q' '1|paq'
+testcase 'set -- a; set -- $@q' '1|aq'
+testcase 'set -- a; set -- p$@' '1|pa'
+testcase 'set -- a; set -- p$@q' '1|paq'
+testcase 'set -- a; set -- $@q' '1|aq'
+testcase 'set -- a; set -- p$@' '1|pa'
+testcase 'set -- a; set -- p$*q' '1|paq'
+testcase 'set -- a; set -- $*q' '1|aq'
+testcase 'set -- a; set -- p$*' '1|pa'
+testcase 'set -- a; set -- p$*q' '1|paq'
+testcase 'set -- a; set -- $*q' '1|aq'
+testcase 'set -- a; set -- p$*' '1|pa'
+testcase 'set -- a; set -- "p$@q"' '1|paq'
+testcase 'set -- a; set -- "$@q"' '1|aq'
+testcase 'set -- a; set -- "p$@"' '1|pa'
+testcase 'set -- a; set -- p"$@"q' '1|paq'
+testcase 'set -- a; set -- "$@"q' '1|aq'
+testcase 'set -- a; set -- p"$@"' '1|pa'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/parameters/pwd1.0 b/bin/sh/tests/parameters/pwd1.0
new file mode 100644
index 0000000..0099379
--- /dev/null
+++ b/bin/sh/tests/parameters/pwd1.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Check that bogus PWD values are not accepted from the environment.
+
+cd / || exit 3
+failures=0
+[ "$(PWD=foo ${SH} -c 'pwd')" = / ] || : $((failures += 1))
+[ "$(PWD=/var/empty ${SH} -c 'pwd')" = / ] || : $((failures += 1))
+[ "$(PWD=/var/empty/foo ${SH} -c 'pwd')" = / ] || : $((failures += 1))
+[ "$(PWD=/bin/ls ${SH} -c 'pwd')" = / ] || : $((failures += 1))
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parameters/pwd2.0 b/bin/sh/tests/parameters/pwd2.0
new file mode 100644
index 0000000..2297f8b
--- /dev/null
+++ b/bin/sh/tests/parameters/pwd2.0
@@ -0,0 +1,24 @@
+# $FreeBSD$
+# Check that PWD is exported and accepted from the environment.
+set -e
+
+T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
+trap 'rm -rf $T' 0
+cd -P $T
+TP=$(pwd)
+mkdir test1
+ln -s test1 link
+cd link
+[ "$PWD" = "$TP/link" ]
+[ "$(pwd)" = "$TP/link" ]
+[ "$(pwd -P)" = "$TP/test1" ]
+[ "$(${SH} -c pwd)" = "$TP/link" ]
+[ "$(${SH} -c pwd\ -P)" = "$TP/test1" ]
+cd ..
+[ "$(pwd)" = "$TP" ]
+cd -P link
+[ "$PWD" = "$TP/test1" ]
+[ "$(pwd)" = "$TP/test1" ]
+[ "$(pwd -P)" = "$TP/test1" ]
+[ "$(${SH} -c pwd)" = "$TP/test1" ]
+[ "$(${SH} -c pwd\ -P)" = "$TP/test1" ]
diff --git a/bin/sh/tests/parser/Makefile b/bin/sh/tests/parser/Makefile
new file mode 100644
index 0000000..03650b1
--- /dev/null
+++ b/bin/sh/tests/parser/Makefile
@@ -0,0 +1,64 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/parser
+KYUAFILE= no
+
+FILES= alias1.0
+FILES+= alias2.0
+FILES+= alias3.0
+FILES+= alias4.0
+FILES+= alias5.0
+FILES+= alias6.0
+FILES+= alias7.0
+FILES+= alias8.0
+FILES+= alias9.0
+FILES+= alias10.0
+FILES+= alias11.0
+FILES+= alias12.0
+FILES+= alias13.0
+FILES+= alias14.0
+FILES+= alias15.0 alias15.0.stdout
+FILES+= and-pipe-not.0
+FILES+= case1.0
+FILES+= case2.0
+FILES+= dollar-quote1.0
+FILES+= dollar-quote2.0
+FILES+= dollar-quote3.0
+FILES+= dollar-quote4.0
+FILES+= dollar-quote5.0
+FILES+= dollar-quote6.0
+FILES+= dollar-quote7.0
+FILES+= dollar-quote8.0
+FILES+= dollar-quote9.0
+FILES+= dollar-quote10.0
+FILES+= dollar-quote11.0
+FILES+= empty-braces1.0
+FILES+= empty-cmd1.0
+FILES+= for1.0
+FILES+= for2.0
+FILES+= func1.0
+FILES+= func2.0
+FILES+= func3.0
+FILES+= heredoc1.0
+FILES+= heredoc2.0
+FILES+= heredoc3.0
+FILES+= heredoc4.0
+FILES+= heredoc5.0
+FILES+= heredoc6.0
+FILES+= heredoc7.0
+FILES+= heredoc8.0
+FILES+= heredoc9.0
+FILES+= heredoc10.0
+FILES+= heredoc11.0
+FILES+= no-space1.0
+FILES+= no-space2.0
+FILES+= only-redir1.0
+FILES+= only-redir2.0
+FILES+= only-redir3.0
+FILES+= only-redir4.0
+FILES+= pipe-not1.0
+FILES+= var-assign1.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/parser/alias1.0 b/bin/sh/tests/parser/alias1.0
new file mode 100644
index 0000000..75dd9ab
--- /dev/null
+++ b/bin/sh/tests/parser/alias1.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+alias alias0=exit
+eval 'alias0 0'
+exit 1
diff --git a/bin/sh/tests/parser/alias10.0 b/bin/sh/tests/parser/alias10.0
new file mode 100644
index 0000000..30d99f4
--- /dev/null
+++ b/bin/sh/tests/parser/alias10.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+# This test may start consuming memory indefinitely if it fails.
+ulimit -t 5 2>/dev/null
+ulimit -v 100000 2>/dev/null
+
+alias echo='echo'
+alias echo='echo'
+[ "`eval echo b`" = b ]
diff --git a/bin/sh/tests/parser/alias11.0 b/bin/sh/tests/parser/alias11.0
new file mode 100644
index 0000000..522264f
--- /dev/null
+++ b/bin/sh/tests/parser/alias11.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+alias alias0=alias1
+alias alias1=exit
+eval 'alias0 0'
+exit 3
diff --git a/bin/sh/tests/parser/alias12.0 b/bin/sh/tests/parser/alias12.0
new file mode 100644
index 0000000..2e43791
--- /dev/null
+++ b/bin/sh/tests/parser/alias12.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+unalias -a
+alias alias0=command
+alias true='echo bad'
+eval 'alias0 true'
diff --git a/bin/sh/tests/parser/alias13.0 b/bin/sh/tests/parser/alias13.0
new file mode 100644
index 0000000..53b949d
--- /dev/null
+++ b/bin/sh/tests/parser/alias13.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+unalias -a
+alias command=command
+alias true='echo bad'
+eval 'command true'
diff --git a/bin/sh/tests/parser/alias14.0 b/bin/sh/tests/parser/alias14.0
new file mode 100644
index 0000000..1b92fc0
--- /dev/null
+++ b/bin/sh/tests/parser/alias14.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+alias command='command '
+alias alias0=exit
+eval 'command alias0 0'
+exit 3
diff --git a/bin/sh/tests/parser/alias15.0 b/bin/sh/tests/parser/alias15.0
new file mode 100644
index 0000000..f0fbadb
--- /dev/null
+++ b/bin/sh/tests/parser/alias15.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+f_echoanddo() {
+ printf '%s\n' "$*"
+ "$@"
+}
+
+alias echoanddo='f_echoanddo '
+alias alias0='echo test2'
+eval 'echoanddo echo test1'
+eval 'echoanddo alias0'
+exit 0
diff --git a/bin/sh/tests/parser/alias15.0.stdout b/bin/sh/tests/parser/alias15.0.stdout
new file mode 100644
index 0000000..6dd179c
--- /dev/null
+++ b/bin/sh/tests/parser/alias15.0.stdout
@@ -0,0 +1,4 @@
+echo test1
+test1
+echo test2
+test2
diff --git a/bin/sh/tests/parser/alias2.0 b/bin/sh/tests/parser/alias2.0
new file mode 100644
index 0000000..ae99b8a
--- /dev/null
+++ b/bin/sh/tests/parser/alias2.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+alias alias0=exit
+x=alias0
+eval 'case $x in alias0) exit 0;; esac'
+exit 1
diff --git a/bin/sh/tests/parser/alias3.0 b/bin/sh/tests/parser/alias3.0
new file mode 100644
index 0000000..e0721e2
--- /dev/null
+++ b/bin/sh/tests/parser/alias3.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+alias alias0=exit
+x=alias0
+eval 'case $x in "alias0") alias0 0;; esac'
+exit 1
diff --git a/bin/sh/tests/parser/alias4.0 b/bin/sh/tests/parser/alias4.0
new file mode 100644
index 0000000..19332ed
--- /dev/null
+++ b/bin/sh/tests/parser/alias4.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+alias alias0=exit
+eval 'x=1 alias0 0'
+exit 1
diff --git a/bin/sh/tests/parser/alias5.0 b/bin/sh/tests/parser/alias5.0
new file mode 100644
index 0000000..3d0205f
--- /dev/null
+++ b/bin/sh/tests/parser/alias5.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+alias alias0=exit
+eval '</dev/null alias0 0'
+exit 1
diff --git a/bin/sh/tests/parser/alias6.0 b/bin/sh/tests/parser/alias6.0
new file mode 100644
index 0000000..c723d08
--- /dev/null
+++ b/bin/sh/tests/parser/alias6.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+alias alias0='| cat >/dev/null'
+
+eval '{ echo bad; } alias0'
+eval '(echo bad)alias0'
diff --git a/bin/sh/tests/parser/alias7.0 b/bin/sh/tests/parser/alias7.0
new file mode 100644
index 0000000..b26f0dd
--- /dev/null
+++ b/bin/sh/tests/parser/alias7.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+alias echo='echo a'
+[ "`eval echo b`" = "a b" ]
diff --git a/bin/sh/tests/parser/alias8.0 b/bin/sh/tests/parser/alias8.0
new file mode 100644
index 0000000..7fc2f15
--- /dev/null
+++ b/bin/sh/tests/parser/alias8.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+alias echo='echo'
+[ "`eval echo b`" = b ]
diff --git a/bin/sh/tests/parser/alias9.0 b/bin/sh/tests/parser/alias9.0
new file mode 100644
index 0000000..6bd8808
--- /dev/null
+++ b/bin/sh/tests/parser/alias9.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+alias alias0=:
+alias alias0=exit
+eval 'alias0 0'
+exit 1
diff --git a/bin/sh/tests/parser/and-pipe-not.0 b/bin/sh/tests/parser/and-pipe-not.0
new file mode 100644
index 0000000..35b125c
--- /dev/null
+++ b/bin/sh/tests/parser/and-pipe-not.0
@@ -0,0 +1,2 @@
+# $FreeBSD$
+true && ! true | false
diff --git a/bin/sh/tests/parser/case1.0 b/bin/sh/tests/parser/case1.0
new file mode 100644
index 0000000..49b4c45
--- /dev/null
+++ b/bin/sh/tests/parser/case1.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+keywords='if then else elif fi while until for do done { } case esac ! in'
+
+# Keywords can be used unquoted in case statements, except the keyword
+# esac as the first pattern of a '|' alternation without a starting '('.
+# (POSIX doesn't seem to require (esac) to work.)
+for k in $keywords; do
+ eval "case $k in (foo|$k) ;; *) echo bad ;; esac"
+ eval "case $k in ($k) ;; *) echo bad ;; esac"
+ eval "case $k in foo|$k) ;; *) echo bad ;; esac"
+ [ "$k" = esac ] && continue
+ eval "case $k in $k) ;; *) echo bad ;; esac"
+done
diff --git a/bin/sh/tests/parser/case2.0 b/bin/sh/tests/parser/case2.0
new file mode 100644
index 0000000..14610e4
--- /dev/null
+++ b/bin/sh/tests/parser/case2.0
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+# Pretty much only ash derivatives can parse all of this.
+
+f1() {
+ x=$(case x in
+ (x|esac) ;;
+ (*) echo bad >&2 ;;
+ esac)
+}
+f1
+f2() {
+ x=$(case x in
+ (x|esac) ;;
+ (*) echo bad >&2
+ esac)
+}
+f2
+f3() {
+ x=$(case x in
+ x|esac) ;;
+ *) echo bad >&2 ;;
+ esac)
+}
+f3
+f4() {
+ x=$(case x in
+ x|esac) ;;
+ *) echo bad >&2
+ esac)
+}
+f4
diff --git a/bin/sh/tests/parser/dollar-quote1.0 b/bin/sh/tests/parser/dollar-quote1.0
new file mode 100644
index 0000000..1206141
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote1.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+set -e
+
+[ $'hi' = hi ]
+[ $'hi
+there' = 'hi
+there' ]
+[ $'\"\'\\\a\b\f\t\v' = "\"'\\$(printf "\a\b\f\t\v")" ]
+[ $'hi\nthere' = 'hi
+there' ]
+[ $'a\rb' = "$(printf "a\rb")" ]
diff --git a/bin/sh/tests/parser/dollar-quote10.0 b/bin/sh/tests/parser/dollar-quote10.0
new file mode 100644
index 0000000..ad166da
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote10.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+# a umlaut
+s=$(printf '\303\244')
+# euro sign
+s=$s$(printf '\342\202\254')
+
+# Start a new shell so the locale change is picked up.
+ss="$(LC_ALL=en_US.UTF-8 ${SH} -c "printf %s \$'\u00e4\u20ac'")"
+[ "$s" = "$ss" ]
diff --git a/bin/sh/tests/parser/dollar-quote11.0 b/bin/sh/tests/parser/dollar-quote11.0
new file mode 100644
index 0000000..2e872ab
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote11.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+# some sort of 't' outside BMP
+s=$s$(printf '\360\235\225\245')
+
+# Start a new shell so the locale change is picked up.
+ss="$(LC_ALL=en_US.UTF-8 ${SH} -c "printf %s \$'\U0001d565'")"
+[ "$s" = "$ss" ]
diff --git a/bin/sh/tests/parser/dollar-quote2.0 b/bin/sh/tests/parser/dollar-quote2.0
new file mode 100644
index 0000000..4617ed8
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote2.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# This depends on the ASCII character set.
+
+[ $'\e' = "$(printf "\033")" ]
diff --git a/bin/sh/tests/parser/dollar-quote3.0 b/bin/sh/tests/parser/dollar-quote3.0
new file mode 100644
index 0000000..a7e6852
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote3.0
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.ISO8859-1
+export LC_CTYPE
+
+e=
+for i in 0 1 2 3; do
+ for j in 0 1 2 3 4 5 6 7; do
+ for k in 0 1 2 3 4 5 6 7; do
+ case $i$j$k in
+ 000) continue ;;
+ esac
+ e="$e\\$i$j$k"
+ done
+ done
+done
+ee=`printf "$e"`
+[ "${#ee}" = 255 ] || echo length bad
+
+# Start a new shell so the locale change is picked up.
+[ "$(${SH} -c "printf %s \$'$e'")" = "$ee" ]
diff --git a/bin/sh/tests/parser/dollar-quote4.0 b/bin/sh/tests/parser/dollar-quote4.0
new file mode 100644
index 0000000..f620af5
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote4.0
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+unset LC_ALL
+LC_CTYPE=en_US.ISO8859-1
+export LC_CTYPE
+
+e=
+for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
+ for j in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
+ case $i$j in
+ 00) continue ;;
+ esac
+ e="$e\x$i$j"
+ done
+done
+
+# Start a new shell so the locale change is picked up.
+ee="$(${SH} -c "printf %s \$'$e'")"
+[ "${#ee}" = 255 ] || echo length bad
diff --git a/bin/sh/tests/parser/dollar-quote5.0 b/bin/sh/tests/parser/dollar-quote5.0
new file mode 100644
index 0000000..c2c44ca
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote5.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+# This depends on the ASCII character set.
+
+set -e
+
+[ $'\ca\cb\cc\cd\ce\cf\cg\ch\ci\cj\ck\cl\cm\cn\co\cp\cq\cr\cs\ct\cu\cv\cw\cx\cy\cz' = $'\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032' ]
+[ $'\cA\cB\cC\cD\cE\cF\cG\cH\cI\cJ\cK\cL\cM\cN\cO\cP\cQ\cR\cS\cT\cU\cV\cW\cX\cY\cZ' = $'\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032' ]
+[ $'\c[' = $'\033' ]
+[ $'\c]' = $'\035' ]
+[ $'\c^' = $'\036' ]
+[ $'\c_' = $'\037' ]
diff --git a/bin/sh/tests/parser/dollar-quote6.0 b/bin/sh/tests/parser/dollar-quote6.0
new file mode 100644
index 0000000..a4b1e3f
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote6.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# This depends on the ASCII character set.
+
+[ $'\c\\' = $'\034' ]
diff --git a/bin/sh/tests/parser/dollar-quote7.0 b/bin/sh/tests/parser/dollar-quote7.0
new file mode 100644
index 0000000..c866b1a
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote7.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+set -e
+
+[ $'\u0024\u0040\u0060' = '$@`' ]
+[ $'\U00000024\U00000040\U00000060' = '$@`' ]
diff --git a/bin/sh/tests/parser/dollar-quote8.0 b/bin/sh/tests/parser/dollar-quote8.0
new file mode 100644
index 0000000..8f0b41a
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote8.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+[ $'hello\0' = hello ]
+[ $'hello\0world' = hello ]
+[ $'hello\0'$'world' = helloworld ]
+[ $'hello\000' = hello ]
+[ $'hello\000world' = hello ]
+[ $'hello\000'$'world' = helloworld ]
+[ $'hello\x00' = hello ]
+[ $'hello\x00world' = hello ]
+[ $'hello\x00'$'world' = helloworld ]
diff --git a/bin/sh/tests/parser/dollar-quote9.0 b/bin/sh/tests/parser/dollar-quote9.0
new file mode 100644
index 0000000..df64b7d
--- /dev/null
+++ b/bin/sh/tests/parser/dollar-quote9.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+# POSIX and C99 say D800-DFFF are undefined in a universal character name.
+# We reject this but many other shells expand to something that looks like
+# CESU-8.
+
+v=$( (eval ": \$'\uD800'") 2>&1 >/dev/null)
+[ $? -ne 0 ] && [ -n "$v" ]
diff --git a/bin/sh/tests/parser/empty-braces1.0 b/bin/sh/tests/parser/empty-braces1.0
new file mode 100644
index 0000000..5ab443c
--- /dev/null
+++ b/bin/sh/tests/parser/empty-braces1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# Unfortunately, some scripts depend on the extension of allowing an empty
+# pair of braces.
+
+{ } &
+wait $!
diff --git a/bin/sh/tests/parser/empty-cmd1.0 b/bin/sh/tests/parser/empty-cmd1.0
new file mode 100644
index 0000000..f8b01e9
--- /dev/null
+++ b/bin/sh/tests/parser/empty-cmd1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+! (eval ': || f()') 2>/dev/null
diff --git a/bin/sh/tests/parser/for1.0 b/bin/sh/tests/parser/for1.0
new file mode 100644
index 0000000..eb7c881
--- /dev/null
+++ b/bin/sh/tests/parser/for1.0
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+nl='
+'
+list=' a b c'
+for s1 in "$nl" " "; do
+ for s2 in "$nl" ";" ";$nl"; do
+ for s3 in "$nl" " "; do
+ r=''
+ eval "for i${s1}in ${list}${s2}do${s3}r=\"\$r \$i\"; done"
+ [ "$r" = "$list" ] || exit 1
+ done
+ done
+done
+set -- $list
+for s2 in "$nl" " "; do
+ for s3 in "$nl" " "; do
+ r=''
+ eval "for i${s2}do${s3}r=\"\$r \$i\"; done"
+ [ "$r" = "$list" ] || exit 1
+ done
+done
+for s1 in "$nl" " "; do
+ for s2 in "$nl" ";" ";$nl"; do
+ for s3 in "$nl" " "; do
+ eval "for i${s1}in${s2}do${s3}exit 1; done"
+ done
+ done
+done
diff --git a/bin/sh/tests/parser/for2.0 b/bin/sh/tests/parser/for2.0
new file mode 100644
index 0000000..54ebfc3
--- /dev/null
+++ b/bin/sh/tests/parser/for2.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+# Common extensions to the 'for' syntax.
+
+nl='
+'
+list=' a b c'
+set -- $list
+for s2 in ";" ";$nl"; do
+ for s3 in "$nl" " "; do
+ r=''
+ eval "for i${s2}do${s3}r=\"\$r \$i\"; done"
+ [ "$r" = "$list" ] || exit 1
+ done
+done
diff --git a/bin/sh/tests/parser/func1.0 b/bin/sh/tests/parser/func1.0
new file mode 100644
index 0000000..4e887b2
--- /dev/null
+++ b/bin/sh/tests/parser/func1.0
@@ -0,0 +1,25 @@
+# $FreeBSD$
+# POSIX does not require these bytes to work in function names,
+# but making them all work seems a good goal.
+
+failures=0
+unset LC_ALL
+export LC_CTYPE=en_US.ISO8859-1
+i=128
+set -f
+while [ "$i" -le 255 ]; do
+ c=$(printf \\"$(printf %o "$i")")
+ ok=0
+ eval "$c() { ok=1; }"
+ $c
+ ok1=$ok
+ ok=0
+ "$c"
+ if [ "$ok" != 1 ] || [ "$ok1" != 1 ]; then
+ echo "Bad results for character $i" >&2
+ : $((failures += 1))
+ fi
+ unset -f $c
+ i=$((i+1))
+done
+exit $((failures > 0))
diff --git a/bin/sh/tests/parser/func2.0 b/bin/sh/tests/parser/func2.0
new file mode 100644
index 0000000..5fd4dda
--- /dev/null
+++ b/bin/sh/tests/parser/func2.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+f() { return 42; }
+f() { return 3; } &
+f
+[ $? -eq 42 ]
diff --git a/bin/sh/tests/parser/func3.0 b/bin/sh/tests/parser/func3.0
new file mode 100644
index 0000000..dcac732
--- /dev/null
+++ b/bin/sh/tests/parser/func3.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+name=/var/empty/nosuch
+f() { true; } <$name
+name=/dev/null
+f
diff --git a/bin/sh/tests/parser/heredoc1.0 b/bin/sh/tests/parser/heredoc1.0
new file mode 100644
index 0000000..5ce3897
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc1.0
@@ -0,0 +1,85 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$(cat <<EOF
+hi
+EOF
+)" = hi'
+
+check '"$(cat <<EOF
+${$+hi}
+EOF
+)" = hi'
+
+unset yy
+check '"$(cat <<EOF
+${yy-hi}
+EOF
+)" = hi'
+
+check '"$(cat <<EOF
+${$+hi
+there}
+EOF
+)" = "hi
+there"'
+
+check '"$(cat <<EOF
+$((1+1))
+EOF
+)" = 2'
+
+check '"$(cat <<EOF
+$(echo hi)
+EOF
+)" = hi'
+
+check '"$(cat <<EOF
+`echo hi`
+EOF
+)" = hi'
+
+check '"$(cat <<\EOF
+${$+hi}
+EOF
+)" = "\${\$+hi}"'
+
+check '"$(cat <<\EOF
+$(
+EOF
+)" = \$\('
+
+check '"$(cat <<\EOF
+`
+EOF
+)" = \`'
+
+check '"$(cat <<EOF
+"
+EOF
+)" = \"'
+
+check '"$(cat <<\EOF
+"
+EOF
+)" = \"'
+
+check '"$(cat <<esac
+'"'"'
+esac
+)" = "'"'"'"'
+
+check '"$(cat <<\)
+'"'"'
+)
+)" = "'"'"'"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/heredoc10.0 b/bin/sh/tests/parser/heredoc10.0
new file mode 100644
index 0000000..27369a0
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc10.0
@@ -0,0 +1,49 @@
+# $FreeBSD$
+
+# It may be argued that
+# x=$(cat <<EOF
+# foo
+# EOF)
+# is a valid complete command that sets x to foo, because
+# cat <<EOF
+# foo
+# EOF
+# is a valid script even without the final newline.
+# However, if the here-document is not within a new-style command substitution
+# or there are other constructs nested inside the command substitution that
+# need terminators, the delimiter at the start of a line followed by a close
+# parenthesis is clearly a literal part of the here-document.
+
+# This file contains tests that may not work with simplistic $(...) parsers.
+# The open parentheses in comments help mksh, but not zsh.
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '"$(cat <<EOF # (
+EOF )
+EOF
+)" = "EOF )"'
+
+check '"$({ cat <<EOF # (
+EOF)
+EOF
+})" = "EOF)"'
+
+check '"$(if :; then cat <<EOF # (
+EOF)
+EOF
+fi)" = "EOF)"'
+
+check '"$( (cat <<EOF # (
+EOF)
+EOF
+))" = "EOF)"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/heredoc11.0 b/bin/sh/tests/parser/heredoc11.0
new file mode 100644
index 0000000..5839e46
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc11.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+failures=''
+
+check() {
+ if eval "[ $* ]"; then
+ :
+ else
+ echo "Failed: $*"
+ failures=x$failures
+ fi
+}
+
+check '`cat <<EOF
+foo
+EOF` = foo'
+
+check '"`cat <<EOF
+foo
+EOF`" = foo'
+
+check '`eval "cat <<EOF
+foo
+EOF"` = foo'
+
+test "x$failures" = x
diff --git a/bin/sh/tests/parser/heredoc2.0 b/bin/sh/tests/parser/heredoc2.0
new file mode 100644
index 0000000..4bb85ad
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc2.0
@@ -0,0 +1,39 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+s='ast*que?non' sq=\' dq=\"
+
+check '"$(cat <<EOF
+${s}
+EOF
+)" = "ast*que?non"'
+
+check '"$(cat <<EOF
+${s+'$sq'x'$sq'}
+EOF
+)" = ${sq}x${sq}'
+
+check '"$(cat <<EOF
+${s#ast}
+EOF
+)" = "*que?non"'
+
+check '"$(cat <<EOF
+${s##"ast"}
+EOF
+)" = "*que?non"'
+
+check '"$(cat <<EOF
+${s##'$sq'ast'$sq'}
+EOF
+)" = "*que?non"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/heredoc3.0 b/bin/sh/tests/parser/heredoc3.0
new file mode 100644
index 0000000..b250272
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc3.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# This may be expected to work, but pretty much only ash derivatives allow it.
+
+test "$(cat <<EOF)" = "hi there"
+hi there
+EOF
diff --git a/bin/sh/tests/parser/heredoc4.0 b/bin/sh/tests/parser/heredoc4.0
new file mode 100644
index 0000000..fa3af5f
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc4.0
@@ -0,0 +1,44 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+f() {
+ cat <<EOF && echo `echo bar`
+foo
+EOF
+}
+check '"`f`" = "foo
+bar"'
+
+f() {
+ cat <<EOF && echo $(echo bar)
+foo
+EOF
+}
+check '"$(f)" = "foo
+bar"'
+
+f() {
+ echo `echo bar` && cat <<EOF
+foo
+EOF
+}
+check '"`f`" = "bar
+foo"'
+
+f() {
+ echo $(echo bar) && cat <<EOF
+foo
+EOF
+}
+check '"$(f)" = "bar
+foo"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/heredoc5.0 b/bin/sh/tests/parser/heredoc5.0
new file mode 100644
index 0000000..84b0eb2
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc5.0
@@ -0,0 +1,56 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+f() {
+ cat <<EOF && echo `cat <<EOF
+bar
+EOF
+`
+foo
+EOF
+}
+check '"`f`" = "foo
+bar"'
+
+f() {
+ cat <<EOF && echo $(cat <<EOF
+bar
+EOF
+)
+foo
+EOF
+}
+check '"$(f)" = "foo
+bar"'
+
+f() {
+ echo `cat <<EOF
+bar
+EOF
+` && cat <<EOF
+foo
+EOF
+}
+check '"`f`" = "bar
+foo"'
+
+f() {
+ echo $(cat <<EOF
+bar
+EOF
+) && cat <<EOF
+foo
+EOF
+}
+check '"$(f)" = "bar
+foo"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/heredoc6.0 b/bin/sh/tests/parser/heredoc6.0
new file mode 100644
index 0000000..3a634de
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc6.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+r=
+! command eval ": <<EOF; )" 2>/dev/null; command eval : hi \${r:=0}
+exit ${r:-3}
diff --git a/bin/sh/tests/parser/heredoc7.0 b/bin/sh/tests/parser/heredoc7.0
new file mode 100644
index 0000000..a150106
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc7.0
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+# Some of these created malformed parse trees with null pointers for here
+# documents, causing the here document writing process to segfault.
+eval ': <<EOF'
+eval ': <<EOF;'
+eval '`: <<EOF`'
+eval '`: <<EOF;`'
+eval '`: <<EOF`;'
+eval '`: <<EOF;`;'
+
+# Some of these created malformed parse trees with null pointers for here
+# documents, causing sh to segfault.
+eval ': <<\EOF'
+eval ': <<\EOF;'
+eval '`: <<\EOF`'
+eval '`: <<\EOF;`'
+eval '`: <<\EOF`;'
+eval '`: <<\EOF;`;'
diff --git a/bin/sh/tests/parser/heredoc8.0 b/bin/sh/tests/parser/heredoc8.0
new file mode 100644
index 0000000..598358a
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc8.0
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+s='ast*que?non' sq=\' dq=\"
+
+# This is possibly useful but differs from other shells.
+check '"$(cat <<EOF
+${s+"x"}
+EOF
+)" = ${dq}x${dq}'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/heredoc9.0 b/bin/sh/tests/parser/heredoc9.0
new file mode 100644
index 0000000..125a542
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc9.0
@@ -0,0 +1,58 @@
+# $FreeBSD$
+
+# It may be argued that
+# x=$(cat <<EOF
+# foo
+# EOF)
+# is a valid complete command that sets x to foo, because
+# cat <<EOF
+# foo
+# EOF
+# is a valid script even without the final newline.
+# However, if the here-document is not within a new-style command substitution
+# or there are other constructs nested inside the command substitution that
+# need terminators, the delimiter at the start of a line followed by a close
+# parenthesis is clearly a literal part of the here-document.
+
+# This file contains tests that also work with simplistic $(...) parsers.
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+check '`${SH} -c "cat <<EOF
+EOF)
+EOF
+"` = "EOF)"'
+
+check '`${SH} -c "(cat <<EOF
+EOF)
+EOF
+)"` = "EOF)"'
+
+check '"`cat <<EOF
+EOF x
+EOF
+`" = "EOF x"'
+
+check '"`cat <<EOF
+EOF )
+EOF
+`" = "EOF )"'
+
+check '"`cat <<EOF
+EOF)
+EOF
+`" = "EOF)"'
+
+check '"$(cat <<EOF
+EOF x
+EOF
+)" = "EOF x"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/no-space1.0 b/bin/sh/tests/parser/no-space1.0
new file mode 100644
index 0000000..6df9f63
--- /dev/null
+++ b/bin/sh/tests/parser/no-space1.0
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+# These are ugly but are required to work.
+
+set -e
+
+while(false)do(:)done
+if(false)then(:)fi
+if(false)then(:)else(:)fi
+(:&&:)||:
+until(:)do(:)done
+case x in(x);;*)exit 1;(:)esac
+case x in(x);;*)exit 1;;esac
+for i do(:)done
+{(:)}
+f(){(:)}
+:|:
+(:)|(:)
diff --git a/bin/sh/tests/parser/no-space2.0 b/bin/sh/tests/parser/no-space2.0
new file mode 100644
index 0000000..4e8447b
--- /dev/null
+++ b/bin/sh/tests/parser/no-space2.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+# This conflicts with ksh extended patterns but occurs in the wild.
+
+set -e
+
+!(false)
diff --git a/bin/sh/tests/parser/only-redir1.0 b/bin/sh/tests/parser/only-redir1.0
new file mode 100644
index 0000000..46076c8
--- /dev/null
+++ b/bin/sh/tests/parser/only-redir1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+</dev/null &
+wait $!
diff --git a/bin/sh/tests/parser/only-redir2.0 b/bin/sh/tests/parser/only-redir2.0
new file mode 100644
index 0000000..b9e9501
--- /dev/null
+++ b/bin/sh/tests/parser/only-redir2.0
@@ -0,0 +1,2 @@
+# $FreeBSD$
+</dev/null | :
diff --git a/bin/sh/tests/parser/only-redir3.0 b/bin/sh/tests/parser/only-redir3.0
new file mode 100644
index 0000000..128a483
--- /dev/null
+++ b/bin/sh/tests/parser/only-redir3.0
@@ -0,0 +1,2 @@
+# $FreeBSD$
+case x in x) </dev/null ;; esac
diff --git a/bin/sh/tests/parser/only-redir4.0 b/bin/sh/tests/parser/only-redir4.0
new file mode 100644
index 0000000..d804e12
--- /dev/null
+++ b/bin/sh/tests/parser/only-redir4.0
@@ -0,0 +1,2 @@
+# $FreeBSD$
+case x in x) </dev/null ;& esac
diff --git a/bin/sh/tests/parser/pipe-not1.0 b/bin/sh/tests/parser/pipe-not1.0
new file mode 100644
index 0000000..9842ff0
--- /dev/null
+++ b/bin/sh/tests/parser/pipe-not1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+: | ! : | false
diff --git a/bin/sh/tests/parser/var-assign1.0 b/bin/sh/tests/parser/var-assign1.0
new file mode 100644
index 0000000..1fd3b26
--- /dev/null
+++ b/bin/sh/tests/parser/var-assign1.0
@@ -0,0 +1,19 @@
+# $FreeBSD$
+# In a variable assignment, both the name and the equals sign must be entirely
+# unquoted. Therefore, there is only one assignment below; the other words
+# containing equals signs are command words.
+
+abc=0
+\abc=1 2>/dev/null
+a\bc=2 2>/dev/null
+abc\=3 2>/dev/null
+a\bc\=4 2>/dev/null
+'abc'=5 2>/dev/null
+a'b'c=6 2>/dev/null
+abc'='7 2>/dev/null
+'abc=8' 2>/dev/null
+"abc"=9 2>/dev/null
+a"b"c=10 2>/dev/null
+abc"="11 2>/dev/null
+"abc=12" 2>/dev/null
+[ "$abc" = 0 ]
diff --git a/bin/sh/tests/set-e/Makefile b/bin/sh/tests/set-e/Makefile
new file mode 100644
index 0000000..55d7917
--- /dev/null
+++ b/bin/sh/tests/set-e/Makefile
@@ -0,0 +1,44 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${TESTSBASE}/bin/sh/set-e
+KYUAFILE= no
+
+FILES= and1.0
+FILES+= and2.1
+FILES+= and3.0
+FILES+= and4.0
+FILES+= background1.0
+FILES+= cmd1.0
+FILES+= cmd2.1
+FILES+= elif1.0
+FILES+= elif2.0
+FILES+= eval1.0
+FILES+= eval2.1
+FILES+= for1.0
+FILES+= func1.0
+FILES+= func2.1
+FILES+= if1.0
+FILES+= if2.0
+FILES+= if3.0
+FILES+= not1.0
+FILES+= not2.0
+FILES+= or1.0
+FILES+= or2.0
+FILES+= or3.1
+FILES+= pipe1.1
+FILES+= pipe2.0
+FILES+= return1.0
+FILES+= semi1.1
+FILES+= semi2.1
+FILES+= subshell1.0
+FILES+= subshell2.1
+FILES+= until1.0
+FILES+= until2.0
+FILES+= until3.0
+FILES+= while1.0
+FILES+= while2.0
+FILES+= while3.0
+
+.include <bsd.test.mk>
diff --git a/bin/sh/tests/set-e/and1.0 b/bin/sh/tests/set-e/and1.0
new file mode 100644
index 0000000..607b7c3
--- /dev/null
+++ b/bin/sh/tests/set-e/and1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+true && true
diff --git a/bin/sh/tests/set-e/and2.1 b/bin/sh/tests/set-e/and2.1
new file mode 100644
index 0000000..78e203a
--- /dev/null
+++ b/bin/sh/tests/set-e/and2.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+true && false
+exit 0
diff --git a/bin/sh/tests/set-e/and3.0 b/bin/sh/tests/set-e/and3.0
new file mode 100644
index 0000000..9fafb1c
--- /dev/null
+++ b/bin/sh/tests/set-e/and3.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+false && true
+exit 0
diff --git a/bin/sh/tests/set-e/and4.0 b/bin/sh/tests/set-e/and4.0
new file mode 100644
index 0000000..25d0e61
--- /dev/null
+++ b/bin/sh/tests/set-e/and4.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+false && false
+exit 0
diff --git a/bin/sh/tests/set-e/background1.0 b/bin/sh/tests/set-e/background1.0
new file mode 100644
index 0000000..21577f4
--- /dev/null
+++ b/bin/sh/tests/set-e/background1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+false &
diff --git a/bin/sh/tests/set-e/cmd1.0 b/bin/sh/tests/set-e/cmd1.0
new file mode 100644
index 0000000..67fdcbc
--- /dev/null
+++ b/bin/sh/tests/set-e/cmd1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+true
diff --git a/bin/sh/tests/set-e/cmd2.1 b/bin/sh/tests/set-e/cmd2.1
new file mode 100644
index 0000000..7cd8b09
--- /dev/null
+++ b/bin/sh/tests/set-e/cmd2.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+false
+exit 0
diff --git a/bin/sh/tests/set-e/elif1.0 b/bin/sh/tests/set-e/elif1.0
new file mode 100644
index 0000000..6a5937d
--- /dev/null
+++ b/bin/sh/tests/set-e/elif1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+set -e
+if false; then
+ :
+elif false; then
+ :
+fi
diff --git a/bin/sh/tests/set-e/elif2.0 b/bin/sh/tests/set-e/elif2.0
new file mode 100644
index 0000000..9dbb4bf
--- /dev/null
+++ b/bin/sh/tests/set-e/elif2.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+set -e
+if false; then
+ :
+elif false; false; then
+ :
+fi
diff --git a/bin/sh/tests/set-e/eval1.0 b/bin/sh/tests/set-e/eval1.0
new file mode 100644
index 0000000..9b7f67b
--- /dev/null
+++ b/bin/sh/tests/set-e/eval1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+eval false || true
diff --git a/bin/sh/tests/set-e/eval2.1 b/bin/sh/tests/set-e/eval2.1
new file mode 100644
index 0000000..8bb7f3a
--- /dev/null
+++ b/bin/sh/tests/set-e/eval2.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+eval false
+exit 0
diff --git a/bin/sh/tests/set-e/for1.0 b/bin/sh/tests/set-e/for1.0
new file mode 100644
index 0000000..67eb718
--- /dev/null
+++ b/bin/sh/tests/set-e/for1.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+set -e
+f() {
+ for i in a b c; do
+ false
+ true
+ done
+}
+f || true
diff --git a/bin/sh/tests/set-e/func1.0 b/bin/sh/tests/set-e/func1.0
new file mode 100644
index 0000000..3c6b704
--- /dev/null
+++ b/bin/sh/tests/set-e/func1.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+set -e
+f() {
+ false
+ true
+}
+f || true
diff --git a/bin/sh/tests/set-e/func2.1 b/bin/sh/tests/set-e/func2.1
new file mode 100644
index 0000000..cc76d6e
--- /dev/null
+++ b/bin/sh/tests/set-e/func2.1
@@ -0,0 +1,7 @@
+# $FreeBSD$
+set -e
+f() {
+ false
+ exit 0
+}
+f
diff --git a/bin/sh/tests/set-e/if1.0 b/bin/sh/tests/set-e/if1.0
new file mode 100644
index 0000000..36aa4bd
--- /dev/null
+++ b/bin/sh/tests/set-e/if1.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+set -e
+if false; then
+ :
+fi
diff --git a/bin/sh/tests/set-e/if2.0 b/bin/sh/tests/set-e/if2.0
new file mode 100644
index 0000000..4955408
--- /dev/null
+++ b/bin/sh/tests/set-e/if2.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+set -e
+# PR 28852
+if true; then
+ false && true
+fi
+exit 0
diff --git a/bin/sh/tests/set-e/if3.0 b/bin/sh/tests/set-e/if3.0
new file mode 100644
index 0000000..a4916a8
--- /dev/null
+++ b/bin/sh/tests/set-e/if3.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+set -e
+if false; false; then
+ :
+fi
diff --git a/bin/sh/tests/set-e/not1.0 b/bin/sh/tests/set-e/not1.0
new file mode 100644
index 0000000..21c089a
--- /dev/null
+++ b/bin/sh/tests/set-e/not1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+! true
+exit 0
diff --git a/bin/sh/tests/set-e/not2.0 b/bin/sh/tests/set-e/not2.0
new file mode 100644
index 0000000..7d93b4d
--- /dev/null
+++ b/bin/sh/tests/set-e/not2.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+! false
+! eval false
diff --git a/bin/sh/tests/set-e/or1.0 b/bin/sh/tests/set-e/or1.0
new file mode 100644
index 0000000..c2dcbe9
--- /dev/null
+++ b/bin/sh/tests/set-e/or1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+true || false
diff --git a/bin/sh/tests/set-e/or2.0 b/bin/sh/tests/set-e/or2.0
new file mode 100644
index 0000000..934e2a6
--- /dev/null
+++ b/bin/sh/tests/set-e/or2.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+false || true
diff --git a/bin/sh/tests/set-e/or3.1 b/bin/sh/tests/set-e/or3.1
new file mode 100644
index 0000000..7a617a1
--- /dev/null
+++ b/bin/sh/tests/set-e/or3.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+false || false
+exit 0
diff --git a/bin/sh/tests/set-e/pipe1.1 b/bin/sh/tests/set-e/pipe1.1
new file mode 100644
index 0000000..c0bad0f
--- /dev/null
+++ b/bin/sh/tests/set-e/pipe1.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+true | false
+exit 0
diff --git a/bin/sh/tests/set-e/pipe2.0 b/bin/sh/tests/set-e/pipe2.0
new file mode 100644
index 0000000..1e25566
--- /dev/null
+++ b/bin/sh/tests/set-e/pipe2.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+false | true
diff --git a/bin/sh/tests/set-e/return1.0 b/bin/sh/tests/set-e/return1.0
new file mode 100644
index 0000000..961bd41
--- /dev/null
+++ b/bin/sh/tests/set-e/return1.0
@@ -0,0 +1,11 @@
+# $FreeBSD$
+set -e
+
+# PR 77067, 85267
+f() {
+ return 1
+ true
+}
+
+f || true
+exit 0
diff --git a/bin/sh/tests/set-e/semi1.1 b/bin/sh/tests/set-e/semi1.1
new file mode 100644
index 0000000..90476a9
--- /dev/null
+++ b/bin/sh/tests/set-e/semi1.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+false; true
+exit 0
diff --git a/bin/sh/tests/set-e/semi2.1 b/bin/sh/tests/set-e/semi2.1
new file mode 100644
index 0000000..8f510ac
--- /dev/null
+++ b/bin/sh/tests/set-e/semi2.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+true; false
+exit 0
diff --git a/bin/sh/tests/set-e/subshell1.0 b/bin/sh/tests/set-e/subshell1.0
new file mode 100644
index 0000000..8e5831b
--- /dev/null
+++ b/bin/sh/tests/set-e/subshell1.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+set -e
+(true)
diff --git a/bin/sh/tests/set-e/subshell2.1 b/bin/sh/tests/set-e/subshell2.1
new file mode 100644
index 0000000..619e98a
--- /dev/null
+++ b/bin/sh/tests/set-e/subshell2.1
@@ -0,0 +1,4 @@
+# $FreeBSD$
+set -e
+(false)
+exit 0
diff --git a/bin/sh/tests/set-e/until1.0 b/bin/sh/tests/set-e/until1.0
new file mode 100644
index 0000000..71ea7f2
--- /dev/null
+++ b/bin/sh/tests/set-e/until1.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+set -e
+until false; do
+ break
+done
diff --git a/bin/sh/tests/set-e/until2.0 b/bin/sh/tests/set-e/until2.0
new file mode 100644
index 0000000..24ea276
--- /dev/null
+++ b/bin/sh/tests/set-e/until2.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+set -e
+until false; false; do
+ break
+done
diff --git a/bin/sh/tests/set-e/until3.0 b/bin/sh/tests/set-e/until3.0
new file mode 100644
index 0000000..597db59
--- /dev/null
+++ b/bin/sh/tests/set-e/until3.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+set -e
+f() {
+ until false; do
+ false
+ break
+ done
+}
+f || true
diff --git a/bin/sh/tests/set-e/while1.0 b/bin/sh/tests/set-e/while1.0
new file mode 100644
index 0000000..371c94a
--- /dev/null
+++ b/bin/sh/tests/set-e/while1.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+set -e
+while false; do
+ :
+done
diff --git a/bin/sh/tests/set-e/while2.0 b/bin/sh/tests/set-e/while2.0
new file mode 100644
index 0000000..124966c
--- /dev/null
+++ b/bin/sh/tests/set-e/while2.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+set -e
+while false; false; do
+ :
+done
diff --git a/bin/sh/tests/set-e/while3.0 b/bin/sh/tests/set-e/while3.0
new file mode 100644
index 0000000..dd3c790
--- /dev/null
+++ b/bin/sh/tests/set-e/while3.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+set -e
+f() {
+ while true; do
+ false
+ break
+ done
+}
+f || true
diff --git a/bin/sh/trap.c b/bin/sh/trap.c
index 3fc8566..e5a2a91 100644
--- a/bin/sh/trap.c
+++ b/bin/sh/trap.c
@@ -80,7 +80,6 @@ static char *volatile trap[NSIG]; /* trap handler commands */
static volatile sig_atomic_t gotsig[NSIG];
/* indicates specified signal received */
static int ignore_sigchld; /* Used while handling SIGCHLD traps. */
-volatile sig_atomic_t gotwinch;
static int last_trapsig;
static int exiting; /* exitshell() has been called */
@@ -293,12 +292,6 @@ setsignal(int signo)
action = S_IGN;
break;
#endif
-#ifndef NO_HISTORY
- case SIGWINCH:
- if (rootshell && iflag)
- action = S_CATCH;
- break;
-#endif
}
}
@@ -362,10 +355,12 @@ void
ignoresig(int signo)
{
+ if (sigmode[signo] == 0)
+ setsignal(signo);
if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
signal(signo, SIG_IGN);
+ sigmode[signo] = S_IGN;
}
- sigmode[signo] = S_HARD_IGN;
}
@@ -398,11 +393,6 @@ onsig(int signo)
gotsig[signo] = 1;
pendingsig = signo;
}
-
-#ifndef NO_HISTORY
- if (signo == SIGWINCH)
- gotwinch = 1;
-#endif
}
@@ -488,9 +478,6 @@ setinteractive(int on)
setsignal(SIGINT);
setsignal(SIGQUIT);
setsignal(SIGTERM);
-#ifndef NO_HISTORY
- setsignal(SIGWINCH);
-#endif
is_interactive = on;
}
diff --git a/bin/sh/trap.h b/bin/sh/trap.h
index a962251..541b9b1 100644
--- a/bin/sh/trap.h
+++ b/bin/sh/trap.h
@@ -36,7 +36,6 @@
extern volatile sig_atomic_t pendingsig;
extern volatile sig_atomic_t pendingsig_waitcmd;
extern int in_dotrap;
-extern volatile sig_atomic_t gotwinch;
void clear_traps(void);
int have_traps(void);
diff --git a/bin/sh/var.c b/bin/sh/var.c
index c20d032..1fd8c77 100644
--- a/bin/sh/var.c
+++ b/bin/sh/var.c
@@ -88,11 +88,9 @@ struct var vifs;
struct var vmail;
struct var vmpath;
struct var vpath;
-struct var vppid;
struct var vps1;
struct var vps2;
struct var vps4;
-struct var vvers;
static struct var voptind;
struct var vdisvfork;
@@ -111,8 +109,6 @@ static const struct varinit varinit[] = {
NULL },
{ &vpath, 0, "PATH=" _PATH_DEFPATH,
changepath },
- { &vppid, VUNSET, "PPID=",
- NULL },
/*
* vps1 depends on uid
*/
@@ -180,15 +176,14 @@ initvar(void)
vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
vps1.flags = VSTRFIXED|VTEXTFIXED;
}
- if ((vppid.flags & VEXPORT) == 0) {
- fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
- setvarsafe("PPID", ppid, 0);
- }
+ fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
+ setvarsafe("PPID", ppid, 0);
for (envp = environ ; *envp ; envp++) {
if (strchr(*envp, '=')) {
setvareq(*envp, VEXPORT|VTEXTFIXED);
}
}
+ setvareq("OPTIND=1", VTEXTFIXED);
}
/*
@@ -224,8 +219,9 @@ void
setvar(const char *name, const char *val, int flags)
{
const char *p;
- int len;
- int namelen;
+ size_t len;
+ size_t namelen;
+ size_t vallen;
char *nameeq;
int isbad;
@@ -244,18 +240,20 @@ setvar(const char *name, const char *val, int flags)
}
namelen = p - name;
if (isbad)
- error("%.*s: bad variable name", namelen, name);
+ error("%.*s: bad variable name", (int)namelen, name);
len = namelen + 2; /* 2 is space for '=' and '\0' */
if (val == NULL) {
flags |= VUNSET;
+ vallen = 0;
} else {
- len += strlen(val);
+ vallen = strlen(val);
+ len += vallen;
}
nameeq = ckmalloc(len);
memcpy(nameeq, name, namelen);
nameeq[namelen] = '=';
if (val)
- scopy(val, nameeq + namelen + 1);
+ memcpy(nameeq + namelen + 1, val, vallen + 1);
else
nameeq[namelen + 1] = '\0';
setvareq(nameeq, flags);
diff --git a/bin/sh/var.h b/bin/sh/var.h
index 6cdfbfe..6ce5b24 100644
--- a/bin/sh/var.h
+++ b/bin/sh/var.h
@@ -75,7 +75,6 @@ extern struct var vifs;
extern struct var vmail;
extern struct var vmpath;
extern struct var vpath;
-extern struct var vppid;
extern struct var vps1;
extern struct var vps2;
extern struct var vps4;
diff --git a/bin/test/Makefile b/bin/test/Makefile
index 7c64b40..e9a0507 100644
--- a/bin/test/Makefile
+++ b/bin/test/Makefile
@@ -1,8 +1,14 @@
# @(#)Makefile 8.1 (Berkeley) 5/31/93
# $FreeBSD$
+.include <bsd.own.mk>
+
PROG= test
LINKS= ${BINDIR}/test ${BINDIR}/[
MLINKS= test.1 [.1
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
diff --git a/bin/test/tests/Makefile b/bin/test/tests/Makefile
new file mode 100644
index 0000000..be32dbb
--- /dev/null
+++ b/bin/test/tests/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin/test
+
+TAP_TESTS_SH= legacy_test
+# Some tests in here are silently not run when the tests are executed as
+# root. Explicitly tell Kyua to drop privileges.
+#
+# TODO(jmmv): Kyua needs to do this by default, not only when explicitly
+# requested. See https://code.google.com/p/kyua/issues/detail?id=6
+TEST_METADATA.legacy_test+= required_user="unprivileged"
+
+.include <tap.test.mk>
diff --git a/bin/test/tests/legacy_test.sh b/bin/test/tests/legacy_test.sh
new file mode 100644
index 0000000..9229551
--- /dev/null
+++ b/bin/test/tests/legacy_test.sh
@@ -0,0 +1,196 @@
+#!/bin/sh
+
+#-
+# Copyright (c) June 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+#
+# TEST.sh - check if test(1) or builtin test works
+#
+# $FreeBSD$
+
+# force a specified test program, e.g. `env test=/bin/test sh regress.sh'
+: ${test=test}
+
+t ()
+{
+ # $1 -> exit code
+ # $2 -> $test expression
+
+ count=$((count+1))
+ # check for syntax errors
+ syntax="`eval $test $2 2>&1`"
+ ret=$?
+ if test -n "$syntax"; then
+ printf "not ok %s - (syntax error)\n" "$count $2"
+ elif [ "$ret" != "$1" ]; then
+ printf "not ok %s - (got $ret, expected $1)\n" "$count $2"
+ else
+ printf "ok %s\n" "$count $2"
+ fi
+}
+
+count=0
+echo "1..130"
+
+t 0 'b = b'
+t 0 'b == b'
+t 1 'b != b'
+t 0 '\( b = b \)'
+t 0 '\( b == b \)'
+t 1 '! \( b = b \)'
+t 1 '! \( b == b \)'
+t 1 '! -f /etc/passwd'
+
+t 0 '-h = -h'
+t 0 '-o = -o'
+t 1 '-f = h'
+t 1 '-h = f'
+t 1 '-o = f'
+t 1 'f = -o'
+t 0 '\( -h = -h \)'
+t 1 '\( a = -h \)'
+t 1 '\( -f = h \)'
+t 0 '-h = -h -o a'
+t 0 '\( -h = -h \) -o 1'
+t 0 '-h = -h -o -h = -h'
+t 0 '\( -h = -h \) -o \( -h = -h \)'
+t 0 'roedelheim = roedelheim'
+t 1 'potsdam = berlin-dahlem'
+
+t 0 '-d /'
+t 0 '-d / -a a != b'
+t 1 '-z "-z"'
+t 0 '-n -n'
+
+t 0 '0'
+t 0 '\( 0 \)'
+t 0 '-E'
+t 0 '-X -a -X'
+t 0 '-XXX'
+t 0 '\( -E \)'
+t 0 'true -o X'
+t 0 'true -o -X'
+t 0 '\( \( \( a = a \) -o 1 \) -a 1 \) -a true'
+t 1 '-h /'
+t 0 '-r /'
+t 1 '-w /'
+t 0 '-x /bin/sh'
+t 0 '-c /dev/null'
+t 0 '-f /etc/passwd'
+t 0 '-s /etc/passwd'
+
+t 1 '! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)'
+t 0 '100 -eq 100'
+t 0 '100 -lt 200'
+t 1 '1000 -lt 200'
+t 0 '1000 -gt 200'
+t 0 '1000 -ge 200'
+t 0 '1000 -ge 1000'
+t 1 '2 -ne 2'
+t 0 '0 -eq 0'
+t 1 '-5 -eq 5'
+t 0 '\( 0 -eq 0 \)'
+t 1 '1 -eq 0 -o a = a -a 1 -eq 0 -o a = aa'
+
+t 1 '"" -o ""'
+t 1 '"" -a ""'
+t 1 '"a" -a ""'
+t 0 '"a" -a ! ""'
+t 1 '""'
+t 0 '! ""'
+
+t 0 '!'
+t 0 '\('
+t 0 '\)'
+
+t 1 '\( = \)'
+t 0 '\( != \)'
+t 0 '\( ! \)'
+t 0 '\( \( \)'
+t 0 '\( \) \)'
+t 0 '! = !'
+t 1 '! != !'
+t 1 '-n = \)'
+t 0 '! != \)'
+t 1 '! = a'
+t 0 '! != -n'
+t 0 '! -c /etc/passwd'
+
+t 1 '! = = ='
+t 0 '! = = \)'
+t 0 '! "" -o ""'
+t 1 '! "x" -o ""'
+t 1 '! "" -o "x"'
+t 1 '! "x" -o "x"'
+t 0 '\( -f /etc/passwd \)'
+t 0 '\( ! "" \)'
+t 1 '\( ! -e \)'
+
+t 0 '0 -eq 0 -a -d /'
+t 0 '-s = "" -o "" = ""'
+t 0 '"" = "" -o -s = ""'
+t 1 '-s = "" -o -s = ""'
+t 0 '-z x -o x = "#" -o x = x'
+t 1 '-z y -o y = "#" -o y = x'
+t 0 '0 -ne 0 -o ! -f /'
+t 0 '1 -ne 0 -o ! -f /etc/passwd'
+t 1 '0 -ne 0 -o ! -f /etc/passwd'
+
+t 0 '-n ='
+t 1 '-z ='
+t 1 '! ='
+t 0 '-n -eq'
+t 1 '-z -eq'
+t 1 '! -eq'
+t 0 '-n -a'
+t 1 '-z -a'
+t 1 '! -a'
+t 0 '-n -o'
+t 1 '-z -o'
+t 1 '! -o'
+t 1 '! -n ='
+t 0 '! -z ='
+t 0 '! ! ='
+t 1 '! -n -eq'
+t 0 '! -z -eq'
+t 0 '! ! -eq'
+t 1 '! -n -a'
+t 0 '! -z -a'
+t 0 '! ! -a'
+t 1 '! -n -o'
+t 0 '! -z -o'
+t 0 '! ! -o'
+t 0 '\( -n = \)'
+t 1 '\( -z = \)'
+t 1 '\( ! = \)'
+t 0 '\( -n -eq \)'
+t 1 '\( -z -eq \)'
+t 1 '\( ! -eq \)'
+t 0 '\( -n -a \)'
+t 1 '\( -z -a \)'
+t 1 '\( ! -a \)'
+t 0 '\( -n -o \)'
+t 1 '\( -z -o \)'
+t 1 '\( ! -o \)'
diff --git a/bin/tests/Makefile b/bin/tests/Makefile
new file mode 100644
index 0000000..7a59b10
--- /dev/null
+++ b/bin/tests/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin
+
+.PATH: ${.CURDIR:H:H}/tests
+KYUAFILE= yes
+
+.include <bsd.test.mk>
OpenPOWER on IntegriCloud