aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2014-04-28 07:50:45 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2014-04-28 07:50:45 +0000
commit3b8f08459569bf0faa21473e5cec2491e95c9349 (patch)
tree80f45dd81ca716bcd7ca9674581e1fc40b93cd34 /bin
parent9d2ab4a62d6733c45958627ac113bdbd818d1e2a (diff)
parentb2ba55951383498f252746f618d513139da06e8e (diff)
downloadsrc-3b8f08459569bf0faa21473e5cec2491e95c9349.tar.gz
src-3b8f08459569bf0faa21473e5cec2491e95c9349.zip
Merge head
Notes
Notes: svn path=/projects/bmake/; revision=265044
Diffstat (limited to 'bin')
-rw-r--r--bin/Makefile6
-rw-r--r--bin/cat/cat.c1
-rw-r--r--bin/date/Makefile6
-rw-r--r--bin/date/date.116
-rw-r--r--bin/date/date.c24
-rw-r--r--bin/date/tests/Makefile9
-rw-r--r--bin/date/tests/legacy_test.sh91
-rw-r--r--bin/dd/args.c14
-rw-r--r--bin/dd/dd.117
-rw-r--r--bin/dd/dd.h57
-rw-r--r--bin/dd/misc.c11
-rw-r--r--bin/df/df.18
-rw-r--r--bin/freebsd-version/freebsd-version.sh.in2
-rw-r--r--bin/kill/kill.c7
-rw-r--r--bin/ls/ls.165
-rw-r--r--bin/ls/ls.c6
-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/options.c2
-rw-r--r--bin/pax/pax.120
-rw-r--r--bin/pax/tests/Makefile9
-rw-r--r--bin/pax/tests/legacy_test.pl89
-rw-r--r--bin/pkill/Makefile6
-rw-r--r--bin/pkill/pkill.c5
-rw-r--r--bin/pkill/tests/Makefile38
-rw-r--r--bin/pkill/tests/pgrep-F_test.sh24
-rw-r--r--bin/pkill/tests/pgrep-LF_test.sh41
-rw-r--r--bin/pkill/tests/pgrep-P_test.sh22
-rw-r--r--bin/pkill/tests/pgrep-U_test.sh38
-rw-r--r--bin/pkill/tests/pgrep-_g_test.sh38
-rw-r--r--bin/pkill/tests/pgrep-_s_test.sh22
-rw-r--r--bin/pkill/tests/pgrep-g_test.sh37
-rw-r--r--bin/pkill/tests/pgrep-i_test.sh24
-rw-r--r--bin/pkill/tests/pgrep-j_test.sh75
-rw-r--r--bin/pkill/tests/pgrep-l_test.sh20
-rw-r--r--bin/pkill/tests/pgrep-n_test.sh24
-rw-r--r--bin/pkill/tests/pgrep-o_test.sh24
-rw-r--r--bin/pkill/tests/pgrep-q_test.sh38
-rw-r--r--bin/pkill/tests/pgrep-s_test.sh37
-rw-r--r--bin/pkill/tests/pgrep-t_test.sh37
-rw-r--r--bin/pkill/tests/pgrep-v_test.sh25
-rw-r--r--bin/pkill/tests/pgrep-x_test.sh36
-rw-r--r--bin/pkill/tests/pkill-F_test.sh27
-rw-r--r--bin/pkill/tests/pkill-LF_test.sh43
-rw-r--r--bin/pkill/tests/pkill-P_test.sh25
-rw-r--r--bin/pkill/tests/pkill-U_test.sh42
-rw-r--r--bin/pkill/tests/pkill-_g_test.sh42
-rw-r--r--bin/pkill/tests/pkill-g_test.sh41
-rw-r--r--bin/pkill/tests/pkill-i_test.sh26
-rw-r--r--bin/pkill/tests/pkill-j_test.sh71
-rw-r--r--bin/pkill/tests/pkill-s_test.sh41
-rw-r--r--bin/pkill/tests/pkill-t_test.sh45
-rw-r--r--bin/pkill/tests/pkill-x_test.sh44
-rw-r--r--bin/ps/print.c8
-rw-r--r--bin/ps/ps.17
-rw-r--r--bin/sh/Makefile7
-rw-r--r--bin/sh/alias.c51
-rw-r--r--bin/sh/bltin/bltin.h2
-rw-r--r--bin/sh/cd.c12
-rw-r--r--bin/sh/eval.c75
-rw-r--r--bin/sh/exec.c23
-rw-r--r--bin/sh/expand.c116
-rw-r--r--bin/sh/expand.h1
-rw-r--r--bin/sh/input.c37
-rw-r--r--bin/sh/jobs.c35
-rw-r--r--bin/sh/memalloc.c6
-rw-r--r--bin/sh/miscbltin.c5
-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.c34
-rw-r--r--bin/sh/sh.137
-rw-r--r--bin/sh/show.c4
-rw-r--r--bin/sh/tests/Makefile18
-rw-r--r--bin/sh/tests/builtins/Makefile149
-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/kill1.08
-rw-r--r--bin/sh/tests/builtins/kill2.07
-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.c52
-rw-r--r--bin/sh/var.h1
-rw-r--r--bin/test/Makefile6
-rw-r--r--bin/test/test.c158
-rw-r--r--bin/test/tests/Makefile15
-rw-r--r--bin/test/tests/legacy_test.sh196
-rw-r--r--bin/tests/Makefile10
501 files changed, 9021 insertions, 411 deletions
diff --git a/bin/Makefile b/bin/Makefile
index 7b6a4ab8c8c0..63d96fedbdfe 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -52,8 +52,14 @@ SUBDIR+= rmail
SUBDIR+= csh
.endif
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.arch.inc.mk>
SUBDIR:= ${SUBDIR:O}
+SUBDIR_PARALLEL=
+
.include <bsd.subdir.mk>
diff --git a/bin/cat/cat.c b/bin/cat/cat.c
index 0daac7caec70..f724cb0b8b9a 100644
--- a/bin/cat/cat.c
+++ b/bin/cat/cat.c
@@ -316,6 +316,7 @@ udom_open(const char *path, int flags)
sou.sun_family = AF_UNIX;
if ((len = strlcpy(sou.sun_path, path,
sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) {
+ close(fd);
errno = ENAMETOOLONG;
return (-1);
}
diff --git a/bin/date/Makefile b/bin/date/Makefile
index 4f5c3fea3a58..6da5848e16ab 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/date.1 b/bin/date/date.1
index d1cbb4457101..5e9e6648d6d0 100644
--- a/bin/date/date.1
+++ b/bin/date/date.1
@@ -32,7 +32,7 @@
.\" @(#)date.1 8.3 (Berkeley) 4/28/95
.\" $FreeBSD$
.\"
-.Dd June 3, 2010
+.Dd April 26, 2014
.Dt DATE 1
.Os
.Sh NAME
@@ -40,7 +40,7 @@
.Nd display or set date and time
.Sh SYNOPSIS
.Nm
-.Op Fl ju
+.Op Fl jRu
.Op Fl r Ar seconds
.Oo
.Fl v
@@ -58,7 +58,7 @@
.Ar MM Op Ar .ss
.Sm on
.Nm
-.Op Fl jnu
+.Op Fl jnRu
.Fl f Ar input_fmt new_date
.Op Cm + Ns Ar output_fmt
.Nm
@@ -130,6 +130,16 @@ The
.Fl n
option suppresses this behavior and causes the time to be set only on the
current machine.
+.It Fl R
+Use RFC 2822 date and time output format. This is equivalent to use
+.Dq Li %a, %d %b %Y \&%T %z
+as
+.Ar output_fmt
+while
+.Ev LC_TIME
+is set to the
+.Dq C
+locale .
.It Fl r Ar seconds
Print the date and time represented by
.Ar seconds ,
diff --git a/bin/date/date.c b/bin/date/date.c
index 58a9afb0d0fc..2c09848a151e 100644
--- a/bin/date/date.c
+++ b/bin/date/date.c
@@ -69,12 +69,14 @@ static void setthetime(const char *, const char *, int, int);
static void badformat(void);
static void usage(void);
+static const char *rfc2822_format = "%a, %d %b %Y %T %z";
+
int
main(int argc, char *argv[])
{
struct timezone tz;
int ch, rflag;
- int jflag, nflag;
+ int jflag, nflag, Rflag;
const char *format;
char buf[1024];
char *endptr, *fmt;
@@ -89,9 +91,9 @@ main(int argc, char *argv[])
(void) setlocale(LC_TIME, "");
tz.tz_dsttime = tz.tz_minuteswest = 0;
rflag = 0;
- jflag = nflag = 0;
+ jflag = nflag = Rflag = 0;
set_timezone = 0;
- while ((ch = getopt(argc, argv, "d:f:jnr:t:uv:")) != -1)
+ while ((ch = getopt(argc, argv, "d:f:jnRr:t:uv:")) != -1)
switch((char)ch) {
case 'd': /* daylight savings time */
tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0;
@@ -108,6 +110,9 @@ main(int argc, char *argv[])
case 'n': /* don't set network */
nflag = 1;
break;
+ case 'R': /* RFC 2822 datetime format */
+ Rflag = 1;
+ break;
case 'r': /* user specified seconds */
rflag = 1;
tval = strtoq(optarg, &tmp, 0);
@@ -145,6 +150,9 @@ main(int argc, char *argv[])
format = "%+";
+ if (Rflag)
+ format = rfc2822_format;
+
/* allow the operands in any order */
if (*argv && **argv == '+') {
format = *argv + 1;
@@ -169,6 +177,14 @@ main(int argc, char *argv[])
usage();
}
vary_destroy(v);
+
+ if (format == rfc2822_format)
+ /*
+ * When using RFC 2822 datetime format, don't honor the
+ * locale.
+ */
+ setlocale(LC_TIME, "C");
+
(void)strftime(buf, sizeof(buf), format, &lt);
(void)printf("%s\n", buf);
if (fflush(stdout))
@@ -301,7 +317,7 @@ static void
usage(void)
{
(void)fprintf(stderr, "%s\n%s\n",
- "usage: date [-jnu] [-d dst] [-r seconds] [-t west] "
+ "usage: date [-jnRu] [-d dst] [-r seconds] [-t west] "
"[-v[+|-]val[ymwdHMS]] ... ",
" "
"[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]");
diff --git a/bin/date/tests/Makefile b/bin/date/tests/Makefile
new file mode 100644
index 000000000000..2301dcf203d1
--- /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 <bsd.test.mk>
diff --git a/bin/date/tests/legacy_test.sh b/bin/date/tests/legacy_test.sh
new file mode 100644
index 000000000000..981bdd017168
--- /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/dd/args.c b/bin/dd/args.c
index cc702f9231a5..2f197f8da505 100644
--- a/bin/dd/args.c
+++ b/bin/dd/args.c
@@ -66,6 +66,7 @@ static void f_obs(char *);
static void f_of(char *);
static void f_seek(char *);
static void f_skip(char *);
+static void f_status(char *);
static uintmax_t get_num(const char *);
static off_t get_off_t(const char *);
@@ -88,6 +89,7 @@ static const struct arg {
{ "oseek", f_seek, C_SEEK, C_SEEK },
{ "seek", f_seek, C_SEEK, C_SEEK },
{ "skip", f_skip, C_SKIP, C_SKIP },
+ { "status", f_status, C_STATUS,C_STATUS },
};
static char *oper;
@@ -292,6 +294,18 @@ f_skip(char *arg)
in.offset = get_off_t(arg);
}
+static void
+f_status(char *arg)
+{
+
+ if (strcmp(arg, "none") == 0)
+ ddflags |= C_NOINFO;
+ else if (strcmp(arg, "noxfer") == 0)
+ ddflags |= C_NOXFER;
+ else
+ errx(1, "unknown status %s", arg);
+}
+
static const struct conv {
const char *name;
u_int set, noset;
diff --git a/bin/dd/dd.1 b/bin/dd/dd.1
index fa6d9a94d645..0541df8864d5 100644
--- a/bin/dd/dd.1
+++ b/bin/dd/dd.1
@@ -32,7 +32,7 @@
.\" @(#)dd.1 8.2 (Berkeley) 1/13/94
.\" $FreeBSD$
.\"
-.Dd October 1, 2013
+.Dd April 2, 2014
.Dt DD 1
.Os
.Sh NAME
@@ -156,6 +156,17 @@ Otherwise, input data is read and discarded.
For pipes, the correct number of bytes is read.
For all other devices, the correct number of blocks is read without
distinguishing between a partial or complete block being read.
+.It Cm status Ns = Ns Ar value
+Where
+.Cm value
+is one of the symbols from the following list.
+.Bl -tag -width ".Cm noxfer"
+.It Cm noxfer
+Do not print the transfer statistics as the last line of status output.
+.It Cm none
+Do not print the status output.
+Error messages are shown; informational messages are not.
+.El
.It Cm conv Ns = Ns Ar value Ns Op , Ns Ar value ...
Where
.Cm value
@@ -410,7 +421,9 @@ utility is expected to be a superset of the
standard.
The
.Cm files
-operand and the
+and
+.Cm status
+operands and the
.Cm ascii ,
.Cm ebcdic ,
.Cm ibm ,
diff --git a/bin/dd/dd.h b/bin/dd/dd.h
index ad283fd516a1..dace84526f52 100644
--- a/bin/dd/dd.h
+++ b/bin/dd/dd.h
@@ -68,32 +68,35 @@ typedef struct {
} STAT;
/* Flags (in ddflags). */
-#define C_ASCII 0x00001
-#define C_BLOCK 0x00002
-#define C_BS 0x00004
-#define C_CBS 0x00008
-#define C_COUNT 0x00010
-#define C_EBCDIC 0x00020
-#define C_FILES 0x00040
-#define C_IBS 0x00080
-#define C_IF 0x00100
-#define C_LCASE 0x00200
-#define C_NOERROR 0x00400
-#define C_NOTRUNC 0x00800
-#define C_OBS 0x01000
-#define C_OF 0x02000
-#define C_OSYNC 0x04000
-#define C_PAREVEN 0x08000
-#define C_PARNONE 0x100000
-#define C_PARODD 0x200000
-#define C_PARSET 0x400000
-#define C_SEEK 0x800000
-#define C_SKIP 0x1000000
-#define C_SPARSE 0x2000000
-#define C_SWAB 0x4000000
-#define C_SYNC 0x8000000
-#define C_UCASE 0x10000000
-#define C_UNBLOCK 0x20000000
-#define C_FILL 0x40000000
+#define C_ASCII 0x00000001
+#define C_BLOCK 0x00000002
+#define C_BS 0x00000004
+#define C_CBS 0x00000008
+#define C_COUNT 0x00000010
+#define C_EBCDIC 0x00000020
+#define C_FILES 0x00000040
+#define C_IBS 0x00000080
+#define C_IF 0x00000100
+#define C_LCASE 0x00000200
+#define C_NOERROR 0x00000400
+#define C_NOTRUNC 0x00000800
+#define C_OBS 0x00001000
+#define C_OF 0x00002000
+#define C_OSYNC 0x00004000
+#define C_PAREVEN 0x00008000
+#define C_PARNONE 0x00010000
+#define C_PARODD 0x00020000
+#define C_PARSET 0x00040000
+#define C_SEEK 0x00080000
+#define C_SKIP 0x00100000
+#define C_SPARSE 0x00200000
+#define C_SWAB 0x00400000
+#define C_SYNC 0x00800000
+#define C_UCASE 0x01000000
+#define C_UNBLOCK 0x02000000
+#define C_FILL 0x04000000
+#define C_STATUS 0x08000000
+#define C_NOXFER 0x10000000
+#define C_NOINFO 0x20000000
#define C_PARITY (C_PAREVEN | C_PARODD | C_PARNONE | C_PARSET)
diff --git a/bin/dd/misc.c b/bin/dd/misc.c
index 84cc5eed9a18..61f843bf13a4 100644
--- a/bin/dd/misc.c
+++ b/bin/dd/misc.c
@@ -59,6 +59,9 @@ summary(void)
struct timeval tv;
double secs;
+ if (ddflags & C_NOINFO)
+ return;
+
(void)gettimeofday(&tv, NULL);
secs = tv.tv_sec + tv.tv_usec * 1e-6 - st.start;
if (secs < 1e-6)
@@ -72,9 +75,11 @@ summary(void)
if (st.trunc)
(void)fprintf(stderr, "%ju truncated %s\n",
st.trunc, (st.trunc == 1) ? "block" : "blocks");
- (void)fprintf(stderr,
- "%ju bytes transferred in %.6f secs (%.0f bytes/sec)\n",
- st.bytes, secs, st.bytes / secs);
+ if (!(ddflags & C_NOXFER)) {
+ (void)fprintf(stderr,
+ "%ju bytes transferred in %.6f secs (%.0f bytes/sec)\n",
+ st.bytes, secs, st.bytes / secs);
+ }
need_summary = 0;
}
diff --git a/bin/df/df.1 b/bin/df/df.1
index b5cf859cb57a..85e1ab475203 100644
--- a/bin/df/df.1
+++ b/bin/df/df.1
@@ -29,7 +29,7 @@
.\" @(#)df.1 8.3 (Berkeley) 5/8/95
.\" $FreeBSD$
.\"
-.Dd January 24, 2013
+.Dd January 16, 2014
.Dt DF 1
.Os
.Sh NAME
@@ -83,13 +83,13 @@ Use 1073741824 byte (1 Gibibyte) blocks rather than the default.
This overrides any
.Ev BLOCKSIZE
specification from the environment.
-.It Fl H
+.It Fl h
.Dq Human-readable
output.
Use unit suffixes: Byte, Kibibyte, Mebibyte, Gibibyte, Tebibyte and
Pebibyte (based on powers of 1024) in order to reduce the number of
digits to four or fewer.
-.It Fl h
+.It Fl H
.Dq Human-readable
output.
Use unit suffixes: Byte, Kilobyte, Megabyte,
@@ -198,7 +198,7 @@ If the value is outside, it will be set to the appropriate limit.
.Xr mount 8 ,
.Xr pstat 8 ,
.Xr quot 8 ,
-.Xr swapinfo 8 .
+.Xr swapinfo 8
.Sh STANDARDS
With the exception of most options,
the
diff --git a/bin/freebsd-version/freebsd-version.sh.in b/bin/freebsd-version/freebsd-version.sh.in
index 0f9a46823fb1..a830d264916b 100644
--- a/bin/freebsd-version/freebsd-version.sh.in
+++ b/bin/freebsd-version/freebsd-version.sh.in
@@ -81,7 +81,7 @@ userland_version() {
# Print a usage string and exit.
#
usage() {
- echo "usage: $progname [-ku]\n" >&2
+ echo "usage: $progname [-ku]" >&2
exit 1
}
diff --git a/bin/kill/kill.c b/bin/kill/kill.c
index 2d41f7865e48..b23c00acf21c 100644
--- a/bin/kill/kill.c
+++ b/bin/kill/kill.c
@@ -67,7 +67,7 @@ static void usage(void);
int
main(int argc, char *argv[])
{
- int errors, numsig, pid;
+ int errors, numsig, pid, ret;
char *ep;
if (argc < 2)
@@ -134,15 +134,16 @@ main(int argc, char *argv[])
for (errors = 0; argc; argc--, argv++) {
#ifdef SHELL
if (**argv == '%')
- pid = getjobpgrp(*argv);
+ ret = killjob(*argv, numsig);
else
#endif
{
pid = strtol(*argv, &ep, 10);
if (!**argv || *ep)
errx(2, "illegal process id: %s", *argv);
+ ret = kill(pid, numsig);
}
- if (kill(pid, numsig) == -1) {
+ if (ret == -1) {
warn("%s", *argv);
errors = 1;
}
diff --git a/bin/ls/ls.1 b/bin/ls/ls.1
index 8b7672b996ce..ee8e4ff16687 100644
--- a/bin/ls/ls.1
+++ b/bin/ls/ls.1
@@ -32,7 +32,7 @@
.\" @(#)ls.1 8.7 (Berkeley) 7/29/94
.\" $FreeBSD$
.\"
-.Dd March 15, 2013
+.Dd April 4, 2014
.Dt LS 1
.Os
.Sh NAME
@@ -87,6 +87,8 @@ and current locale settings) in file names as
where
.Va xxx
is the numeric value of the character in octal.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl C
Force multi-column output; this is the default when output is to a terminal.
.It Fl D Ar format
@@ -104,6 +106,8 @@ different number of columns in the output.
This option overrides the
.Fl T
option.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl F
Display a slash
.Pq Ql /
@@ -132,6 +136,8 @@ in the environment.
(See below.)
This functionality can be compiled out by removing the definition of
.Ev COLORLS .
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl H
Symbolic links on the command line are followed.
This option is assumed if
@@ -144,6 +150,8 @@ options are specified.
Prevent
.Fl A
from being automatically set for the super-user.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl L
If argument is a symbolic link, list the file or directory the link references
rather than the link itself.
@@ -171,13 +179,21 @@ month, day, hour, minute, second, and year.
The
.Fl D
option gives even more control over the output format.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl U
Use time when file was created for sorting or printing.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl W
Display whiteouts when scanning directories.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl Z
Display each file's MAC label; see
.Xr maclabel 7 .
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl a
Include directory entries whose names begin with a
dot
@@ -188,25 +204,48 @@ As
but use
.Tn C
escape codes whenever possible.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl c
Use time when file status was last changed for sorting or printing.
.It Fl d
Directories are listed as plain files (not searched recursively).
.It Fl f
Output is not sorted.
+This option turns on
+.Fl a .
+It also negates the effect of the
+.Fl r ,
+.Fl S
+and
+.Fl t
+options.
+As allowed by
+.St -p1003.1-2001 ,
+this option has no effect on the
+.Fl d ,
+.Fl l ,
+.Fl R
+and
+.Fl s
+options.
.It Fl g
-This option is deprecated and is only available for compatibility
-with
-.Bx 4.3 ;
-it was used to display the group name in the long
+This option has no effect.
+It is only available for compatibility with
+.Bx 4.3 ,
+where it was used to display the group name in the long
.Pq Fl l
format output.
+This option is incompatible with
+.St -p1003.1-2001 .
.It Fl h
When used with the
.Fl l
option, use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte
and Petabyte in order to reduce the number of digits to four or fewer
using base 2 for sizes.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl i
For each file, print the file's file serial number (inode number).
.It Fl k
@@ -232,6 +271,8 @@ output.
Include the file flags in a long
.Pq Fl l
output.
+This option is incompatible with
+.St -p1003.1-2001 .
See
.Xr chflags 1
for a list of file flags and their meanings.
@@ -291,6 +332,8 @@ or printing
Force raw printing of non-printable characters.
This is the default
when output is not to a terminal.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl x
The same as
.Fl C ,
@@ -305,6 +348,8 @@ This has the same effect as setting
See the description of the
.Fl t
option for more details.
+This option is not defined in
+.St -p1003.1-2001 .
.It Fl 1
(The numeric digit
.Dq one . )
@@ -321,6 +366,8 @@ non-monetary separator returned by
typically a comma or period.
If no locale is set, or the locale does not have a non-monetary separator, this
option has no effect.
+This option is not defined in
+.St -p1003.1-2001 .
.El
.Pp
The
@@ -760,13 +807,19 @@ specification.
.Xr getfmac 8
.Sh STANDARDS
With the exception of options
-.Fl I , g , n
+.Fl g , n
and
.Fl o ,
the
.Nm
utility conforms to
.St -p1003.1-2001 .
+The options
+.Fl B , D , G , I , T , U , W , Z , b , h , w , y
+and
+.Fl ,
+are compatible extensions not defined in
+.St -p1003.1-2001 .
.Pp
The ACL support is compatible with
.Tn IEEE
diff --git a/bin/ls/ls.c b/bin/ls/ls.c
index be0903561f0c..a46f358bd0c6 100644
--- a/bin/ls/ls.c
+++ b/bin/ls/ls.c
@@ -226,6 +226,9 @@ main(int argc, char *argv[])
f_accesstime = 0;
f_statustime = 0;
break;
+ case 'f':
+ f_nosort = 1;
+ /* FALLTHROUGH */
case 'a':
fts_options |= FTS_SEEDOT;
/* FALLTHROUGH */
@@ -300,9 +303,6 @@ main(int argc, char *argv[])
f_listdir = 1;
f_recursive = 0;
break;
- case 'f':
- f_nosort = 1;
- break;
case 'g': /* Compatibility with 4.3BSD. */
break;
case 'h':
diff --git a/bin/mv/Makefile b/bin/mv/Makefile
index 840578276d5a..ad8cc4f02d44 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 000000000000..3d437ef916a6
--- /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 <bsd.test.mk>
diff --git a/bin/mv/tests/legacy_test.sh b/bin/mv/tests/legacy_test.sh
new file mode 100644
index 000000000000..d0a5e83440c6
--- /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 9e8d4e111862..95f18bba1c06 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/options.c b/bin/pax/options.c
index 32d967e1d857..c6ebea28cefc 100644
--- a/bin/pax/options.c
+++ b/bin/pax/options.c
@@ -1385,6 +1385,7 @@ opt_add(const char *str)
free(lstr);
return(-1);
}
+ lstr = NULL; /* parts of string going onto the OPLIST */
*pt++ = '\0';
opt->name = frpt;
opt->value = pt;
@@ -1400,6 +1401,7 @@ opt_add(const char *str)
optail->fow = opt;
optail = opt;
}
+ free(lstr);
return(0);
}
diff --git a/bin/pax/pax.1 b/bin/pax/pax.1
index 33fedf6d2339..aa3025a13100 100644
--- a/bin/pax/pax.1
+++ b/bin/pax/pax.1
@@ -33,7 +33,7 @@
.\" @(#)pax.1 8.4 (Berkeley) 4/18/94
.\" $FreeBSD$
.\"
-.Dd August 29, 2010
+.Dd December 21, 2013
.Dt PAX 1
.Os
.Sh NAME
@@ -587,6 +587,16 @@ precedence.
For example, if
.Dl Fl p Ar eme
is specified, file modification times are still preserved.
+.Pp
+File flags set by
+.Xr chflags 1
+are not understood by
+.Nm ,
+however
+.Xr tar 1
+and
+.Xr dump 1
+will preserve these.
.It Fl s Ar replstr
Modify the file or archive member names specified by the
.Ar pattern
@@ -1195,3 +1205,11 @@ at the University of California, San Diego
The
.Nm
utility does not recognize multibyte characters.
+.Pp
+File flags set by
+.Xr chflags 1
+are not preserved by
+.Nm .
+The BUGS section of
+.Xr chflags 1
+has a list of utilities that are unaware of flags.
diff --git a/bin/pax/tests/Makefile b/bin/pax/tests/Makefile
new file mode 100644
index 000000000000..1f27c184130a
--- /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 <bsd.test.mk>
diff --git a/bin/pax/tests/legacy_test.pl b/bin/pax/tests/legacy_test.pl
new file mode 100644
index 000000000000..dabba42b3f31
--- /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/pkill/Makefile b/bin/pkill/Makefile
index e98ed2c4e522..bb4d865b7ae2 100644
--- a/bin/pkill/Makefile
+++ b/bin/pkill/Makefile
@@ -1,6 +1,8 @@
# $NetBSD: Makefile,v 1.1 2002/03/01 11:21:58 ad Exp $
# $FreeBSD$
+.include <bsd.own.mk>
+
PROG= pkill
DPADD= ${LIBKVM}
@@ -17,4 +19,8 @@ MLINKS= pkill.1 pgrep.1
SYMLINKS= ${BINDIR}/pkill /usr/bin/pkill
SYMLINKS+= ${BINDIR}/pgrep /usr/bin/pgrep
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
diff --git a/bin/pkill/pkill.c b/bin/pkill/pkill.c
index add989f06887..e73f5f258e3d 100644
--- a/bin/pkill/pkill.c
+++ b/bin/pkill/pkill.c
@@ -318,7 +318,10 @@ main(int argc, char **argv)
* Use KERN_PROC_PROC instead of KERN_PROC_ALL, since we
* just want processes and not individual kernel threads.
*/
- plist = kvm_getprocs(kd, KERN_PROC_PROC, 0, &nproc);
+ if (pidfromfile >= 0)
+ plist = kvm_getprocs(kd, KERN_PROC_PID, pidfromfile, &nproc);
+ else
+ plist = kvm_getprocs(kd, KERN_PROC_PROC, 0, &nproc);
if (plist == NULL) {
errx(STATUS_ERROR, "Cannot get process list (%s)",
kvm_geterr(kd));
diff --git a/bin/pkill/tests/Makefile b/bin/pkill/tests/Makefile
new file mode 100644
index 000000000000..b131d5925667
--- /dev/null
+++ b/bin/pkill/tests/Makefile
@@ -0,0 +1,38 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/bin/pkill
+
+TAP_TESTS_SH= pgrep-F_test
+TAP_TESTS_SH+= pgrep-LF_test
+TAP_TESTS_SH+= pgrep-P_test
+TAP_TESTS_SH+= pgrep-U_test
+TAP_TESTS_SH+= pgrep-_g_test
+TAP_TESTS_SH+= pgrep-_s_test
+TAP_TESTS_SH+= pgrep-g_test
+TAP_TESTS_SH+= pgrep-i_test
+TAP_TESTS_SH+= pgrep-j_test
+TEST_METADATA.pgrep-j_test+= required_user="root"
+TAP_TESTS_SH+= pgrep-l_test
+TAP_TESTS_SH+= pgrep-n_test
+TAP_TESTS_SH+= pgrep-o_test
+TAP_TESTS_SH+= pgrep-q_test
+TAP_TESTS_SH+= pgrep-s_test
+TAP_TESTS_SH+= pgrep-t_test
+TAP_TESTS_SH+= pgrep-v_test
+TAP_TESTS_SH+= pgrep-x_test
+TAP_TESTS_SH+= pkill-F_test
+TAP_TESTS_SH+= pkill-LF_test
+TAP_TESTS_SH+= pkill-P_test
+TAP_TESTS_SH+= pkill-U_test
+TAP_TESTS_SH+= pkill-_g_test
+TAP_TESTS_SH+= pkill-g_test
+TAP_TESTS_SH+= pkill-i_test
+TAP_TESTS_SH+= pkill-j_test
+TEST_METADATA.pkill-j_test+= required_user="root"
+TAP_TESTS_SH+= pkill-s_test
+TAP_TESTS_SH+= pkill-t_test
+TAP_TESTS_SH+= pkill-x_test
+
+.include <bsd.test.mk>
diff --git a/bin/pkill/tests/pgrep-F_test.sh b/bin/pkill/tests/pgrep-F_test.sh
new file mode 100644
index 000000000000..4d8feaa34eb2
--- /dev/null
+++ b/bin/pkill/tests/pgrep-F_test.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pgrep -F <pidfile>"
+pidfile=$(pwd)/pidfile.txt
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+echo $chpid > $pidfile
+pid=`pgrep -f -F $pidfile $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok - $name"
+else
+ echo "not ok - $name"
+fi
+kill "$chpid"
+rm -f $pidfile
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-LF_test.sh b/bin/pkill/tests/pgrep-LF_test.sh
new file mode 100644
index 000000000000..4818869d8d6e
--- /dev/null
+++ b/bin/pkill/tests/pgrep-LF_test.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -LF <pidfile>"
+pidfile=$(pwd)/pidfile.txt
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+daemon -p $pidfile $sleep 5
+sleep 0.3
+chpid=`cat $pidfile`
+pid=`pgrep -f -L -F $pidfile $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+kill "$chpid"
+
+# Be sure we cannot find process which pidfile is not locked.
+$sleep 5 &
+sleep 0.3
+chpid=$!
+echo $chpid > $pidfile
+pgrep -f -L -F $pidfile $sleep 2>/dev/null
+ec=$?
+case $ec in
+0)
+ echo "not ok 2 - $name"
+ ;;
+*)
+ echo "ok 2 - $name"
+ ;;
+esac
+
+kill "$chpid"
+rm -f $pidfile
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-P_test.sh b/bin/pkill/tests/pgrep-P_test.sh
new file mode 100644
index 000000000000..5a5cdcf8e97c
--- /dev/null
+++ b/bin/pkill/tests/pgrep-P_test.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pgrep -P <ppid>"
+ppid=$$
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -P $ppid $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok - $name"
+else
+ echo "not ok - $name"
+fi
+kill $chpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-U_test.sh b/bin/pkill/tests/pgrep-U_test.sh
new file mode 100644
index 000000000000..2e7f24d94659
--- /dev/null
+++ b/bin/pkill/tests/pgrep-U_test.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -U <uid>"
+ruid=`id -ur`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -U $ruid $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+kill $chpid
+rm -f $sleep
+
+name="pgrep -U <user>"
+ruid=`id -urn`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -U $ruid $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+kill $chpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-_g_test.sh b/bin/pkill/tests/pgrep-_g_test.sh
new file mode 100644
index 000000000000..fbe7b08161a0
--- /dev/null
+++ b/bin/pkill/tests/pgrep-_g_test.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -G <gid>"
+rgid=`id -gr`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -G $rgid $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+kill $chpid
+rm -f $sleep
+
+name="pgrep -G <group>"
+rgid=`id -grn`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -G $rgid $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+kill $chpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-_s_test.sh b/bin/pkill/tests/pgrep-_s_test.sh
new file mode 100644
index 000000000000..ce0bde3d8d12
--- /dev/null
+++ b/bin/pkill/tests/pgrep-_s_test.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+exp_pid="$(ps ax | grep '\[idle\]' | awk '{print $1}')"
+
+name="pgrep -S"
+pid=`pgrep -Sx idle`
+if [ "$pid" = "$exp_pid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+pid=`pgrep -x idle`
+if [ "$pid" != "$exp_pid" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
diff --git a/bin/pkill/tests/pgrep-g_test.sh b/bin/pkill/tests/pgrep-g_test.sh
new file mode 100644
index 000000000000..14149082338f
--- /dev/null
+++ b/bin/pkill/tests/pgrep-g_test.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -g <pgrp>"
+pgrp=`ps -o tpgid -p $$ | tail -1`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -g $pgrp $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+kill $chpid
+rm -f $sleep
+
+name="pgrep -g 0"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -g 0 $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+kill $chpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-i_test.sh b/bin/pkill/tests/pgrep-i_test.sh
new file mode 100644
index 000000000000..b9ecfbc1511d
--- /dev/null
+++ b/bin/pkill/tests/pgrep-i_test.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pgrep -i"
+sleep=$(pwd)/sleep.txt
+usleep="${sleep}XXX"
+touch $usleep
+lsleep="${sleep}xxx"
+ln -sf /bin/sleep $usleep
+$usleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -i $lsleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok - $name"
+else
+ echo "not ok - $name"
+fi
+kill $chpid
+rm -f $sleep $usleep
diff --git a/bin/pkill/tests/pgrep-j_test.sh b/bin/pkill/tests/pgrep-j_test.sh
new file mode 100644
index 000000000000..6656a9bd789f
--- /dev/null
+++ b/bin/pkill/tests/pgrep-j_test.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..3"
+
+name="pgrep -j <jid>"
+if [ `id -u` -eq 0 ]; then
+ sleep=$(pwd)/sleep.txt
+ ln -sf /bin/sleep $sleep
+ jail / $base-1 127.0.0.1 $sleep 5 &
+ chpid=$!
+ jail / $base-2 127.0.0.1 $sleep 5 &
+ chpid2=$!
+ $sleep 5 &
+ chpid3=$!
+ sleep 0.5
+ jid=`jls | awk "/127\\.0\\.0\\.1.*${base}-1/ {print \$1}"`
+ pid=`pgrep -f -j $jid $sleep`
+ if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+ else
+ echo "not ok 1 - $name"
+ fi
+ kill $chpid $chpid2 $chpid3
+ rm -f $sleep
+else
+ echo "ok 1 - $name # skip Test needs uid 0."
+fi
+
+name="pgrep -j any"
+if [ `id -u` -eq 0 ]; then
+ sleep=$(pwd)/sleep.txt
+ ln -sf /bin/sleep $sleep
+ jail / $base-1 127.0.0.1 $sleep 5 &
+ chpid=$!
+ jail / $base-2 127.0.0.1 $sleep 5 &
+ chpid2=$!
+ $sleep 5 &
+ chpid3=$!
+ sleep 0.5
+ pids=`pgrep -f -j any $sleep | sort`
+ refpids=`{ echo $chpid; echo $chpid2; } | sort`
+ if [ "$pids" = "$refpids" ]; then
+ echo "ok 2 - $name"
+ else
+ echo "not ok 2 - $name"
+ fi
+ kill $chpid $chpid2 $chpid3
+ rm -f $sleep
+else
+ echo "ok 2 - $name # skip Test needs uid 0."
+fi
+
+name="pgrep -j none"
+if [ `id -u` -eq 0 ]; then
+ sleep=$(pwd)/sleep.txt
+ ln -sf /bin/sleep $sleep
+ $sleep 5 &
+ chpid=$!
+ jail / $base 127.0.0.1 $sleep 5 &
+ chpid2=$!
+ sleep 0.5
+ pid=`pgrep -f -j none $sleep`
+ if [ "$pid" = "$chpid" ]; then
+ echo "ok 3 - $name"
+ else
+ echo "not ok 3 - $name"
+ fi
+ kill $chpid $chpid2
+ rm -f $sleep
+else
+ echo "ok 3 - $name # skip Test needs uid 0."
+fi
diff --git a/bin/pkill/tests/pgrep-l_test.sh b/bin/pkill/tests/pgrep-l_test.sh
new file mode 100644
index 000000000000..49273b315b6c
--- /dev/null
+++ b/bin/pkill/tests/pgrep-l_test.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pgrep -l"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pid=$!
+if [ "$pid $sleep 5" = "`pgrep -f -l $sleep`" ]; then
+ echo "ok - $name"
+else
+ echo "not ok - $name"
+fi
+kill $pid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-n_test.sh b/bin/pkill/tests/pgrep-n_test.sh
new file mode 100644
index 000000000000..1b6fe8302867
--- /dev/null
+++ b/bin/pkill/tests/pgrep-n_test.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pgrep -n"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+oldpid=$!
+$sleep 5 &
+sleep 0.3
+newpid=$!
+pid=`pgrep -f -n $sleep`
+if [ "$pid" = "$newpid" ]; then
+ echo "ok - $name"
+else
+ echo "not ok - $name"
+fi
+kill $oldpid
+kill $newpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-o_test.sh b/bin/pkill/tests/pgrep-o_test.sh
new file mode 100644
index 000000000000..250f230ec84f
--- /dev/null
+++ b/bin/pkill/tests/pgrep-o_test.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pgrep -o"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+oldpid=$!
+$sleep 5 &
+sleep 0.3
+newpid=$!
+pid=`pgrep -f -o $sleep`
+if [ "$pid" = "$oldpid" ]; then
+ echo "ok - $name"
+else
+ echo "not ok - $name"
+fi
+kill $oldpid
+kill $newpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-q_test.sh b/bin/pkill/tests/pgrep-q_test.sh
new file mode 100644
index 000000000000..2626a5e6bbac
--- /dev/null
+++ b/bin/pkill/tests/pgrep-q_test.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..4"
+
+name="pgrep -q"
+sleep0=$(pwd)/sleep0.txt
+sleep1=$(pwd)/sleep1.txt
+ln -sf /bin/sleep $sleep0
+$sleep0 5 &
+sleep 0.3
+pid=$!
+out="`pgrep -q -f $sleep0 2>&1`"
+if [ $? -eq 0 ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+if [ -z "${out}" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+out="`pgrep -q -f $sleep1 2>&1`"
+if [ $? -ne 0 ]; then
+ echo "ok 3 - $name"
+else
+ echo "not ok 3 - $name"
+fi
+if [ -z "${out}" ]; then
+ echo "ok 4 - $name"
+else
+ echo "not ok 4 - $name"
+fi
+kill $pid
+rm -f $sleep0 $sleep1
diff --git a/bin/pkill/tests/pgrep-s_test.sh b/bin/pkill/tests/pgrep-s_test.sh
new file mode 100644
index 000000000000..82c1ef98ddb1
--- /dev/null
+++ b/bin/pkill/tests/pgrep-s_test.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -s <sid>"
+sid=`ps -o tsid -p $$ | tail -1`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -s $sid $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+kill $chpid
+rm -f $sleep
+
+name="pgrep -s 0"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -s 0 $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+kill $chpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-t_test.sh b/bin/pkill/tests/pgrep-t_test.sh
new file mode 100644
index 000000000000..a8527d5be611
--- /dev/null
+++ b/bin/pkill/tests/pgrep-t_test.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -t <tty>"
+tty=`ps -x -o tty -p $$ | tail -1`
+if [ "$tty" = "??" -o "$tty" = "-" ]; then
+ tty="-"
+ ttyshort="-"
+else
+ case $tty in
+ pts/*) ttyshort=`echo $tty | cut -c 5-` ;;
+ *) ttyshort=`echo $tty | cut -c 4-` ;;
+ esac
+fi
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+chpid=$!
+pid=`pgrep -f -t $tty $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+pid=`pgrep -f -t $ttyshort $sleep`
+if [ "$pid" = "$chpid" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+kill $chpid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-v_test.sh b/bin/pkill/tests/pgrep-v_test.sh
new file mode 100644
index 000000000000..b9835bc29f0e
--- /dev/null
+++ b/bin/pkill/tests/pgrep-v_test.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pgrep -v"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pid=$!
+if [ -z "`pgrep -f -v $sleep | egrep '^'"$pid"'$'`" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+if [ ! -z "`pgrep -f -v -x x | egrep '^'"$pid"'$'`" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+kill $pid
+rm -f $sleep
diff --git a/bin/pkill/tests/pgrep-x_test.sh b/bin/pkill/tests/pgrep-x_test.sh
new file mode 100644
index 000000000000..1defde23d962
--- /dev/null
+++ b/bin/pkill/tests/pgrep-x_test.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..4"
+
+name="pgrep -x"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pid=$!
+if [ ! -z "`pgrep -x sleep | egrep '^'"$pid"'$'`" ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+if [ -z "`pgrep -x slee | egrep '^'"$pid"'$'`" ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+name="pgrep -x -f"
+if [ ! -z "`pgrep -x -f ''"$sleep"' 5' | egrep '^'"$pid"'$'`" ]; then
+ echo "ok 3 - $name"
+else
+ echo "not ok 3 - $name"
+fi
+if [ -z "`pgrep -x -f ''"$sleep"' ' | egrep '^'"$pid"'$'`" ]; then
+ echo "ok 4 - $name"
+else
+ echo "not ok 4 - $name"
+fi
+kill $pid
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-F_test.sh b/bin/pkill/tests/pkill-F_test.sh
new file mode 100644
index 000000000000..48ad47456839
--- /dev/null
+++ b/bin/pkill/tests/pkill-F_test.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pkill -F <pidfile>"
+pidfile=$(pwd)/pidfile.txt
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+echo $! > $pidfile
+pkill -f -F $pidfile $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok - $name"
+ ;;
+*)
+ echo "not ok - $name"
+ ;;
+esac
+
+rm -f $pidfile
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-LF_test.sh b/bin/pkill/tests/pkill-LF_test.sh
new file mode 100644
index 000000000000..fcafd0a1ee2a
--- /dev/null
+++ b/bin/pkill/tests/pkill-LF_test.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pkill -LF <pidfile>"
+pidfile=$(pwd)/pidfile.txt
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+daemon -p $pidfile $sleep 5
+sleep 0.3
+pkill -f -L -F $pidfile $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 1 - $name"
+ ;;
+*)
+ echo "not ok 1 - $name"
+ ;;
+esac
+
+# Be sure we cannot kill process which pidfile is not locked.
+$sleep 5 &
+sleep 0.3
+chpid=$!
+echo $chpid > $pidfile
+pkill -f -L -F $pidfile $sleep 2>/dev/null
+ec=$?
+case $ec in
+0)
+ echo "not ok 2 - $name"
+ ;;
+*)
+ echo "ok 2 - $name"
+ ;;
+esac
+
+kill "$chpid"
+rm -f $pidfile
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-P_test.sh b/bin/pkill/tests/pkill-P_test.sh
new file mode 100644
index 000000000000..2030710af659
--- /dev/null
+++ b/bin/pkill/tests/pkill-P_test.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pkill -P <ppid>"
+ppid=$$
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -P $ppid $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok - $name"
+ ;;
+*)
+ echo "not ok - $name"
+ ;;
+esac
+
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-U_test.sh b/bin/pkill/tests/pkill-U_test.sh
new file mode 100644
index 000000000000..04395e559387
--- /dev/null
+++ b/bin/pkill/tests/pkill-U_test.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pkill -U <uid>"
+ruid=`id -ur`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -U $ruid $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 1 - $name"
+ ;;
+*)
+ echo "not ok 1 - $name"
+ ;;
+esac
+rm -f $sleep
+
+name="pkill -U <user>"
+ruid=`id -urn`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -U $ruid $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 2 - $name"
+ ;;
+*)
+ echo "not ok 2 - $name"
+ ;;
+esac
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-_g_test.sh b/bin/pkill/tests/pkill-_g_test.sh
new file mode 100644
index 000000000000..1739ac88efa3
--- /dev/null
+++ b/bin/pkill/tests/pkill-_g_test.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pkill -G <gid>"
+rgid=`id -gr`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -G $rgid $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 1 - $name"
+ ;;
+*)
+ echo "not ok 1 - $name"
+ ;;
+esac
+rm -f $sleep
+
+name="pkill -G <group>"
+rgid=`id -grn`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -G $rgid $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 2 - $name"
+ ;;
+*)
+ echo "not ok 2 - $name"
+ ;;
+esac
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-g_test.sh b/bin/pkill/tests/pkill-g_test.sh
new file mode 100644
index 000000000000..29d6ab77b6ab
--- /dev/null
+++ b/bin/pkill/tests/pkill-g_test.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pkill -g <pgrp>"
+pgrp=`ps -o tpgid -p $$ | tail -1`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -g $pgrp $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 1 - $name"
+ ;;
+*)
+ echo "not ok 1 - $name"
+ ;;
+esac
+rm -f $sleep
+
+name="pkill -g 0"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -g 0 $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 2 - $name"
+ ;;
+*)
+ echo "not ok 2 - $name"
+ ;;
+esac
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-i_test.sh b/bin/pkill/tests/pkill-i_test.sh
new file mode 100644
index 000000000000..b9b8df8be459
--- /dev/null
+++ b/bin/pkill/tests/pkill-i_test.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..1"
+
+name="pkill -i"
+sleep=$(pwd)/sleep.txt
+usleep="${sleep}XXX"
+touch $usleep
+lsleep="${sleep}xxx"
+ln -sf /bin/sleep $usleep
+$usleep 5 &
+sleep 0.3
+pkill -f -i $lsleep
+ec=$?
+case $ec in
+0)
+ echo "ok - $name"
+ ;;
+*)
+ echo "not ok - $name"
+ ;;
+esac
+rm -f $sleep $usleep
diff --git a/bin/pkill/tests/pkill-j_test.sh b/bin/pkill/tests/pkill-j_test.sh
new file mode 100644
index 000000000000..48e7c721bbec
--- /dev/null
+++ b/bin/pkill/tests/pkill-j_test.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..3"
+
+name="pkill -j <jid>"
+if [ `id -u` -eq 0 ]; then
+ sleep=$(pwd)/sleep.txt
+ ln -sf /bin/sleep $sleep
+ jail / $base-1 127.0.0.1 $sleep 5 &
+ chpid=$!
+ jail / $base-2 127.0.0.1 $sleep 5 &
+ chpid2=$!
+ $sleep 5 &
+ chpid3=$!
+ sleep 0.5
+ jid=`jls | awk "/127\\.0\\.0\\.1.*${base}-1/ {print \$1}"`
+ if pkill -f -j $jid $sleep && sleep 0.5 &&
+ ! kill $chpid && kill $chpid2 $chpid3; then
+ echo "ok 1 - $name"
+ else
+ echo "not ok 1 - $name"
+ fi 2>/dev/null
+ rm -f $sleep
+else
+ echo "ok 1 - $name # skip Test needs uid 0."
+fi
+
+name="pkill -j any"
+if [ `id -u` -eq 0 ]; then
+ sleep=$(pwd)/sleep.txt
+ ln -sf /bin/sleep $sleep
+ jail / $base-1 127.0.0.1 $sleep 5 &
+ chpid=$!
+ jail / $base-2 127.0.0.1 $sleep 5 &
+ chpid2=$!
+ $sleep 5 &
+ chpid3=$!
+ sleep 0.5
+ if pkill -f -j any $sleep && sleep 0.5 &&
+ ! kill $chpid && ! kill $chpid2 && kill $chpid3; then
+ echo "ok 2 - $name"
+ else
+ echo "not ok 2 - $name"
+ fi 2>/dev/null
+ rm -f $sleep
+else
+ echo "ok 2 - $name # skip Test needs uid 0."
+fi
+
+name="pkill -j none"
+if [ `id -u` -eq 0 ]; then
+ sleep=$(pwd)/sleep.txt
+ ln -sf /bin/sleep $sleep
+ $sleep 5 &
+ chpid=$!
+ jail / $base 127.0.0.1 $sleep 5 &
+ chpid2=$!
+ sleep 0.5
+ if pkill -f -j none $sleep && sleep 0.5 &&
+ ! kill $chpid && kill $chpid2; then
+ echo "ok 3 - $name"
+ else
+ echo "not ok 3 - $name"
+ fi 2>/dev/null
+ rm -f $sleep
+else
+ echo "ok 3 - $name # skip Test needs uid 0."
+fi
diff --git a/bin/pkill/tests/pkill-s_test.sh b/bin/pkill/tests/pkill-s_test.sh
new file mode 100644
index 000000000000..0a9587ba6f16
--- /dev/null
+++ b/bin/pkill/tests/pkill-s_test.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pkill -s <sid>"
+sid=`ps -o tsid -p $$ | tail -1`
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -s $sid $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 1 - $name"
+ ;;
+*)
+ echo "not ok 1 - $name"
+ ;;
+esac
+rm -f $sleep
+
+name="pkill -s 0"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -s 0 $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 2 - $name"
+ ;;
+*)
+ echo "not ok 2 - $name"
+ ;;
+esac
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-t_test.sh b/bin/pkill/tests/pkill-t_test.sh
new file mode 100644
index 000000000000..07f511bd1ad7
--- /dev/null
+++ b/bin/pkill/tests/pkill-t_test.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..2"
+
+name="pkill -t <tty>"
+tty=`ps -x -o tty -p $$ | tail -1`
+if [ "$tty" = "??" -o "$tty" = "-" ]; then
+ tty="-"
+ ttyshort="-"
+else
+ case $tty in
+ pts/*) ttyshort=`echo $tty | cut -c 5-` ;;
+ *) ttyshort=`echo $tty | cut -c 4-` ;;
+ esac
+fi
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -f -t $tty $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 1 - $name"
+ ;;
+*)
+ echo "not ok 1 - $name"
+ ;;
+esac
+$sleep 5 &
+sleep 0.3
+pkill -f -t $ttyshort $sleep
+ec=$?
+case $ec in
+0)
+ echo "ok 2 - $name"
+ ;;
+*)
+ echo "not ok 2 - $name"
+ ;;
+esac
+rm -f $sleep
diff --git a/bin/pkill/tests/pkill-x_test.sh b/bin/pkill/tests/pkill-x_test.sh
new file mode 100644
index 000000000000..5ff0d680a83a
--- /dev/null
+++ b/bin/pkill/tests/pkill-x_test.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+# $FreeBSD$
+
+base=`basename $0`
+
+echo "1..4"
+
+name="pkill -x"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -x slee -P $$
+if [ $? -ne 0 ]; then
+ echo "ok 1 - $name"
+else
+ echo "not ok 1 - $name"
+fi
+pkill -x sleep -P $$
+if [ $? -eq 0 ]; then
+ echo "ok 2 - $name"
+else
+ echo "not ok 2 - $name"
+fi
+rm -f $sleep
+
+name="pkill -x -f"
+sleep=$(pwd)/sleep.txt
+ln -sf /bin/sleep $sleep
+$sleep 5 &
+sleep 0.3
+pkill -x -f "$sleep " -P $$
+if [ $? -ne 0 ]; then
+ echo "ok 3 - $name"
+else
+ echo "not ok 3 - $name"
+fi
+pkill -x -f "$sleep 5" -P $$
+if [ $? -eq 0 ]; then
+ echo "ok 4 - $name"
+else
+ echo "not ok 4 - $name"
+fi
+rm -f $sleep
diff --git a/bin/ps/print.c b/bin/ps/print.c
index f36411e786a9..bcd451e1fb59 100644
--- a/bin/ps/print.c
+++ b/bin/ps/print.c
@@ -797,8 +797,6 @@ char *
emulname(KINFO *k, VARENT *ve __unused)
{
- if (k->ki_p->ki_emul == NULL)
- return (NULL);
return (strdup(k->ki_p->ki_emul));
}
@@ -827,7 +825,6 @@ out:
char *
loginclass(KINFO *k, VARENT *ve __unused)
{
- char *s;
/*
* Don't display login class for system processes;
@@ -837,8 +834,5 @@ loginclass(KINFO *k, VARENT *ve __unused)
if (k->ki_p->ki_flag & P_SYSTEM) {
return (strdup("-"));
}
- s = k->ki_p->ki_loginclass;
- if (s == NULL)
- return (NULL);
- return (strdup(s));
+ return (strdup(k->ki_p->ki_loginclass));
}
diff --git a/bin/ps/ps.1 b/bin/ps/ps.1
index 81a1f6d44ed2..118e9618566b 100644
--- a/bin/ps/ps.1
+++ b/bin/ps/ps.1
@@ -29,7 +29,7 @@
.\" @(#)ps.1 8.3 (Berkeley) 4/18/94
.\" $FreeBSD$
.\"
-.Dd March 15, 2013
+.Dd December 27, 2013
.Dt PS 1
.Os
.Sh NAME
@@ -416,8 +416,9 @@ The process has reduced CPU scheduling priority (see
.It Li s
The process is a session leader.
.It Li V
-The process is suspended during a
-.Xr vfork 2 .
+The process' parent is suspended during a
+.Xr vfork 2 ,
+waiting for the process to exec or exit.
.It Li W
The process is swapped out.
.It Li X
diff --git a/bin/sh/Makefile b/bin/sh/Makefile
index 00d34fa749da..e1ce5feb1851 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 044c869ed940..21f3f44a34e5 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');
}
@@ -224,6 +180,7 @@ printaliases(void)
int i, j;
struct alias **sorted, *ap;
+ INTOFF;
sorted = ckmalloc(aliases * sizeof(*sorted));
j = 0;
for (i = 0; i < ATABSIZE; i++)
@@ -231,9 +188,13 @@ printaliases(void)
if (*ap->name != '\0')
sorted[j++] = ap;
qsort(sorted, aliases, sizeof(*sorted), comparealiases);
- for (i = 0; i < aliases; i++)
+ for (i = 0; i < aliases; i++) {
printalias(sorted[i]);
+ if (int_pending())
+ break;
+ }
ckfree(sorted);
+ INTON;
}
int
diff --git a/bin/sh/bltin/bltin.h b/bin/sh/bltin/bltin.h
index 0143b6e27aca..bbf55f1ae79f 100644
--- a/bin/sh/bltin/bltin.h
+++ b/bin/sh/bltin/bltin.h
@@ -74,6 +74,6 @@
pointer stalloc(int);
void error(const char *, ...) __printf0like(1, 2);
-pid_t getjobpgrp(char *);
+int killjob(const char *, int);
extern char *commandname;
diff --git a/bin/sh/cd.c b/bin/sh/cd.c
index fa6f4925acee..7720fad7d64c 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 b8f76d57f3d8..4f7559e18487 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 9f8e029933eb..e547e3136658 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;
@@ -610,6 +612,7 @@ defun(const char *name, union node *func)
/*
* Delete a function if it exists.
+ * Called with interrupts off.
*/
int
@@ -672,9 +675,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 414bfa7a1988..a126ca60a6dd 100644
--- a/bin/sh/expand.c
+++ b/bin/sh/expand.c
@@ -98,13 +98,14 @@ static struct ifsregion ifsfirst; /* first struct in list of ifs regions */
static struct ifsregion *ifslastp; /* last struct in list */
static struct arglist exparg; /* holds expanded arg list */
-static void argstr(char *, int);
+static char *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);
-static int varisset(char *, int);
-static void varvalue(char *, int, int, int);
+static int varisset(const char *, int);
+static void varvalue(const char *, int, int, int);
static void recordregion(int, int, int);
static void removerecordregions(int);
static void ifsbreakup(char *, struct arglist *);
@@ -206,13 +207,13 @@ 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.
* If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
*/
-static void
+static char *
argstr(char *p, int flag)
{
char c;
@@ -230,8 +231,10 @@ argstr(char *p, int flag)
CHECKSTRSPACE(2, expdest);
switch (c = *p++) {
case '\0':
+ return (p - 1);
case CTLENDVAR:
- goto breakloop;
+ case CTLENDARI:
+ return (p);
case CTLQUOTEMARK:
lit_quoted = 1;
/* "$@" syntax adherence hack */
@@ -261,8 +264,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 '=':
@@ -288,7 +291,6 @@ argstr(char *p, int flag)
expdest - stackblock(), 0);
}
}
-breakloop:;
}
/*
@@ -387,59 +389,40 @@ 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 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();
+ p = 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);
+ return p;
}
@@ -650,7 +633,7 @@ evalvar(char *p, int flag)
int subtype;
int varflags;
char *var;
- char *val;
+ const char *val;
int patloc;
int c;
int set;
@@ -671,10 +654,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 +684,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 +799,6 @@ record:
default:
abort();
}
- p[-1] = '='; /* recover overwritten '=' */
if (subtype != VSNORMAL) { /* skip to end of alternative */
int nesting = 1;
@@ -844,7 +827,7 @@ record:
*/
static int
-varisset(char *name, int nulok)
+varisset(const char *name, int nulok)
{
if (*name == '!')
@@ -893,7 +876,7 @@ strtodest(const char *p, int flag, int subtype, int quoted)
*/
static void
-varvalue(char *name, int quoted, int subtype, int flag)
+varvalue(const char *name, int quoted, int subtype, int flag)
{
int num;
char *p;
@@ -973,6 +956,7 @@ recordregion(int start, int end, int inquotes)
{
struct ifsregion *ifsp;
+ INTOFF;
if (ifslastp == NULL) {
ifsp = &ifsfirst;
} else {
@@ -980,6 +964,7 @@ recordregion(int start, int end, int inquotes)
&& ifslastp->inquotes == inquotes) {
/* extend previous area */
ifslastp->endoff = end;
+ INTON;
return;
}
ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
@@ -990,6 +975,7 @@ recordregion(int start, int end, int inquotes)
ifslastp->begoff = start;
ifslastp->endoff = end;
ifslastp->inquotes = inquotes;
+ INTON;
}
@@ -1307,9 +1293,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 8d44321587b7..7495a633ef40 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 e46095d5ed54..05cc1ebe606f 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 bbb954ac0a1a..af5887e02804 100644
--- a/bin/sh/jobs.c
+++ b/bin/sh/jobs.c
@@ -95,9 +95,9 @@ static void restartjob(struct job *);
#endif
static void freejob(struct job *);
static int waitcmdloop(struct job *);
-static struct job *getjob_nonotfound(char *);
-static struct job *getjob(char *);
-pid_t getjobpgrp(char *);
+static struct job *getjob_nonotfound(const char *);
+static struct job *getjob(const char *);
+pid_t killjob(const char *, int);
static pid_t dowait(int, struct job *);
static void checkzombies(void);
static void cmdtxt(union node *);
@@ -558,7 +558,7 @@ jobidcmd(int argc __unused, char **argv __unused)
*/
static struct job *
-getjob_nonotfound(char *name)
+getjob_nonotfound(const char *name)
{
int jobno;
struct job *found, *jp;
@@ -628,7 +628,7 @@ currentjob: if ((jp = getcurjob(NULL)) == NULL)
static struct job *
-getjob(char *name)
+getjob(const char *name)
{
struct job *jp;
@@ -639,13 +639,26 @@ getjob(char *name)
}
-pid_t
-getjobpgrp(char *name)
+int
+killjob(const char *name, int sig)
{
struct job *jp;
+ int i, ret;
jp = getjob(name);
- return -jp->ps[0].pid;
+ if (jp->state == JOBDONE)
+ return 0;
+ if (jp->jobctl)
+ return kill(-jp->ps[0].pid, sig);
+ ret = -1;
+ errno = ESRCH;
+ for (i = 0; i < jp->nprocs; i++)
+ if (jp->ps[i].status == -1 || WIFSTOPPED(jp->ps[i].status)) {
+ if (kill(jp->ps[i].pid, sig) == 0)
+ ret = 0;
+ } else
+ ret = 0;
+ return ret;
}
/*
@@ -978,7 +991,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 +1004,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)
@@ -1109,7 +1121,8 @@ dowait(int mode, struct job *job)
for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
if (sp->pid == -1)
continue;
- if (sp->pid == pid) {
+ if (sp->pid == pid && (sp->status == -1 ||
+ WIFSTOPPED(sp->status))) {
TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
(int)pid, sp->status,
status));
diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c
index cb330d0ec0fa..119f12e9fc74 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/miscbltin.c b/bin/sh/miscbltin.c
index 2ecd4f22f27e..27fa53db2fb4 100644
--- a/bin/sh/miscbltin.c
+++ b/bin/sh/miscbltin.c
@@ -405,6 +405,9 @@ static const struct limits limits[] = {
#ifdef RLIMIT_NPTS
{ "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' },
#endif
+#ifdef RLIMIT_KQUEUES
+ { "kqueues", (char *)0, RLIMIT_KQUEUES, 1, 'k' },
+#endif
{ (char *) 0, (char *)0, 0, 0, '\0' }
};
@@ -421,7 +424,7 @@ ulimitcmd(int argc __unused, char **argv __unused)
struct rlimit limit;
what = 'f';
- while ((optc = nextopt("HSatfdsmcnuvlbpw")) != '\0')
+ while ((optc = nextopt("HSatfdsmcnuvlbpwk")) != '\0')
switch (optc) {
case 'H':
how = HARD;
diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c
index 67617ec04876..de7e9b648958 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 5a6e3d8a4001..87b76c8acff6 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 5e70c64e7ffb..603c77707f20 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 a0def64c6395..e6e9f82ccfd7 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 d500d2fd5b8b..598259439124 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 932554578e89..6127e8603305 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;
};
@@ -91,6 +92,13 @@ static int openhere(union node *);
* undone by calling popredir. If the REDIR_BACKQ flag is set, then the
* standard output, and the standard error if it becomes a duplicate of
* stdout, is saved in memory.
+*
+ * We suppress interrupts so that we won't leave open file
+ * descriptors around. Because the signal handler remains
+ * installed and we do not use system call restart, interrupts
+ * will still abort blocking opens such as fifos (they will fail
+ * with EINTR). There is, however, a race condition if an interrupt
+ * arrives after INTOFF and before open blocks.
*/
void
@@ -102,6 +110,7 @@ redirect(union node *redir, int flags)
int fd;
char memory[10]; /* file descriptors to write to memory */
+ INTOFF;
for (i = 10 ; --i >= 0 ; )
memory[i] = 0;
memory[1] = flags & REDIR_BACKQ;
@@ -109,11 +118,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,14 +146,15 @@ redirect(union node *redir, int flags)
sv->renamed[fd] = i;
INTON;
}
- if (fd == 0)
- fd0_redirected++;
openredirect(n, memory);
+ INTON;
+ INTOFF;
}
if (memory[1])
out1 = &memout;
if (memory[2])
out2 = &memout;
+ INTON;
}
@@ -150,19 +163,10 @@ openredirect(union node *redir, char memory[10])
{
struct stat sb;
int fd = redir->nfile.fd;
- char *fname;
+ const char *fname;
int f;
int e;
- /*
- * We suppress interrupts so that we won't leave open file
- * descriptors around. Because the signal handler remains
- * installed and we do not use system call restart, interrupts
- * will still abort blocking opens such as fifos (they will fail
- * with EINTR). There is, however, a race condition if an interrupt
- * arrives after INTOFF and before open blocks.
- */
- INTOFF;
memory[fd] = 0;
switch (redir->nfile.type) {
case NFROM:
@@ -235,7 +239,6 @@ movefd:
default:
abort();
}
- INTON;
}
@@ -248,7 +251,7 @@ movefd:
static int
openhere(union node *redir)
{
- char *p;
+ const char *p;
int pip[2];
size_t len = 0;
int flags;
@@ -303,8 +306,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 +315,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 ea1d899ed8e1..1c3f8fb8b4e1 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
@@ -40,14 +40,14 @@
.Nd command interpreter (shell)
.Sh SYNOPSIS
.Nm
-.Op Fl /+abCEefIimnPpTuVvx
+.Op Fl /+abCEefhIimnPpTuVvx
.Op Fl /+o Ar longname
.Oo
.Ar script
.Op Ar arg ...
.Oc
.Nm
-.Op Fl /+abCEefIimnPpTuVvx
+.Op Fl /+abCEefhIimnPpTuVvx
.Op Fl /+o Ar longname
.Fl c Ar string
.Oo
@@ -55,7 +55,7 @@
.Op Ar arg ...
.Oc
.Nm
-.Op Fl /+abCEefIimnPpTuVvx
+.Op Fl /+abCEefhIimnPpTuVvx
.Op Fl /+o Ar longname
.Fl s
.Op Ar arg ...
@@ -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.
@@ -2551,7 +2571,7 @@ and not found.
For aliases the alias expansion is printed;
for commands and tracked aliases
the complete pathname of the command is printed.
-.It Ic ulimit Oo Fl HSabcdflmnpstuvw Oc Op Ar limit
+.It Ic ulimit Oo Fl HSabcdfklmnpstuvw Oc Op Ar limit
Set or display resource limits (see
.Xr getrlimit 2 ) .
If
@@ -2598,6 +2618,11 @@ The maximal size of core dump files, in 512-byte blocks.
The maximal size of the data segment of a process, in kilobytes.
.It Fl f Ar filesize
The maximal size of a file, in 512-byte blocks.
+.It Fl k Ar kqueues
+The maximal number of kqueues
+(see
+.Xr kqueue 2 )
+for this user ID.
.It Fl l Ar lockedmem
The maximal size of memory that can be locked by a process, in
kilobytes.
diff --git a/bin/sh/show.c b/bin/sh/show.c
index 7cb354707dd6..db18ae9701b3 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 000000000000..51c6dc443367
--- /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 <bsd.test.mk>
diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile
new file mode 100644
index 000000000000..945a14d6c518
--- /dev/null
+++ b/bin/sh/tests/builtins/Makefile
@@ -0,0 +1,149 @@
+# $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+= kill1.0 kill2.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 000000000000..d9b27969cfd6
--- /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 000000000000..52efaf0b72e7
--- /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 000000000000..31403dc760bf
--- /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 000000000000..c9f4011bea00
--- /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 000000000000..fe65e31f5f03
--- /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 000000000000..52efaf0b72e7
--- /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 000000000000..3d5efeccf2c7
--- /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 000000000000..ba0cbb4bd268
--- /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 000000000000..ff52dd321469
--- /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 000000000000..12799ccbe7ce
--- /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 000000000000..10a5ca88edff
--- /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 000000000000..d52ff52be4d4
--- /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 000000000000..7df8e186311c
--- /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 000000000000..b6083858f671
--- /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 000000000000..860fc67dacda
--- /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 000000000000..a627b5cd996f
--- /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 000000000000..0e66e11f0dee
--- /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 000000000000..2a442ba7993a
--- /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 000000000000..78f4e9bdffde
--- /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 000000000000..0338e8a224e3
--- /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 000000000000..09b0e1133544
--- /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 000000000000..24303027ba91
--- /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 000000000000..ed1d25f0277a
--- /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 000000000000..470253f622ea
--- /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 000000000000..215066ad6321
--- /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 000000000000..e319148cdf5d
--- /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 000000000000..42e53d62a3cb
--- /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 000000000000..18219f51ee09
--- /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 000000000000..8c6db5ab8e57
--- /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 000000000000..8d791831c3d8
--- /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 000000000000..96b9de66fe27
--- /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 000000000000..8d9f8b604d88
--- /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 000000000000..476caec261ba
--- /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 000000000000..bc5108e6be3b
--- /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 000000000000..f2b6416a12ec
--- /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 000000000000..7729c54a6895
--- /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 000000000000..df3a9a48961c
--- /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 000000000000..3dff604583c9
--- /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 000000000000..083a06190e64
--- /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 000000000000..9adda86c1aed
--- /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 000000000000..a68f77f26f75
--- /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 000000000000..fd0afdfa0300
--- /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 000000000000..2c1adf1eb0bd
--- /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 000000000000..10c86479d81b
--- /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 000000000000..f981db324946
--- /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 000000000000..ff7b5f2c8dc8
--- /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 000000000000..9d4ae89e0e2e
--- /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 000000000000..67b869156a75
--- /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 000000000000..3e496137ff58
--- /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 000000000000..13b3fe1fe3f4
--- /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 000000000000..523f7b22a187
--- /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 000000000000..5b63bfecc0f0
--- /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 000000000000..3180207a4e2b
--- /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 000000000000..fc652f207584
--- /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 000000000000..9e3a2b645fbb
--- /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 000000000000..212e52aafc98
--- /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 000000000000..43eab0ddeb33
--- /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 000000000000..ed6379b993c3
--- /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 000000000000..b337f0f6d338
--- /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 000000000000..b898131c0e1c
--- /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 000000000000..04606a4a87fe
--- /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 000000000000..bf06b6e14e0c
--- /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 000000000000..dfb8357c727c
--- /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 000000000000..67da2f5c832d
--- /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 000000000000..3e86de92fbae
--- /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 000000000000..6752bb65ad4d
--- /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 000000000000..dd30a4c9aa93
--- /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 000000000000..3dcb6c41156a
--- /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 000000000000..496d448761cc
--- /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 000000000000..124c32e01a3c
--- /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 000000000000..80655ac539c5
--- /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 000000000000..7b08c9de4349
--- /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 000000000000..ab7a387b5077
--- /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 000000000000..7eb92acc2cdf
--- /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 000000000000..cd55e2ca029e
--- /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 000000000000..48c22ce71119
--- /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 000000000000..cc37238abbc6
--- /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 000000000000..af31d5599118
--- /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 000000000000..a0a347eea8f8
--- /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 000000000000..5a5dbe20cb6a
--- /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 000000000000..587be6b4c3f9
--- /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 000000000000..45cc3003fb59
--- /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 000000000000..3afc3e7b3839
--- /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 000000000000..e5cd21bc57dd
--- /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 000000000000..3afc3e7b3839
--- /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 000000000000..eade0b319546
--- /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 000000000000..a34864cd6dff
--- /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 000000000000..dec584c4538f
--- /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 000000000000..483fda20743c
--- /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 000000000000..101831a2e4b8
--- /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/kill1.0 b/bin/sh/tests/builtins/kill1.0
new file mode 100644
index 000000000000..c1b85503848e
--- /dev/null
+++ b/bin/sh/tests/builtins/kill1.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+: &
+p1=$!
+: &
+p2=$!
+wait $p2
+kill %1
diff --git a/bin/sh/tests/builtins/kill2.0 b/bin/sh/tests/builtins/kill2.0
new file mode 100644
index 000000000000..31e0ba362b80
--- /dev/null
+++ b/bin/sh/tests/builtins/kill2.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+sleep 1 | sleep 1 &
+kill %+
+wait "$!"
+r=$?
+[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = TERM ]
diff --git a/bin/sh/tests/builtins/lineno.0 b/bin/sh/tests/builtins/lineno.0
new file mode 100644
index 000000000000..c9311f82d253
--- /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 000000000000..82583a93c82e
--- /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 000000000000..ddbd10433a36
--- /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 000000000000..b28837ec005f
--- /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 000000000000..cc8c10f40618
--- /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 000000000000..39ee370099e7
--- /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 000000000000..3955aaa12f1a
--- /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 000000000000..90b10944c5a5
--- /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 000000000000..99a82d014794
--- /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 000000000000..7763d6fe9636
--- /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 000000000000..0e7ea85cddb4
--- /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 000000000000..2dd3e729574a
--- /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 000000000000..06a68faa32a3
--- /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 000000000000..dbcb1af9bae7
--- /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 000000000000..fc7451191586
--- /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 000000000000..c6ae9c1023c0
--- /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 000000000000..8ed98ca9d86e
--- /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 000000000000..7204a35aa0a5
--- /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 000000000000..a8747a46ffeb
--- /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 000000000000..7d83391cba22
--- /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 000000000000..2168e10c841a
--- /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 000000000000..e78f887b60b4
--- /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 000000000000..787e892a7698
--- /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 000000000000..0ef817179d73
--- /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 000000000000..605ec680d66a
--- /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 000000000000..be5582b458b4
--- /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 000000000000..6e4b7bd751a8
--- /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 000000000000..e4d8e0d641a1
--- /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 000000000000..2047373473ab
--- /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 000000000000..f00e859a74ce
--- /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 000000000000..fc39fade6270
--- /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 000000000000..ad13eab0a7b8
--- /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 000000000000..313f6a387678
--- /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 000000000000..fa0e35d6a6b2
--- /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 000000000000..cfeea9ed9ded
--- /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 000000000000..8c62ffd2b570
--- /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 000000000000..d90eb08eb242
--- /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 000000000000..97cce8d0d244
--- /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 000000000000..a05287a16771
--- /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 000000000000..81607293531e
--- /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 000000000000..7f2080ee4dca
--- /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 000000000000..56e0fb1b89fe
--- /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 000000000000..bd2bf7efe769
--- /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 000000000000..35529b80f4bf
--- /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 000000000000..cdce976e36da
--- /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 000000000000..0f584ecec584
--- /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 000000000000..c5e456437946
--- /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 000000000000..7853418a4827
--- /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 000000000000..fe44d957fc0a
--- /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 000000000000..87cccdd812cc
--- /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 000000000000..34d8d6e07d2e
--- /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 000000000000..ace39c042d5f
--- /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 000000000000..eafec89a681b
--- /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 000000000000..1ca85308c9ac
--- /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 000000000000..864fc7817276
--- /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 000000000000..e61455cf5f0e
--- /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 000000000000..1ed52999630d
--- /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 000000000000..79351315d384
--- /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 000000000000..6874bf669302
--- /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 000000000000..20e3c6808679
--- /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 000000000000..0fb092f8f614
--- /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 000000000000..b59ff59622eb
--- /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 000000000000..661f275421f8
--- /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 000000000000..9f8b0f2045d9
--- /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 000000000000..00eaed9b8671
--- /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 000000000000..ff4e62995262
--- /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 000000000000..43e33034af9f
--- /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 000000000000..5b49e2be17ce
--- /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 000000000000..d92e9ded5689
--- /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 000000000000..ac0153655f8d
--- /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 000000000000..6e949945f9f3
--- /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 000000000000..7e13d2b895d2
--- /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 000000000000..d027a5ad4ef3
--- /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 000000000000..a5ecba5a7e52
--- /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 000000000000..ef40251f23f6
--- /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 000000000000..9eec8d08470d
--- /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 000000000000..89bd80f0e8e2
--- /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 000000000000..459281f04c3a
--- /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 000000000000..89b19978f9d1
--- /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 000000000000..ba51442fb7fb
--- /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 000000000000..dbf14b596e1f
--- /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 000000000000..b4b44c4a4062
--- /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 000000000000..cb8c0b113c13
--- /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 000000000000..32bccd8e784a
--- /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 000000000000..8a07d037baec
--- /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 000000000000..206097478e35
--- /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 000000000000..1fcd47eebf96
--- /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 000000000000..17d1109b390f
--- /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 000000000000..5b20f04beb83
--- /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 000000000000..fcb52e74178c
--- /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 000000000000..302d0d824c41
--- /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 000000000000..edb92ae2ddd0
--- /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 000000000000..44a25dc23b2e
--- /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 000000000000..7cfab5b05d62
--- /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 000000000000..2e2fbc53bd26
--- /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 000000000000..359fc6f476e6
--- /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 000000000000..25e4f4e34fa5
--- /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 000000000000..cc9ceaa41d4e
--- /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 000000000000..b0faf9e73708
--- /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 000000000000..7cfab5b05d62
--- /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 000000000000..f771edc56cde
--- /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 000000000000..33667cb1922b
--- /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 000000000000..64fde3e3e333
--- /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 000000000000..2eeac79ae8c3
--- /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 000000000000..62a25379123c
--- /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 000000000000..3cb678c2d08a
--- /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 000000000000..29fcc077b592
--- /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 000000000000..9830b5e8a7cf
--- /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 000000000000..e0ed581f85ef
--- /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 000000000000..a645c2aef735
--- /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 000000000000..a1f097b774d6
--- /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 000000000000..41d3e055723e
--- /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 000000000000..7ff3fe2900d1
--- /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 000000000000..12c6265a9282
--- /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 000000000000..1b128d096716
--- /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 000000000000..50829d629a4d
--- /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 000000000000..dd0011f064b0
--- /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 000000000000..1588105f599a
--- /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 000000000000..d68e4504ed3d
--- /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 000000000000..57054c17c45f
--- /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 000000000000..707ca68f737a
--- /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 000000000000..4e3ac0cae055
--- /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 000000000000..2487bcf2fc0d
--- /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 000000000000..14c9b9396ada
--- /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 000000000000..c7f31629f474
--- /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 000000000000..24a9159fdeb9
--- /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 000000000000..36985084b26b
--- /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 000000000000..7fe1dbf1076b
--- /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 000000000000..56d54e38d251
--- /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 000000000000..1ca57aca6baa
--- /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 000000000000..1326bc27f1c1
--- /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 000000000000..347806ed45ac
--- /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 000000000000..8c71af3cd79f
--- /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 000000000000..32164495c9ca
--- /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 000000000000..9a87acb15e1c
--- /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 000000000000..b39edb12eb2c
--- /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 000000000000..45f541e6eeea
--- /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 000000000000..26e54249c1cc
--- /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 000000000000..bd2431971f4a
--- /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 000000000000..118ba2265687
--- /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 000000000000..1aaf6194fbaf
--- /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 000000000000..6bc73697ffab
--- /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 000000000000..cb7da3b2e21a
--- /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 000000000000..207e4881935b
--- /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 000000000000..95b48a06aae6
--- /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 000000000000..b69159d9b5b8
--- /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 000000000000..610dad89e4dc
--- /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 000000000000..d0f23312f9ca
--- /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 000000000000..fc4589c1bac5
--- /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 000000000000..5aada2b86586
--- /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 000000000000..2d03e503387d
--- /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 000000000000..cc8b597d4755
--- /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 000000000000..d4fa7727f593
--- /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 000000000000..515c7da9aca1
--- /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 000000000000..7cf17a3e8fe1
--- /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 000000000000..f1af547421f9
--- /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 000000000000..50394dbb00ca
--- /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 000000000000..7fdc5b217f19
--- /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 000000000000..bdbbb823e2b2
--- /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 000000000000..31d85d497e23
--- /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 000000000000..71df562298e5
--- /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 000000000000..8c29e8318327
--- /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 000000000000..b86776ed24b0
--- /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 000000000000..abb6b225a257
--- /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 000000000000..ee1ce73e7e38
--- /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 000000000000..afca3719e8b0
--- /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 000000000000..6586f330db5b
--- /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 000000000000..dbd1aec5f9da
--- /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 000000000000..52adaea33b93
--- /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 000000000000..0b1f81f3537a
--- /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 000000000000..4ee3ef4ffa41
--- /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 000000000000..57f64e7f7f7b
--- /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 000000000000..a1a0e6658a08
--- /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 000000000000..a67b2da2e5f2
--- /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 000000000000..255143296d4a
--- /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 000000000000..e7f53c77a5f9
--- /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 000000000000..e91b86707183
--- /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 000000000000..0569b5729cae
--- /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 000000000000..5b896a25413b
--- /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 000000000000..2aaebf9048a2
--- /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 000000000000..d749b51f4b20
--- /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 000000000000..2093eed8bbd3
--- /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 000000000000..5348be511379
--- /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 000000000000..322ca162b0b6
--- /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 000000000000..6b78309f6b81
--- /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 000000000000..b79b11616c15
--- /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 000000000000..3cc6c15e7ece
--- /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 000000000000..34778351f1f3
--- /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 000000000000..19842900e0c1
--- /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 000000000000..8c6b01c8356a
--- /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 000000000000..8a884ff2901e
--- /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 000000000000..d1672e057ec5
--- /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 000000000000..18269c4318e6
--- /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 000000000000..9a6a53a2414a
--- /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 000000000000..f5a87525ae37
--- /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 000000000000..49fcdc2f8f56
--- /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 000000000000..66dea3851efc
--- /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 000000000000..0b25e53cf82a
--- /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 000000000000..bc6680526823
--- /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 000000000000..9e81f58a0a99
--- /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 000000000000..beba009b0660
--- /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 000000000000..663c68d2046d
--- /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 000000000000..5ad0e143f81f
--- /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 000000000000..763eb7dcfc8f
--- /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 000000000000..f81aa62cb6ba
--- /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 000000000000..7f199b42c796
--- /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 000000000000..7d8581bbc8ee
--- /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 000000000000..4f8ed9b491c7
--- /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 000000000000..b548e521792a
--- /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 000000000000..619ef651cce6
--- /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 000000000000..b89a04140c37
--- /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 000000000000..1000bd3d0245
--- /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 000000000000..937ec9a708e0
--- /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 000000000000..3f753c4113f6
--- /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 000000000000..352bdea920bf
--- /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 000000000000..f7272f371dce
--- /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 000000000000..d43f5ddd8aab
--- /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 000000000000..fafc059ec24f
--- /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 000000000000..c0d4a2cc9141
--- /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 000000000000..696823d58043
--- /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 000000000000..5791a5accc7c
--- /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 000000000000..343c99de9b27
--- /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 000000000000..33e0288e861f
--- /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 000000000000..a7689f6841a5
--- /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 000000000000..67d19516a5d9
--- /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 000000000000..fcec2a4b676e
--- /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 000000000000..0099379a9d3c
--- /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 000000000000..2297f8b753d4
--- /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 000000000000..03650b1d2d91
--- /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 000000000000..75dd9ab9b8f1
--- /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 000000000000..30d99f49bbf4
--- /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 000000000000..522264ff823f
--- /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 000000000000..2e4379155d4a
--- /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 000000000000..53b949dc23e9
--- /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 000000000000..1b92fc07d5b2
--- /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 000000000000..f0fbadbb20e7
--- /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 000000000000..6dd179c065a7
--- /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 000000000000..ae99b8a588c2
--- /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 000000000000..e0721e2aaa01
--- /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 000000000000..19332ed09056
--- /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 000000000000..3d0205fd2332
--- /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 000000000000..c723d08ab3eb
--- /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 000000000000..b26f0dd067cd
--- /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 000000000000..7fc2f15f0931
--- /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 000000000000..6bd8808cc379
--- /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 000000000000..35b125c14767
--- /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 000000000000..49b4c45155fa
--- /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 000000000000..14610e415c19
--- /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 000000000000..12061417e108
--- /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 000000000000..ad166da23ffe
--- /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 000000000000..2e872abfe5b0
--- /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 000000000000..4617ed8d9086
--- /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 000000000000..a7e68527791c
--- /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 000000000000..f620af5b12c2
--- /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 000000000000..c2c44ca620e5
--- /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 000000000000..a4b1e3f48729
--- /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 000000000000..c866b1af68bb
--- /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 000000000000..8f0b41a0a3f7
--- /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 000000000000..df64b7dfc0b1
--- /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 000000000000..5ab443c48d8a
--- /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 000000000000..f8b01e9c7997
--- /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 000000000000..eb7c881237fd
--- /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 000000000000..54ebfc3d7193
--- /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 000000000000..4e887b25f285
--- /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 000000000000..5fd4dda8cca9
--- /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 000000000000..dcac7323ad3f
--- /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 000000000000..5ce38977d7f5
--- /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 000000000000..27369a0b1b4c
--- /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 000000000000..5839e46b36ce
--- /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 000000000000..4bb85ad80c17
--- /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 000000000000..b250272f3319
--- /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 000000000000..fa3af5fd5a97
--- /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 000000000000..84b0eb2705d4
--- /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 000000000000..3a634de167a7
--- /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 000000000000..a15010648780
--- /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 000000000000..598358a0494b
--- /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 000000000000..125a542ab717
--- /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 000000000000..6df9f6395ff6
--- /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 000000000000..4e8447b11e4c
--- /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 000000000000..46076c882a5e
--- /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 000000000000..b9e9501c2027
--- /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 000000000000..128a48391ded
--- /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 000000000000..d804e128debe
--- /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 000000000000..9842ff0afd05
--- /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 000000000000..1fd3b26f8a26
--- /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 000000000000..55d79174f98a
--- /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 000000000000..607b7c350020
--- /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 000000000000..78e203ab0059
--- /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 000000000000..9fafb1c45dd8
--- /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 000000000000..25d0e6147dea
--- /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 000000000000..21577f4f78c7
--- /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 000000000000..67fdcbc3d83e
--- /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 000000000000..7cd8b09d64a8
--- /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 000000000000..6a5937d496ae
--- /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 000000000000..9dbb4bf514af
--- /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 000000000000..9b7f67b6b0d4
--- /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 000000000000..8bb7f3a92fcb
--- /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 000000000000..67eb718ee614
--- /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 000000000000..3c6b70492fa2
--- /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 000000000000..cc76d6edfa53
--- /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 000000000000..36aa4bdcbcd3
--- /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 000000000000..495540854099
--- /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 000000000000..a4916a842664
--- /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 000000000000..21c089a23221
--- /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 000000000000..7d93b4d09e85
--- /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 000000000000..c2dcbe9b682a
--- /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 000000000000..934e2a68de9e
--- /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 000000000000..7a617a14b4a4
--- /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 000000000000..c0bad0fa732d
--- /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 000000000000..1e25566a156b
--- /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 000000000000..961bd4120865
--- /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 000000000000..90476a98f1ec
--- /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 000000000000..8f510ac9c164
--- /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 000000000000..8e5831bd1907
--- /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 000000000000..619e98a7d2df
--- /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 000000000000..71ea7f2cf704
--- /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 000000000000..24ea2760400b
--- /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 000000000000..597db593bdaf
--- /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 000000000000..371c94a0440d
--- /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 000000000000..124966ca59e9
--- /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 000000000000..dd3c79025236
--- /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 3fc85668c642..e5a2a9135576 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 a9622514c0b4..541b9b149a8d 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 c20d03263708..ebeff16635fb 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,21 +240,25 @@ 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;
}
+ INTOFF;
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);
+ INTON;
}
static int
@@ -291,6 +291,7 @@ change_env(const char *s, int set)
char *eqp;
char *ss;
+ INTOFF;
ss = savestr(s);
if ((eqp = strchr(ss, '=')) != NULL)
*eqp = '\0';
@@ -299,6 +300,7 @@ change_env(const char *s, int set)
else
(void) unsetenv(ss);
ckfree(ss);
+ INTON;
return;
}
@@ -323,10 +325,16 @@ setvareq(char *s, int flags)
mklocal(s);
vp = find_var(s, &vpp, &nlen);
if (vp != NULL) {
- if (vp->flags & VREADONLY)
+ if (vp->flags & VREADONLY) {
+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+ ckfree(s);
error("%.*s: is read only", vp->name_len, s);
- if (flags & VNOSET)
+ }
+ if (flags & VNOSET) {
+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+ ckfree(s);
return;
+ }
INTOFF;
if (vp->func && (flags & VNOFUNC) == 0)
@@ -359,15 +367,18 @@ setvareq(char *s, int flags)
return;
}
/* not found */
- if (flags & VNOSET)
+ if (flags & VNOSET) {
+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+ ckfree(s);
return;
+ }
+ INTOFF;
vp = ckmalloc(sizeof (*vp));
vp->flags = flags;
vp->text = s;
vp->name_len = nlen;
vp->next = *vpp;
vp->func = NULL;
- INTOFF;
*vpp = vp;
if ((vp->flags & VEXPORT) && localevar(s)) {
change_env(s, 1);
@@ -775,6 +786,7 @@ poplocalvars(void)
struct localvar *lvp;
struct var *vp;
+ INTOFF;
while ((lvp = localvars) != NULL) {
localvars = lvp->next;
vp = lvp->vp;
@@ -792,6 +804,7 @@ poplocalvars(void)
}
ckfree(lvp);
}
+ INTON;
}
@@ -830,18 +843,21 @@ unsetcmd(int argc __unused, char **argv __unused)
if (flg_func == 0 && flg_var == 0)
flg_var = 1;
+ INTOFF;
for (ap = argptr; *ap ; ap++) {
if (flg_func)
ret |= unsetfunc(*ap);
if (flg_var)
ret |= unsetvar(*ap);
}
+ INTON;
return ret;
}
/*
* Unset the specified variable.
+ * Called with interrupts off.
*/
int
@@ -855,7 +871,6 @@ unsetvar(const char *s)
return (0);
if (vp->flags & VREADONLY)
return (1);
- INTOFF;
if (vp->text[vp->name_len + 1] != '\0')
setvar(s, nullstr, 0);
if ((vp->flags & VEXPORT) && localevar(vp->text)) {
@@ -871,7 +886,6 @@ unsetvar(const char *s)
*vpp = vp->next;
ckfree(vp);
}
- INTON;
return (0);
}
diff --git a/bin/sh/var.h b/bin/sh/var.h
index 6cdfbfec9841..6ce5b24e0312 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 7c64b40347ef..e9a0507f0cac 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/test.c b/bin/test/test.c
index 96aa9f54970f..46e9999eb0bb 100644
--- a/bin/test/test.c
+++ b/bin/test/test.c
@@ -67,9 +67,18 @@ error(const char *msg, ...)
operand ::= <any legal UNIX file name>
*/
+enum token_types {
+ UNOP = 0x100,
+ BINOP = 0x200,
+ BUNOP = 0x300,
+ BBINOP = 0x400,
+ PAREN = 0x500
+};
+
enum token {
EOI,
- FILRD,
+ OPERAND,
+ FILRD = UNOP + 1,
FILWR,
FILEX,
FILEXIST,
@@ -85,13 +94,13 @@ enum token {
FILSUID,
FILSGID,
FILSTCK,
- FILNT,
- FILOT,
- FILEQ,
- FILUID,
- FILGID,
STREZ,
STRNZ,
+ FILUID,
+ FILGID,
+ FILNT = BINOP + 1,
+ FILOT,
+ FILEQ,
STREQ,
STRNE,
STRLT,
@@ -102,76 +111,68 @@ enum token {
INTGT,
INTLE,
INTLT,
- UNOT,
- BAND,
+ UNOT = BUNOP + 1,
+ BAND = BBINOP + 1,
BOR,
- LPAREN,
- RPAREN,
- OPERAND
+ LPAREN = PAREN + 1,
+ RPAREN
};
-enum token_types {
- UNOP,
- BINOP,
- BUNOP,
- BBINOP,
- PAREN
-};
+#define TOKEN_TYPE(token) ((token) & 0xff00)
static struct t_op {
char op_text[4];
- short op_num, op_type;
+ short op_num;
} const ops [] = {
- {"-r", FILRD, UNOP},
- {"-w", FILWR, UNOP},
- {"-x", FILEX, UNOP},
- {"-e", FILEXIST,UNOP},
- {"-f", FILREG, UNOP},
- {"-d", FILDIR, UNOP},
- {"-c", FILCDEV,UNOP},
- {"-b", FILBDEV,UNOP},
- {"-p", FILFIFO,UNOP},
- {"-u", FILSUID,UNOP},
- {"-g", FILSGID,UNOP},
- {"-k", FILSTCK,UNOP},
- {"-s", FILGZ, UNOP},
- {"-t", FILTT, UNOP},
- {"-z", STREZ, UNOP},
- {"-n", STRNZ, UNOP},
- {"-h", FILSYM, UNOP}, /* for backwards compat */
- {"-O", FILUID, UNOP},
- {"-G", FILGID, UNOP},
- {"-L", FILSYM, UNOP},
- {"-S", FILSOCK,UNOP},
- {"=", STREQ, BINOP},
- {"==", STREQ, BINOP},
- {"!=", STRNE, BINOP},
- {"<", STRLT, BINOP},
- {">", STRGT, BINOP},
- {"-eq", INTEQ, BINOP},
- {"-ne", INTNE, BINOP},
- {"-ge", INTGE, BINOP},
- {"-gt", INTGT, BINOP},
- {"-le", INTLE, BINOP},
- {"-lt", INTLT, BINOP},
- {"-nt", FILNT, BINOP},
- {"-ot", FILOT, BINOP},
- {"-ef", FILEQ, BINOP},
- {"!", UNOT, BUNOP},
- {"-a", BAND, BBINOP},
- {"-o", BOR, BBINOP},
- {"(", LPAREN, PAREN},
- {")", RPAREN, PAREN},
- {"", 0, 0}
+ {"-r", FILRD},
+ {"-w", FILWR},
+ {"-x", FILEX},
+ {"-e", FILEXIST},
+ {"-f", FILREG},
+ {"-d", FILDIR},
+ {"-c", FILCDEV},
+ {"-b", FILBDEV},
+ {"-p", FILFIFO},
+ {"-u", FILSUID},
+ {"-g", FILSGID},
+ {"-k", FILSTCK},
+ {"-s", FILGZ},
+ {"-t", FILTT},
+ {"-z", STREZ},
+ {"-n", STRNZ},
+ {"-h", FILSYM}, /* for backwards compat */
+ {"-O", FILUID},
+ {"-G", FILGID},
+ {"-L", FILSYM},
+ {"-S", FILSOCK},
+ {"=", STREQ},
+ {"==", STREQ},
+ {"!=", STRNE},
+ {"<", STRLT},
+ {">", STRGT},
+ {"-eq", INTEQ},
+ {"-ne", INTNE},
+ {"-ge", INTGE},
+ {"-gt", INTGT},
+ {"-le", INTLE},
+ {"-lt", INTLT},
+ {"-nt", FILNT},
+ {"-ot", FILOT},
+ {"-ef", FILEQ},
+ {"!", UNOT},
+ {"-a", BAND},
+ {"-o", BOR},
+ {"(", LPAREN},
+ {")", RPAREN},
+ {"", 0}
};
-static struct t_op const *t_wp_op;
static int nargc;
static char **t_wp;
static int parenlevel;
static int aexpr(enum token);
-static int binop(void);
+static int binop(enum token);
static int equalf(const char *, const char *);
static int filstat(char *, enum token);
static int getn(const char *);
@@ -295,10 +296,10 @@ primary(enum token n)
parenlevel--;
return res;
}
- if (t_wp_op && t_wp_op->op_type == UNOP) {
+ if (TOKEN_TYPE(n) == UNOP) {
/* unary expression */
if (--nargc == 0)
- syntax(t_wp_op->op_text, "argument expected");
+ syntax(NULL, "argument expected"); /* impossible */
switch (n) {
case STREZ:
return strlen(*++t_wp) == 0;
@@ -311,28 +312,25 @@ primary(enum token n)
}
}
- if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type ==
- BINOP) {
- return binop();
- }
+ nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
+ if (TOKEN_TYPE(nn) == BINOP)
+ return binop(nn);
return strlen(*t_wp) > 0;
}
static int
-binop(void)
+binop(enum token n)
{
- const char *opnd1, *opnd2;
- struct t_op const *op;
+ const char *opnd1, *op, *opnd2;
opnd1 = *t_wp;
- (void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL);
- op = t_wp_op;
+ op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
- syntax(op->op_text, "argument expected");
+ syntax(op, "argument expected");
- switch (op->op_num) {
+ switch (n) {
case STREQ:
return strcmp(opnd1, opnd2) == 0;
case STRNE:
@@ -424,22 +422,20 @@ t_lex(char *s)
struct t_op const *op = ops;
if (s == 0) {
- t_wp_op = NULL;
return EOI;
}
while (*op->op_text) {
if (strcmp(s, op->op_text) == 0) {
- if (((op->op_type == UNOP || op->op_type == BUNOP)
+ if (((TOKEN_TYPE(op->op_num) == UNOP ||
+ TOKEN_TYPE(op->op_num) == BUNOP)
&& isunopoperand()) ||
(op->op_num == LPAREN && islparenoperand()) ||
(op->op_num == RPAREN && isrparenoperand()))
break;
- t_wp_op = op;
return op->op_num;
}
op++;
}
- t_wp_op = NULL;
return OPERAND;
}
@@ -458,7 +454,7 @@ isunopoperand(void)
t = *(t_wp + 2);
while (*op->op_text) {
if (strcmp(s, op->op_text) == 0)
- return op->op_type == BINOP &&
+ return TOKEN_TYPE(op->op_num) == BINOP &&
(parenlevel == 0 || t[0] != ')' || t[1] != '\0');
op++;
}
@@ -480,7 +476,7 @@ islparenoperand(void)
return 0;
while (*op->op_text) {
if (strcmp(s, op->op_text) == 0)
- return op->op_type == BINOP;
+ return TOKEN_TYPE(op->op_num) == BINOP;
op++;
}
return 0;
diff --git a/bin/test/tests/Makefile b/bin/test/tests/Makefile
new file mode 100644
index 000000000000..5ee337a213fb
--- /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 <bsd.test.mk>
diff --git a/bin/test/tests/legacy_test.sh b/bin/test/tests/legacy_test.sh
new file mode 100644
index 000000000000..922955129d9f
--- /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 000000000000..7a59b1029513
--- /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>