aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-10-19 17:01:55 +0100
committerRon Yorston <rmy@pobox.com>2016-10-19 17:01:55 +0100
commit075814c60a316cfd088c88f26f75ab21b5850b98 (patch)
treef6e33ac693630827deb309faa5fa4931588db57d
parent977d65c1bbc57f5cdd0c8bfd67c8b5bb1cd390dd (diff)
parentf37e1155aabde6bd95d267a8aec347cedccb8bc3 (diff)
downloadbusybox-w32-075814c60a316cfd088c88f26f75ab21b5850b98.tar.gz
busybox-w32-075814c60a316cfd088c88f26f75ab21b5850b98.tar.bz2
busybox-w32-075814c60a316cfd088c88f26f75ab21b5850b98.zip
Merge branch busybox (up to "ash: comment out free(p) just before...")
-rw-r--r--AUTHORS4
-rw-r--r--applets/Kbuild.src18
-rw-r--r--applets/applet_tables.c25
-rw-r--r--archival/cpio.c8
-rw-r--r--archival/gzip.c5
-rw-r--r--configs/TEST_nommu_defconfig1
-rw-r--r--configs/TEST_noprintf_defconfig1
-rw-r--r--configs/TEST_rh9_defconfig1
-rw-r--r--configs/android2_defconfig1
-rw-r--r--configs/android_502_defconfig1
-rw-r--r--configs/android_defconfig1
-rw-r--r--configs/android_ndk_defconfig1
-rw-r--r--configs/cygwin_defconfig1
-rw-r--r--configs/freebsd_defconfig1
-rw-r--r--coreutils/Config.src43
-rw-r--r--coreutils/Kbuild.src5
-rw-r--r--coreutils/expr.c4
-rw-r--r--coreutils/md5_sha1_sum.c145
-rw-r--r--coreutils/sync.c2
-rw-r--r--coreutils/test.c1
-rw-r--r--docs/keep_data_small.txt4
-rw-r--r--editors/sed.c19
-rw-r--r--editors/vi.c14
-rw-r--r--examples/var_service/dhcpd_if/README5
-rwxr-xr-xexamples/var_service/dhcpd_if/log/run21
-rwxr-xr-xexamples/var_service/dhcpd_if/p_log4
-rwxr-xr-xexamples/var_service/dhcpd_if/run23
-rw-r--r--examples/var_service/dhcpd_if/udhcpc.conf28
-rwxr-xr-xexamples/var_service/dhcpd_if/w_dumpleases3
-rwxr-xr-xexamples/var_service/dhcpd_if/w_dumpleases_countdown3
-rwxr-xr-xexamples/var_service/dhcpd_if/w_log4
-rwxr-xr-xexamples/var_service/fw/run48
-rwxr-xr-xexamples/var_service/ntpd/p_log_important4
-rw-r--r--examples/var_service/supplicant_if/README5
-rwxr-xr-xexamples/var_service/supplicant_if/log/run21
-rwxr-xr-xexamples/var_service/supplicant_if/p_log4
-rwxr-xr-xexamples/var_service/supplicant_if/run21
-rwxr-xr-xexamples/var_service/supplicant_if/w_log4
-rw-r--r--examples/var_service/supplicant_if/wpa_supplicant.conf28
-rw-r--r--findutils/grep.c14
-rw-r--r--include/applets.src.h5
-rw-r--r--include/busybox.h4
-rw-r--r--include/libbb.h26
-rw-r--r--init/init.c5
-rw-r--r--libbb/appletlib.c12
-rw-r--r--libbb/copy_file.c16
-rw-r--r--libbb/dump.c51
-rw-r--r--libbb/hash_md5_sha.c55
-rw-r--r--libbb/lineedit.c22
-rw-r--r--libbb/login.c40
-rw-r--r--libbb/speed_table.c81
-rw-r--r--libbb/udp_io.c10
-rw-r--r--libbb/vfork_daemon_rexec.c5
-rw-r--r--libbb/xconnect.c7
-rw-r--r--libbb/xfuncs_printf.c12
-rw-r--r--mailutils/sendmail.c54
-rw-r--r--miscutils/less.c43
-rw-r--r--miscutils/man.c4
-rw-r--r--modutils/modprobe-small.c33
-rw-r--r--modutils/modutils.c21
-rw-r--r--networking/Config.src12
-rw-r--r--networking/ifplugd.c8
-rw-r--r--networking/ifupdown.c14
-rw-r--r--networking/libiproute/iproute.c3
-rw-r--r--networking/libiproute/iprule.c8
-rw-r--r--networking/libiproute/libnetlink.c66
-rw-r--r--networking/ntpd.c7
-rw-r--r--networking/route.c4
-rwxr-xr-xnetworking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.839
-rw-r--r--networking/ssl_helper-wolfssl/README7
-rwxr-xr-xnetworking/ssl_helper-wolfssl/ssl_helper.sh2
-rw-r--r--networking/traceroute.c124
-rw-r--r--networking/udhcp/Kbuild.src2
-rw-r--r--networking/udhcp/arpping.c1
-rw-r--r--networking/udhcp/d6_dhcpc.c9
-rw-r--r--networking/udhcp/dhcpc.c13
-rw-r--r--networking/udhcp/dhcpd.c502
-rw-r--r--networking/udhcp/dhcpd.h33
-rw-r--r--networking/udhcp/files.c234
-rw-r--r--networking/udhcp/leases.c211
-rw-r--r--networking/udhcp/static_leases.c77
-rw-r--r--networking/wget.c130
-rw-r--r--procps/top.c12
-rw-r--r--runit/runsvdir.c4
-rw-r--r--runit/svlogd.c2
-rwxr-xr-xscripts/trylink4
-rw-r--r--shell/Config.src4
-rw-r--r--shell/ash.c1245
-rw-r--r--shell/ash_test/ash-glob/glob_dir.right19
-rwxr-xr-xshell/ash_test/ash-glob/glob_dir.tests25
-rw-r--r--shell/ash_test/ash-heredoc/heredoc5.right (renamed from shell/hush_test/hush-misc/heredoc2.right)0
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc5.tests (renamed from shell/hush_test/hush-misc/heredoc2.tests)0
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_huge.right (renamed from shell/hush_test/hush-misc/heredoc_huge.right)0
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_huge.tests (renamed from shell/hush_test/hush-misc/heredoc_huge.tests)0
-rw-r--r--shell/ash_test/ash-misc/and-or.right18
-rwxr-xr-xshell/ash_test/ash-misc/and-or.tests34
-rw-r--r--shell/ash_test/ash-misc/assignment1.right9
-rwxr-xr-xshell/ash_test/ash-misc/assignment1.tests42
-rw-r--r--shell/ash_test/ash-misc/assignment3.right2
-rwxr-xr-xshell/ash_test/ash-misc/assignment3.tests5
-rw-r--r--shell/ash_test/ash-misc/assignment4.right1
-rwxr-xr-xshell/ash_test/ash-misc/assignment4.tests3
-rw-r--r--shell/ash_test/ash-misc/break1.right2
-rwxr-xr-xshell/ash_test/ash-misc/break1.tests2
-rw-r--r--shell/ash_test/ash-misc/break2.right3
-rwxr-xr-xshell/ash_test/ash-misc/break2.tests6
-rw-r--r--shell/ash_test/ash-misc/break3.right2
-rwxr-xr-xshell/ash_test/ash-misc/break3.tests2
-rw-r--r--shell/ash_test/ash-misc/break4.right6
-rwxr-xr-xshell/ash_test/ash-misc/break4.tests12
-rw-r--r--shell/ash_test/ash-misc/break5.right13
-rwxr-xr-xshell/ash_test/ash-misc/break5.tests4
-rw-r--r--shell/ash_test/ash-misc/builtin1.right2
-rwxr-xr-xshell/ash_test/ash-misc/builtin1.tests6
-rw-r--r--shell/ash_test/ash-misc/case1.right22
-rwxr-xr-xshell/ash_test/ash-misc/case1.tests40
-rw-r--r--shell/ash_test/ash-misc/colon.right2
-rwxr-xr-xshell/ash_test/ash-misc/colon.tests5
-rw-r--r--shell/ash_test/ash-misc/compound.right14
-rwxr-xr-xshell/ash_test/ash-misc/compound.tests21
-rw-r--r--shell/ash_test/ash-misc/continue1.right8
-rwxr-xr-xshell/ash_test/ash-misc/continue1.tests4
-rw-r--r--shell/ash_test/ash-misc/continue2.right1
-rwxr-xr-xshell/ash_test/ash-misc/continue2.tests3
-rw-r--r--shell/ash_test/ash-misc/continue3.right2
-rwxr-xr-xshell/ash_test/ash-misc/continue3.tests3
-rw-r--r--shell/ash_test/ash-misc/empty_for.right1
-rwxr-xr-xshell/ash_test/ash-misc/empty_for.tests3
-rw-r--r--shell/ash_test/ash-misc/empty_for2.right4
-rwxr-xr-xshell/ash_test/ash-misc/empty_for2.tests6
-rw-r--r--shell/ash_test/ash-misc/errexit1.right (renamed from shell/msh_test/msh-execution/many_continues.right)0
-rwxr-xr-xshell/ash_test/ash-misc/errexit1.tests5
-rw-r--r--shell/ash_test/ash-misc/eval1.right1
-rwxr-xr-xshell/ash_test/ash-misc/eval1.tests4
-rw-r--r--shell/ash_test/ash-misc/eval2.right3
-rwxr-xr-xshell/ash_test/ash-misc/eval2.tests4
-rw-r--r--shell/ash_test/ash-misc/exit1.right1
-rwxr-xr-xshell/ash_test/ash-misc/exit1.tests4
-rw-r--r--shell/ash_test/ash-misc/exitcode1.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode1.tests2
-rw-r--r--shell/ash_test/ash-misc/exitcode_EACCES.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_EACCES.tests (renamed from shell/msh_test/msh-execution/exitcode_EACCES.tests)0
-rw-r--r--shell/ash_test/ash-misc/exitcode_ENOENT.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_ENOENT.tests (renamed from shell/msh_test/msh-execution/exitcode_ENOENT.tests)0
-rw-r--r--shell/ash_test/ash-misc/for_with_bslashes.right8
-rwxr-xr-xshell/ash_test/ash-misc/for_with_bslashes.tests10
-rw-r--r--shell/ash_test/ash-misc/for_with_keywords.right4
-rwxr-xr-xshell/ash_test/ash-misc/for_with_keywords.tests2
-rw-r--r--shell/ash_test/ash-misc/func6.right2
-rwxr-xr-xshell/ash_test/ash-misc/func6.tests11
-rw-r--r--shell/ash_test/ash-misc/if_false_exitcode.right1
-rwxr-xr-xshell/ash_test/ash-misc/if_false_exitcode.tests2
-rw-r--r--shell/ash_test/ash-misc/nommu1.right7
-rwxr-xr-xshell/ash_test/ash-misc/nommu1.tests12
-rw-r--r--shell/ash_test/ash-misc/nommu2.right5
-rwxr-xr-xshell/ash_test/ash-misc/nommu2.tests5
-rw-r--r--shell/ash_test/ash-misc/nommu3.right2
-rwxr-xr-xshell/ash_test/ash-misc/nommu3.tests15
-rw-r--r--shell/ash_test/ash-misc/opts1.right2
-rwxr-xr-xshell/ash_test/ash-misc/opts1.tests5
-rw-r--r--shell/ash_test/ash-misc/pid.right1
-rwxr-xr-xshell/ash_test/ash-misc/pid.tests1
-rw-r--r--shell/ash_test/ash-misc/pipefail.right40
-rwxr-xr-xshell/ash_test/ash-misc/pipefail.tests45
-rw-r--r--shell/ash_test/ash-misc/read.right (renamed from shell/msh_test/msh-bugs/read.right)0
-rwxr-xr-xshell/ash_test/ash-misc/read.tests (renamed from shell/msh_test/msh-bugs/read.tests)0
-rw-r--r--shell/ash_test/ash-misc/return1.right1
-rwxr-xr-xshell/ash_test/ash-misc/return1.tests4
-rw-r--r--shell/ash_test/ash-misc/shift.right (renamed from shell/msh_test/msh-bugs/shift.right)0
-rwxr-xr-xshell/ash_test/ash-misc/shift.tests (renamed from shell/msh_test/msh-bugs/shift.tests)0
-rw-r--r--shell/ash_test/ash-misc/sigint1.right1
-rwxr-xr-xshell/ash_test/ash-misc/sigint1.tests41
-rw-r--r--shell/ash_test/ash-misc/source3.right2
-rwxr-xr-xshell/ash_test/ash-misc/source3.tests6
-rw-r--r--shell/ash_test/ash-misc/source5.right4
-rwxr-xr-xshell/ash_test/ash-misc/source5.tests8
-rw-r--r--shell/ash_test/ash-misc/unicode1.right (renamed from shell/msh_test/msh-misc/tick.right)1
-rwxr-xr-xshell/ash_test/ash-misc/unicode1.tests13
-rw-r--r--shell/ash_test/ash-misc/until1.right3
-rwxr-xr-xshell/ash_test/ash-misc/until1.tests11
-rw-r--r--shell/ash_test/ash-misc/while1.right1
-rwxr-xr-xshell/ash_test/ash-misc/while1.tests2
-rw-r--r--shell/ash_test/ash-misc/while2.right2
-rwxr-xr-xshell/ash_test/ash-misc/while2.tests2
-rw-r--r--shell/ash_test/ash-misc/while4.right1
-rwxr-xr-xshell/ash_test/ash-misc/while4.tests6
-rw-r--r--shell/ash_test/ash-misc/while_in_subshell.right1
-rwxr-xr-xshell/ash_test/ash-misc/while_in_subshell.tests2
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests2
-rw-r--r--shell/ash_test/ash-quoting/dollar_squote_bash2.right6
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_squote_bash2.tests10
-rwxr-xr-xshell/ash_test/ash-read/read_r.tests6
-rw-r--r--shell/ash_test/ash-redir/redir1.right (renamed from shell/hush_test/hush-misc/redir1.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir1.tests (renamed from shell/hush_test/hush-misc/redir1.tests)0
-rwxr-xr-xshell/ash_test/ash-redir/redir7.tests6
-rwxr-xr-xshell/ash_test/ash-redir/redir8.tests8
-rw-r--r--shell/ash_test/ash-redir/redir_escapednum.right (renamed from shell/hush_test/hush-misc/redir2.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_escapednum.tests (renamed from shell/hush_test/hush-misc/redir2.tests)0
-rw-r--r--shell/ash_test/ash-redir/redir_expand.right (renamed from shell/hush_test/hush-misc/redir4.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_expand.tests (renamed from shell/hush_test/hush-misc/redir4.tests)0
-rw-r--r--shell/ash_test/ash-redir/redir_multi.right (renamed from shell/hush_test/hush-misc/redir6.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_multi.tests (renamed from shell/hush_test/hush-misc/redir6.tests)0
-rw-r--r--shell/ash_test/ash-redir/redir_script.right1
-rwxr-xr-xshell/ash_test/ash-redir/redir_script.tests29
-rw-r--r--shell/ash_test/ash-redir/redir_space.right (renamed from shell/hush_test/hush-parsing/redir_space.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_space.tests (renamed from shell/hush_test/hush-parsing/redir_space.tests)0
-rw-r--r--shell/ash_test/ash-signals/continue_and_trap1.right1
-rwxr-xr-xshell/ash_test/ash-signals/continue_and_trap1.tests7
-rw-r--r--shell/ash_test/ash-signals/return_in_trap1.right4
-rwxr-xr-xshell/ash_test/ash-signals/return_in_trap1.tests18
-rw-r--r--shell/ash_test/ash-signals/save-ret.right (renamed from shell/hush_test/hush-trap/save-ret.right)0
-rwxr-xr-xshell/ash_test/ash-signals/save-ret.tests (renamed from shell/hush_test/hush-trap/save-ret.tests)0
-rwxr-xr-xshell/ash_test/ash-signals/signal1.tests2
-rw-r--r--shell/ash_test/ash-signals/sigquit_exec.right2
-rwxr-xr-xshell/ash_test/ash-signals/sigquit_exec.tests4
-rw-r--r--shell/ash_test/ash-vars/empty.right3
-rwxr-xr-xshell/ash_test/ash-vars/empty.tests5
-rw-r--r--shell/ash_test/ash-vars/glob_and_vars.right1
-rwxr-xr-xshell/ash_test/ash-vars/glob_and_vars.tests2
-rw-r--r--shell/ash_test/ash-vars/param_expand_len.right9
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_len.tests17
-rw-r--r--shell/ash_test/ash-vars/param_glob.right4
-rwxr-xr-xshell/ash_test/ash-vars/param_glob.tests9
-rw-r--r--shell/ash_test/ash-vars/param_subshell.right7
-rwxr-xr-xshell/ash_test/ash-vars/param_subshell.tests15
-rw-r--r--shell/ash_test/ash-vars/star.right (renamed from shell/msh_test/msh-vars/star.right)0
-rwxr-xr-xshell/ash_test/ash-vars/star.tests (renamed from shell/msh_test/msh-vars/star.tests)0
-rw-r--r--shell/ash_test/ash-vars/var1.right10
-rwxr-xr-xshell/ash_test/ash-vars/var1.tests19
-rw-r--r--shell/ash_test/ash-vars/var2.right3
-rwxr-xr-xshell/ash_test/ash-vars/var2.tests5
-rw-r--r--shell/ash_test/ash-vars/var4.right1
-rwxr-xr-xshell/ash_test/ash-vars/var4.tests1
-rw-r--r--shell/ash_test/ash-vars/var5.right6
-rwxr-xr-xshell/ash_test/ash-vars/var5.tests14
-rw-r--r--shell/ash_test/ash-vars/var_bash1a.right6
-rwxr-xr-xshell/ash_test/ash-vars/var_bash1a.tests11
-rw-r--r--shell/ash_test/ash-vars/var_bash4.right25
-rwxr-xr-xshell/ash_test/ash-vars/var_bash4.tests52
-rw-r--r--shell/ash_test/ash-vars/var_bash5.right13
-rwxr-xr-xshell/ash_test/ash-vars/var_bash5.tests24
-rw-r--r--shell/ash_test/ash-vars/var_expand_in_assign.right (renamed from shell/msh_test/msh-bugs/var_expand_in_assign.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_expand_in_assign.tests (renamed from shell/msh_test/msh-bugs/var_expand_in_assign.tests)0
-rw-r--r--shell/ash_test/ash-vars/var_expand_in_redir.right (renamed from shell/msh_test/msh-bugs/var_expand_in_redir.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_expand_in_redir.tests (renamed from shell/msh_test/msh-bugs/var_expand_in_redir.tests)0
-rw-r--r--shell/ash_test/ash-vars/var_expand_on_ifs.right9
-rwxr-xr-xshell/ash_test/ash-vars/var_expand_on_ifs.tests11
-rw-r--r--shell/ash_test/ash-vars/var_in_pipes.right6
-rwxr-xr-xshell/ash_test/ash-vars/var_in_pipes.tests7
-rw-r--r--shell/ash_test/ash-vars/var_leaks.right (renamed from shell/msh_test/msh-parsing/argv0.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_leaks.tests14
-rw-r--r--shell/ash_test/ash-vars/var_posix1.right30
-rwxr-xr-xshell/ash_test/ash-vars/var_posix1.tests37
-rw-r--r--shell/ash_test/ash-vars/var_serial.right5
-rwxr-xr-xshell/ash_test/ash-vars/var_serial.tests22
-rw-r--r--shell/ash_test/ash-vars/var_subst_in_for.right (renamed from shell/msh_test/msh-vars/var_subst_in_for.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_subst_in_for.tests (renamed from shell/msh_test/msh-vars/var_subst_in_for.tests)2
-rw-r--r--shell/ash_test/ash-vars/var_unbackslash.right11
-rwxr-xr-xshell/ash_test/ash-vars/var_unbackslash.tests23
-rw-r--r--shell/ash_test/ash-vars/var_unbackslash1.right7
-rwxr-xr-xshell/ash_test/ash-vars/var_unbackslash1.tests35
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs1.right41
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs1.tests42
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs2.right3
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs2.tests13
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs3.right12
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs3.tests5
-rw-r--r--shell/hush.c1139
-rw-r--r--shell/hush_test/hush-bugs/var3.right5
-rwxr-xr-xshell/hush_test/hush-bugs/var3.tests1
-rwxr-xr-xshell/hush_test/hush-glob/bash_brace1.tests2
-rw-r--r--shell/hush_test/hush-glob/glob_dir.right19
-rwxr-xr-xshell/hush_test/hush-glob/glob_dir.tests25
-rw-r--r--shell/hush_test/hush-heredoc/heredoc1.right (renamed from shell/hush_test/hush-misc/heredoc1.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc1.tests (renamed from shell/hush_test/hush-misc/heredoc1.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc2.right2
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc2.tests7
-rw-r--r--shell/hush_test/hush-heredoc/heredoc3.right (renamed from shell/hush_test/hush-misc/heredoc3.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc3.tests (renamed from shell/hush_test/hush-misc/heredoc3.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc4.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc4.tests3
-rw-r--r--shell/hush_test/hush-heredoc/heredoc5.right9
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc5.tests12
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_backslash1.right (renamed from shell/hush_test/hush-misc/heredoc_backslash1.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_backslash1.tests (renamed from shell/hush_test/hush-misc/heredoc_backslash1.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_huge.right3
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_huge.tests9
-rw-r--r--shell/hush_test/hush-invert/invert.right10
-rwxr-xr-xshell/hush_test/hush-invert/invert.tests19
-rw-r--r--shell/hush_test/hush-misc/assignment2.right2
-rw-r--r--shell/hush_test/hush-misc/assignment2.rigth2
-rwxr-xr-xshell/hush_test/hush-misc/assignment2.tests1
-rw-r--r--shell/hush_test/hush-misc/eval1.right1
-rwxr-xr-xshell/hush_test/hush-misc/eval1.tests4
-rw-r--r--shell/hush_test/hush-misc/eval2.right3
-rwxr-xr-xshell/hush_test/hush-misc/eval2.tests4
-rw-r--r--shell/hush_test/hush-misc/exitcode1.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode1.tests2
-rw-r--r--shell/hush_test/hush-misc/exitcode_EACCES.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_EACCES.tests2
-rw-r--r--shell/hush_test/hush-misc/exitcode_ENOENT.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_ENOENT.tests2
-rw-r--r--shell/hush_test/hush-misc/func6.right2
-rwxr-xr-xshell/hush_test/hush-misc/func6.tests11
-rw-r--r--shell/hush_test/hush-misc/last_amp.right2
-rwxr-xr-xshell/hush_test/hush-misc/last_amp.tests8
-rw-r--r--shell/hush_test/hush-misc/local1.right4
-rwxr-xr-xshell/hush_test/hush-misc/local1.tests11
-rw-r--r--shell/hush_test/hush-misc/nulltick1.right3
-rwxr-xr-xshell/hush_test/hush-misc/nulltick1.tests3
-rw-r--r--shell/hush_test/hush-misc/source1.right7
-rwxr-xr-xshell/hush_test/hush-misc/source1.tests15
-rw-r--r--shell/hush_test/hush-misc/source2.right5
-rwxr-xr-xshell/hush_test/hush-misc/source2.tests11
-rw-r--r--shell/hush_test/hush-misc/source3.right2
-rwxr-xr-xshell/hush_test/hush-misc/source3.tests6
-rw-r--r--shell/hush_test/hush-misc/source4.right5
-rwxr-xr-xshell/hush_test/hush-misc/source4.tests10
-rw-r--r--shell/hush_test/hush-misc/source5.right4
-rwxr-xr-xshell/hush_test/hush-misc/source5.tests8
-rw-r--r--shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right10
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests21
-rwxr-xr-xshell/hush_test/hush-read/read_r.tests6
-rw-r--r--shell/hush_test/hush-redir/redir1.right12
-rwxr-xr-xshell/hush_test/hush-redir/redir1.tests40
-rw-r--r--shell/hush_test/hush-redir/redir2.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir2.tests5
-rw-r--r--shell/hush_test/hush-redir/redir3.right (renamed from shell/hush_test/hush-misc/redir3.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir3.tests (renamed from shell/hush_test/hush-misc/redir3.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir4.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir4.tests72
-rw-r--r--shell/hush_test/hush-redir/redir5.right (renamed from shell/hush_test/hush-misc/redir5.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir5.tests (renamed from shell/hush_test/hush-misc/redir5.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir6.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir6.tests3
-rw-r--r--shell/hush_test/hush-redir/redir7.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir7.tests12
-rw-r--r--shell/hush_test/hush-redir/redir8.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir8.tests15
-rw-r--r--shell/hush_test/hush-redir/redir9.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir9.tests4
-rw-r--r--shell/hush_test/hush-redir/redirA.right2
-rwxr-xr-xshell/hush_test/hush-redir/redirA.tests11
-rw-r--r--shell/hush_test/hush-redir/redir_escapednum.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir_escapednum.tests2
-rw-r--r--shell/hush_test/hush-redir/redir_expand.right18
-rwxr-xr-xshell/hush_test/hush-redir/redir_expand.tests85
-rw-r--r--shell/hush_test/hush-redir/redir_multi.right4
-rwxr-xr-xshell/hush_test/hush-redir/redir_multi.tests5
-rw-r--r--shell/hush_test/hush-redir/redir_script.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir_script.tests29
-rw-r--r--shell/hush_test/hush-redir/redir_space.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir_space.tests6
-rw-r--r--shell/hush_test/hush-signals/catch.right (renamed from shell/hush_test/hush-trap/catch.right)0
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests (renamed from shell/hush_test/hush-trap/catch.tests)0
-rw-r--r--shell/hush_test/hush-signals/continue_and_trap1.right1
-rwxr-xr-xshell/hush_test/hush-signals/continue_and_trap1.tests7
-rw-r--r--shell/hush_test/hush-signals/exit.right (renamed from shell/hush_test/hush-trap/exit.right)0
-rwxr-xr-xshell/hush_test/hush-signals/exit.tests (renamed from shell/hush_test/hush-trap/exit.tests)0
-rw-r--r--shell/hush_test/hush-signals/reap1.right1
-rwxr-xr-xshell/hush_test/hush-signals/reap1.tests14
-rw-r--r--shell/hush_test/hush-signals/return_in_trap1.right4
-rwxr-xr-xshell/hush_test/hush-signals/return_in_trap1.tests18
-rw-r--r--shell/hush_test/hush-signals/save-ret.right2
-rwxr-xr-xshell/hush_test/hush-signals/save-ret.tests4
-rw-r--r--shell/hush_test/hush-signals/savetrap.right (renamed from shell/hush_test/hush-trap/savetrap.right)0
-rwxr-xr-xshell/hush_test/hush-signals/savetrap.tests (renamed from shell/hush_test/hush-trap/savetrap.tests)0
-rw-r--r--shell/hush_test/hush-signals/sigint1.right1
-rwxr-xr-xshell/hush_test/hush-signals/sigint1.tests41
-rw-r--r--shell/hush_test/hush-signals/signal2.right3
-rwxr-xr-xshell/hush_test/hush-signals/signal2.tests18
-rw-r--r--shell/hush_test/hush-signals/signal3.right4
-rwxr-xr-xshell/hush_test/hush-signals/signal3.tests17
-rw-r--r--shell/hush_test/hush-signals/signal5.right12
-rwxr-xr-xshell/hush_test/hush-signals/signal5.tests14
-rw-r--r--shell/hush_test/hush-signals/signal6.right2
-rwxr-xr-xshell/hush_test/hush-signals/signal6.tests2
-rw-r--r--shell/hush_test/hush-signals/signal7.right (renamed from shell/hush_test/hush-trap/signal7.right)0
-rwxr-xr-xshell/hush_test/hush-signals/signal7.tests (renamed from shell/hush_test/hush-trap/signal7.tests)0
-rw-r--r--shell/hush_test/hush-signals/signal_read1.right (renamed from shell/hush_test/hush-trap/signal_read1.right)0
-rwxr-xr-xshell/hush_test/hush-signals/signal_read1.tests (renamed from shell/hush_test/hush-trap/signal_read1.tests)0
-rw-r--r--shell/hush_test/hush-signals/signal_read2.right (renamed from shell/hush_test/hush-trap/signal_read2.right)0
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests (renamed from shell/hush_test/hush-trap/signal_read2.tests)0
-rw-r--r--shell/hush_test/hush-signals/sigquit_exec.right2
-rwxr-xr-xshell/hush_test/hush-signals/sigquit_exec.tests4
-rw-r--r--shell/hush_test/hush-signals/subshell.right (renamed from shell/hush_test/hush-trap/subshell.right)0
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests (renamed from shell/hush_test/hush-trap/subshell.tests)0
-rw-r--r--shell/hush_test/hush-signals/usage.right (renamed from shell/hush_test/hush-trap/usage.right)0
-rwxr-xr-xshell/hush_test/hush-signals/usage.tests (renamed from shell/hush_test/hush-trap/usage.tests)0
-rw-r--r--shell/hush_test/hush-standalone/noexec_gets_no_env.right4
-rwxr-xr-xshell/hush_test/hush-standalone/noexec_gets_no_env.tests5
-rw-r--r--shell/hush_test/hush-standalone/nofork_trashes_getopt.right1
-rwxr-xr-xshell/hush_test/hush-standalone/nofork_trashes_getopt.tests6
-rw-r--r--shell/hush_test/hush-standalone/var_standalone1.right1
-rwxr-xr-xshell/hush_test/hush-standalone/var_standalone1.tests2
-rw-r--r--shell/hush_test/hush-vars/var3.right7
-rwxr-xr-xshell/hush_test/hush-vars/var3.tests5
-rw-r--r--shell/hush_test/hush-vars/var4.right1
-rwxr-xr-xshell/hush_test/hush-vars/var4.tests1
-rw-r--r--shell/hush_test/hush-vars/var5.right6
-rwxr-xr-xshell/hush_test/hush-vars/var5.tests14
-rw-r--r--shell/hush_test/hush-vars/var6.right2
-rwxr-xr-xshell/hush_test/hush-vars/var6.tests4
-rw-r--r--shell/hush_test/hush-vars/var_unbackslash1.right7
-rwxr-xr-xshell/hush_test/hush-vars/var_unbackslash1.tests35
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs1.right41
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs1.tests42
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs2.right3
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs2.tests13
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs3.right12
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs3.tests5
-rwxr-xr-xshell/hush_test/run-all3
-rw-r--r--shell/msh_test/msh-bugs/noeol3.right1
-rwxr-xr-xshell/msh_test/msh-bugs/noeol3.tests2
-rw-r--r--shell/msh_test/msh-bugs/process_subst.right3
-rwxr-xr-xshell/msh_test/msh-bugs/process_subst.tests3
-rw-r--r--shell/msh_test/msh-bugs/starquoted.right8
-rwxr-xr-xshell/msh_test/msh-bugs/starquoted.tests8
-rw-r--r--shell/msh_test/msh-bugs/syntax_err.right2
-rwxr-xr-xshell/msh_test/msh-bugs/syntax_err.tests3
-rw-r--r--shell/msh_test/msh-execution/exitcode_EACCES.right2
-rw-r--r--shell/msh_test/msh-execution/exitcode_ENOENT.right2
-rwxr-xr-xshell/msh_test/msh-execution/many_continues.tests15
-rw-r--r--shell/msh_test/msh-execution/nested_break.right8
-rwxr-xr-xshell/msh_test/msh-execution/nested_break.tests17
-rwxr-xr-xshell/msh_test/msh-misc/tick.tests4
-rwxr-xr-xshell/msh_test/msh-parsing/argv0.tests4
-rw-r--r--shell/msh_test/msh-parsing/noeol.right1
-rwxr-xr-xshell/msh_test/msh-parsing/noeol.tests2
-rw-r--r--shell/msh_test/msh-parsing/noeol2.right1
-rwxr-xr-xshell/msh_test/msh-parsing/noeol2.tests7
-rw-r--r--shell/msh_test/msh-parsing/quote1.right1
-rwxr-xr-xshell/msh_test/msh-parsing/quote1.tests2
-rw-r--r--shell/msh_test/msh-parsing/quote2.right1
-rwxr-xr-xshell/msh_test/msh-parsing/quote2.tests2
-rw-r--r--shell/msh_test/msh-parsing/quote3.right3
-rwxr-xr-xshell/msh_test/msh-parsing/quote3.tests8
-rw-r--r--shell/msh_test/msh-parsing/quote4.right1
-rwxr-xr-xshell/msh_test/msh-parsing/quote4.tests2
-rw-r--r--shell/msh_test/msh-vars/var.right4
-rwxr-xr-xshell/msh_test/msh-vars/var.tests9
-rwxr-xr-xshell/msh_test/run-all64
-rw-r--r--shell/shell_common.c1
-rw-r--r--shell/shell_common.h2
-rw-r--r--testsuite/gzip/gzip-compression-levels5
-rwxr-xr-xtestsuite/md5sum.tests22
-rwxr-xr-xtestsuite/sed.tests6
-rwxr-xr-xtestsuite/sha3sum.tests2
-rw-r--r--util-linux/fdisk.c167
-rw-r--r--util-linux/mdev.c88
-rw-r--r--util-linux/mount.c28
-rw-r--r--util-linux/unshare.c4
-rw-r--r--util-linux/volume_id/get_devname.c6
-rw-r--r--util-linux/volume_id/ubifs.c125
-rw-r--r--util-linux/volume_id/volume_id.c3
-rw-r--r--util-linux/volume_id/volume_id_internal.h2
456 files changed, 5694 insertions, 2350 deletions
diff --git a/AUTHORS b/AUTHORS
index 0908fc719..fa58697f7 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -146,6 +146,10 @@ Rob Sullivan <cogito.ergo.cogito@gmail.com>
146Linus Torvalds 146Linus Torvalds
147 mkswap, fsck.minix, mkfs.minix 147 mkswap, fsck.minix, mkfs.minix
148 148
149Linus Walleij
150 fbset and fbsplash config RGBA parsing
151 rewrite of mdev helper to create devices from /sys/dev
152
149Mark Whitley <markw@codepoet.org> 153Mark Whitley <markw@codepoet.org>
150 grep, sed, cut, xargs(previous), 154 grep, sed, cut, xargs(previous),
151 style-guide, new-applet-HOWTO, bug fixes, etc. 155 style-guide, new-applet-HOWTO, bug fixes, etc.
diff --git a/applets/Kbuild.src b/applets/Kbuild.src
index b61239948..3aedbbffe 100644
--- a/applets/Kbuild.src
+++ b/applets/Kbuild.src
@@ -29,7 +29,7 @@ applets/applets.o: include/usage_compressed.h include/applet_tables.h
29 29
30applets/applet_tables: .config include/applets.h 30applets/applet_tables: .config include/applets.h
31applets/usage: .config include/applets.h 31applets/usage: .config include/applets.h
32applets/usage_pod: .config include/applet_tables.h include/applets.h 32applets/usage_pod: .config include/applets.h include/applet_tables.h
33 33
34quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h 34quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
35 cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets 35 cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets
@@ -37,11 +37,21 @@ quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
37include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed 37include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed
38 $(call cmd,gen_usage_compressed) 38 $(call cmd,gen_usage_compressed)
39 39
40quiet_cmd_gen_applet_tables = GEN include/applet_tables.h 40quiet_cmd_gen_applet_tables = GEN include/applet_tables.h include/NUM_APPLETS.h
41 cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h 41 cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h
42 42
43include/applet_tables.h: applets/applet_tables 43include/NUM_APPLETS.h: applets/applet_tables
44 $(call cmd,gen_applet_tables) 44 $(call cmd,gen_applet_tables)
45 45
46include/NUM_APPLETS.h: applets/applet_tables 46# In fact, include/applet_tables.h depends only on applets/applet_tables,
47# and is generated by it. But specifying only it can run
48# applets/applet_tables twice, possibly in parallel.
49# We say that it also needs NUM_APPLETS.h
50#
51# Unfortunately, we need to list the same command,
52# and it can be executed twice (sequentially).
53# The alternative is to not list any command,
54# and then if include/applet_tables.h is deleted, it won't be rebuilt.
55#
56include/applet_tables.h: include/NUM_APPLETS.h applets/applet_tables
47 $(call cmd,gen_applet_tables) 57 $(call cmd,gen_applet_tables)
diff --git a/applets/applet_tables.c b/applets/applet_tables.c
index 7894b2c33..9a2aa5329 100644
--- a/applets/applet_tables.c
+++ b/applets/applet_tables.c
@@ -143,7 +143,9 @@ int main(int argc, char **argv)
143 printf("};\n"); 143 printf("};\n");
144 printf("#endif\n\n"); 144 printf("#endif\n\n");
145 145
146#if ENABLE_FEATURE_PREFER_APPLETS 146#if ENABLE_FEATURE_PREFER_APPLETS \
147 || ENABLE_FEATURE_SH_STANDALONE \
148 || ENABLE_FEATURE_SH_NOFORK
147 printf("const uint8_t applet_flags[] ALIGN1 = {\n"); 149 printf("const uint8_t applet_flags[] ALIGN1 = {\n");
148 i = 0; 150 i = 0;
149 while (i < NUM_APPLETS) { 151 while (i < NUM_APPLETS) {
@@ -190,27 +192,28 @@ int main(int argc, char **argv)
190 printf("};\n"); 192 printf("};\n");
191#endif 193#endif
192 //printf("#endif /* SKIP_definitions */\n"); 194 //printf("#endif /* SKIP_definitions */\n");
195
193// printf("\n"); 196// printf("\n");
194// printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN); 197// printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN);
195 198
196 if (argv[2]) { 199 if (argv[2]) {
197 char line_old[80];
198 char line_new[80];
199 FILE *fp; 200 FILE *fp;
201 char line_new[80];
202// char line_old[80];
200 203
201 line_old[0] = 0;
202 fp = fopen(argv[2], "r");
203 if (fp) {
204 fgets(line_old, sizeof(line_old), fp);
205 fclose(fp);
206 }
207 sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS); 204 sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS);
208 if (strcmp(line_old, line_new) != 0) { 205// line_old[0] = 0;
206// fp = fopen(argv[2], "r");
207// if (fp) {
208// fgets(line_old, sizeof(line_old), fp);
209// fclose(fp);
210// }
211// if (strcmp(line_old, line_new) != 0) {
209 fp = fopen(argv[2], "w"); 212 fp = fopen(argv[2], "w");
210 if (!fp) 213 if (!fp)
211 return 1; 214 return 1;
212 fputs(line_new, fp); 215 fputs(line_new, fp);
213 } 216// }
214 } 217 }
215 218
216 return 0; 219 return 0;
diff --git a/archival/cpio.c b/archival/cpio.c
index 3b1550720..540218cb2 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -23,7 +23,7 @@
23//config: cpio has 110 bytes of overheads for every stored file. 23//config: cpio has 110 bytes of overheads for every stored file.
24//config: 24//config:
25//config: This implementation of cpio can extract cpio archives created in the 25//config: This implementation of cpio can extract cpio archives created in the
26//config: "newc" or "crc" format, it cannot create or modify them. 26//config: "newc" or "crc" format.
27//config: 27//config:
28//config: Unless you have a specific application which requires cpio, you 28//config: Unless you have a specific application which requires cpio, you
29//config: should probably say N here. 29//config: should probably say N here.
@@ -51,10 +51,10 @@
51//usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]") 51//usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]")
52//usage: " [EXTR_FILE]..." 52//usage: " [EXTR_FILE]..."
53//usage:#define cpio_full_usage "\n\n" 53//usage:#define cpio_full_usage "\n\n"
54//usage: "Extract or list files from a cpio archive" 54//usage: "Extract (-i) or list (-t) files from a cpio archive"
55//usage: IF_FEATURE_CPIO_O(", or" 55//usage: IF_FEATURE_CPIO_O(", or"
56//usage: "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)") 56//usage: "\ntake file list from stdin and create an archive (-o)"
57//usage: " using file list on stdin" 57//usage: IF_FEATURE_CPIO_P(" or copy files (-p)")
58//usage: ) 58//usage: )
59//usage: "\n" 59//usage: "\n"
60//usage: "\nMain operation mode:" 60//usage: "\nMain operation mode:"
diff --git a/archival/gzip.c b/archival/gzip.c
index 8f1e4ff29..9e0bee815 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -2220,10 +2220,7 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
2220 opt >>= ENABLE_GUNZIP ? 7 : 5; /* drop cfv[dt]qn bits */ 2220 opt >>= ENABLE_GUNZIP ? 7 : 5; /* drop cfv[dt]qn bits */
2221 if (opt == 0) 2221 if (opt == 0)
2222 opt = 1 << 6; /* default: 6 */ 2222 opt = 1 << 6; /* default: 6 */
2223 /* Map 1..3 to 4 */ 2223 opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */
2224 if (opt & 0x7)
2225 opt |= 1 << 4;
2226 opt = ffs(opt >> 3);
2227 max_chain_length = 1 << gzip_level_config[opt].chain_shift; 2224 max_chain_length = 1 << gzip_level_config[opt].chain_shift;
2228 good_match = gzip_level_config[opt].good; 2225 good_match = gzip_level_config[opt].good;
2229 max_lazy_match = gzip_level_config[opt].lazy2 * 2; 2226 max_lazy_match = gzip_level_config[opt].lazy2 * 2;
diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig
index 5f822e598..20c2e1550 100644
--- a/configs/TEST_nommu_defconfig
+++ b/configs/TEST_nommu_defconfig
@@ -760,7 +760,6 @@ CONFIG_TFTP_DEBUG=y
760CONFIG_TRACEROUTE=y 760CONFIG_TRACEROUTE=y
761CONFIG_TRACEROUTE6=y 761CONFIG_TRACEROUTE6=y
762CONFIG_FEATURE_TRACEROUTE_VERBOSE=y 762CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
763CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y
764CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y 763CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y
765CONFIG_UDHCPD=y 764CONFIG_UDHCPD=y
766CONFIG_DHCPRELAY=y 765CONFIG_DHCPRELAY=y
diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig
index c56781e32..845032ea1 100644
--- a/configs/TEST_noprintf_defconfig
+++ b/configs/TEST_noprintf_defconfig
@@ -762,7 +762,6 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
762# CONFIG_TRACEROUTE is not set 762# CONFIG_TRACEROUTE is not set
763# CONFIG_TRACEROUTE6 is not set 763# CONFIG_TRACEROUTE6 is not set
764# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set 764# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
765# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
766# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 765# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
767# CONFIG_TUNCTL is not set 766# CONFIG_TUNCTL is not set
768# CONFIG_FEATURE_TUNCTL_UG is not set 767# CONFIG_FEATURE_TUNCTL_UG is not set
diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig
index 28daa6273..d8c5af4f7 100644
--- a/configs/TEST_rh9_defconfig
+++ b/configs/TEST_rh9_defconfig
@@ -778,7 +778,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
778CONFIG_TRACEROUTE=y 778CONFIG_TRACEROUTE=y
779CONFIG_TRACEROUTE6=y 779CONFIG_TRACEROUTE6=y
780CONFIG_FEATURE_TRACEROUTE_VERBOSE=y 780CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
781# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
782# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 781# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
783CONFIG_TUNCTL=y 782CONFIG_TUNCTL=y
784CONFIG_FEATURE_TUNCTL_UG=y 783CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android2_defconfig b/configs/android2_defconfig
index fbc0da091..857f9f5df 100644
--- a/configs/android2_defconfig
+++ b/configs/android2_defconfig
@@ -814,7 +814,6 @@ CONFIG_TCPSVD=y
814# CONFIG_TRACEROUTE is not set 814# CONFIG_TRACEROUTE is not set
815# CONFIG_TRACEROUTE6 is not set 815# CONFIG_TRACEROUTE6 is not set
816# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set 816# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
817# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
818# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 817# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
819CONFIG_TUNCTL=y 818CONFIG_TUNCTL=y
820CONFIG_FEATURE_TUNCTL_UG=y 819CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android_502_defconfig b/configs/android_502_defconfig
index 7ef1585fb..cd06affab 100644
--- a/configs/android_502_defconfig
+++ b/configs/android_502_defconfig
@@ -959,7 +959,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
959CONFIG_TRACEROUTE=y 959CONFIG_TRACEROUTE=y
960CONFIG_TRACEROUTE6=y 960CONFIG_TRACEROUTE6=y
961CONFIG_FEATURE_TRACEROUTE_VERBOSE=y 961CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
962# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
963# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 962# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
964CONFIG_TUNCTL=y 963CONFIG_TUNCTL=y
965CONFIG_FEATURE_TUNCTL_UG=y 964CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android_defconfig b/configs/android_defconfig
index 4e0224207..f1ddc45d4 100644
--- a/configs/android_defconfig
+++ b/configs/android_defconfig
@@ -843,7 +843,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
843CONFIG_TRACEROUTE=y 843CONFIG_TRACEROUTE=y
844# CONFIG_TRACEROUTE6 is not set 844# CONFIG_TRACEROUTE6 is not set
845CONFIG_FEATURE_TRACEROUTE_VERBOSE=y 845CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
846# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
847# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 846# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
848CONFIG_TUNCTL=y 847CONFIG_TUNCTL=y
849CONFIG_FEATURE_TUNCTL_UG=y 848CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android_ndk_defconfig b/configs/android_ndk_defconfig
index d657d33e9..18651fd7c 100644
--- a/configs/android_ndk_defconfig
+++ b/configs/android_ndk_defconfig
@@ -869,7 +869,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
869CONFIG_TRACEROUTE=y 869CONFIG_TRACEROUTE=y
870# CONFIG_TRACEROUTE6 is not set 870# CONFIG_TRACEROUTE6 is not set
871CONFIG_FEATURE_TRACEROUTE_VERBOSE=y 871CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
872# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
873# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 872# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
874CONFIG_TUNCTL=y 873CONFIG_TUNCTL=y
875CONFIG_FEATURE_TUNCTL_UG=y 874CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig
index 38d580ad1..dd7c21edb 100644
--- a/configs/cygwin_defconfig
+++ b/configs/cygwin_defconfig
@@ -814,7 +814,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
814# CONFIG_TRACEROUTE is not set 814# CONFIG_TRACEROUTE is not set
815# CONFIG_TRACEROUTE6 is not set 815# CONFIG_TRACEROUTE6 is not set
816# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set 816# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
817# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
818# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 817# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
819# CONFIG_TUNCTL is not set 818# CONFIG_TUNCTL is not set
820# CONFIG_FEATURE_TUNCTL_UG is not set 819# CONFIG_FEATURE_TUNCTL_UG is not set
diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig
index ae62f1389..265ab1307 100644
--- a/configs/freebsd_defconfig
+++ b/configs/freebsd_defconfig
@@ -795,7 +795,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
795# CONFIG_TRACEROUTE is not set 795# CONFIG_TRACEROUTE is not set
796# CONFIG_TRACEROUTE6 is not set 796# CONFIG_TRACEROUTE6 is not set
797# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set 797# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
798# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
799# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 798# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
800# CONFIG_TUNCTL is not set 799# CONFIG_TUNCTL is not set
801# CONFIG_FEATURE_TUNCTL_UG is not set 800# CONFIG_FEATURE_TUNCTL_UG is not set
diff --git a/coreutils/Config.src b/coreutils/Config.src
index 619c2efe8..b9dde1209 100644
--- a/coreutils/Config.src
+++ b/coreutils/Config.src
@@ -328,12 +328,6 @@ config FEATURE_LS_COLOR_IS_DEFAULT
328 configurable, and the output may not be legible on 328 configurable, and the output may not be legible on
329 many output screens. 329 many output screens.
330 330
331config MD5SUM
332 bool "md5sum"
333 default y
334 help
335 md5sum is used to print or check MD5 checksums.
336
337config MKDIR 331config MKDIR
338 bool "mkdir" 332 bool "mkdir"
339 default y 333 default y
@@ -458,30 +452,6 @@ config SEQ
458 help 452 help
459 print a sequence of numbers 453 print a sequence of numbers
460 454
461config SHA1SUM
462 bool "sha1sum"
463 default y
464 help
465 Compute and check SHA1 message digest
466
467config SHA256SUM
468 bool "sha256sum"
469 default y
470 help
471 Compute and check SHA256 message digest
472
473config SHA512SUM
474 bool "sha512sum"
475 default y
476 help
477 Compute and check SHA512 message digest
478
479config SHA3SUM
480 bool "sha3sum"
481 default y
482 help
483 Compute and check SHA3 (512-bit) message digest
484
485config SLEEP 455config SLEEP
486 bool "sleep" 456 bool "sleep"
487 default y 457 default y
@@ -731,17 +701,4 @@ config FEATURE_HUMAN_READABLE
731 help 701 help
732 Allow df, du, and ls to have human readable output. 702 Allow df, du, and ls to have human readable output.
733 703
734comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum"
735 depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
736
737config FEATURE_MD5_SHA1_SUM_CHECK
738 bool "Enable -c, -s and -w options"
739 default y
740 depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
741 help
742 Enabling the -c options allows files to be checked
743 against pre-calculated hash values.
744
745 -s and -w are useful options when verifying checksums.
746
747endmenu 704endmenu
diff --git a/coreutils/Kbuild.src b/coreutils/Kbuild.src
index 4ec075ac6..5a64fee35 100644
--- a/coreutils/Kbuild.src
+++ b/coreutils/Kbuild.src
@@ -41,7 +41,6 @@ lib-$(CONFIG_LN) += ln.o
41lib-$(CONFIG_LOGNAME) += logname.o 41lib-$(CONFIG_LOGNAME) += logname.o
42lib-$(CONFIG_LS) += ls.o 42lib-$(CONFIG_LS) += ls.o
43lib-$(CONFIG_FTPD) += ls.o 43lib-$(CONFIG_FTPD) += ls.o
44lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
45lib-$(CONFIG_MKDIR) += mkdir.o 44lib-$(CONFIG_MKDIR) += mkdir.o
46lib-$(CONFIG_MKFIFO) += mkfifo.o 45lib-$(CONFIG_MKFIFO) += mkfifo.o
47lib-$(CONFIG_MKNOD) += mknod.o 46lib-$(CONFIG_MKNOD) += mknod.o
@@ -58,10 +57,6 @@ lib-$(CONFIG_REALPATH) += realpath.o
58lib-$(CONFIG_RM) += rm.o 57lib-$(CONFIG_RM) += rm.o
59lib-$(CONFIG_RMDIR) += rmdir.o 58lib-$(CONFIG_RMDIR) += rmdir.o
60lib-$(CONFIG_SEQ) += seq.o 59lib-$(CONFIG_SEQ) += seq.o
61lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
62lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
63lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
64lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
65lib-$(CONFIG_SLEEP) += sleep.o 60lib-$(CONFIG_SLEEP) += sleep.o
66lib-$(CONFIG_SPLIT) += split.o 61lib-$(CONFIG_SPLIT) += split.o
67lib-$(CONFIG_SORT) += sort.o 62lib-$(CONFIG_SORT) += sort.o
diff --git a/coreutils/expr.c b/coreutils/expr.c
index ce6b2d189..efc435443 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -114,7 +114,7 @@ static VALUE *int_value(arith_t i)
114 VALUE *v; 114 VALUE *v;
115 115
116 v = xzalloc(sizeof(VALUE)); 116 v = xzalloc(sizeof(VALUE));
117 if (INTEGER) /* otherwise xzaaloc did it already */ 117 if (INTEGER) /* otherwise xzalloc did it already */
118 v->type = INTEGER; 118 v->type = INTEGER;
119 v->u.i = i; 119 v->u.i = i;
120 return v; 120 return v;
@@ -127,7 +127,7 @@ static VALUE *str_value(const char *s)
127 VALUE *v; 127 VALUE *v;
128 128
129 v = xzalloc(sizeof(VALUE)); 129 v = xzalloc(sizeof(VALUE));
130 if (STRING) /* otherwise xzaaloc did it already */ 130 if (STRING) /* otherwise xzalloc did it already */
131 v->type = STRING; 131 v->type = STRING;
132 v->u.s = xstrdup(s); 132 v->u.s = xstrdup(s);
133 return v; 133 return v;
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c
index 1a5342e87..783f44027 100644
--- a/coreutils/md5_sha1_sum.c
+++ b/coreutils/md5_sha1_sum.c
@@ -5,6 +5,60 @@
5 * 5 *
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7 */ 7 */
8//config:config MD5SUM
9//config: bool "md5sum"
10//config: default y
11//config: help
12//config: md5sum is used to print or check MD5 checksums.
13//config:
14//config:config SHA1SUM
15//config: bool "sha1sum"
16//config: default y
17//config: help
18//config: Compute and check SHA1 message digest
19//config:
20//config:config SHA256SUM
21//config: bool "sha256sum"
22//config: default y
23//config: help
24//config: Compute and check SHA256 message digest
25//config:
26//config:config SHA512SUM
27//config: bool "sha512sum"
28//config: default y
29//config: help
30//config: Compute and check SHA512 message digest
31//config:
32//config:config SHA3SUM
33//config: bool "sha3sum"
34//config: default y
35//config: help
36//config: Compute and check SHA3 message digest
37//config:
38//config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum"
39//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
40//config:
41//config:config FEATURE_MD5_SHA1_SUM_CHECK
42//config: bool "Enable -c, -s and -w options"
43//config: default y
44//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
45//config: help
46//config: Enabling the -c options allows files to be checked
47//config: against pre-calculated hash values.
48//config:
49//config: -s and -w are useful options when verifying checksums.
50
51//applet:IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum))
52//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
53//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
54//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
55//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
56
57//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
58//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
59//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
60//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
61//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
8 62
9//usage:#define md5sum_trivial_usage 63//usage:#define md5sum_trivial_usage
10//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." 64//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
@@ -57,15 +111,20 @@
57//usage: ) 111//usage: )
58//usage: 112//usage:
59//usage:#define sha3sum_trivial_usage 113//usage:#define sha3sum_trivial_usage
60//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." 114//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[-a BITS] [FILE]..."
61//usage:#define sha3sum_full_usage "\n\n" 115//usage:#define sha3sum_full_usage "\n\n"
62//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3-512 checksums" 116//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3 checksums"
63//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" 117//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
64//usage: "\n -c Check sums against list in FILEs" 118//usage: "\n -c Check sums against list in FILEs"
65//usage: "\n -s Don't output anything, status code shows success" 119//usage: "\n -s Don't output anything, status code shows success"
66//usage: "\n -w Warn about improperly formatted checksum lines" 120//usage: "\n -w Warn about improperly formatted checksum lines"
121//usage: "\n -a BITS 224 (default), 256, 384, 512"
67//usage: ) 122//usage: )
68 123
124//FIXME: GNU coreutils 8.25 has no -s option, it has only these two long opts:
125// --quiet don't print OK for each successfully verified file
126// --status don't output anything, status code shows success
127
69#include "libbb.h" 128#include "libbb.h"
70 129
71/* This is a NOEXEC applet. Be very careful! */ 130/* This is a NOEXEC applet. Be very careful! */
@@ -93,7 +152,10 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
93 return (unsigned char *)hex_value; 152 return (unsigned char *)hex_value;
94} 153}
95 154
96static uint8_t *hash_file(const char *filename) 155#if !ENABLE_SHA3SUM
156# define hash_file(f,w) hash_file(f)
157#endif
158static uint8_t *hash_file(const char *filename, unsigned sha3_width)
97{ 159{
98 int src_fd, hash_len, count; 160 int src_fd, hash_len, count;
99 union _ctx_ { 161 union _ctx_ {
@@ -121,27 +183,47 @@ static uint8_t *hash_file(const char *filename)
121 update = (void*)md5_hash; 183 update = (void*)md5_hash;
122 final = (void*)md5_end; 184 final = (void*)md5_end;
123 hash_len = 16; 185 hash_len = 16;
124 } else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) { 186 }
187 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
125 sha1_begin(&context.sha1); 188 sha1_begin(&context.sha1);
126 update = (void*)sha1_hash; 189 update = (void*)sha1_hash;
127 final = (void*)sha1_end; 190 final = (void*)sha1_end;
128 hash_len = 20; 191 hash_len = 20;
129 } else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) { 192 }
193 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
130 sha256_begin(&context.sha256); 194 sha256_begin(&context.sha256);
131 update = (void*)sha256_hash; 195 update = (void*)sha256_hash;
132 final = (void*)sha256_end; 196 final = (void*)sha256_end;
133 hash_len = 32; 197 hash_len = 32;
134 } else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) { 198 }
199 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
135 sha512_begin(&context.sha512); 200 sha512_begin(&context.sha512);
136 update = (void*)sha512_hash; 201 update = (void*)sha512_hash;
137 final = (void*)sha512_end; 202 final = (void*)sha512_end;
138 hash_len = 64; 203 hash_len = 64;
139 } else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) { 204 }
205#if ENABLE_SHA3SUM
206 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
140 sha3_begin(&context.sha3); 207 sha3_begin(&context.sha3);
141 update = (void*)sha3_hash; 208 update = (void*)sha3_hash;
142 final = (void*)sha3_end; 209 final = (void*)sha3_end;
143 hash_len = 64; 210 /*
144 } else { 211 * Should support 224, 256, 384, 512.
212 * We allow any value which does not blow the algorithm up.
213 */
214 if (sha3_width >= 1600/2 /* input block can't be <= 0 */
215 || sha3_width == 0 /* hash len can't be 0 */
216 || (sha3_width & 0x1f) /* should be multiple of 32 */
217 /* (because input uses up to 8 byte wide word XORs. 32/4=8) */
218 ) {
219 bb_error_msg_and_die("bad -a%u", sha3_width);
220 }
221 sha3_width /= 4;
222 context.sha3.input_block_bytes = 1600/8 - sha3_width;
223 hash_len = sha3_width/2;
224 }
225#endif
226 else {
145 xfunc_die(); /* can't reach this */ 227 xfunc_die(); /* can't reach this */
146 } 228 }
147 229
@@ -172,28 +254,33 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
172{ 254{
173 int return_value = EXIT_SUCCESS; 255 int return_value = EXIT_SUCCESS;
174 unsigned flags; 256 unsigned flags;
257#if ENABLE_SHA3SUM
258 unsigned sha3_width = 224;
259#endif
175 260
176 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) { 261 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) {
262 /* -s and -w require -c */
263 opt_complementary = "s?c:w?c";
177 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ 264 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
178 flags = getopt32(argv, "scwbt"); 265#if ENABLE_SHA3SUM
179 argv += optind; 266 if (applet_name[3] == HASH_SHA3)
180 //argc -= optind; 267 flags = getopt32(argv, "scwbta:+", &sha3_width);
268 else
269#endif
270 flags = getopt32(argv, "scwbt");
181 } else { 271 } else {
182 argv += 1; 272#if ENABLE_SHA3SUM
183 //argc -= 1; 273 if (applet_name[3] == HASH_SHA3)
274 getopt32(argv, "a:+", &sha3_width);
275 else
276#endif
277 getopt32(argv, "");
184 } 278 }
279 argv += optind;
280 //argc -= optind;
185 if (!*argv) 281 if (!*argv)
186 *--argv = (char*)"-"; 282 *--argv = (char*)"-";
187 283
188 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) {
189 if (flags & FLAG_SILENT) {
190 bb_error_msg_and_die("-%c is meaningful only with -c", 's');
191 }
192 if (flags & FLAG_WARN) {
193 bb_error_msg_and_die("-%c is meaningful only with -c", 'w');
194 }
195 }
196
197 do { 284 do {
198 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { 285 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
199 FILE *pre_computed_stream; 286 FILE *pre_computed_stream;
@@ -225,7 +312,7 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
225 *filename_ptr = '\0'; 312 *filename_ptr = '\0';
226 filename_ptr += 2; 313 filename_ptr += 2;
227 314
228 hash_value = hash_file(filename_ptr); 315 hash_value = hash_file(filename_ptr, sha3_width);
229 316
230 if (hash_value && (strcmp((char*)hash_value, line) == 0)) { 317 if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
231 if (!(flags & FLAG_SILENT)) 318 if (!(flags & FLAG_SILENT))
@@ -244,9 +331,17 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
244 bb_error_msg("WARNING: %d of %d computed checksums did NOT match", 331 bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
245 count_failed, count_total); 332 count_failed, count_total);
246 } 333 }
334 if (count_total == 0) {
335 return_value = EXIT_FAILURE;
336 /*
337 * md5sum from GNU coreutils 8.25 says:
338 * md5sum: <FILE>: no properly formatted MD5 checksum lines found
339 */
340 bb_error_msg("%s: no checksum lines found", *argv);
341 }
247 fclose_if_not_stdin(pre_computed_stream); 342 fclose_if_not_stdin(pre_computed_stream);
248 } else { 343 } else {
249 uint8_t *hash_value = hash_file(*argv); 344 uint8_t *hash_value = hash_file(*argv, sha3_width);
250 if (hash_value == NULL) { 345 if (hash_value == NULL) {
251 return_value = EXIT_FAILURE; 346 return_value = EXIT_FAILURE;
252 } else { 347 } else {
diff --git a/coreutils/sync.c b/coreutils/sync.c
index 974e90452..e65d9cd8d 100644
--- a/coreutils/sync.c
+++ b/coreutils/sync.c
@@ -15,7 +15,7 @@
15//config: help 15//config: help
16//config: sync is used to flush filesystem buffers. 16//config: sync is used to flush filesystem buffers.
17//config:config FEATURE_SYNC_FANCY 17//config:config FEATURE_SYNC_FANCY
18//config: bool "Enable -d and -f flags (requres syncfs(2) in libc)" 18//config: bool "Enable -d and -f flags (requires syncfs(2) in libc)"
19//config: default y 19//config: default y
20//config: depends on SYNC 20//config: depends on SYNC
21//config: help 21//config: help
diff --git a/coreutils/test.c b/coreutils/test.c
index 422d24c54..b8a5d798c 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -394,6 +394,7 @@ extern struct test_statics *const test_ptr_to_statics;
394 barrier(); \ 394 barrier(); \
395} while (0) 395} while (0)
396#define DEINIT_S() do { \ 396#define DEINIT_S() do { \
397 free(group_array); \
397 free(test_ptr_to_statics); \ 398 free(test_ptr_to_statics); \
398} while (0) 399} while (0)
399 400
diff --git a/docs/keep_data_small.txt b/docs/keep_data_small.txt
index 3ced1a61d..218d4f2ee 100644
--- a/docs/keep_data_small.txt
+++ b/docs/keep_data_small.txt
@@ -59,7 +59,7 @@ wait
59 Example 1 59 Example 1
60 60
61One example how to reduce global data usage is in 61One example how to reduce global data usage is in
62archival/libarchive/decompress_unzip.c: 62archival/libarchive/decompress_gunzip.c:
63 63
64/* This is somewhat complex-looking arrangement, but it allows 64/* This is somewhat complex-looking arrangement, but it allows
65 * to place decompressor state either in bss or in 65 * to place decompressor state either in bss or in
@@ -87,7 +87,7 @@ take a look at archival/gzip.c. Here all global data is replaced by
87single global pointer (ptr_to_globals) to allocated storage. 87single global pointer (ptr_to_globals) to allocated storage.
88 88
89In order to not duplicate ptr_to_globals in every applet, you can 89In order to not duplicate ptr_to_globals in every applet, you can
90reuse single common one. It is defined in libbb/messages.c 90reuse single common one. It is defined in libbb/ptr_to_globals.c
91as struct globals *const ptr_to_globals, but the struct globals is 91as struct globals *const ptr_to_globals, but the struct globals is
92NOT defined in libbb.h. You first define your own struct: 92NOT defined in libbb.h. You first define your own struct:
93 93
diff --git a/editors/sed.c b/editors/sed.c
index 2e99dc792..63ca1dc99 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -1305,16 +1305,17 @@ static void process_files(void)
1305 case 'n': 1305 case 'n':
1306 if (!G.be_quiet) 1306 if (!G.be_quiet)
1307 sed_puts(pattern_space, last_gets_char); 1307 sed_puts(pattern_space, last_gets_char);
1308 if (next_line) { 1308 if (next_line == NULL) {
1309 free(pattern_space); 1309 /* If no next line, jump to end of script and exit. */
1310 pattern_space = next_line; 1310 goto discard_line;
1311 last_gets_char = next_gets_char;
1312 next_line = get_next_line(&next_gets_char, &last_puts_char);
1313 substituted = 0;
1314 linenum++;
1315 break;
1316 } 1311 }
1317 /* fall through */ 1312 free(pattern_space);
1313 pattern_space = next_line;
1314 last_gets_char = next_gets_char;
1315 next_line = get_next_line(&next_gets_char, &last_puts_char);
1316 substituted = 0;
1317 linenum++;
1318 break;
1318 1319
1319 /* Quit. End of script, end of input. */ 1320 /* Quit. End of script, end of input. */
1320 case 'q': 1321 case 'q':
diff --git a/editors/vi.c b/editors/vi.c
index da7464b58..b81f2b92d 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -2722,9 +2722,8 @@ static char *swap_context(char *p) // goto new context for '' command make this
2722 // only swap context if other context is valid 2722 // only swap context if other context is valid
2723 if (text <= mark[27] && mark[27] <= end - 1) { 2723 if (text <= mark[27] && mark[27] <= end - 1) {
2724 tmp = mark[27]; 2724 tmp = mark[27];
2725 mark[27] = mark[26]; 2725 mark[27] = p;
2726 mark[26] = tmp; 2726 mark[26] = p = tmp;
2727 p = mark[26]; // where we are going- previous context
2728 context_start = prev_line(prev_line(prev_line(p))); 2727 context_start = prev_line(prev_line(prev_line(p)));
2729 context_end = next_line(next_line(next_line(p))); 2728 context_end = next_line(next_line(next_line(p)));
2730 } 2729 }
@@ -3665,8 +3664,9 @@ static void do_cmd(int c)
3665 } 3664 }
3666 break; 3665 break;
3667 case '\'': // '- goto a specific mark 3666 case '\'': // '- goto a specific mark
3668 c1 = (get_one_char() | 0x20) - 'a'; 3667 c1 = (get_one_char() | 0x20);
3669 if ((unsigned)c1 <= 25) { // a-z? 3668 if ((unsigned)(c1 - 'a') <= 25) { // a-z?
3669 c1 = (c1 - 'a');
3670 // get the b-o-l 3670 // get the b-o-l
3671 q = mark[c1]; 3671 q = mark[c1];
3672 if (text <= q && q < end) { 3672 if (text <= q && q < end) {
@@ -3980,7 +3980,9 @@ static void do_cmd(int c)
3980 c1 = get_one_char(); 3980 c1 = get_one_char();
3981 if (c1 != 'g') { 3981 if (c1 != 'g') {
3982 buf[0] = 'g'; 3982 buf[0] = 'g';
3983 buf[1] = c1; // TODO: if Unicode? 3983 // c1 < 0 if the key was special. Try "g<up-arrow>"
3984 // TODO: if Unicode?
3985 buf[1] = (c1 >= 0 ? c1 : '*');
3984 buf[2] = '\0'; 3986 buf[2] = '\0';
3985 not_implemented(buf); 3987 not_implemented(buf);
3986 break; 3988 break;
diff --git a/examples/var_service/dhcpd_if/README b/examples/var_service/dhcpd_if/README
new file mode 100644
index 000000000..4ddccb22d
--- /dev/null
+++ b/examples/var_service/dhcpd_if/README
@@ -0,0 +1,5 @@
1The real README file is one directory up.
2
3This directory's run script can have useful comments.
4If it doesn't but you feel it should, please send a patch
5to busybox's mailing list.
diff --git a/examples/var_service/dhcpd_if/log/run b/examples/var_service/dhcpd_if/log/run
new file mode 100755
index 000000000..69d74b73f
--- /dev/null
+++ b/examples/var_service/dhcpd_if/log/run
@@ -0,0 +1,21 @@
1#!/bin/sh
2
3user=logger
4
5logdir="/var/log/service/`(cd ..;basename $PWD)`"
6mkdir -p "$logdir" 2>/dev/null
7chown -R "$user": "$logdir"
8chmod -R go-rwxst,u+rwX "$logdir"
9rm -rf logdir
10ln -s "$logdir" logdir
11
12# make this dir accessible to logger
13chmod a+rX .
14
15exec >/dev/null
16exec 2>&1
17exec \
18env - PATH="$PATH" \
19softlimit \
20setuidgid "$user" \
21svlogd -tt "$logdir"
diff --git a/examples/var_service/dhcpd_if/p_log b/examples/var_service/dhcpd_if/p_log
new file mode 100755
index 000000000..a2521be05
--- /dev/null
+++ b/examples/var_service/dhcpd_if/p_log
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3cd log/logdir || exit 1
4cat @* current | $PAGER
diff --git a/examples/var_service/dhcpd_if/run b/examples/var_service/dhcpd_if/run
new file mode 100755
index 000000000..de85dece0
--- /dev/null
+++ b/examples/var_service/dhcpd_if/run
@@ -0,0 +1,23 @@
1#!/bin/sh
2
3exec 2>&1
4exec </dev/null
5
6pwd="$PWD"
7
8if="${PWD##*/dhcpd_}"
9
10echo "* Upping iface $if"
11ip link set dev $if up
12
13>>udhcpd.leases
14sed 's/^interface.*$/interface '"$if/" -i udhcpc.conf
15
16echo "* Starting udhcpd"
17exec \
18env - PATH="$PATH" \
19softlimit \
20setuidgid root \
21udhcpd -f -vv udhcpc.conf
22
23exit $?
diff --git a/examples/var_service/dhcpd_if/udhcpc.conf b/examples/var_service/dhcpd_if/udhcpc.conf
new file mode 100644
index 000000000..a81925970
--- /dev/null
+++ b/examples/var_service/dhcpd_if/udhcpc.conf
@@ -0,0 +1,28 @@
1# Directives with defaults:
2# start 192.168.0.20
3# end 192.168.0.254
4# interface eth0
5# max_leases 235
6# auto_time 7200
7# decline_time 3600
8# conflict_time 3600
9# offer_time 60
10# min_lease 60
11# lease_file /var/lib/misc/udhcpd.leases
12# pidfile /var/run/udhcpd.pid
13# siaddr 0.0.0.0
14#
15# Directives with no defaults (or with empty defaults):
16# option/opt NAME VALUE
17# notify_file /path/to/script_to_run_after_leasefile_is_written
18# (it is run with $1 = lease_file_name)
19# sname dhcp_packet_sname_field_contents
20# boot_file dhcp_packet_bootfile_field_contents
21# static_lease XX:XX:XX:XX:XX:XX IP.ADD.RE.SS
22
23interface if
24pidfile /dev/null
25lease_file udhcpd.leases
26option subnet 255.255.255.0
27option lease 3600
28#option router 192.168.0.1
diff --git a/examples/var_service/dhcpd_if/w_dumpleases b/examples/var_service/dhcpd_if/w_dumpleases
new file mode 100755
index 000000000..ff772055a
--- /dev/null
+++ b/examples/var_service/dhcpd_if/w_dumpleases
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3watch -n1 'dumpleases -af udhcpd.leases'
diff --git a/examples/var_service/dhcpd_if/w_dumpleases_countdown b/examples/var_service/dhcpd_if/w_dumpleases_countdown
new file mode 100755
index 000000000..7fcd9602a
--- /dev/null
+++ b/examples/var_service/dhcpd_if/w_dumpleases_countdown
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3watch -n1 'dumpleases -f udhcpd.leases'
diff --git a/examples/var_service/dhcpd_if/w_log b/examples/var_service/dhcpd_if/w_log
new file mode 100755
index 000000000..dba76c69b
--- /dev/null
+++ b/examples/var_service/dhcpd_if/w_log
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3cd log/logdir
4watch -n1 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b0-$((w-2))'
diff --git a/examples/var_service/fw/run b/examples/var_service/fw/run
index 81c7f2e7c..1fd71cc01 100755
--- a/examples/var_service/fw/run
+++ b/examples/var_service/fw/run
@@ -1,18 +1,20 @@
1#!/bin/bash 1#!/bin/bash
2# (using bashism: arrays) 2# (using bashism: arrays)
3 3
4service="${PWD##*/}" 4user="root"
5rundir="/var/run/service/$service" 5reset_all_netdevs=true
6 6preferred_default_route_iface="if"
7user=root 7extif="if"
8extif=if 8ext_open_tcp="22 80 88" # space-separated
9ext_open_tcp="21 22 80" # space-separated
10 9
11# Make ourself one-shot 10# Make ourself one-shot
12sv o . 11sv o .
13# Debug 12# Debug
14#date '+%Y-%m-%d %H:%M:%S' >>"$0.log" 13#date '+%Y-%m-%d %H:%M:%S' >>"$0.log"
15 14
15service=`basename $PWD`
16rundir="/var/run/service/$service"
17
16### filter This is the default table (if no -t option is passed). It contains 18### filter This is the default table (if no -t option is passed). It contains
17### the built-in chains INPUT (for packets coming into the box itself), 19### the built-in chains INPUT (for packets coming into the box itself),
18### FORWARD (for packets being routed through the box), and OUTPUT (for 20### FORWARD (for packets being routed through the box), and OUTPUT (for
@@ -70,7 +72,6 @@ ln -s "$rundir" rundir
70# Timestamping 72# Timestamping
71date '+%Y-%m-%d %H:%M:%S' 73date '+%Y-%m-%d %H:%M:%S'
72 74
73
74echo; echo "* Reading IP config" 75echo; echo "* Reading IP config"
75cfg=-1 76cfg=-1
76# static cfg dhcp,zeroconf etc 77# static cfg dhcp,zeroconf etc
@@ -86,11 +87,19 @@ echo; echo "* Configuring hardware"
86#doit ethtool -K if rx off tx off sg off tso off 87#doit ethtool -K if rx off tx off sg off tso off
87 88
88echo; echo "* Resetting address and routing info" 89echo; echo "* Resetting address and routing info"
89doit ip a f dev lo 90if $reset_all_netdevs; then
90i=0; while test "${if[$i]}"; do 91 devs=`sed -n 's/ //g;s/:.*$//p' </proc/net/dev`
91 doit ip a f dev "${if[$i]}" 92 for iface in $devs; do
92 doit ip r f dev "${if[$i]}" root 0/0 93 doit ip a f dev "$iface"
93let i++; done 94 doit ip r f dev "$iface" root 0/0
95 done
96else
97 doit ip a f dev lo
98 i=0; while test "${if[$i]}"; do
99 doit ip a f dev "${if[$i]}"
100 doit ip r f dev "${if[$i]}" root 0/0
101 let i++; done
102fi
94 103
95echo; echo "* Configuring addresses" 104echo; echo "* Configuring addresses"
96doit ip a a dev lo 127.0.0.1/8 scope host 105doit ip a a dev lo 127.0.0.1/8 scope host
@@ -103,7 +112,22 @@ i=0; while test "${if[$i]}"; do
103let i++; done 112let i++; done
104 113
105echo; echo "* Configuring routes" 114echo; echo "* Configuring routes"
115# If several ifaces are configured via DHCP, they often both have 0/0 route.
116# They have no way of knowing that this route is offered on more than one iface.
117# Often, it's desirable to prefer one iface: say, wired eth over wireless.
118# if preferred_default_route_iface is not set, 0/0 route will be assigned randomly.
119if test "$preferred_default_route_iface"; then
120 i=0; while test "${if[$i]}"; do
121 if test "${if[$i]}" = "$preferred_default_route_iface" \
122 && test "${net[$i]}" = "0/0" \
123 && test "${gw[$i]}"; then
124 echo "+ default route through ${if[$i]}, ${gw[$i]}:"
125 doit ip r a "${net[$i]}" via "${gw[$i]}"
126 fi
127 let i++; done
128fi
106i=0; while test "${if[$i]}"; do 129i=0; while test "${if[$i]}"; do
130 #echo $i:"${if[$i]}"
107 if test "${net[$i]}" && test "${gw[$i]}"; then 131 if test "${net[$i]}" && test "${gw[$i]}"; then
108 doit ip r a "${net[$i]}" via "${gw[$i]}" 132 doit ip r a "${net[$i]}" via "${gw[$i]}"
109 fi 133 fi
diff --git a/examples/var_service/ntpd/p_log_important b/examples/var_service/ntpd/p_log_important
new file mode 100755
index 000000000..09b248fb3
--- /dev/null
+++ b/examples/var_service/ntpd/p_log_important
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3cd log/logdir || exit 1
4cat @* current | grep -v -eolder -esending -esockets -e'reply from.*offset' -executing | $PAGER
diff --git a/examples/var_service/supplicant_if/README b/examples/var_service/supplicant_if/README
new file mode 100644
index 000000000..4ddccb22d
--- /dev/null
+++ b/examples/var_service/supplicant_if/README
@@ -0,0 +1,5 @@
1The real README file is one directory up.
2
3This directory's run script can have useful comments.
4If it doesn't but you feel it should, please send a patch
5to busybox's mailing list.
diff --git a/examples/var_service/supplicant_if/log/run b/examples/var_service/supplicant_if/log/run
new file mode 100755
index 000000000..69d74b73f
--- /dev/null
+++ b/examples/var_service/supplicant_if/log/run
@@ -0,0 +1,21 @@
1#!/bin/sh
2
3user=logger
4
5logdir="/var/log/service/`(cd ..;basename $PWD)`"
6mkdir -p "$logdir" 2>/dev/null
7chown -R "$user": "$logdir"
8chmod -R go-rwxst,u+rwX "$logdir"
9rm -rf logdir
10ln -s "$logdir" logdir
11
12# make this dir accessible to logger
13chmod a+rX .
14
15exec >/dev/null
16exec 2>&1
17exec \
18env - PATH="$PATH" \
19softlimit \
20setuidgid "$user" \
21svlogd -tt "$logdir"
diff --git a/examples/var_service/supplicant_if/p_log b/examples/var_service/supplicant_if/p_log
new file mode 100755
index 000000000..a2521be05
--- /dev/null
+++ b/examples/var_service/supplicant_if/p_log
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3cd log/logdir || exit 1
4cat @* current | $PAGER
diff --git a/examples/var_service/supplicant_if/run b/examples/var_service/supplicant_if/run
new file mode 100755
index 000000000..45211e001
--- /dev/null
+++ b/examples/var_service/supplicant_if/run
@@ -0,0 +1,21 @@
1#!/bin/sh
2
3exec 2>&1
4exec </dev/null
5
6pwd="$PWD"
7
8if="${PWD##*/dhcp_}"
9
10echo "* Upping iface $if"
11ip link set dev "$if" up
12
13##echo "* Powersave disable on $if"
14##iw dev "$if" set power_save off
15
16echo "* Starting wpa_supplicant"
17exec \
18env - PATH="$PATH" \
19softlimit \
20setuidgid root \
21wpa_supplicant -i "$if" -c "$pwd/wpa_supplicant.conf" -d
diff --git a/examples/var_service/supplicant_if/w_log b/examples/var_service/supplicant_if/w_log
new file mode 100755
index 000000000..aa36ef13b
--- /dev/null
+++ b/examples/var_service/supplicant_if/w_log
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3cd log/logdir || exit 1
4watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/examples/var_service/supplicant_if/wpa_supplicant.conf b/examples/var_service/supplicant_if/wpa_supplicant.conf
new file mode 100644
index 000000000..e317e2ebe
--- /dev/null
+++ b/examples/var_service/supplicant_if/wpa_supplicant.conf
@@ -0,0 +1,28 @@
1# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
2ctrl_interface=DIR=/var/run/wpa_supplicant
3#GROUP=wheel
4
5# Typical minimal wifi setup:
6network={
7 ssid="--your-ESSID--"
8 key_mgmt=WPA-PSK
9 psk="--your-password--"
10}
11
12# Other fields:
13# scan_ssid=1
14# key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
15# pairwise=CCMP TKIP
16# group=CCMP TKIP WEP104 WEP40
17# eap=TTLS PEAP TLS
18# identity="user@example.com"
19# password="foobar"
20# ca_cert="/etc/cert/ca.pem"
21# client_cert="/etc/cert/user.pem"
22# private_key="/etc/cert/user.prv"
23# private_key_passwd="password"
24# phase1="peaplabel=0"
25# ca_cert2="/etc/cert/ca2.pem"
26# client_cert2="/etc/cer/user.pem"
27# private_key2="/etc/cer/user.prv"
28# private_key2_passwd="password"
diff --git a/findutils/grep.c b/findutils/grep.c
index 02ce65159..94bcb6002 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -255,7 +255,7 @@ typedef struct grep_list_data_t {
255#endif 255#endif
256#define ALLOCATED 1 256#define ALLOCATED 1
257#define COMPILED 2 257#define COMPILED 2
258 int flg_mem_alocated_compiled; 258 int flg_mem_allocated_compiled;
259} grep_list_data_t; 259} grep_list_data_t;
260 260
261#if !ENABLE_EXTRA_COMPAT 261#if !ENABLE_EXTRA_COMPAT
@@ -388,8 +388,8 @@ static int grep_file(FILE *file)
388#endif 388#endif
389 char *match_at; 389 char *match_at;
390 390
391 if (!(gl->flg_mem_alocated_compiled & COMPILED)) { 391 if (!(gl->flg_mem_allocated_compiled & COMPILED)) {
392 gl->flg_mem_alocated_compiled |= COMPILED; 392 gl->flg_mem_allocated_compiled |= COMPILED;
393#if !ENABLE_EXTRA_COMPAT 393#if !ENABLE_EXTRA_COMPAT
394 xregcomp(&gl->compiled_regex, gl->pattern, reflags); 394 xregcomp(&gl->compiled_regex, gl->pattern, reflags);
395#else 395#else
@@ -627,9 +627,9 @@ static char *add_grep_list_data(char *pattern)
627 grep_list_data_t *gl = xzalloc(sizeof(*gl)); 627 grep_list_data_t *gl = xzalloc(sizeof(*gl));
628 gl->pattern = pattern; 628 gl->pattern = pattern;
629#if ENABLE_FEATURE_CLEAN_UP 629#if ENABLE_FEATURE_CLEAN_UP
630 gl->flg_mem_alocated_compiled = flg_used_mem; 630 gl->flg_mem_allocated_compiled = flg_used_mem;
631#else 631#else
632 /*gl->flg_mem_alocated_compiled = 0;*/ 632 /*gl->flg_mem_allocated_compiled = 0;*/
633#endif 633#endif
634 return (char *)gl; 634 return (char *)gl;
635} 635}
@@ -845,9 +845,9 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
845 grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data; 845 grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
846 846
847 pattern_head = pattern_head->link; 847 pattern_head = pattern_head->link;
848 if (gl->flg_mem_alocated_compiled & ALLOCATED) 848 if (gl->flg_mem_allocated_compiled & ALLOCATED)
849 free(gl->pattern); 849 free(gl->pattern);
850 if (gl->flg_mem_alocated_compiled & COMPILED) 850 if (gl->flg_mem_allocated_compiled & COMPILED)
851 regfree(&gl->compiled_regex); 851 regfree(&gl->compiled_regex);
852 free(gl); 852 free(gl);
853 free(pattern_head_ptr); 853 free(pattern_head_ptr);
diff --git a/include/applets.src.h b/include/applets.src.h
index 6e1b02fc3..248d539c4 100644
--- a/include/applets.src.h
+++ b/include/applets.src.h
@@ -205,7 +205,6 @@ IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP))
205IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP)) 205IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP))
206IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP)) 206IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP))
207IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP)) 207IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP))
208IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum))
209IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP)) 208IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP))
210IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir)) 209IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir))
211IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat)) 210IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
@@ -280,10 +279,6 @@ IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP))
280IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP)) 279IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP))
281IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP)) 280IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
282IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP)) 281IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP))
283IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
284IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
285IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
286IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
287IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP)) 282IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP))
288IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) 283IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP))
289/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */ 284/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */
diff --git a/include/busybox.h b/include/busybox.h
index 737627bd0..6a003d544 100644
--- a/include/busybox.h
+++ b/include/busybox.h
@@ -19,7 +19,9 @@ extern const uint8_t applet_flags[] ALIGN1;
19extern const uint8_t applet_suid[] ALIGN1; 19extern const uint8_t applet_suid[] ALIGN1;
20extern const uint8_t applet_install_loc[] ALIGN1; 20extern const uint8_t applet_install_loc[] ALIGN1;
21 21
22#if ENABLE_FEATURE_PREFER_APPLETS 22#if ENABLE_FEATURE_PREFER_APPLETS \
23 || ENABLE_FEATURE_SH_STANDALONE \
24 || ENABLE_FEATURE_SH_NOFORK
23# define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4)))) 25# define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4))))
24# define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1))) 26# define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1)))
25#else 27#else
diff --git a/include/libbb.h b/include/libbb.h
index 6db03d2df..30a0e00ba 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -196,24 +196,33 @@ int klogctl(int type, char *b, int len);
196/* Busybox does not use threads, we can speed up stdio. */ 196/* Busybox does not use threads, we can speed up stdio. */
197#ifdef HAVE_UNLOCKED_STDIO 197#ifdef HAVE_UNLOCKED_STDIO
198# undef getc 198# undef getc
199# define getc(stream) getc_unlocked(stream) 199# define getc(stream) getc_unlocked(stream)
200# undef getchar 200# undef getchar
201# define getchar() getchar_unlocked() 201# define getchar() getchar_unlocked()
202# undef putc 202# undef putc
203# define putc(c, stream) putc_unlocked(c, stream) 203# define putc(c,stream) putc_unlocked(c,stream)
204# undef putchar 204# undef putchar
205# define putchar(c) putchar_unlocked(c) 205# define putchar(c) putchar_unlocked(c)
206# undef fgetc 206# undef fgetc
207# define fgetc(stream) getc_unlocked(stream) 207# define fgetc(stream) getc_unlocked(stream)
208# undef fputc 208# undef fputc
209# define fputc(c, stream) putc_unlocked(c, stream) 209# define fputc(c,stream) putc_unlocked(c,stream)
210#endif 210#endif
211/* Above functions are required by POSIX.1-2008, below ones are extensions */ 211/* Above functions are required by POSIX.1-2008, below ones are extensions */
212#ifdef HAVE_UNLOCKED_LINE_OPS 212#ifdef HAVE_UNLOCKED_LINE_OPS
213# undef fgets 213# undef fgets
214# define fgets(s, n, stream) fgets_unlocked(s, n, stream) 214# define fgets(s,n,stream) fgets_unlocked(s,n,stream)
215# undef fputs 215# undef fputs
216# define fputs(s, stream) fputs_unlocked(s, stream) 216# define fputs(s,stream) fputs_unlocked(s,stream)
217/* musl <= 1.1.15 does not support fflush_unlocked(NULL) */
218//# undef fflush
219//# define fflush(stream) fflush_unlocked(stream)
220# undef feof
221# define feof(stream) feof_unlocked(stream)
222# undef ferror
223# define ferror(stream) ferror_unlocked(stream)
224# undef fileno
225# define fileno(stream) fileno_unlocked(stream)
217#endif 226#endif
218 227
219 228
@@ -1755,6 +1764,7 @@ typedef struct sha512_ctx_t {
1755typedef struct sha3_ctx_t { 1764typedef struct sha3_ctx_t {
1756 uint64_t state[25]; 1765 uint64_t state[25];
1757 unsigned bytes_queued; 1766 unsigned bytes_queued;
1767 unsigned input_block_bytes;
1758} sha3_ctx_t; 1768} sha3_ctx_t;
1759void md5_begin(md5_ctx_t *ctx) FAST_FUNC; 1769void md5_begin(md5_ctx_t *ctx) FAST_FUNC;
1760void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 1770void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
diff --git a/init/init.c b/init/init.c
index 6eb76b80e..0813d1b5f 100644
--- a/init/init.c
+++ b/init/init.c
@@ -297,6 +297,11 @@ static void console_init(void)
297 s = getenv("CONSOLE"); 297 s = getenv("CONSOLE");
298 if (!s) 298 if (!s)
299 s = getenv("console"); 299 s = getenv("console");
300#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
301 /* BSD people say their kernels do not open fd 0,1,2; they need this: */
302 if (!s)
303 s = (char*)"/dev/console";
304#endif
300 if (s) { 305 if (s) {
301 int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY); 306 int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY);
302 if (fd >= 0) { 307 if (fd >= 0) {
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 28adf2740..4aa40454f 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -930,7 +930,9 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
930 } 930 }
931 if (ENABLE_FEATURE_SUID) 931 if (ENABLE_FEATURE_SUID)
932 check_suid(applet_no); 932 check_suid(applet_no);
933 exit(applet_main[applet_no](argc, argv)); 933 xfunc_error_retval = applet_main[applet_no](argc, argv);
934 /* Note: applet_main() may also not return (die on a xfunc or such) */
935 xfunc_die();
934} 936}
935# endif /* NUM_APPLETS > 0 */ 937# endif /* NUM_APPLETS > 0 */
936 938
@@ -993,6 +995,14 @@ int main(int argc UNUSED_PARAM, char **argv)
993 */ 995 */
994 mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256); 996 mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256);
995#endif 997#endif
998#if 0 /*def M_TOP_PAD*/
999 /* When the program break is increased, then M_TOP_PAD bytes are added
1000 * to the sbrk(2) request. When the heap is trimmed because of free(3),
1001 * this much free space is preserved at the top of the heap.
1002 * glibc default seems to be way too big: 128k, but need to verify.
1003 */
1004 mallopt(M_TOP_PAD, 8 * 1024);
1005#endif
996 1006
997#if !BB_MMU 1007#if !BB_MMU
998 /* NOMMU re-exec trick sets high-order bit in first byte of name */ 1008 /* NOMMU re-exec trick sets high-order bit in first byte of name */
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index c52297376..cb6d12359 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -299,11 +299,16 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
299 if (!S_ISREG(source_stat.st_mode)) 299 if (!S_ISREG(source_stat.st_mode))
300 new_mode = 0666; 300 new_mode = 0666;
301 301
302 // POSIX way is a security problem versus (sym)link attacks 302 if (ENABLE_FEATURE_NON_POSIX_CP || (flags & FILEUTILS_INTERACTIVE)) {
303 if (!ENABLE_FEATURE_NON_POSIX_CP) { 303 /*
304 dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); 304 * O_CREAT|O_EXCL: require that file did not exist before creation
305 } else { /* safe way: */ 305 */
306 dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); 306 dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
307 } else { /* POSIX, and not "cp -i" */
308 /*
309 * O_CREAT|O_TRUNC: create, or truncate (security problem versus (sym)link attacks)
310 */
311 dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
307 } 312 }
308 if (dst_fd == -1) { 313 if (dst_fd == -1) {
309 ovr = ask_and_unlink(dest, flags); 314 ovr = ask_and_unlink(dest, flags);
@@ -378,7 +383,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
378 } 383 }
379 /* _Not_ jumping to preserve_mode_ugid_time: 384 /* _Not_ jumping to preserve_mode_ugid_time:
380 * symlinks don't have those */ 385 * symlinks don't have those */
381 return 0; 386 goto verb_and_exit;
382 } 387 }
383 if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) 388 if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
384 || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) 389 || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
@@ -413,6 +418,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
413 bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); 418 bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
414 } 419 }
415 420
421 verb_and_exit:
416 if (flags & FILEUTILS_VERBOSE) { 422 if (flags & FILEUTILS_VERBOSE) {
417 printf("'%s' -> '%s'\n", source, dest); 423 printf("'%s' -> '%s'\n", source, dest);
418 } 424 }
diff --git a/libbb/dump.c b/libbb/dump.c
index 566881a78..154be5d80 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -157,7 +157,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
157 /* 157 /*
158 * figure out the byte count for each conversion; 158 * figure out the byte count for each conversion;
159 * rewrite the format as necessary, set up blank- 159 * rewrite the format as necessary, set up blank-
160 * pbb_dump_adding for end of data. 160 * padding for end of data.
161 */ 161 */
162 if (*p1 == 'c') { 162 if (*p1 == 'c') {
163 pr->flags = F_CHAR; 163 pr->flags = F_CHAR;
@@ -466,14 +466,14 @@ static void bpad(PR *pr)
466} 466}
467 467
468static const char conv_str[] ALIGN1 = 468static const char conv_str[] ALIGN1 =
469 "\0\\0\0" 469 "\0" "\\""0""\0"
470 "\007\\a\0" /* \a */ 470 "\007""\\""a""\0" /* \a */
471 "\b\\b\0" 471 "\b" "\\""b""\0"
472 "\f\\b\0" 472 "\f" "\\""f""\0"
473 "\n\\n\0" 473 "\n" "\\""n""\0"
474 "\r\\r\0" 474 "\r" "\\""r""\0"
475 "\t\\t\0" 475 "\t" "\\""t""\0"
476 "\v\\v\0" 476 "\v" "\\""v""\0"
477 ; 477 ;
478 478
479 479
@@ -485,7 +485,7 @@ static void conv_c(PR *pr, unsigned char *p)
485 do { 485 do {
486 if (*p == *str) { 486 if (*p == *str) {
487 ++str; 487 ++str;
488 goto strpr; 488 goto strpr; /* map e.g. '\n' to "\\n" */
489 } 489 }
490 str += 4; 490 str += 4;
491 } while (*str); 491 } while (*str);
@@ -702,8 +702,6 @@ int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
702void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) 702void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
703{ 703{
704 const char *p; 704 const char *p;
705 char *p1;
706 char *p2;
707 FS *tfs; 705 FS *tfs;
708 FU *tfu, **nextfupp; 706 FU *tfu, **nextfupp;
709 const char *savep; 707 const char *savep;
@@ -779,29 +777,42 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
779 } 777 }
780 } 778 }
781 tfu->fmt = xstrndup(savep, p - savep); 779 tfu->fmt = xstrndup(savep, p - savep);
782/* escape(tfu->fmt); */
783
784 p1 = tfu->fmt;
785 780
786 /* alphabetic escape sequences have to be done in place */ 781 /* alphabetic escape sequences have to be done in place */
782 strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
783 /* unknown mappings are not changed: "\z" -> '\\' 'z' */
784 /* trailing backslash, if any, is preserved */
785#if 0
786 char *p1;
787 char *p2;
788 p1 = tfu->fmt;
787 for (p2 = p1;; ++p1, ++p2) { 789 for (p2 = p1;; ++p1, ++p2) {
788 if (*p1 == '\0') { 790 *p2 = *p1;
789 *p2 = *p1; 791 if (*p1 == '\0')
790 break; 792 break;
791 } 793
792 if (*p1 == '\\') { 794 if (*p1 == '\\') {
793 const char *cs = conv_str + 4; 795 const char *cs;
794 ++p1; 796
797 p1++;
795 *p2 = *p1; 798 *p2 = *p1;
799 if (*p1 == '\0') {
800 /* "...\" trailing backslash. Eaten. */
801 break;
802 }
803 cs = conv_str + 4; /* skip NUL element */
796 do { 804 do {
805 /* map e.g. "\n" -> '\n' */
797 if (*p1 == cs[2]) { 806 if (*p1 == cs[2]) {
798 *p2 = cs[0]; 807 *p2 = cs[0];
799 break; 808 break;
800 } 809 }
801 cs += 4; 810 cs += 4;
802 } while (*cs); 811 } while (*cs);
812 /* unknown mappings remove bkslash: "\z" -> 'z' */
803 } 813 }
804 } 814 }
815#endif
805 816
806 p++; 817 p++;
807 } 818 }
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index d08c6b2f7..7e7d8da2f 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -941,10 +941,6 @@ void FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
941# define OPTIMIZE_SHA3_FOR_32 1 941# define OPTIMIZE_SHA3_FOR_32 1
942#endif 942#endif
943 943
944enum {
945 SHA3_IBLK_BYTES = 72, /* 576 bits / 8 */
946};
947
948#if OPTIMIZE_SHA3_FOR_32 944#if OPTIMIZE_SHA3_FOR_32
949/* This splits every 64-bit word into a pair of 32-bit words, 945/* This splits every 64-bit word into a pair of 32-bit words,
950 * even bits go into first word, odd bits go to second one. 946 * even bits go into first word, odd bits go to second one.
@@ -1352,6 +1348,8 @@ static void sha3_process_block72(uint64_t *state)
1352void FAST_FUNC sha3_begin(sha3_ctx_t *ctx) 1348void FAST_FUNC sha3_begin(sha3_ctx_t *ctx)
1353{ 1349{
1354 memset(ctx, 0, sizeof(*ctx)); 1350 memset(ctx, 0, sizeof(*ctx));
1351 /* SHA3-512, user can override */
1352 ctx->input_block_bytes = (1600 - 512*2) / 8; /* 72 bytes */
1355} 1353}
1356 1354
1357void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) 1355void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
@@ -1361,7 +1359,7 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1361 unsigned bufpos = ctx->bytes_queued; 1359 unsigned bufpos = ctx->bytes_queued;
1362 1360
1363 while (1) { 1361 while (1) {
1364 unsigned remaining = SHA3_IBLK_BYTES - bufpos; 1362 unsigned remaining = ctx->input_block_bytes - bufpos;
1365 if (remaining > len) 1363 if (remaining > len)
1366 remaining = len; 1364 remaining = len;
1367 len -= remaining; 1365 len -= remaining;
@@ -1373,38 +1371,41 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1373 remaining--; 1371 remaining--;
1374 } 1372 }
1375 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ 1373 /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
1376 bufpos -= SHA3_IBLK_BYTES; 1374 bufpos -= ctx->input_block_bytes;
1377 if (bufpos != 0) 1375 if (bufpos != 0)
1378 break; 1376 break;
1379 /* Buffer is filled up, process it */ 1377 /* Buffer is filled up, process it */
1380 sha3_process_block72(ctx->state); 1378 sha3_process_block72(ctx->state);
1381 /*bufpos = 0; - already is */ 1379 /*bufpos = 0; - already is */
1382 } 1380 }
1383 ctx->bytes_queued = bufpos + SHA3_IBLK_BYTES; 1381 ctx->bytes_queued = bufpos + ctx->input_block_bytes;
1384#else 1382#else
1385 /* +50 bytes code size, but a bit faster because of long-sized XORs */ 1383 /* +50 bytes code size, but a bit faster because of long-sized XORs */
1386 const uint8_t *data = buffer; 1384 const uint8_t *data = buffer;
1387 unsigned bufpos = ctx->bytes_queued; 1385 unsigned bufpos = ctx->bytes_queued;
1386 unsigned iblk_bytes = ctx->input_block_bytes;
1388 1387
1389 /* If already data in queue, continue queuing first */ 1388 /* If already data in queue, continue queuing first */
1390 while (len != 0 && bufpos != 0) { 1389 if (bufpos != 0) {
1391 uint8_t *buf = (uint8_t*)ctx->state; 1390 while (len != 0) {
1392 buf[bufpos] ^= *data++; 1391 uint8_t *buf = (uint8_t*)ctx->state;
1393 len--; 1392 buf[bufpos] ^= *data++;
1394 bufpos++; 1393 len--;
1395 if (bufpos == SHA3_IBLK_BYTES) { 1394 bufpos++;
1396 bufpos = 0; 1395 if (bufpos == iblk_bytes) {
1397 goto do_block; 1396 bufpos = 0;
1397 goto do_block;
1398 }
1398 } 1399 }
1399 } 1400 }
1400 1401
1401 /* Absorb complete blocks */ 1402 /* Absorb complete blocks */
1402 while (len >= SHA3_IBLK_BYTES) { 1403 while (len >= iblk_bytes) {
1403 /* XOR data onto beginning of state[]. 1404 /* XOR data onto beginning of state[].
1404 * We try to be efficient - operate one word at a time, not byte. 1405 * We try to be efficient - operate one word at a time, not byte.
1405 * Careful wrt unaligned access: can't just use "*(long*)data"! 1406 * Careful wrt unaligned access: can't just use "*(long*)data"!
1406 */ 1407 */
1407 unsigned count = SHA3_IBLK_BYTES / sizeof(long); 1408 unsigned count = iblk_bytes / sizeof(long);
1408 long *buf = (long*)ctx->state; 1409 long *buf = (long*)ctx->state;
1409 do { 1410 do {
1410 long v; 1411 long v;
@@ -1412,7 +1413,7 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1412 *buf++ ^= v; 1413 *buf++ ^= v;
1413 data += sizeof(long); 1414 data += sizeof(long);
1414 } while (--count); 1415 } while (--count);
1415 len -= SHA3_IBLK_BYTES; 1416 len -= iblk_bytes;
1416 do_block: 1417 do_block:
1417 sha3_process_block72(ctx->state); 1418 sha3_process_block72(ctx->state);
1418 } 1419 }
@@ -1433,8 +1434,22 @@ void FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1433{ 1434{
1434 /* Padding */ 1435 /* Padding */
1435 uint8_t *buf = (uint8_t*)ctx->state; 1436 uint8_t *buf = (uint8_t*)ctx->state;
1436 buf[ctx->bytes_queued] ^= 1; 1437 /*
1437 buf[SHA3_IBLK_BYTES - 1] ^= 0x80; 1438 * Keccak block padding is: add 1 bit after last bit of input,
1439 * then add zero bits until the end of block, and add the last 1 bit
1440 * (the last bit in the block) - the "10*1" pattern.
1441 * SHA3 standard appends additional two bits, 01, before that padding:
1442 *
1443 * SHA3-224(M) = KECCAK[448](M||01, 224)
1444 * SHA3-256(M) = KECCAK[512](M||01, 256)
1445 * SHA3-384(M) = KECCAK[768](M||01, 384)
1446 * SHA3-512(M) = KECCAK[1024](M||01, 512)
1447 * (M is the input, || is bit concatenation)
1448 *
1449 * The 6 below contains 01 "SHA3" bits and the first 1 "Keccak" bit:
1450 */
1451 buf[ctx->bytes_queued] ^= 6; /* bit pattern 00000110 */
1452 buf[ctx->input_block_bytes - 1] ^= 0x80;
1438 1453
1439 sha3_process_block72(ctx->state); 1454 sha3_process_block72(ctx->state);
1440 1455
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 2566abd38..34eb16e6b 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -81,7 +81,9 @@
81# define CHAR_T wchar_t 81# define CHAR_T wchar_t
82static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); } 82static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
83# if ENABLE_FEATURE_EDITING_VI 83# if ENABLE_FEATURE_EDITING_VI
84static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); } 84static bool BB_isalnum_or_underscore(CHAR_T c) {
85 return ((unsigned)c < 256 && isalnum(c)) || c == '_';
86}
85# endif 87# endif
86static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } 88static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
87# undef isspace 89# undef isspace
@@ -96,7 +98,11 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
96# define BB_NUL '\0' 98# define BB_NUL '\0'
97# define CHAR_T char 99# define CHAR_T char
98# define BB_isspace(c) isspace(c) 100# define BB_isspace(c) isspace(c)
99# define BB_isalnum(c) isalnum(c) 101# if ENABLE_FEATURE_EDITING_VI
102static bool BB_isalnum_or_underscore(CHAR_T c) {
103 return ((unsigned)c < 256 && isalnum(c)) || c == '_';
104}
105# endif
100# define BB_ispunct(c) ispunct(c) 106# define BB_ispunct(c) ispunct(c)
101#endif 107#endif
102#if ENABLE_UNICODE_PRESERVE_BROKEN 108#if ENABLE_UNICODE_PRESERVE_BROKEN
@@ -1647,9 +1653,9 @@ vi_word_motion(int eat)
1647{ 1653{
1648 CHAR_T *command = command_ps; 1654 CHAR_T *command = command_ps;
1649 1655
1650 if (BB_isalnum(command[cursor]) || command[cursor] == '_') { 1656 if (BB_isalnum_or_underscore(command[cursor])) {
1651 while (cursor < command_len 1657 while (cursor < command_len
1652 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') 1658 && (BB_isalnum_or_underscore(command[cursor+1]))
1653 ) { 1659 ) {
1654 input_forward(); 1660 input_forward();
1655 } 1661 }
@@ -1691,9 +1697,9 @@ vi_end_motion(void)
1691 input_forward(); 1697 input_forward();
1692 if (cursor >= command_len-1) 1698 if (cursor >= command_len-1)
1693 return; 1699 return;
1694 if (BB_isalnum(command[cursor]) || command[cursor] == '_') { 1700 if (BB_isalnum_or_underscore(command[cursor])) {
1695 while (cursor < command_len-1 1701 while (cursor < command_len-1
1696 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') 1702 && (BB_isalnum_or_underscore(command[cursor+1]))
1697 ) { 1703 ) {
1698 input_forward(); 1704 input_forward();
1699 } 1705 }
@@ -1726,9 +1732,9 @@ vi_back_motion(void)
1726 input_backward(1); 1732 input_backward(1);
1727 if (cursor <= 0) 1733 if (cursor <= 0)
1728 return; 1734 return;
1729 if (BB_isalnum(command[cursor]) || command[cursor] == '_') { 1735 if (BB_isalnum_or_underscore(command[cursor])) {
1730 while (cursor > 0 1736 while (cursor > 0
1731 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_') 1737 && (BB_isalnum_or_underscore(command[cursor-1]))
1732 ) { 1738 ) {
1733 input_backward(1); 1739 input_backward(1);
1734 } 1740 }
diff --git a/libbb/login.c b/libbb/login.c
index 8f080b775..5a7acfcf0 100644
--- a/libbb/login.c
+++ b/libbb/login.c
@@ -45,6 +45,45 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
45 if (c == '\\' || c == '%') { 45 if (c == '\\' || c == '%') {
46 c = fgetc(fp); 46 c = fgetc(fp);
47 switch (c) { 47 switch (c) {
48//From getty manpage (* - supported by us)
49//========================================
50//4 or 4{interface}
51// Insert the IPv4 address of the network interface (example: \4{eth0}).
52// If the interface argument is not specified, then select the first
53// fully configured (UP, non-LOOPBACK, RUNNING) interface.
54//6 or 6{interface} -- The same as \4 but for IPv6.
55//b -- Insert the baudrate of the current line.
56//*d -- Insert the current date.
57//*t -- Insert the current time.
58//e or e{name}
59// Translate the human-readable name to an escape sequence and insert it
60// (for example: \e{red}Alert text.\e{reset}). If the name argument
61// is not specified, then insert \033. The currently supported names are:
62// black, blink, blue, bold, brown, cyan, darkgray, gray, green, halfbright,
63// lightblue, lightcyan, lightgray, lightgreen, lightmagenta, lightred,
64// magenta, red, reset, reverse, and yellow. Unknown names are ignored.
65//*s
66// Insert the system name (the name of the operating system - `uname -s`)
67//*S or S{VARIABLE}
68// Insert the VARIABLE data from /etc/os-release.
69// If the VARIABLE argument is not specified, use PRETTY_NAME.
70// If PRETTY_NAME is not in /etc/os-release, \S is the same as \s.
71//*l -- Insert the name of the current tty line.
72//*m -- Insert the architecture identifier of the machine: `uname -m`.
73//*n -- Insert the nodename of the machine: `uname -n`.
74//*o -- Insert the NIS domainname of the machine. Same as `hostname -d'.
75//*O -- Insert the DNS domainname of the machine.
76//*r -- Insert the release number of the OS: `uname -r`.
77//u -- Insert the number of current users logged in.
78//U -- Insert the string "1 user" or "N users" (current users logged in).
79//*v -- Insert the version of the OS, e.g. the build-date etc: `uname -v`.
80//We also implement:
81//*D -- same as \O "DNS domainname"
82//*h -- same as \n "nodename"
83
84 case 'S':
85 /* minimal implementation, not reading /etc/os-release */
86 /*FALLTHROUGH*/
48 case 's': 87 case 's':
49 outbuf = uts.sysname; 88 outbuf = uts.sysname;
50 break; 89 break;
@@ -65,6 +104,7 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
65#if defined(__linux__) 104#if defined(__linux__)
66 case 'D': 105 case 'D':
67 case 'o': 106 case 'o':
107 case 'O':
68 outbuf = uts.domainname; 108 outbuf = uts.domainname;
69 break; 109 break;
70#endif 110#endif
diff --git a/libbb/speed_table.c b/libbb/speed_table.c
index 174d531b2..11ced01d0 100644
--- a/libbb/speed_table.c
+++ b/libbb/speed_table.c
@@ -10,7 +10,16 @@
10#include "libbb.h" 10#include "libbb.h"
11 11
12struct speed_map { 12struct speed_map {
13#if defined __FreeBSD__ 13#if defined __FreeBSD__ \
14 || (defined B115200 && B115200 > 0xffff) \
15 || (defined B230400 && B230400 > 0xffff) \
16 || (defined B460800 && B460800 > 0xffff) \
17 || (defined B921600 && B921600 > 0xffff) \
18 || (defined B1152000 && B1152000 > 0xffff) \
19 || (defined B1000000 && B1000000 > 0xffff) \
20 || (defined B2000000 && B2000000 > 0xffff) \
21 || (defined B3000000 && B3000000 > 0xffff) \
22 || (defined B4000000 && B4000000 > 0xffff)
14 /* On FreeBSD, B<num> constants don't fit into a short */ 23 /* On FreeBSD, B<num> constants don't fit into a short */
15 unsigned speed; 24 unsigned speed;
16#else 25#else
@@ -19,6 +28,7 @@ struct speed_map {
19 unsigned short value; 28 unsigned short value;
20}; 29};
21 30
31/* On Linux, Bxx constants are 0..15 (up to B38400) and 0x1001..0x100f */
22static const struct speed_map speeds[] = { 32static const struct speed_map speeds[] = {
23 {B0, 0}, 33 {B0, 0},
24 {B50, 50}, 34 {B50, 50},
@@ -37,30 +47,79 @@ static const struct speed_map speeds[] = {
37#ifdef B19200 47#ifdef B19200
38 {B19200, 19200}, 48 {B19200, 19200},
39#elif defined(EXTA) 49#elif defined(EXTA)
40 {EXTA, 19200}, 50 {EXTA, 19200},
41#endif 51#endif
52/* 19200 = 0x4b00 */
53/* 38400 = 0x9600, this value would use bit#15 if not "/200" encoded: */
42#ifdef B38400 54#ifdef B38400
43 {B38400, 38400/256 + 0x8000U}, 55 {B38400, 38400/200 + 0x8000u},
44#elif defined(EXTB) 56#elif defined(EXTB)
45 {EXTB, 38400/256 + 0x8000U}, 57 {EXTB, 38400/200 + 0x8000u},
46#endif 58#endif
47#ifdef B57600 59#ifdef B57600
48 {B57600, 57600/256 + 0x8000U}, 60 {B57600, 57600/200 + 0x8000u},
49#endif 61#endif
50#ifdef B115200 62#ifdef B115200
51 {B115200, 115200/256 + 0x8000U}, 63 {B115200, 115200/200 + 0x8000u},
52#endif 64#endif
53#ifdef B230400 65#ifdef B230400
54 {B230400, 230400/256 + 0x8000U}, 66 {B230400, 230400/200 + 0x8000u},
55#endif 67#endif
56#ifdef B460800 68#ifdef B460800
57 {B460800, 460800/256 + 0x8000U}, 69 {B460800, 460800/200 + 0x8000u},
70#endif
71#ifdef B576000
72 {B576000, 576000/200 + 0x8000u},
58#endif 73#endif
59#ifdef B921600 74#ifdef B921600
60 {B921600, 921600/256 + 0x8000U}, 75 {B921600, 921600/200 + 0x8000u},
76#endif
77#ifdef B1152000
78 {B1152000, 1152000/200 + 0x8000u},
79#endif
80
81#ifdef B500000
82 {B500000, 500000/200 + 0x8000u},
83#endif
84#ifdef B1000000
85 {B1000000, 1000000/200 + 0x8000u},
86#endif
87#ifdef B1500000
88 {B1500000, 1500000/200 + 0x8000u},
89#endif
90#ifdef B2000000
91 {B2000000, 2000000/200 + 0x8000u},
92#endif
93#ifdef B2500000
94 {B2500000, 2500000/200 + 0x8000u},
61#endif 95#endif
96#ifdef B3000000
97 {B3000000, 3000000/200 + 0x8000u},
98#endif
99#ifdef B3500000
100 {B3500000, 3500000/200 + 0x8000u},
101#endif
102#ifdef B4000000
103 {B4000000, 4000000/200 + 0x8000u},
104#endif
105/* 4000000/200 = 0x4e20, bit#15 still does not interfere with the value */
106/* (can use /800 if higher speeds would appear, /1600 won't work for B500000) */
62}; 107};
63 108
109/*
110 * TODO: maybe we can just bite the bullet, ditch the table and use termios2
111 * Linux API (supports arbitrary baud rates, no Bxxxx mess needed)? Example:
112 *
113 * #include <asm/termios.h>
114 * #include <asm/ioctls.h>
115 * struct termios2 t;
116 * ioctl(fd, TCGETS2, &t);
117 * t.c_ospeed = t.c_ispeed = 543210;
118 * t.c_cflag &= ~CBAUD;
119 * t.c_cflag |= BOTHER;
120 * ioctl(fd, TCSETS2, &t);
121 */
122
64enum { NUM_SPEEDS = ARRAY_SIZE(speeds) }; 123enum { NUM_SPEEDS = ARRAY_SIZE(speeds) };
65 124
66unsigned FAST_FUNC tty_baud_to_value(speed_t speed) 125unsigned FAST_FUNC tty_baud_to_value(speed_t speed)
@@ -69,8 +128,8 @@ unsigned FAST_FUNC tty_baud_to_value(speed_t speed)
69 128
70 do { 129 do {
71 if (speed == speeds[i].speed) { 130 if (speed == speeds[i].speed) {
72 if (speeds[i].value & 0x8000U) { 131 if (speeds[i].value & 0x8000u) {
73 return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256; 132 return ((unsigned)(speeds[i].value) & 0x7fffU) * 200;
74 } 133 }
75 return speeds[i].value; 134 return speeds[i].value;
76 } 135 }
diff --git a/libbb/udp_io.c b/libbb/udp_io.c
index a32af9bd2..6e3ef484e 100644
--- a/libbb/udp_io.c
+++ b/libbb/udp_io.c
@@ -70,7 +70,13 @@ send_to_from(int fd, void *buf, size_t len, int flags,
70 msg.msg_flags = flags; 70 msg.msg_flags = flags;
71 71
72 cmsgptr = CMSG_FIRSTHDR(&msg); 72 cmsgptr = CMSG_FIRSTHDR(&msg);
73 if (to->sa_family == AF_INET && from->sa_family == AF_INET) { 73 /*
74 * Users report that to->sa_family can be AF_INET6 too,
75 * if "to" was acquired by recv_from_to(). IOW: recv_from_to()
76 * was seen showing IPv6 "from" even when the destination
77 * of received packet (our local address) was IPv4.
78 */
79 if (/* to->sa_family == AF_INET && */ from->sa_family == AF_INET) {
74 struct in_pktinfo *pktptr; 80 struct in_pktinfo *pktptr;
75 cmsgptr->cmsg_level = IPPROTO_IP; 81 cmsgptr->cmsg_level = IPPROTO_IP;
76 cmsgptr->cmsg_type = IP_PKTINFO; 82 cmsgptr->cmsg_type = IP_PKTINFO;
@@ -86,7 +92,7 @@ send_to_from(int fd, void *buf, size_t len, int flags,
86 pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; 92 pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
87 } 93 }
88# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) 94# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
89 else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { 95 else if (/* to->sa_family == AF_INET6 && */ from->sa_family == AF_INET6) {
90 struct in6_pktinfo *pktptr; 96 struct in6_pktinfo *pktptr;
91 cmsgptr->cmsg_level = IPPROTO_IPV6; 97 cmsgptr->cmsg_level = IPPROTO_IPV6;
92 cmsgptr->cmsg_type = IPV6_PKTINFO; 98 cmsgptr->cmsg_type = IPV6_PKTINFO;
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index f488f8e0c..766a89e5f 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -70,7 +70,8 @@ pid_t FAST_FUNC xspawn(char **argv)
70 return pid; 70 return pid;
71} 71}
72 72
73#if ENABLE_FEATURE_PREFER_APPLETS 73#if ENABLE_FEATURE_PREFER_APPLETS \
74 || ENABLE_FEATURE_SH_NOFORK
74static jmp_buf die_jmp; 75static jmp_buf die_jmp;
75static void jump(void) 76static void jump(void)
76{ 77{
@@ -176,7 +177,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
176 177
177 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ 178 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
178} 179}
179#endif /* FEATURE_PREFER_APPLETS */ 180#endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */
180 181
181int FAST_FUNC spawn_and_wait(char **argv) 182int FAST_FUNC spawn_and_wait(char **argv)
182{ 183{
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index c318af419..ee54898e3 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -500,12 +500,15 @@ char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
500{ 500{
501 return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); 501 return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
502} 502}
503#ifndef NI_NUMERICSCOPE
504# define NI_NUMERICSCOPE 0
505#endif
503char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) 506char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
504{ 507{
505 return sockaddr2str(sa, NI_NUMERICHOST); 508 return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE);
506} 509}
507 510
508char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) 511char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
509{ 512{
510 return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); 513 return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE | IGNORE_PORT);
511} 514}
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index e9222f690..1b11caf6b 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -235,8 +235,16 @@ void FAST_FUNC xwrite(int fd, const void *buf, size_t count)
235{ 235{
236 if (count) { 236 if (count) {
237 ssize_t size = full_write(fd, buf, count); 237 ssize_t size = full_write(fd, buf, count);
238 if ((size_t)size != count) 238 if ((size_t)size != count) {
239 bb_error_msg_and_die("short write"); 239 /*
240 * Two cases: write error immediately;
241 * or some writes succeeded, then we hit an error.
242 * In either case, errno is set.
243 */
244 bb_perror_msg_and_die(
245 size >= 0 ? "short write" : "write error"
246 );
247 }
240 } 248 }
241} 249}
242void FAST_FUNC xwrite_str(int fd, const char *str) 250void FAST_FUNC xwrite_str(int fd, const char *str)
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index fb4dbb3da..1242795b8 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -10,7 +10,8 @@
10//kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o 10//kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
11 11
12//usage:#define sendmail_trivial_usage 12//usage:#define sendmail_trivial_usage
13//usage: "[OPTIONS] [RECIPIENT_EMAIL]..." 13//usage: "[-tv] [-f SENDER] [-amLOGIN 4<user_pass.txt | -auUSER -apPASS]"
14//usage: "\n [-w SECS] [-H 'PROG ARGS' | -S HOST] [RECIPIENT_EMAIL]..."
14//usage:#define sendmail_full_usage "\n\n" 15//usage:#define sendmail_full_usage "\n\n"
15//usage: "Read email from stdin and send it\n" 16//usage: "Read email from stdin and send it\n"
16//usage: "\nStandard options:" 17//usage: "\nStandard options:"
@@ -18,27 +19,25 @@
18//usage: "\n -f SENDER For use in MAIL FROM:<sender>. Can be empty string" 19//usage: "\n -f SENDER For use in MAIL FROM:<sender>. Can be empty string"
19//usage: "\n Default: -auUSER, or username of current UID" 20//usage: "\n Default: -auUSER, or username of current UID"
20//usage: "\n -o OPTIONS Various options. -oi implied, others are ignored" 21//usage: "\n -o OPTIONS Various options. -oi implied, others are ignored"
21//usage: "\n -i -oi synonym. implied and ignored" 22//usage: "\n -i -oi synonym, implied and ignored"
22//usage: "\n" 23//usage: "\n"
23//usage: "\nBusybox specific options:" 24//usage: "\nBusybox specific options:"
24//usage: "\n -v Verbose" 25//usage: "\n -v Verbose"
25//usage: "\n -w SECS Network timeout" 26//usage: "\n -w SECS Network timeout"
26//usage: "\n -H 'PROG ARGS' Run connection helper" 27//usage: "\n -H 'PROG ARGS' Run connection helper. Examples:"
27//usage: "\n Examples:" 28//usage: "\n openssl s_client -quiet -tls1 -starttls smtp -connect smtp.gmail.com:25"
28//usage: "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp" 29//usage: "\n openssl s_client -quiet -tls1 -connect smtp.gmail.com:465"
29//usage: "\n -connect smtp.gmail.com:25' <email.txt" 30//usage: "\n $SMTP_ANTISPAM_DELAY: seconds to wait after helper connect"
30//usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]" 31//usage: "\n -S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)"
31//usage: "\n -H 'exec openssl s_client -quiet -tls1" 32//usage: "\n -amLOGIN Log in using AUTH LOGIN (-amCRAM-MD5 not supported)"
32//usage: "\n -connect smtp.gmail.com:465' <email.txt" 33//usage: "\n -auUSER Username for AUTH"
33//usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]" 34//usage: "\n -apPASS Password for AUTH"
34//usage: "\n -S HOST[:PORT] Server"
35//usage: "\n -auUSER Username for AUTH LOGIN"
36//usage: "\n -apPASS Password for AUTH LOGIN"
37////usage: "\n -amMETHOD Authentication method. Ignored. LOGIN is implied"
38//usage: "\n" 35//usage: "\n"
39//usage: "\nOther options are silently ignored; -oi -t is implied" 36//usage: "\nIf no -a options are given, authentication is not done."
37//usage: "\nIf -amLOGIN is given but no -au/-ap, user/password is read from fd #4."
38//usage: "\nOther options are silently ignored; -oi is implied."
40//usage: IF_MAKEMIME( 39//usage: IF_MAKEMIME(
41//usage: "\nUse makemime to create emails with attachments" 40//usage: "\nUse makemime to create emails with attachments."
42//usage: ) 41//usage: )
43 42
44/* Currently we don't sanitize or escape user-supplied SENDER and RECIPIENT_EMAILs. 43/* Currently we don't sanitize or escape user-supplied SENDER and RECIPIENT_EMAILs.
@@ -209,7 +208,7 @@ static void rcptto_list(const char *list)
209int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 208int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
210int sendmail_main(int argc UNUSED_PARAM, char **argv) 209int sendmail_main(int argc UNUSED_PARAM, char **argv)
211{ 210{
212 char *opt_connect = opt_connect; 211 char *opt_connect;
213 char *opt_from = NULL; 212 char *opt_from = NULL;
214 char *s; 213 char *s;
215 llist_t *list = NULL; 214 llist_t *list = NULL;
@@ -241,6 +240,11 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
241 // init global variables 240 // init global variables
242 INIT_G(); 241 INIT_G();
243 242
243 // default HOST[:PORT] is $SMTPHOST, or localhost
244 opt_connect = getenv("SMTPHOST");
245 if (!opt_connect)
246 opt_connect = (char *)"127.0.0.1";
247
244 // save initial stdin since body is piped! 248 // save initial stdin since body is piped!
245 xdup2(STDIN_FILENO, 3); 249 xdup2(STDIN_FILENO, 3);
246 G.fp0 = xfdopen_for_read(3); 250 G.fp0 = xfdopen_for_read(3);
@@ -276,6 +280,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
276 280
277 // connection helper ordered? -> 281 // connection helper ordered? ->
278 if (opts & OPT_H) { 282 if (opts & OPT_H) {
283 const char *delay;
279 const char *args[] = { "sh", "-c", opt_connect, NULL }; 284 const char *args[] = { "sh", "-c", opt_connect, NULL };
280 // plug it in 285 // plug it in
281 launch_helper(args); 286 launch_helper(args);
@@ -294,7 +299,12 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
294 // before 220 reached it. The code below is unsafe in this regard: 299 // before 220 reached it. The code below is unsafe in this regard:
295 // in non-STARTTLSed case, we potentially send NOOP before 220 300 // in non-STARTTLSed case, we potentially send NOOP before 220
296 // is sent by server. 301 // is sent by server.
297 // Ideas? (--delay SECS opt? --assume-starttls-helper opt?) 302 //
303 // If $SMTP_ANTISPAM_DELAY is set, we pause before sending NOOP.
304 //
305 delay = getenv("SMTP_ANTISPAM_DELAY");
306 if (delay)
307 sleep(atoi(delay));
298 code = smtp_check("NOOP", -1); 308 code = smtp_check("NOOP", -1);
299 if (code == 220) 309 if (code == 220)
300 // we got 220 - this is not STARTTLSed connection, 310 // we got 220 - this is not STARTTLSed connection,
@@ -306,14 +316,6 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
306 } else { 316 } else {
307 // vanilla connection 317 // vanilla connection
308 int fd; 318 int fd;
309 // host[:port] not explicitly specified? -> use $SMTPHOST
310 // no $SMTPHOST? -> use localhost
311 if (!(opts & OPT_S)) {
312 opt_connect = getenv("SMTPHOST");
313 if (!opt_connect)
314 opt_connect = (char *)"127.0.0.1";
315 }
316 // do connect
317 fd = create_and_connect_stream_or_die(opt_connect, 25); 319 fd = create_and_connect_stream_or_die(opt_connect, 25);
318 // and make ourselves a simple IO filter 320 // and make ourselves a simple IO filter
319 xmove_fd(fd, STDIN_FILENO); 321 xmove_fd(fd, STDIN_FILENO);
diff --git a/miscutils/less.c b/miscutils/less.c
index f9f6f62d9..8ba687872 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -173,6 +173,7 @@ enum { pattern_valid = 0 };
173struct globals { 173struct globals {
174 int cur_fline; /* signed */ 174 int cur_fline; /* signed */
175 int kbd_fd; /* fd to get input from */ 175 int kbd_fd; /* fd to get input from */
176 int kbd_fd_orig_flags;
176 int less_gets_pos; 177 int less_gets_pos;
177/* last position in last line, taking into account tabs */ 178/* last position in last line, taking into account tabs */
178 size_t last_line_pos; 179 size_t last_line_pos;
@@ -308,6 +309,8 @@ static void print_statusline(const char *str)
308static void less_exit(int code) 309static void less_exit(int code)
309{ 310{
310 set_tty_cooked(); 311 set_tty_cooked();
312 if (!(G.kbd_fd_orig_flags & O_NONBLOCK))
313 ndelay_off(kbd_fd);
311 clear_line(); 314 clear_line();
312 if (code < 0) 315 if (code < 0)
313 kill_myself_with_sig(- code); /* does not return */ 316 kill_myself_with_sig(- code); /* does not return */
@@ -1661,16 +1664,23 @@ static char opp_bracket(char bracket)
1661 1664
1662static void match_right_bracket(char bracket) 1665static void match_right_bracket(char bracket)
1663{ 1666{
1664 unsigned i; 1667 unsigned i = cur_fline;
1665 1668
1666 if (strchr(flines[cur_fline], bracket) == NULL) { 1669 if (i >= max_fline
1670 || strchr(flines[i], bracket) == NULL
1671 ) {
1667 print_statusline("No bracket in top line"); 1672 print_statusline("No bracket in top line");
1668 return; 1673 return;
1669 } 1674 }
1675
1670 bracket = opp_bracket(bracket); 1676 bracket = opp_bracket(bracket);
1671 for (i = cur_fline + 1; i < max_fline; i++) { 1677 for (; i < max_fline; i++) {
1672 if (strchr(flines[i], bracket) != NULL) { 1678 if (strchr(flines[i], bracket) != NULL) {
1673 buffer_line(i); 1679 /*
1680 * Line with matched right bracket becomes
1681 * last visible line
1682 */
1683 buffer_line(i - max_displayed_line);
1674 return; 1684 return;
1675 } 1685 }
1676 } 1686 }
@@ -1679,16 +1689,22 @@ static void match_right_bracket(char bracket)
1679 1689
1680static void match_left_bracket(char bracket) 1690static void match_left_bracket(char bracket)
1681{ 1691{
1682 int i; 1692 int i = cur_fline + max_displayed_line;
1683 1693
1684 if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) { 1694 if (i >= max_fline
1695 || strchr(flines[i], bracket) == NULL
1696 ) {
1685 print_statusline("No bracket in bottom line"); 1697 print_statusline("No bracket in bottom line");
1686 return; 1698 return;
1687 } 1699 }
1688 1700
1689 bracket = opp_bracket(bracket); 1701 bracket = opp_bracket(bracket);
1690 for (i = cur_fline + max_displayed_line; i >= 0; i--) { 1702 for (; i >= 0; i--) {
1691 if (strchr(flines[i], bracket) != NULL) { 1703 if (strchr(flines[i], bracket) != NULL) {
1704 /*
1705 * Line with matched left bracket becomes
1706 * first visible line
1707 */
1692 buffer_line(i); 1708 buffer_line(i);
1693 return; 1709 return;
1694 } 1710 }
@@ -1859,9 +1875,10 @@ int less_main(int argc, char **argv)
1859 /* Some versions of less can survive w/o controlling tty, 1875 /* Some versions of less can survive w/o controlling tty,
1860 * try to do the same. This also allows to specify an alternative 1876 * try to do the same. This also allows to specify an alternative
1861 * tty via "less 1<>TTY". 1877 * tty via "less 1<>TTY".
1862 * We don't try to use STDOUT_FILENO directly, 1878 *
1879 * We prefer not to use STDOUT_FILENO directly,
1863 * since we want to set this fd to non-blocking mode, 1880 * since we want to set this fd to non-blocking mode,
1864 * and not bother with restoring it on exit. 1881 * and not interfere with other processes which share stdout with us.
1865 */ 1882 */
1866 tty_name = xmalloc_ttyname(STDOUT_FILENO); 1883 tty_name = xmalloc_ttyname(STDOUT_FILENO);
1867 if (tty_name) { 1884 if (tty_name) {
@@ -1873,10 +1890,12 @@ int less_main(int argc, char **argv)
1873 /* Try controlling tty */ 1890 /* Try controlling tty */
1874 try_ctty: 1891 try_ctty:
1875 tty_fd = open(CURRENT_TTY, O_RDONLY); 1892 tty_fd = open(CURRENT_TTY, O_RDONLY);
1876 if (tty_fd < 0) 1893 if (tty_fd < 0) {
1877 return bb_cat(argv); 1894 /* If all else fails, less 481 uses stdout. Mimic that */
1895 tty_fd = STDOUT_FILENO;
1896 }
1878 } 1897 }
1879 ndelay_on(tty_fd); 1898 G.kbd_fd_orig_flags = ndelay_on(tty_fd);
1880 kbd_fd = tty_fd; /* save in a global */ 1899 kbd_fd = tty_fd; /* save in a global */
1881#else 1900#else
1882 kbd_fd = 0; 1901 kbd_fd = 0;
diff --git a/miscutils/man.c b/miscutils/man.c
index 4ce40b247..6d32890d1 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -232,8 +232,8 @@ int man_main(int argc UNUSED_PARAM, char **argv)
232 if (!token[1]) 232 if (!token[1])
233 continue; 233 continue;
234 if (strcmp("DEFINE", token[0]) == 0) { 234 if (strcmp("DEFINE", token[0]) == 0) {
235 if (is_prefixed_with("pager", token[1])) { 235 if (is_prefixed_with(token[1], "pager")) {
236 pager = xstrdup(skip_whitespace(token[1]) + 5); 236 pager = xstrdup(skip_whitespace(token[1] + 5));
237 } 237 }
238 } else 238 } else
239 if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ 239 if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 5936e48cf..a47e52234 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -18,9 +18,13 @@
18/* After libbb.h, since it needs sys/types.h on some systems */ 18/* After libbb.h, since it needs sys/types.h on some systems */
19#include <sys/utsname.h> /* uname() */ 19#include <sys/utsname.h> /* uname() */
20#include <fnmatch.h> 20#include <fnmatch.h>
21#include <sys/syscall.h>
21 22
22extern int init_module(void *module, unsigned long len, const char *options); 23extern int init_module(void *module, unsigned long len, const char *options);
23extern int delete_module(const char *module, unsigned flags); 24extern int delete_module(const char *module, unsigned flags);
25#ifdef __NR_finit_module
26# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
27#endif
24/* linux/include/linux/module.h has limit of 64 chars on module names */ 28/* linux/include/linux/module.h has limit of 64 chars on module names */
25#undef MODULE_NAME_LEN 29#undef MODULE_NAME_LEN
26#define MODULE_NAME_LEN 64 30#define MODULE_NAME_LEN 64
@@ -209,11 +213,34 @@ static int load_module(const char *fname, const char *options)
209 int r; 213 int r;
210 size_t len = MAXINT(ssize_t); 214 size_t len = MAXINT(ssize_t);
211 char *module_image; 215 char *module_image;
216
217 if (!options)
218 options = "";
219
212 dbg1_error_msg("load_module('%s','%s')", fname, options); 220 dbg1_error_msg("load_module('%s','%s')", fname, options);
213 221
214 module_image = xmalloc_open_zipped_read_close(fname, &len); 222 /*
215 r = (!module_image || init_module(module_image, len, options ? options : "") != 0); 223 * First we try finit_module if available. Some kernels are configured
216 free(module_image); 224 * to only allow loading of modules off of secure storage (like a read-
225 * only rootfs) which needs the finit_module call. If it fails, we fall
226 * back to normal module loading to support compressed modules.
227 */
228 r = 1;
229# ifdef __NR_finit_module
230 {
231 int fd = open(fname, O_RDONLY | O_CLOEXEC);
232 if (fd >= 0) {
233 r = finit_module(fd, options, 0) != 0;
234 close(fd);
235 }
236 }
237# endif
238 if (r != 0) {
239 module_image = xmalloc_open_zipped_read_close(fname, &len);
240 r = (!module_image || init_module(module_image, len, options) != 0);
241 free(module_image);
242 }
243
217 dbg1_error_msg("load_module:%d", r); 244 dbg1_error_msg("load_module:%d", r);
218 return r; /* 0 = success */ 245 return r; /* 0 = success */
219#else 246#else
diff --git a/modutils/modutils.c b/modutils/modutils.c
index 0a056731d..d36caaf68 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -13,6 +13,9 @@ extern int delete_module(const char *module, unsigned int flags);
13#else 13#else
14# include <sys/syscall.h> 14# include <sys/syscall.h>
15# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) 15# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
16# if defined(__NR_finit_module)
17# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
18# endif
16# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) 19# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
17#endif 20#endif
18 21
@@ -212,6 +215,24 @@ int FAST_FUNC bb_init_module(const char *filename, const char *options)
212 return bb_init_module_24(filename, options); 215 return bb_init_module_24(filename, options);
213#endif 216#endif
214 217
218 /*
219 * First we try finit_module if available. Some kernels are configured
220 * to only allow loading of modules off of secure storage (like a read-
221 * only rootfs) which needs the finit_module call. If it fails, we fall
222 * back to normal module loading to support compressed modules.
223 */
224# ifdef __NR_finit_module
225 {
226 int fd = open(filename, O_RDONLY | O_CLOEXEC);
227 if (fd >= 0) {
228 rc = finit_module(fd, options, 0) != 0;
229 close(fd);
230 if (rc == 0)
231 return rc;
232 }
233 }
234# endif
235
215 image_size = INT_MAX - 4095; 236 image_size = INT_MAX - 4095;
216 mmaped = 0; 237 mmaped = 0;
217 image = try_to_mmap_module(filename, &image_size); 238 image = try_to_mmap_module(filename, &image_size);
diff --git a/networking/Config.src b/networking/Config.src
index 27c604a31..eb0536a7c 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -912,17 +912,9 @@ config FEATURE_TRACEROUTE_VERBOSE
912 Add some verbosity to traceroute. This includes among other things 912 Add some verbosity to traceroute. This includes among other things
913 hostnames and ICMP response types. 913 hostnames and ICMP response types.
914 914
915config FEATURE_TRACEROUTE_SOURCE_ROUTE
916 bool "Enable loose source route"
917 default n
918 depends on TRACEROUTE
919 help
920 Add option to specify a loose source route gateway
921 (8 maximum).
922
923config FEATURE_TRACEROUTE_USE_ICMP 915config FEATURE_TRACEROUTE_USE_ICMP
924 bool "Use ICMP instead of UDP" 916 bool "Enable -I option (use ICMP instead of UDP)"
925 default n 917 default y
926 depends on TRACEROUTE 918 depends on TRACEROUTE
927 help 919 help
928 Add option -I to use ICMP ECHO instead of UDP datagrams. 920 Add option -I to use ICMP ECHO instead of UDP datagrams.
diff --git a/networking/ifplugd.c b/networking/ifplugd.c
index 28c49e218..029cba147 100644
--- a/networking/ifplugd.c
+++ b/networking/ifplugd.c
@@ -358,8 +358,12 @@ static void up_iface(void)
358 ifrequest.ifr_flags |= IFF_UP; 358 ifrequest.ifr_flags |= IFF_UP;
359 /* Let user know we mess up with interface */ 359 /* Let user know we mess up with interface */
360 bb_error_msg("upping interface"); 360 bb_error_msg("upping interface");
361 if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) 361 if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) {
362 xfunc_die(); 362 if (errno != ENODEV)
363 xfunc_die();
364 G.iface_exists = 0;
365 return;
366 }
363 } 367 }
364 368
365#if 0 /* why do we mess with IP addr? It's not our business */ 369#if 0 /* why do we mess with IP addr? It's not our business */
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index 25b04c9d7..b0bc0d70f 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -21,7 +21,7 @@
21//usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..." 21//usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
22//usage:#define ifup_full_usage "\n\n" 22//usage:#define ifup_full_usage "\n\n"
23//usage: " -a De/configure all interfaces automatically" 23//usage: " -a De/configure all interfaces automatically"
24//usage: "\n -i FILE Use FILE for interface definitions" 24//usage: "\n -i FILE Use FILE instead of /etc/network/interfaces"
25//usage: "\n -n Print out what would happen, but don't do it" 25//usage: "\n -n Print out what would happen, but don't do it"
26//usage: IF_FEATURE_IFUPDOWN_MAPPING( 26//usage: IF_FEATURE_IFUPDOWN_MAPPING(
27//usage: "\n (note: doesn't disable mappings)" 27//usage: "\n (note: doesn't disable mappings)"
@@ -492,7 +492,7 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
492 result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up", 492 result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
493 ifd, exec); 493 ifd, exec);
494 result += execute("ifconfig %iface% %address% netmask %netmask%" 494 result += execute("ifconfig %iface% %address% netmask %netmask%"
495 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", 495 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]]",
496 ifd, exec); 496 ifd, exec);
497 result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec); 497 result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec);
498 return ((result == 3) ? 3 : 0); 498 return ((result == 3) ? 3 : 0);
@@ -503,7 +503,10 @@ static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
503{ 503{
504 int result; 504 int result;
505# if ENABLE_FEATURE_IFUPDOWN_IP 505# if ENABLE_FEATURE_IFUPDOWN_IP
506 result = execute("ip addr flush dev %iface%", ifd, exec); 506 /* Optional "label LBL" is necessary if interface is an alias (eth0:0),
507 * otherwise "ip addr flush dev eth0:0" flushes all addresses on eth0.
508 */
509 result = execute("ip addr flush dev %iface%[[ label %label%]]", ifd, exec);
507 result += execute("ip link set %iface% down", ifd, exec); 510 result += execute("ip link set %iface% down", ifd, exec);
508# else 511# else
509 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */ 512 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
@@ -1066,6 +1069,11 @@ static int execute_all(struct interface_defn_t *ifd, const char *opt)
1066 } 1069 }
1067 } 1070 }
1068 1071
1072 /* Tested on Debian Squeeze: "standard" ifup runs this without
1073 * checking that directory exists. If it doesn't, run-parts
1074 * complains, and this message _is_ annoyingly visible.
1075 * Don't "fix" this (unless newer Debian does).
1076 */
1069 buf = xasprintf("run-parts /etc/network/if-%s.d", opt); 1077 buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
1070 /* heh, we don't bother free'ing it */ 1078 /* heh, we don't bother free'ing it */
1071 return doit(buf); 1079 return doit(buf);
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index e674e9a0d..48dc6e3d9 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -362,10 +362,9 @@ IF_FEATURE_IP_RULE(ARG_table,)
362 req.r.rtm_scope = RT_SCOPE_NOWHERE; 362 req.r.rtm_scope = RT_SCOPE_NOWHERE;
363 363
364 if (cmd != RTM_DELROUTE) { 364 if (cmd != RTM_DELROUTE) {
365 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
365 if (RTPROT_BOOT != 0) 366 if (RTPROT_BOOT != 0)
366 req.r.rtm_protocol = RTPROT_BOOT; 367 req.r.rtm_protocol = RTPROT_BOOT;
367 if (RT_SCOPE_UNIVERSE != 0)
368 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
369 if (RTN_UNICAST != 0) 368 if (RTN_UNICAST != 0)
370 req.r.rtm_type = RTN_UNICAST; 369 req.r.rtm_type = RTN_UNICAST;
371 } 370 }
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c
index dba64346f..c486834b0 100644
--- a/networking/libiproute/iprule.c
+++ b/networking/libiproute/iprule.c
@@ -197,9 +197,11 @@ static int iprule_modify(int cmd, char **argv)
197 req.n.nlmsg_flags = NLM_F_REQUEST; 197 req.n.nlmsg_flags = NLM_F_REQUEST;
198 req.r.rtm_family = preferred_family; 198 req.r.rtm_family = preferred_family;
199 req.r.rtm_protocol = RTPROT_BOOT; 199 req.r.rtm_protocol = RTPROT_BOOT;
200 req.r.rtm_scope = RT_SCOPE_UNIVERSE; 200 if (RT_SCOPE_UNIVERSE != 0)
201 req.r.rtm_table = 0; 201 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
202 req.r.rtm_type = RTN_UNSPEC; 202 /*req.r.rtm_table = 0; - already is */
203 if (RTN_UNSPEC != 0)
204 req.r.rtm_type = RTN_UNSPEC;
203 205
204 if (cmd == RTM_NEWRULE) { 206 if (cmd == RTM_NEWRULE) {
205 req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; 207 req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
index cbb5daf95..7e0ff1b6c 100644
--- a/networking/libiproute/libnetlink.c
+++ b/networking/libiproute/libnetlink.c
@@ -68,30 +68,32 @@ int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
68 68
69int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) 69int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
70{ 70{
71 struct nlmsghdr nlh; 71 struct {
72 struct sockaddr_nl nladdr; 72 struct nlmsghdr nlh;
73 struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; 73 struct msghdr msg;
74 /* Use designated initializers, struct layout is non-portable */ 74 struct sockaddr_nl nladdr;
75 struct msghdr msg = { 75 } s;
76 .msg_name = (void*)&nladdr, 76 struct iovec iov[2] = { { &s.nlh, sizeof(s.nlh) }, { req, len } };
77 .msg_namelen = sizeof(nladdr), 77
78 .msg_iov = iov, 78 memset(&s, 0, sizeof(s));
79 .msg_iovlen = 2, 79
80 .msg_control = NULL, 80 s.msg.msg_name = (void*)&s.nladdr;
81 .msg_controllen = 0, 81 s.msg.msg_namelen = sizeof(s.nladdr);
82 .msg_flags = 0 82 s.msg.msg_iov = iov;
83 }; 83 s.msg.msg_iovlen = 2;
84 84 /*s.msg.msg_control = NULL; - already is */
85 memset(&nladdr, 0, sizeof(nladdr)); 85 /*s.msg.msg_controllen = 0; - already is */
86 nladdr.nl_family = AF_NETLINK; 86 /*s.msg.msg_flags = 0; - already is */
87 87
88 nlh.nlmsg_len = NLMSG_LENGTH(len); 88 s.nladdr.nl_family = AF_NETLINK;
89 nlh.nlmsg_type = type; 89
90 nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; 90 s.nlh.nlmsg_len = NLMSG_LENGTH(len);
91 nlh.nlmsg_pid = 0; 91 s.nlh.nlmsg_type = type;
92 nlh.nlmsg_seq = rth->dump = ++rth->seq; 92 s.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
93 93 /*s.nlh.nlmsg_pid = 0; - already is */
94 return sendmsg(rth->fd, &msg, 0); 94 s.nlh.nlmsg_seq = rth->dump = ++rth->seq;
95
96 return sendmsg(rth->fd, &s.msg, 0);
95} 97}
96 98
97static int rtnl_dump_filter(struct rtnl_handle *rth, 99static int rtnl_dump_filter(struct rtnl_handle *rth,
@@ -338,14 +340,14 @@ int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
338 int len = RTA_LENGTH(4); 340 int len = RTA_LENGTH(4);
339 struct rtattr *rta; 341 struct rtattr *rta;
340 342
341 if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { 343 if ((int)(NLMSG_ALIGN(n->nlmsg_len + len)) > maxlen) {
342 return -1; 344 return -1;
343 } 345 }
344 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); 346 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
345 rta->rta_type = type; 347 rta->rta_type = type;
346 rta->rta_len = len; 348 rta->rta_len = len;
347 move_to_unaligned32(RTA_DATA(rta), data); 349 move_to_unaligned32(RTA_DATA(rta), data);
348 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 350 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len + len);
349 return 0; 351 return 0;
350} 352}
351 353
@@ -354,14 +356,14 @@ int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, in
354 int len = RTA_LENGTH(alen); 356 int len = RTA_LENGTH(alen);
355 struct rtattr *rta; 357 struct rtattr *rta;
356 358
357 if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { 359 if ((int)(NLMSG_ALIGN(n->nlmsg_len + len)) > maxlen) {
358 return -1; 360 return -1;
359 } 361 }
360 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); 362 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
361 rta->rta_type = type; 363 rta->rta_type = type;
362 rta->rta_len = len; 364 rta->rta_len = len;
363 memcpy(RTA_DATA(rta), data, alen); 365 memcpy(RTA_DATA(rta), data, alen);
364 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 366 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len + len);
365 return 0; 367 return 0;
366} 368}
367 369
@@ -370,14 +372,14 @@ int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t d
370 int len = RTA_LENGTH(4); 372 int len = RTA_LENGTH(4);
371 struct rtattr *subrta; 373 struct rtattr *subrta;
372 374
373 if (RTA_ALIGN(rta->rta_len) + len > maxlen) { 375 if (RTA_ALIGN(rta->rta_len + len) > maxlen) {
374 return -1; 376 return -1;
375 } 377 }
376 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); 378 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
377 subrta->rta_type = type; 379 subrta->rta_type = type;
378 subrta->rta_len = len; 380 subrta->rta_len = len;
379 move_to_unaligned32(RTA_DATA(subrta), data); 381 move_to_unaligned32(RTA_DATA(subrta), data);
380 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; 382 rta->rta_len = NLMSG_ALIGN(rta->rta_len + len);
381 return 0; 383 return 0;
382} 384}
383 385
@@ -386,14 +388,14 @@ int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data
386 struct rtattr *subrta; 388 struct rtattr *subrta;
387 int len = RTA_LENGTH(alen); 389 int len = RTA_LENGTH(alen);
388 390
389 if (RTA_ALIGN(rta->rta_len) + len > maxlen) { 391 if (RTA_ALIGN(rta->rta_len + len) > maxlen) {
390 return -1; 392 return -1;
391 } 393 }
392 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); 394 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
393 subrta->rta_type = type; 395 subrta->rta_type = type;
394 subrta->rta_len = len; 396 subrta->rta_len = len;
395 memcpy(RTA_DATA(subrta), data, alen); 397 memcpy(RTA_DATA(subrta), data, alen);
396 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; 398 rta->rta_len = NLMSG_ALIGN(rta->rta_len + len);
397 return 0; 399 return 0;
398} 400}
399 401
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 130cef0af..8ca62cf1b 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -2051,6 +2051,13 @@ recv_and_process_client_pkt(void /*int fd*/)
2051 goto bail; 2051 goto bail;
2052 } 2052 }
2053 2053
2054 /* Respond only to client and symmetric active packets */
2055 if ((msg.m_status & MODE_MASK) != MODE_CLIENT
2056 && (msg.m_status & MODE_MASK) != MODE_SYM_ACT
2057 ) {
2058 goto bail;
2059 }
2060
2054 query_status = msg.m_status; 2061 query_status = msg.m_status;
2055 query_xmttime = msg.m_xmttime; 2062 query_xmttime = msg.m_xmttime;
2056 2063
diff --git a/networking/route.c b/networking/route.c
index 65c2fb7c8..102a6ec67 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -295,7 +295,7 @@ static NOINLINE void INET_setroute(int action, char **args)
295#endif 295#endif
296 296
297 /* Device is special in that it can be the last arg specified 297 /* Device is special in that it can be the last arg specified
298 * and doesn't requre the dev/device keyword in that case. */ 298 * and doesn't require the dev/device keyword in that case. */
299 if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { 299 if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
300 /* Don't use args_m1 here since args may have changed! */ 300 /* Don't use args_m1 here since args may have changed! */
301 rt->rt_dev = args[-1]; 301 rt->rt_dev = args[-1];
@@ -416,7 +416,7 @@ static NOINLINE void INET6_setroute(int action, char **args)
416 } 416 }
417 417
418 /* Device is special in that it can be the last arg specified 418 /* Device is special in that it can be the last arg specified
419 * and doesn't requre the dev/device keyword in that case. */ 419 * and doesn't require the dev/device keyword in that case. */
420 if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { 420 if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
421 /* Don't use args_m1 here since args may have changed! */ 421 /* Don't use args_m1 here since args may have changed! */
422 devname = args[-1]; 422 devname = args[-1];
diff --git a/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8
new file mode 100755
index 000000000..208a7087d
--- /dev/null
+++ b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8
@@ -0,0 +1,39 @@
1#!/bin/sh
2
3# How to configure & build a static wolfssl library
4# suitable for static build of ssl_helper.
5
6export CC="i686-gcc"
7export CFLAGS="\
8-Os \
9-static \
10-fomit-frame-pointer \
11-falign-functions=1 -falign-labels=1 -falign-loops=1 -falign-jumps=1 \
12-ffunction-sections -fdata-sections \
13"
14
15{
16
17./configure \
18 --host="i686" \
19 --enable-static \
20 --enable-singlethreaded \
21 --disable-shared \
22\
23 C_EXTRA_FLAGS="-DWOLFSSL_STATIC_RSA" \
24|| exit $?
25
26# The second group of options was added when "vanilla" config did not work.
27# A good tool to debug problems is to try wolfssl's client tool, e.g.:
28# examples/client/client -h www.google.com -p 443 -d -x
29#
30# configure has many other options, see ./configure --help
31# --enable-ecc \
32# --enable-sni \
33#
34# Also consult "wolfSSL - Embedded SSL Library Product Support Forums"
35# for recent report of users having problems connecting.
36
37make
38
39} 2>&1 | tee "$0.log"
diff --git a/networking/ssl_helper-wolfssl/README b/networking/ssl_helper-wolfssl/README
index 58a381c20..ff46f4bdf 100644
--- a/networking/ssl_helper-wolfssl/README
+++ b/networking/ssl_helper-wolfssl/README
@@ -11,6 +11,13 @@ Build instructions:
11* Drop this directory into wolfssl-3.6.8/ssl_helper 11* Drop this directory into wolfssl-3.6.8/ssl_helper
12* Run ssl_helper.sh to compile and link the helper 12* Run ssl_helper.sh to compile and link the helper
13 13
14* Unpack wolfssl-3.9.8.tar.gz from https://github.com/wolfSSL/wolfssl/releases
15* Create configure:
16 ./autogen.sh
17* Build it: see 00cfg-wolfssl-3.9.8 shell script
18* Drop this directory into wolfssl-x.y.z/ssl_helper
19* Run ssl_helper.sh to compile and link the helper
20
14Usage: "ssl_helper -d FILE_DESCRIPTOR" where FILE_DESCRIPTOR is open to the peer. 21Usage: "ssl_helper -d FILE_DESCRIPTOR" where FILE_DESCRIPTOR is open to the peer.
15 22
16In bash, you can do it this way: 23In bash, you can do it this way:
diff --git a/networking/ssl_helper-wolfssl/ssl_helper.sh b/networking/ssl_helper-wolfssl/ssl_helper.sh
index 184ffe67e..c6cbf353f 100755
--- a/networking/ssl_helper-wolfssl/ssl_helper.sh
+++ b/networking/ssl_helper-wolfssl/ssl_helper.sh
@@ -9,4 +9,4 @@ STATIC="-static"
9 9
10${PREFIX}gcc -Os -Wall -I.. -c ssl_helper.c -o ssl_helper.o 10${PREFIX}gcc -Os -Wall -I.. -c ssl_helper.c -o ssl_helper.o
11${PREFIX}gcc $STATIC --start-group ssl_helper.o -lm ../src/.libs/libwolfssl.a --end-group -o ssl_helper 11${PREFIX}gcc $STATIC --start-group ssl_helper.o -lm ../src/.libs/libwolfssl.a --end-group -o ssl_helper
12${PREFIX}-strip ssl_helper 12${PREFIX}strip ssl_helper
diff --git a/networking/traceroute.c b/networking/traceroute.c
index e43a36dc7..b9a9ca4bb 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -212,8 +212,7 @@
212 212
213//usage:#define traceroute_trivial_usage 213//usage:#define traceroute_trivial_usage
214//usage: "[-"IF_TRACEROUTE6("46")"FIlnrv] [-f 1ST_TTL] [-m MAXTTL] [-q PROBES] [-p PORT]\n" 214//usage: "[-"IF_TRACEROUTE6("46")"FIlnrv] [-f 1ST_TTL] [-m MAXTTL] [-q PROBES] [-p PORT]\n"
215//usage: " [-t TOS] [-w WAIT_SEC]" 215//usage: " [-t TOS] [-w WAIT_SEC] [-s SRC_IP] [-i IFACE]\n"
216//usage: IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(" [-g GATEWAY]")" [-s SRC_IP] [-i IFACE]\n"
217//usage: " [-z PAUSE_MSEC] HOST [BYTES]" 216//usage: " [-z PAUSE_MSEC] HOST [BYTES]"
218//usage:#define traceroute_full_usage "\n\n" 217//usage:#define traceroute_full_usage "\n\n"
219//usage: "Trace the route to HOST\n" 218//usage: "Trace the route to HOST\n"
@@ -294,7 +293,6 @@
294 293
295#define OPT_STRING \ 294#define OPT_STRING \
296 "FIlnrdvxt:i:m:p:q:s:w:z:f:" \ 295 "FIlnrdvxt:i:m:p:q:s:w:z:f:" \
297 IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:*") \
298 "4" IF_TRACEROUTE6("6") 296 "4" IF_TRACEROUTE6("6")
299enum { 297enum {
300 OPT_DONT_FRAGMNT = (1 << 0), /* F */ 298 OPT_DONT_FRAGMNT = (1 << 0), /* F */
@@ -314,9 +312,8 @@ enum {
314 OPT_WAITTIME = (1 << 14), /* w */ 312 OPT_WAITTIME = (1 << 14), /* w */
315 OPT_PAUSE_MS = (1 << 15), /* z */ 313 OPT_PAUSE_MS = (1 << 15), /* z */
316 OPT_FIRST_TTL = (1 << 16), /* f */ 314 OPT_FIRST_TTL = (1 << 16), /* f */
317 OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */ 315 OPT_IPV4 = (1 << 17), /* 4 */
318 OPT_IPV4 = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)), /* 4 */ 316 OPT_IPV6 = (1 << 18) * ENABLE_TRACEROUTE6, /* 6 */
319 OPT_IPV6 = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */
320}; 317};
321#define verbose (option_mask32 & OPT_VERBOSE) 318#define verbose (option_mask32 & OPT_VERBOSE)
322 319
@@ -343,26 +340,18 @@ struct outdata6_t {
343#endif 340#endif
344 341
345struct globals { 342struct globals {
343 /* Pointer to entire malloced IP packet, "packlen" bytes long: */
346 struct ip *outip; 344 struct ip *outip;
345 /* Pointer to ICMP or UDP payload (not header): */
347 struct outdata_t *outdata; 346 struct outdata_t *outdata;
347
348 len_and_sockaddr *dest_lsa; 348 len_and_sockaddr *dest_lsa;
349 int packlen; /* total length of packet */ 349 int packlen; /* total length of packet */
350 int pmtu; /* Path MTU Discovery (RFC1191) */ 350 int pmtu; /* Path MTU Discovery (RFC1191) */
351 uint32_t ident; 351 uint32_t ident;
352 uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */ 352 uint16_t port; // 33434; /* start udp dest port # for probe packets */
353 int waittime; // 5; /* time to wait for response (in seconds) */ 353 int waittime; // 5; /* time to wait for response (in seconds) */
354#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
355 int optlen; /* length of ip options */
356#else
357#define optlen 0
358#endif
359 unsigned char recv_pkt[512]; /* last inbound (icmp) packet */ 354 unsigned char recv_pkt[512]; /* last inbound (icmp) packet */
360#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
361 /* Maximum number of gateways (include room for one noop) */
362#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
363 /* loose source route gateway list (including room for final destination) */
364 uint32_t gwlist[NGATEWAYS + 1];
365#endif
366}; 355};
367 356
368#define G (*ptr_to_globals) 357#define G (*ptr_to_globals)
@@ -374,14 +363,11 @@ struct globals {
374#define ident (G.ident ) 363#define ident (G.ident )
375#define port (G.port ) 364#define port (G.port )
376#define waittime (G.waittime ) 365#define waittime (G.waittime )
377#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
378# define optlen (G.optlen )
379#endif
380#define recv_pkt (G.recv_pkt ) 366#define recv_pkt (G.recv_pkt )
381#define gwlist (G.gwlist ) 367#define gwlist (G.gwlist )
382#define INIT_G() do { \ 368#define INIT_G() do { \
383 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 369 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
384 port = 32768 + 666; \ 370 port = 33434; \
385 waittime = 5; \ 371 waittime = 5; \
386} while (0) 372} while (0)
387 373
@@ -421,7 +407,7 @@ send_probe(int seq, int ttl)
421 /* Payload */ 407 /* Payload */
422#if ENABLE_TRACEROUTE6 408#if ENABLE_TRACEROUTE6
423 if (dest_lsa->u.sa.sa_family == AF_INET6) { 409 if (dest_lsa->u.sa.sa_family == AF_INET6) {
424 struct outdata6_t *pkt = (struct outdata6_t *) outip; 410 struct outdata6_t *pkt = (struct outdata6_t *) outdata;
425 pkt->ident6 = htonl(ident); 411 pkt->ident6 = htonl(ident);
426 pkt->seq6 = htonl(seq); 412 pkt->seq6 = htonl(seq);
427 /*gettimeofday(&pkt->tv, &tz);*/ 413 /*gettimeofday(&pkt->tv, &tz);*/
@@ -438,8 +424,10 @@ send_probe(int seq, int ttl)
438 424
439 /* Always calculate checksum for icmp packets */ 425 /* Always calculate checksum for icmp packets */
440 outicmp->icmp_cksum = 0; 426 outicmp->icmp_cksum = 0;
441 outicmp->icmp_cksum = inet_cksum((uint16_t *)outicmp, 427 outicmp->icmp_cksum = inet_cksum(
442 packlen - (sizeof(*outip) + optlen)); 428 (uint16_t *)outicmp,
429 ((char*)outip + packlen) - (char*)outicmp
430 );
443 if (outicmp->icmp_cksum == 0) 431 if (outicmp->icmp_cksum == 0)
444 outicmp->icmp_cksum = 0xffff; 432 outicmp->icmp_cksum = 0xffff;
445 } 433 }
@@ -471,13 +459,12 @@ send_probe(int seq, int ttl)
471 } 459 }
472#endif 460#endif
473 461
462 out = outdata;
474#if ENABLE_TRACEROUTE6 463#if ENABLE_TRACEROUTE6
475 if (dest_lsa->u.sa.sa_family == AF_INET6) { 464 if (dest_lsa->u.sa.sa_family == AF_INET6) {
476 res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl); 465 res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl);
477 if (res != 0) 466 if (res != 0)
478 bb_perror_msg_and_die("setsockopt(%s) %d", "UNICAST_HOPS", ttl); 467 bb_perror_msg_and_die("setsockopt(%s) %d", "UNICAST_HOPS", ttl);
479 out = outip;
480 len = packlen;
481 } else 468 } else
482#endif 469#endif
483 { 470 {
@@ -486,15 +473,14 @@ send_probe(int seq, int ttl)
486 if (res != 0) 473 if (res != 0)
487 bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl); 474 bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl);
488#endif 475#endif
489 out = outicmp; 476 if (option_mask32 & OPT_USE_ICMP)
490 len = packlen - sizeof(*outip); 477 out = outicmp;
491 if (!(option_mask32 & OPT_USE_ICMP)) {
492 out = outdata;
493 len -= sizeof(*outudp);
494 set_nport(&dest_lsa->u.sa, htons(port + seq));
495 }
496 } 478 }
497 479
480 if (!(option_mask32 & OPT_USE_ICMP)) {
481 set_nport(&dest_lsa->u.sa, htons(port + seq));
482 }
483 len = ((char*)outip + packlen) - (char*)out;
498 res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len); 484 res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len);
499 if (res != len) 485 if (res != len)
500 bb_error_msg("sent %d octets, ret=%d", len, res); 486 bb_error_msg("sent %d octets, ret=%d", len, res);
@@ -801,10 +787,6 @@ common_traceroute_main(int op, char **argv)
801 char *pausemsecs_str; 787 char *pausemsecs_str;
802 char *first_ttl_str; 788 char *first_ttl_str;
803 char *dest_str; 789 char *dest_str;
804#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
805 llist_t *source_route_list = NULL;
806 int lsrr = 0;
807#endif
808#if ENABLE_TRACEROUTE6 790#if ENABLE_TRACEROUTE6
809 sa_family_t af; 791 sa_family_t af;
810#else 792#else
@@ -823,9 +805,6 @@ common_traceroute_main(int op, char **argv)
823 op |= getopt32(argv, OPT_STRING 805 op |= getopt32(argv, OPT_STRING
824 , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str 806 , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
825 , &source, &waittime_str, &pausemsecs_str, &first_ttl_str 807 , &source, &waittime_str, &pausemsecs_str, &first_ttl_str
826#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
827 , &source_route_list
828#endif
829 ); 808 );
830 argv += optind; 809 argv += optind;
831 810
@@ -858,26 +837,14 @@ common_traceroute_main(int op, char **argv)
858 if (op & OPT_FIRST_TTL) 837 if (op & OPT_FIRST_TTL)
859 first_ttl = xatou_range(first_ttl_str, 1, max_ttl); 838 first_ttl = xatou_range(first_ttl_str, 1, max_ttl);
860 839
861#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
862 if (source_route_list) {
863 while (source_route_list) {
864 len_and_sockaddr *lsa;
865
866 if (lsrr >= NGATEWAYS)
867 bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
868 lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET);
869 gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr;
870 free(lsa);
871 ++lsrr;
872 }
873 optlen = (lsrr + 1) * sizeof(gwlist[0]);
874 }
875#endif
876
877 /* Process destination and optional packet size */ 840 /* Process destination and optional packet size */
878 minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen; 841 minpacket = sizeof(struct ip)
842 + SIZEOF_ICMP_HDR
843 + sizeof(struct outdata_t);
879 if (!(op & OPT_USE_ICMP)) 844 if (!(op & OPT_USE_ICMP))
880 minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR; 845 minpacket = sizeof(struct ip)
846 + sizeof(struct udphdr)
847 + sizeof(struct outdata_t);
881#if ENABLE_TRACEROUTE6 848#if ENABLE_TRACEROUTE6
882 af = AF_UNSPEC; 849 af = AF_UNSPEC;
883 if (op & OPT_IPV4) 850 if (op & OPT_IPV4)
@@ -887,7 +854,9 @@ common_traceroute_main(int op, char **argv)
887 dest_lsa = xhost_and_af2sockaddr(argv[0], port, af); 854 dest_lsa = xhost_and_af2sockaddr(argv[0], port, af);
888 af = dest_lsa->u.sa.sa_family; 855 af = dest_lsa->u.sa.sa_family;
889 if (af == AF_INET6) 856 if (af == AF_INET6)
890 minpacket = sizeof(struct outdata6_t); 857 minpacket = sizeof(struct ip6_hdr)
858 + sizeof(struct udphdr)
859 + sizeof(struct outdata6_t);
891#else 860#else
892 dest_lsa = xhost2sockaddr(argv[0], port); 861 dest_lsa = xhost2sockaddr(argv[0], port);
893#endif 862#endif
@@ -932,31 +901,6 @@ common_traceroute_main(int op, char **argv)
932 xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock); 901 xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock);
933 else 902 else
934 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock); 903 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
935#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS
936 if (lsrr > 0) {
937 unsigned char optlist[MAX_IPOPTLEN];
938 unsigned size;
939
940 /* final hop */
941 gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
942 ++lsrr;
943
944 /* force 4 byte alignment */
945 optlist[0] = IPOPT_NOP;
946 /* loose source route option */
947 optlist[1] = IPOPT_LSRR;
948 size = lsrr * sizeof(gwlist[0]);
949 optlist[2] = size + 3;
950 /* pointer to LSRR addresses */
951 optlist[3] = IPOPT_MINOFF;
952 memcpy(optlist + 4, gwlist, size);
953
954 if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
955 (char *)optlist, size + sizeof(gwlist[0])) < 0) {
956 bb_perror_msg_and_die("IP_OPTIONS");
957 }
958 }
959#endif
960 } 904 }
961 905
962#ifdef SO_SNDBUF 906#ifdef SO_SNDBUF
@@ -984,7 +928,7 @@ common_traceroute_main(int op, char **argv)
984 928
985 ident = getpid(); 929 ident = getpid();
986 930
987 if (af == AF_INET) { 931 if (!ENABLE_TRACEROUTE6 || af == AF_INET) {
988 if (op & OPT_USE_ICMP) { 932 if (op & OPT_USE_ICMP) {
989 ident |= 0x8000; 933 ident |= 0x8000;
990 outicmp->icmp_type = ICMP_ECHO; 934 outicmp->icmp_type = ICMP_ECHO;
@@ -994,6 +938,14 @@ common_traceroute_main(int op, char **argv)
994 outdata = (struct outdata_t *)(outudp + 1); 938 outdata = (struct outdata_t *)(outudp + 1);
995 } 939 }
996 } 940 }
941#if ENABLE_TRACEROUTE6
942 if (af == AF_INET6) {
943 outdata = (void*)((char*)outip
944 + sizeof(struct ip6_hdr)
945 + sizeof(struct udphdr)
946 );
947 }
948#endif
997 949
998 if (op & OPT_DEVICE) /* hmm, do we need error check? */ 950 if (op & OPT_DEVICE) /* hmm, do we need error check? */
999 setsockopt_bindtodevice(sndsock, device); 951 setsockopt_bindtodevice(sndsock, device);
diff --git a/networking/udhcp/Kbuild.src b/networking/udhcp/Kbuild.src
index b8767baea..5ea77df06 100644
--- a/networking/udhcp/Kbuild.src
+++ b/networking/udhcp/Kbuild.src
@@ -13,7 +13,7 @@ lib-$(CONFIG_UDHCPC) += common.o packet.o signalpipe.o socket.o
13lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o 13lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o
14 14
15lib-$(CONFIG_UDHCPC) += dhcpc.o 15lib-$(CONFIG_UDHCPC) += dhcpc.o
16lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o files.o leases.o static_leases.o 16lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o
17lib-$(CONFIG_DUMPLEASES) += dumpleases.o 17lib-$(CONFIG_DUMPLEASES) += dumpleases.o
18lib-$(CONFIG_DHCPRELAY) += dhcprelay.o 18lib-$(CONFIG_DHCPRELAY) += dhcprelay.o
19 19
diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c
index c98027316..215d023ce 100644
--- a/networking/udhcp/arpping.c
+++ b/networking/udhcp/arpping.c
@@ -9,7 +9,6 @@
9#include <net/if_arp.h> 9#include <net/if_arp.h>
10 10
11#include "common.h" 11#include "common.h"
12#include "dhcpd.h"
13 12
14struct arpMsg { 13struct arpMsg {
15 /* Ethernet header */ 14 /* Ethernet header */
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 6ff040d9e..ddf3412a0 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -800,10 +800,15 @@ static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *ou
800 ) { 800 ) {
801 bb_error_msg("unicasting a release"); 801 bb_error_msg("unicasting a release");
802 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */ 802 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
803 d6_run_script(NULL, "deconfig");
804 } 803 }
805 bb_error_msg("entering released state"); 804 bb_error_msg("entering released state");
806 805/*
806 * We can be here on: SIGUSR2,
807 * or on exit (SIGTERM) and -R "release on quit" is specified.
808 * Users requested to be notified in all cases, even if not in one
809 * of the states above.
810 */
811 d6_run_script(NULL, "deconfig");
807 change_listen_mode(LISTEN_NONE); 812 change_listen_mode(LISTEN_NONE);
808 state = RELEASED; 813 state = RELEASED;
809} 814}
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 8a16e987d..bef73277a 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -1129,9 +1129,15 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip)
1129 bb_error_msg("unicasting a release of %s to %s", 1129 bb_error_msg("unicasting a release of %s to %s",
1130 inet_ntoa(temp_addr), buffer); 1130 inet_ntoa(temp_addr), buffer);
1131 send_release(server_addr, requested_ip); /* unicast */ 1131 send_release(server_addr, requested_ip); /* unicast */
1132 udhcp_run_script(NULL, "deconfig");
1133 } 1132 }
1134 bb_error_msg("entering released state"); 1133 bb_error_msg("entering released state");
1134/*
1135 * We can be here on: SIGUSR2,
1136 * or on exit (SIGTERM) and -R "release on quit" is specified.
1137 * Users requested to be notified in all cases, even if not in one
1138 * of the states above.
1139 */
1140 udhcp_run_script(NULL, "deconfig");
1135 1141
1136 change_listen_mode(LISTEN_NONE); 1142 change_listen_mode(LISTEN_NONE);
1137 state = RELEASED; 1143 state = RELEASED;
@@ -1813,9 +1819,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1813 temp = udhcp_get_option(&packet, DHCP_SERVER_ID); 1819 temp = udhcp_get_option(&packet, DHCP_SERVER_ID);
1814 if (!temp) { 1820 if (!temp) {
1815 non_matching_svid: 1821 non_matching_svid:
1816 log1("%s with wrong server ID, ignoring packet", 1822 log1("received DHCP NAK with wrong"
1817 "Received DHCP NAK" 1823 " server ID, ignoring packet");
1818 );
1819 continue; 1824 continue;
1820 } 1825 }
1821 move_from_unaligned32(svid, temp); 1826 move_from_unaligned32(svid, temp);
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index e93a9f1da..56116d01f 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -33,11 +33,507 @@
33//usage: "\n -P N Use port N (default 67)" 33//usage: "\n -P N Use port N (default 67)"
34//usage: ) 34//usage: )
35 35
36#include <netinet/ether.h>
36#include <syslog.h> 37#include <syslog.h>
37#include "common.h" 38#include "common.h"
38#include "dhcpc.h" 39#include "dhcpc.h"
39#include "dhcpd.h" 40#include "dhcpd.h"
40 41
42/* globals */
43struct dyn_lease *g_leases;
44/* struct server_config_t server_config is in bb_common_bufsiz1 */
45
46/* Takes the address of the pointer to the static_leases linked list,
47 * address to a 6 byte mac address,
48 * 4 byte IP address */
49static void add_static_lease(struct static_lease **st_lease_pp,
50 uint8_t *mac,
51 uint32_t nip)
52{
53 struct static_lease *st_lease;
54
55 /* Find the tail of the list */
56 while ((st_lease = *st_lease_pp) != NULL) {
57 st_lease_pp = &st_lease->next;
58 }
59
60 /* Add new node */
61 *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
62 memcpy(st_lease->mac, mac, 6);
63 st_lease->nip = nip;
64 /*st_lease->next = NULL;*/
65}
66
67/* Find static lease IP by mac */
68static uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *mac)
69{
70 while (st_lease) {
71 if (memcmp(st_lease->mac, mac, 6) == 0)
72 return st_lease->nip;
73 st_lease = st_lease->next;
74 }
75
76 return 0;
77}
78
79static int is_nip_reserved(struct static_lease *st_lease, uint32_t nip)
80{
81 while (st_lease) {
82 if (st_lease->nip == nip)
83 return 1;
84 st_lease = st_lease->next;
85 }
86
87 return 0;
88}
89
90#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
91/* Print out static leases just to check what's going on */
92/* Takes the address of the pointer to the static_leases linked list */
93static void log_static_leases(struct static_lease **st_lease_pp)
94{
95 struct static_lease *cur;
96
97 if (dhcp_verbose < 2)
98 return;
99
100 cur = *st_lease_pp;
101 while (cur) {
102 bb_error_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
103 cur->mac[0], cur->mac[1], cur->mac[2],
104 cur->mac[3], cur->mac[4], cur->mac[5],
105 cur->nip
106 );
107 cur = cur->next;
108 }
109}
110#else
111# define log_static_leases(st_lease_pp) ((void)0)
112#endif
113
114/* Find the oldest expired lease, NULL if there are no expired leases */
115static struct dyn_lease *oldest_expired_lease(void)
116{
117 struct dyn_lease *oldest_lease = NULL;
118 leasetime_t oldest_time = time(NULL);
119 unsigned i;
120
121 /* Unexpired leases have g_leases[i].expires >= current time
122 * and therefore can't ever match */
123 for (i = 0; i < server_config.max_leases; i++) {
124 if (g_leases[i].expires == 0 /* empty entry */
125 || g_leases[i].expires < oldest_time
126 ) {
127 oldest_time = g_leases[i].expires;
128 oldest_lease = &g_leases[i];
129 }
130 }
131 return oldest_lease;
132}
133
134/* Clear out all leases with matching nonzero chaddr OR yiaddr.
135 * If chaddr == NULL, this is a conflict lease.
136 */
137static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
138{
139 unsigned i;
140
141 for (i = 0; i < server_config.max_leases; i++) {
142 if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
143 || (yiaddr && g_leases[i].lease_nip == yiaddr)
144 ) {
145 memset(&g_leases[i], 0, sizeof(g_leases[i]));
146 }
147 }
148}
149
150/* Add a lease into the table, clearing out any old ones.
151 * If chaddr == NULL, this is a conflict lease.
152 */
153static struct dyn_lease *add_lease(
154 const uint8_t *chaddr, uint32_t yiaddr,
155 leasetime_t leasetime,
156 const char *hostname, int hostname_len)
157{
158 struct dyn_lease *oldest;
159
160 /* clean out any old ones */
161 clear_leases(chaddr, yiaddr);
162
163 oldest = oldest_expired_lease();
164
165 if (oldest) {
166 memset(oldest, 0, sizeof(*oldest));
167 if (hostname) {
168 char *p;
169
170 hostname_len++; /* include NUL */
171 if (hostname_len > sizeof(oldest->hostname))
172 hostname_len = sizeof(oldest->hostname);
173 p = safe_strncpy(oldest->hostname, hostname, hostname_len);
174 /*
175 * Sanitization (s/bad_char/./g).
176 * The intent is not to allow only "DNS-valid" hostnames,
177 * but merely make dumpleases output safe for shells to use.
178 * We accept "0-9A-Za-z._-", all other chars turn to dots.
179 */
180 while (*p) {
181 if (!isalnum(*p) && *p != '-' && *p != '_')
182 *p = '.';
183 p++;
184 }
185 }
186 if (chaddr)
187 memcpy(oldest->lease_mac, chaddr, 6);
188 oldest->lease_nip = yiaddr;
189 oldest->expires = time(NULL) + leasetime;
190 }
191
192 return oldest;
193}
194
195/* True if a lease has expired */
196static int is_expired_lease(struct dyn_lease *lease)
197{
198 return (lease->expires < (leasetime_t) time(NULL));
199}
200
201/* Find the first lease that matches MAC, NULL if no match */
202static struct dyn_lease *find_lease_by_mac(const uint8_t *mac)
203{
204 unsigned i;
205
206 for (i = 0; i < server_config.max_leases; i++)
207 if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
208 return &g_leases[i];
209
210 return NULL;
211}
212
213/* Find the first lease that matches IP, NULL is no match */
214static struct dyn_lease *find_lease_by_nip(uint32_t nip)
215{
216 unsigned i;
217
218 for (i = 0; i < server_config.max_leases; i++)
219 if (g_leases[i].lease_nip == nip)
220 return &g_leases[i];
221
222 return NULL;
223}
224
225/* Check if the IP is taken; if it is, add it to the lease table */
226static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
227{
228 struct in_addr temp;
229 int r;
230
231 r = arpping(nip, safe_mac,
232 server_config.server_nip,
233 server_config.server_mac,
234 server_config.interface,
235 arpping_ms);
236 if (r)
237 return r;
238
239 temp.s_addr = nip;
240 bb_error_msg("%s belongs to someone, reserving it for %u seconds",
241 inet_ntoa(temp), (unsigned)server_config.conflict_time);
242 add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
243 return 0;
244}
245
246/* Find a new usable (we think) address */
247static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
248{
249 uint32_t addr;
250 struct dyn_lease *oldest_lease = NULL;
251
252#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
253 uint32_t stop;
254 unsigned i, hash;
255
256 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
257 * dispersal even with similarly-valued "strings".
258 */
259 hash = 0;
260 for (i = 0; i < 6; i++)
261 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
262
263 /* pick a seed based on hwaddr then iterate until we find a free address. */
264 addr = server_config.start_ip
265 + (hash % (1 + server_config.end_ip - server_config.start_ip));
266 stop = addr;
267#else
268 addr = server_config.start_ip;
269#define stop (server_config.end_ip + 1)
270#endif
271 do {
272 uint32_t nip;
273 struct dyn_lease *lease;
274
275 /* ie, 192.168.55.0 */
276 if ((addr & 0xff) == 0)
277 goto next_addr;
278 /* ie, 192.168.55.255 */
279 if ((addr & 0xff) == 0xff)
280 goto next_addr;
281 nip = htonl(addr);
282 /* skip our own address */
283 if (nip == server_config.server_nip)
284 goto next_addr;
285 /* is this a static lease addr? */
286 if (is_nip_reserved(server_config.static_leases, nip))
287 goto next_addr;
288
289 lease = find_lease_by_nip(nip);
290 if (!lease) {
291//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
292 if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
293 return nip;
294 } else {
295 if (!oldest_lease || lease->expires < oldest_lease->expires)
296 oldest_lease = lease;
297 }
298
299 next_addr:
300 addr++;
301#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
302 if (addr > server_config.end_ip)
303 addr = server_config.start_ip;
304#endif
305 } while (addr != stop);
306
307 if (oldest_lease
308 && is_expired_lease(oldest_lease)
309 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
310 ) {
311 return oldest_lease->lease_nip;
312 }
313
314 return 0;
315}
316
317/* On these functions, make sure your datatype matches */
318static int FAST_FUNC read_str(const char *line, void *arg)
319{
320 char **dest = arg;
321
322 free(*dest);
323 *dest = xstrdup(line);
324 return 1;
325}
326
327static int FAST_FUNC read_u32(const char *line, void *arg)
328{
329 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
330 return errno == 0;
331}
332
333static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
334{
335 char *line;
336 char *mac_string;
337 char *ip_string;
338 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
339 uint32_t nip;
340
341 /* Read mac */
342 line = (char *) const_line;
343 mac_string = strtok_r(line, " \t", &line);
344 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
345 return 0;
346
347 /* Read ip */
348 ip_string = strtok_r(NULL, " \t", &line);
349 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
350 return 0;
351
352 add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
353
354 log_static_leases(arg);
355
356 return 1;
357}
358
359struct config_keyword {
360 const char *keyword;
361 int (*handler)(const char *line, void *var) FAST_FUNC;
362 unsigned ofs;
363 const char *def;
364};
365
366#define OFS(field) offsetof(struct server_config_t, field)
367
368static const struct config_keyword keywords[] = {
369 /* keyword handler variable address default */
370 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
371 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
372 {"interface" , read_str , OFS(interface ), "eth0"},
373 /* Avoid "max_leases value not sane" warning by setting default
374 * to default_end_ip - default_start_ip + 1: */
375 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
376 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
377 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
378 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
379 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
380 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
381 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
382 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
383 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
384 /* keywords with no defaults must be last! */
385 {"option" , udhcp_str2optset, OFS(options ), ""},
386 {"opt" , udhcp_str2optset, OFS(options ), ""},
387 {"notify_file" , read_str , OFS(notify_file ), NULL},
388 {"sname" , read_str , OFS(sname ), NULL},
389 {"boot_file" , read_str , OFS(boot_file ), NULL},
390 {"static_lease" , read_staticlease, OFS(static_leases), ""},
391};
392enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
393
394static NOINLINE void read_config(const char *file)
395{
396 parser_t *parser;
397 const struct config_keyword *k;
398 unsigned i;
399 char *token[2];
400
401 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
402 keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs);
403
404 parser = config_open(file);
405 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
406 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
407 if (strcasecmp(token[0], k->keyword) == 0) {
408 if (!k->handler(token[1], (char*)&server_config + k->ofs)) {
409 bb_error_msg("can't parse line %u in %s",
410 parser->lineno, file);
411 /* reset back to the default value */
412 k->handler(k->def, (char*)&server_config + k->ofs);
413 }
414 break;
415 }
416 }
417 }
418 config_close(parser);
419
420 server_config.start_ip = ntohl(server_config.start_ip);
421 server_config.end_ip = ntohl(server_config.end_ip);
422}
423
424static void write_leases(void)
425{
426 int fd;
427 unsigned i;
428 leasetime_t curr;
429 int64_t written_at;
430
431 fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
432 if (fd < 0)
433 return;
434
435 curr = written_at = time(NULL);
436
437 written_at = SWAP_BE64(written_at);
438 full_write(fd, &written_at, sizeof(written_at));
439
440 for (i = 0; i < server_config.max_leases; i++) {
441 leasetime_t tmp_time;
442
443 if (g_leases[i].lease_nip == 0)
444 continue;
445
446 /* Screw with the time in the struct, for easier writing */
447 tmp_time = g_leases[i].expires;
448
449 g_leases[i].expires -= curr;
450 if ((signed_leasetime_t) g_leases[i].expires < 0)
451 g_leases[i].expires = 0;
452 g_leases[i].expires = htonl(g_leases[i].expires);
453
454 /* No error check. If the file gets truncated,
455 * we lose some leases on restart. Oh well. */
456 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
457
458 /* Then restore it when done */
459 g_leases[i].expires = tmp_time;
460 }
461 close(fd);
462
463 if (server_config.notify_file) {
464 char *argv[3];
465 argv[0] = server_config.notify_file;
466 argv[1] = server_config.lease_file;
467 argv[2] = NULL;
468 spawn_and_wait(argv);
469 }
470}
471
472static NOINLINE void read_leases(const char *file)
473{
474 struct dyn_lease lease;
475 int64_t written_at, time_passed;
476 int fd;
477#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
478 unsigned i = 0;
479#endif
480
481 fd = open_or_warn(file, O_RDONLY);
482 if (fd < 0)
483 return;
484
485 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
486 goto ret;
487 written_at = SWAP_BE64(written_at);
488
489 time_passed = time(NULL) - written_at;
490 /* Strange written_at, or lease file from old version of udhcpd
491 * which had no "written_at" field? */
492 if ((uint64_t)time_passed > 12 * 60 * 60)
493 goto ret;
494
495 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
496 uint32_t y = ntohl(lease.lease_nip);
497 if (y >= server_config.start_ip && y <= server_config.end_ip) {
498 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
499 uint32_t static_nip;
500
501 if (expires <= 0)
502 /* We keep expired leases: add_lease() will add
503 * a lease with 0 seconds remaining.
504 * Fewer IP address changes this way for mass reboot scenario.
505 */
506 expires = 0;
507
508 /* Check if there is a different static lease for this IP or MAC */
509 static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac);
510 if (static_nip) {
511 /* NB: we do not add lease even if static_nip == lease.lease_nip.
512 */
513 continue;
514 }
515 if (is_nip_reserved(server_config.static_leases, lease.lease_nip))
516 continue;
517
518 /* NB: add_lease takes "relative time", IOW,
519 * lease duration, not lease deadline. */
520 if (add_lease(lease.lease_mac, lease.lease_nip,
521 expires,
522 lease.hostname, sizeof(lease.hostname)
523 ) == 0
524 ) {
525 bb_error_msg("too many leases while loading %s", file);
526 break;
527 }
528#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
529 i++;
530#endif
531 }
532 }
533 log1("read %d leases", i);
534 ret:
535 close(fd);
536}
41 537
42/* Send a packet to a specific mac address and ip address by creating our own ip packet */ 538/* Send a packet to a specific mac address and ip address by creating our own ip packet */
43static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast) 539static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
@@ -290,12 +786,6 @@ static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
290 send_packet(&packet, /*force_bcast:*/ 0); 786 send_packet(&packet, /*force_bcast:*/ 0);
291} 787}
292 788
293
294/* globals */
295struct dyn_lease *g_leases;
296/* struct server_config_t server_config is in bb_common_bufsiz1 */
297
298
299int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 789int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
300int udhcpd_main(int argc UNUSED_PARAM, char **argv) 790int udhcpd_main(int argc UNUSED_PARAM, char **argv)
301{ 791{
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h
index 9dd5bef9e..b8f96b029 100644
--- a/networking/udhcp/dhcpd.h
+++ b/networking/udhcp/dhcpd.h
@@ -90,39 +90,6 @@ struct dyn_lease {
90 /* total size is a multiply of 4 */ 90 /* total size is a multiply of 4 */
91} PACKED; 91} PACKED;
92 92
93extern struct dyn_lease *g_leases;
94
95struct dyn_lease *add_lease(
96 const uint8_t *chaddr, uint32_t yiaddr,
97 leasetime_t leasetime,
98 const char *hostname, int hostname_len
99 ) FAST_FUNC;
100int is_expired_lease(struct dyn_lease *lease) FAST_FUNC;
101struct dyn_lease *find_lease_by_mac(const uint8_t *mac) FAST_FUNC;
102struct dyn_lease *find_lease_by_nip(uint32_t nip) FAST_FUNC;
103uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms) FAST_FUNC;
104
105
106/* Config file parser will pass static lease info to this function
107 * which will add it to a data structure that can be searched later */
108void add_static_lease(struct static_lease **st_lease_pp, uint8_t *mac, uint32_t nip) FAST_FUNC;
109/* Find static lease IP by mac */
110uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *arg) FAST_FUNC;
111/* Check to see if an IP is reserved as a static IP */
112int is_nip_reserved(struct static_lease *st_lease, uint32_t nip) FAST_FUNC;
113/* Print out static leases just to check what's going on (debug code) */
114#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
115void log_static_leases(struct static_lease **st_lease_pp) FAST_FUNC;
116#else
117# define log_static_leases(st_lease_pp) ((void)0)
118#endif
119
120
121void read_config(const char *file) FAST_FUNC;
122void write_leases(void) FAST_FUNC;
123void read_leases(const char *file) FAST_FUNC;
124
125
126POP_SAVED_FUNCTION_VISIBILITY 93POP_SAVED_FUNCTION_VISIBILITY
127 94
128#endif 95#endif
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c
deleted file mode 100644
index b22425352..000000000
--- a/networking/udhcp/files.c
+++ /dev/null
@@ -1,234 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * DHCP server config and lease file manipulation
4 *
5 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9#include <netinet/ether.h>
10
11#include "common.h"
12#include "dhcpd.h"
13
14/* on these functions, make sure your datatype matches */
15static int FAST_FUNC read_str(const char *line, void *arg)
16{
17 char **dest = arg;
18
19 free(*dest);
20 *dest = xstrdup(line);
21 return 1;
22}
23
24static int FAST_FUNC read_u32(const char *line, void *arg)
25{
26 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
27 return errno == 0;
28}
29
30static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
31{
32 char *line;
33 char *mac_string;
34 char *ip_string;
35 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
36 uint32_t nip;
37
38 /* Read mac */
39 line = (char *) const_line;
40 mac_string = strtok_r(line, " \t", &line);
41 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
42 return 0;
43
44 /* Read ip */
45 ip_string = strtok_r(NULL, " \t", &line);
46 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
47 return 0;
48
49 add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
50
51 log_static_leases(arg);
52
53 return 1;
54}
55
56
57struct config_keyword {
58 const char *keyword;
59 int (*handler)(const char *line, void *var) FAST_FUNC;
60 unsigned ofs;
61 const char *def;
62};
63
64#define OFS(field) offsetof(struct server_config_t, field)
65
66static const struct config_keyword keywords[] = {
67 /* keyword handler variable address default */
68 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
69 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
70 {"interface" , read_str , OFS(interface ), "eth0"},
71 /* Avoid "max_leases value not sane" warning by setting default
72 * to default_end_ip - default_start_ip + 1: */
73 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
74 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
75 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
76 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
77 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
78 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
79 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
80 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
81 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
82 /* keywords with no defaults must be last! */
83 {"option" , udhcp_str2optset, OFS(options ), ""},
84 {"opt" , udhcp_str2optset, OFS(options ), ""},
85 {"notify_file" , read_str , OFS(notify_file ), NULL},
86 {"sname" , read_str , OFS(sname ), NULL},
87 {"boot_file" , read_str , OFS(boot_file ), NULL},
88 {"static_lease" , read_staticlease, OFS(static_leases), ""},
89};
90enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
91
92void FAST_FUNC read_config(const char *file)
93{
94 parser_t *parser;
95 const struct config_keyword *k;
96 unsigned i;
97 char *token[2];
98
99 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
100 keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs);
101
102 parser = config_open(file);
103 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
104 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
105 if (strcasecmp(token[0], k->keyword) == 0) {
106 if (!k->handler(token[1], (char*)&server_config + k->ofs)) {
107 bb_error_msg("can't parse line %u in %s",
108 parser->lineno, file);
109 /* reset back to the default value */
110 k->handler(k->def, (char*)&server_config + k->ofs);
111 }
112 break;
113 }
114 }
115 }
116 config_close(parser);
117
118 server_config.start_ip = ntohl(server_config.start_ip);
119 server_config.end_ip = ntohl(server_config.end_ip);
120}
121
122void FAST_FUNC write_leases(void)
123{
124 int fd;
125 unsigned i;
126 leasetime_t curr;
127 int64_t written_at;
128
129 fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
130 if (fd < 0)
131 return;
132
133 curr = written_at = time(NULL);
134
135 written_at = SWAP_BE64(written_at);
136 full_write(fd, &written_at, sizeof(written_at));
137
138 for (i = 0; i < server_config.max_leases; i++) {
139 leasetime_t tmp_time;
140
141 if (g_leases[i].lease_nip == 0)
142 continue;
143
144 /* Screw with the time in the struct, for easier writing */
145 tmp_time = g_leases[i].expires;
146
147 g_leases[i].expires -= curr;
148 if ((signed_leasetime_t) g_leases[i].expires < 0)
149 g_leases[i].expires = 0;
150 g_leases[i].expires = htonl(g_leases[i].expires);
151
152 /* No error check. If the file gets truncated,
153 * we lose some leases on restart. Oh well. */
154 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
155
156 /* Then restore it when done */
157 g_leases[i].expires = tmp_time;
158 }
159 close(fd);
160
161 if (server_config.notify_file) {
162 char *argv[3];
163 argv[0] = server_config.notify_file;
164 argv[1] = server_config.lease_file;
165 argv[2] = NULL;
166 spawn_and_wait(argv);
167 }
168}
169
170void FAST_FUNC read_leases(const char *file)
171{
172 struct dyn_lease lease;
173 int64_t written_at, time_passed;
174 int fd;
175#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
176 unsigned i = 0;
177#endif
178
179 fd = open_or_warn(file, O_RDONLY);
180 if (fd < 0)
181 return;
182
183 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
184 goto ret;
185 written_at = SWAP_BE64(written_at);
186
187 time_passed = time(NULL) - written_at;
188 /* Strange written_at, or lease file from old version of udhcpd
189 * which had no "written_at" field? */
190 if ((uint64_t)time_passed > 12 * 60 * 60)
191 goto ret;
192
193 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
194 uint32_t y = ntohl(lease.lease_nip);
195 if (y >= server_config.start_ip && y <= server_config.end_ip) {
196 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
197 uint32_t static_nip;
198
199 if (expires <= 0)
200 /* We keep expired leases: add_lease() will add
201 * a lease with 0 seconds remaining.
202 * Fewer IP address changes this way for mass reboot scenario.
203 */
204 expires = 0;
205
206 /* Check if there is a different static lease for this IP or MAC */
207 static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac);
208 if (static_nip) {
209 /* NB: we do not add lease even if static_nip == lease.lease_nip.
210 */
211 continue;
212 }
213 if (is_nip_reserved(server_config.static_leases, lease.lease_nip))
214 continue;
215
216 /* NB: add_lease takes "relative time", IOW,
217 * lease duration, not lease deadline. */
218 if (add_lease(lease.lease_mac, lease.lease_nip,
219 expires,
220 lease.hostname, sizeof(lease.hostname)
221 ) == 0
222 ) {
223 bb_error_msg("too many leases while loading %s", file);
224 break;
225 }
226#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
227 i++;
228#endif
229 }
230 }
231 log1("read %d leases", i);
232 ret:
233 close(fd);
234}
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c
deleted file mode 100644
index 6642e396d..000000000
--- a/networking/udhcp/leases.c
+++ /dev/null
@@ -1,211 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Russ Dill <Russ.Dill@asu.edu> July 2001
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7#include "common.h"
8#include "dhcpd.h"
9
10/* Find the oldest expired lease, NULL if there are no expired leases */
11static struct dyn_lease *oldest_expired_lease(void)
12{
13 struct dyn_lease *oldest_lease = NULL;
14 leasetime_t oldest_time = time(NULL);
15 unsigned i;
16
17 /* Unexpired leases have g_leases[i].expires >= current time
18 * and therefore can't ever match */
19 for (i = 0; i < server_config.max_leases; i++) {
20 if (g_leases[i].expires == 0 /* empty entry */
21 || g_leases[i].expires < oldest_time
22 ) {
23 oldest_time = g_leases[i].expires;
24 oldest_lease = &g_leases[i];
25 }
26 }
27 return oldest_lease;
28}
29
30/* Clear out all leases with matching nonzero chaddr OR yiaddr.
31 * If chaddr == NULL, this is a conflict lease.
32 */
33static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
34{
35 unsigned i;
36
37 for (i = 0; i < server_config.max_leases; i++) {
38 if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
39 || (yiaddr && g_leases[i].lease_nip == yiaddr)
40 ) {
41 memset(&g_leases[i], 0, sizeof(g_leases[i]));
42 }
43 }
44}
45
46/* Add a lease into the table, clearing out any old ones.
47 * If chaddr == NULL, this is a conflict lease.
48 */
49struct dyn_lease* FAST_FUNC add_lease(
50 const uint8_t *chaddr, uint32_t yiaddr,
51 leasetime_t leasetime,
52 const char *hostname, int hostname_len)
53{
54 struct dyn_lease *oldest;
55
56 /* clean out any old ones */
57 clear_leases(chaddr, yiaddr);
58
59 oldest = oldest_expired_lease();
60
61 if (oldest) {
62 memset(oldest, 0, sizeof(*oldest));
63 if (hostname) {
64 char *p;
65
66 hostname_len++; /* include NUL */
67 if (hostname_len > sizeof(oldest->hostname))
68 hostname_len = sizeof(oldest->hostname);
69 p = safe_strncpy(oldest->hostname, hostname, hostname_len);
70 /*
71 * Sanitization (s/bad_char/./g).
72 * The intent is not to allow only "DNS-valid" hostnames,
73 * but merely make dumpleases output safe for shells to use.
74 * We accept "0-9A-Za-z._-", all other chars turn to dots.
75 */
76 while (*p) {
77 if (!isalnum(*p) && *p != '-' && *p != '_')
78 *p = '.';
79 p++;
80 }
81 }
82 if (chaddr)
83 memcpy(oldest->lease_mac, chaddr, 6);
84 oldest->lease_nip = yiaddr;
85 oldest->expires = time(NULL) + leasetime;
86 }
87
88 return oldest;
89}
90
91/* True if a lease has expired */
92int FAST_FUNC is_expired_lease(struct dyn_lease *lease)
93{
94 return (lease->expires < (leasetime_t) time(NULL));
95}
96
97/* Find the first lease that matches MAC, NULL if no match */
98struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac)
99{
100 unsigned i;
101
102 for (i = 0; i < server_config.max_leases; i++)
103 if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
104 return &g_leases[i];
105
106 return NULL;
107}
108
109/* Find the first lease that matches IP, NULL is no match */
110struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
111{
112 unsigned i;
113
114 for (i = 0; i < server_config.max_leases; i++)
115 if (g_leases[i].lease_nip == nip)
116 return &g_leases[i];
117
118 return NULL;
119}
120
121/* Check if the IP is taken; if it is, add it to the lease table */
122static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
123{
124 struct in_addr temp;
125 int r;
126
127 r = arpping(nip, safe_mac,
128 server_config.server_nip,
129 server_config.server_mac,
130 server_config.interface,
131 arpping_ms);
132 if (r)
133 return r;
134
135 temp.s_addr = nip;
136 bb_error_msg("%s belongs to someone, reserving it for %u seconds",
137 inet_ntoa(temp), (unsigned)server_config.conflict_time);
138 add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
139 return 0;
140}
141
142/* Find a new usable (we think) address */
143uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
144{
145 uint32_t addr;
146 struct dyn_lease *oldest_lease = NULL;
147
148#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
149 uint32_t stop;
150 unsigned i, hash;
151
152 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
153 * dispersal even with similarly-valued "strings".
154 */
155 hash = 0;
156 for (i = 0; i < 6; i++)
157 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
158
159 /* pick a seed based on hwaddr then iterate until we find a free address. */
160 addr = server_config.start_ip
161 + (hash % (1 + server_config.end_ip - server_config.start_ip));
162 stop = addr;
163#else
164 addr = server_config.start_ip;
165#define stop (server_config.end_ip + 1)
166#endif
167 do {
168 uint32_t nip;
169 struct dyn_lease *lease;
170
171 /* ie, 192.168.55.0 */
172 if ((addr & 0xff) == 0)
173 goto next_addr;
174 /* ie, 192.168.55.255 */
175 if ((addr & 0xff) == 0xff)
176 goto next_addr;
177 nip = htonl(addr);
178 /* skip our own address */
179 if (nip == server_config.server_nip)
180 goto next_addr;
181 /* is this a static lease addr? */
182 if (is_nip_reserved(server_config.static_leases, nip))
183 goto next_addr;
184
185 lease = find_lease_by_nip(nip);
186 if (!lease) {
187//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
188 if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
189 return nip;
190 } else {
191 if (!oldest_lease || lease->expires < oldest_lease->expires)
192 oldest_lease = lease;
193 }
194
195 next_addr:
196 addr++;
197#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
198 if (addr > server_config.end_ip)
199 addr = server_config.start_ip;
200#endif
201 } while (addr != stop);
202
203 if (oldest_lease
204 && is_expired_lease(oldest_lease)
205 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
206 ) {
207 return oldest_lease->lease_nip;
208 }
209
210 return 0;
211}
diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c
deleted file mode 100644
index b7f9e5c59..000000000
--- a/networking/udhcp/static_leases.c
+++ /dev/null
@@ -1,77 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Storing and retrieving data for static leases
4 *
5 * Wade Berrier <wberrier@myrealbox.com> September 2004
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9#include "common.h"
10#include "dhcpd.h"
11
12/* Takes the address of the pointer to the static_leases linked list,
13 * address to a 6 byte mac address,
14 * 4 byte IP address */
15void FAST_FUNC add_static_lease(struct static_lease **st_lease_pp,
16 uint8_t *mac,
17 uint32_t nip)
18{
19 struct static_lease *st_lease;
20
21 /* Find the tail of the list */
22 while ((st_lease = *st_lease_pp) != NULL) {
23 st_lease_pp = &st_lease->next;
24 }
25
26 /* Add new node */
27 *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
28 memcpy(st_lease->mac, mac, 6);
29 st_lease->nip = nip;
30 /*st_lease->next = NULL;*/
31}
32
33/* Find static lease IP by mac */
34uint32_t FAST_FUNC get_static_nip_by_mac(struct static_lease *st_lease, void *mac)
35{
36 while (st_lease) {
37 if (memcmp(st_lease->mac, mac, 6) == 0)
38 return st_lease->nip;
39 st_lease = st_lease->next;
40 }
41
42 return 0;
43}
44
45/* Check to see if an IP is reserved as a static IP */
46int FAST_FUNC is_nip_reserved(struct static_lease *st_lease, uint32_t nip)
47{
48 while (st_lease) {
49 if (st_lease->nip == nip)
50 return 1;
51 st_lease = st_lease->next;
52 }
53
54 return 0;
55}
56
57#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
58/* Print out static leases just to check what's going on */
59/* Takes the address of the pointer to the static_leases linked list */
60void FAST_FUNC log_static_leases(struct static_lease **st_lease_pp)
61{
62 struct static_lease *cur;
63
64 if (dhcp_verbose < 2)
65 return;
66
67 cur = *st_lease_pp;
68 while (cur) {
69 bb_error_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
70 cur->mac[0], cur->mac[1], cur->mac[2],
71 cur->mac[3], cur->mac[4], cur->mac[5],
72 cur->nip
73 );
74 cur = cur->next;
75 }
76}
77#endif
diff --git a/networking/wget.c b/networking/wget.c
index c886dd391..460b4b833 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -62,9 +62,10 @@
62//config: a helper program to talk over HTTPS. 62//config: a helper program to talk over HTTPS.
63//config: 63//config:
64//config: OpenSSL has a simple SSL client for debug purposes. 64//config: OpenSSL has a simple SSL client for debug purposes.
65//config: If you select "openssl" helper, wget will effectively call 65//config: If you select "openssl" helper, wget will effectively run:
66//config: "openssl s_client -quiet -connect IP:443 2>/dev/null" 66//config: "openssl s_client -quiet -connect hostname:443
67//config: and pipe its data through it. 67//config: -servername hostname 2>/dev/null" and pipe its data
68//config: through it. -servername is not used if hostname is numeric.
68//config: Note inconvenient API: host resolution is done twice, 69//config: Note inconvenient API: host resolution is done twice,
69//config: and there is no guarantee openssl's idea of IPv6 address 70//config: and there is no guarantee openssl's idea of IPv6 address
70//config: format is the same as ours. 71//config: format is the same as ours.
@@ -99,7 +100,7 @@
99 100
100//usage:#define wget_trivial_usage 101//usage:#define wget_trivial_usage
101//usage: IF_FEATURE_WGET_LONG_OPTIONS( 102//usage: IF_FEATURE_WGET_LONG_OPTIONS(
102//usage: "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n" 103//usage: "[-c|--continue] [--spider] [-q|--quiet] [-O|--output-document FILE]\n"
103//usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n" 104//usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n"
104/* Since we ignore these opts, we don't show them in --help */ 105/* Since we ignore these opts, we don't show them in --help */
105/* //usage: " [--no-check-certificate] [--no-cache] [--passive-ftp] [-t TRIES]" */ 106/* //usage: " [--no-check-certificate] [--no-cache] [--passive-ftp] [-t TRIES]" */
@@ -107,21 +108,23 @@
107//usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." 108//usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
108//usage: ) 109//usage: )
109//usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS( 110//usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS(
110//usage: "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]" 111//usage: "[-cq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]"
111//usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." 112//usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
112//usage: ) 113//usage: )
113//usage:#define wget_full_usage "\n\n" 114//usage:#define wget_full_usage "\n\n"
114//usage: "Retrieve files via HTTP or FTP\n" 115//usage: "Retrieve files via HTTP or FTP\n"
115//usage: "\n -s Spider mode - only check file existence" 116//usage: IF_FEATURE_WGET_LONG_OPTIONS(
116//usage: "\n -c Continue retrieval of aborted transfer" 117//usage: "\n --spider Spider mode - only check file existence"
117//usage: "\n -q Quiet" 118//usage: )
118//usage: "\n -P DIR Save to DIR (default .)" 119//usage: "\n -c Continue retrieval of aborted transfer"
120//usage: "\n -q Quiet"
121//usage: "\n -P DIR Save to DIR (default .)"
119//usage: IF_FEATURE_WGET_TIMEOUT( 122//usage: IF_FEATURE_WGET_TIMEOUT(
120//usage: "\n -T SEC Network read timeout is SEC seconds" 123//usage: "\n -T SEC Network read timeout is SEC seconds"
121//usage: ) 124//usage: )
122//usage: "\n -O FILE Save to FILE ('-' for stdout)" 125//usage: "\n -O FILE Save to FILE ('-' for stdout)"
123//usage: "\n -U STR Use STR for User-Agent header" 126//usage: "\n -U STR Use STR for User-Agent header"
124//usage: "\n -Y Use proxy ('on' or 'off')" 127//usage: "\n -Y on/off Use proxy"
125 128
126#include "libbb.h" 129#include "libbb.h"
127 130
@@ -228,17 +231,17 @@ struct globals {
228/* Must match option string! */ 231/* Must match option string! */
229enum { 232enum {
230 WGET_OPT_CONTINUE = (1 << 0), 233 WGET_OPT_CONTINUE = (1 << 0),
231 WGET_OPT_SPIDER = (1 << 1), 234 WGET_OPT_QUIET = (1 << 1),
232 WGET_OPT_QUIET = (1 << 2), 235 WGET_OPT_OUTNAME = (1 << 2),
233 WGET_OPT_OUTNAME = (1 << 3), 236 WGET_OPT_PREFIX = (1 << 3),
234 WGET_OPT_PREFIX = (1 << 4), 237 WGET_OPT_PROXY = (1 << 4),
235 WGET_OPT_PROXY = (1 << 5), 238 WGET_OPT_USER_AGENT = (1 << 5),
236 WGET_OPT_USER_AGENT = (1 << 6), 239 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 6),
237 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7), 240 WGET_OPT_RETRIES = (1 << 7),
238 WGET_OPT_RETRIES = (1 << 8), 241 WGET_OPT_nsomething = (1 << 8),
239 WGET_OPT_PASSIVE = (1 << 9), 242 WGET_OPT_HEADER = (1 << 9) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
240 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS, 243 WGET_OPT_POST_DATA = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
241 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, 244 WGET_OPT_SPIDER = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
242}; 245};
243 246
244enum { 247enum {
@@ -349,6 +352,30 @@ static void set_alarm(void)
349# define clear_alarm() ((void)0) 352# define clear_alarm() ((void)0)
350#endif 353#endif
351 354
355#if ENABLE_FEATURE_WGET_OPENSSL
356/*
357 * is_ip_address() attempts to verify whether or not a string
358 * contains an IPv4 or IPv6 address (vs. an FQDN). The result
359 * of inet_pton() can be used to determine this.
360 *
361 * TODO add proper error checking when inet_pton() returns -1
362 * (some form of system error has occurred, and errno is set)
363 */
364static int is_ip_address(const char *string)
365{
366 struct sockaddr_in sa;
367
368 int result = inet_pton(AF_INET, string, &(sa.sin_addr));
369# if ENABLE_FEATURE_IPV6
370 if (result == 0) {
371 struct sockaddr_in6 sa6;
372 result = inet_pton(AF_INET6, string, &(sa6.sin6_addr));
373 }
374# endif
375 return (result == 1);
376}
377#endif
378
352static FILE *open_socket(len_and_sockaddr *lsa) 379static FILE *open_socket(len_and_sockaddr *lsa)
353{ 380{
354 int fd; 381 int fd;
@@ -635,6 +662,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
635static int spawn_https_helper_openssl(const char *host, unsigned port) 662static int spawn_https_helper_openssl(const char *host, unsigned port)
636{ 663{
637 char *allocated = NULL; 664 char *allocated = NULL;
665 char *servername;
638 int sp[2]; 666 int sp[2];
639 int pid; 667 int pid;
640 IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;) 668 IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;)
@@ -645,12 +673,14 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
645 673
646 if (!strchr(host, ':')) 674 if (!strchr(host, ':'))
647 host = allocated = xasprintf("%s:%u", host, port); 675 host = allocated = xasprintf("%s:%u", host, port);
676 servername = xstrdup(host);
677 strrchr(servername, ':')[0] = '\0';
648 678
649 fflush_all(); 679 fflush_all();
650 pid = xvfork(); 680 pid = xvfork();
651 if (pid == 0) { 681 if (pid == 0) {
652 /* Child */ 682 /* Child */
653 char *argv[6]; 683 char *argv[8];
654 684
655 close(sp[0]); 685 close(sp[0]);
656 xmove_fd(sp[1], 0); 686 xmove_fd(sp[1], 0);
@@ -662,12 +692,22 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
662 */ 692 */
663 xmove_fd(2, 3); 693 xmove_fd(2, 3);
664 xopen("/dev/null", O_RDWR); 694 xopen("/dev/null", O_RDWR);
695 memset(&argv, 0, sizeof(argv));
665 argv[0] = (char*)"openssl"; 696 argv[0] = (char*)"openssl";
666 argv[1] = (char*)"s_client"; 697 argv[1] = (char*)"s_client";
667 argv[2] = (char*)"-quiet"; 698 argv[2] = (char*)"-quiet";
668 argv[3] = (char*)"-connect"; 699 argv[3] = (char*)"-connect";
669 argv[4] = (char*)host; 700 argv[4] = (char*)host;
670 argv[5] = NULL; 701 /*
702 * Per RFC 6066 Section 3, the only permitted values in the
703 * TLS server_name (SNI) field are FQDNs (DNS hostnames).
704 * IPv4 and IPv6 addresses, port numbers are not allowed.
705 */
706 if (!is_ip_address(servername)) {
707 argv[5] = (char*)"-servername";
708 argv[6] = (char*)servername;
709 }
710
671 BB_EXECVP(argv[0], argv); 711 BB_EXECVP(argv[0], argv);
672 xmove_fd(3, 2); 712 xmove_fd(3, 2);
673# if ENABLE_FEATURE_WGET_SSL_HELPER 713# if ENABLE_FEATURE_WGET_SSL_HELPER
@@ -680,6 +720,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
680 } 720 }
681 721
682 /* Parent */ 722 /* Parent */
723 free(servername);
683 free(allocated); 724 free(allocated);
684 close(sp[1]); 725 close(sp[1]);
685# if ENABLE_FEATURE_WGET_SSL_HELPER 726# if ENABLE_FEATURE_WGET_SSL_HELPER
@@ -1058,6 +1099,12 @@ static void download_one_url(const char *url)
1058 } 1099 }
1059 1100
1060 fflush(sfp); 1101 fflush(sfp);
1102 /* If we use SSL helper, keeping our end of the socket open for writing
1103 * makes our end (i.e. the same fd!) readable (EAGAIN instead of EOF)
1104 * even after child closes its copy of the fd.
1105 * This helps:
1106 */
1107 shutdown(fileno(sfp), SHUT_WR);
1061 1108
1062 /* 1109 /*
1063 * Retrieve HTTP response line and check for "200" status code. 1110 * Retrieve HTTP response line and check for "200" status code.
@@ -1077,7 +1124,21 @@ static void download_one_url(const char *url)
1077 while (gethdr(sfp) != NULL) 1124 while (gethdr(sfp) != NULL)
1078 /* eat all remaining headers */; 1125 /* eat all remaining headers */;
1079 goto read_response; 1126 goto read_response;
1127
1128 /* Success responses */
1080 case 200: 1129 case 200:
1130 /* fall through */
1131 case 201: /* 201 Created */
1132/* "The request has been fulfilled and resulted in a new resource being created" */
1133 /* Standard wget is reported to treat this as success */
1134 /* fall through */
1135 case 202: /* 202 Accepted */
1136/* "The request has been accepted for processing, but the processing has not been completed" */
1137 /* Treat as success: fall through */
1138 case 203: /* 203 Non-Authoritative Information */
1139/* "Use of this response code is not required and is only appropriate when the response would otherwise be 200 (OK)" */
1140 /* fall through */
1141 case 204: /* 204 No Content */
1081/* 1142/*
1082Response 204 doesn't say "null file", it says "metadata 1143Response 204 doesn't say "null file", it says "metadata
1083has changed but data didn't": 1144has changed but data didn't":
@@ -1102,7 +1163,6 @@ is always terminated by the first empty line after the header fields."
1102However, in real world it was observed that some web servers 1163However, in real world it was observed that some web servers
1103(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero. 1164(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
1104*/ 1165*/
1105 case 204:
1106 if (G.beg_range != 0) { 1166 if (G.beg_range != 0) {
1107 /* "Range:..." was not honored by the server. 1167 /* "Range:..." was not honored by the server.
1108 * Restart download from the beginning. 1168 * Restart download from the beginning.
@@ -1110,11 +1170,14 @@ However, in real world it was observed that some web servers
1110 reset_beg_range_to_zero(); 1170 reset_beg_range_to_zero();
1111 } 1171 }
1112 break; 1172 break;
1173 /* 205 Reset Content ?? what to do on this ?? */
1174
1113 case 300: /* redirection */ 1175 case 300: /* redirection */
1114 case 301: 1176 case 301:
1115 case 302: 1177 case 302:
1116 case 303: 1178 case 303:
1117 break; 1179 break;
1180
1118 case 206: /* Partial Content */ 1181 case 206: /* Partial Content */
1119 if (G.beg_range != 0) 1182 if (G.beg_range != 0)
1120 /* "Range:..." worked. Good. */ 1183 /* "Range:..." worked. Good. */
@@ -1231,8 +1294,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
1231 static const char wget_longopts[] ALIGN1 = 1294 static const char wget_longopts[] ALIGN1 =
1232 /* name, has_arg, val */ 1295 /* name, has_arg, val */
1233 "continue\0" No_argument "c" 1296 "continue\0" No_argument "c"
1234//FIXME: -s isn't --spider, it's --save-headers!
1235 "spider\0" No_argument "s"
1236 "quiet\0" No_argument "q" 1297 "quiet\0" No_argument "q"
1237 "output-document\0" Required_argument "O" 1298 "output-document\0" Required_argument "O"
1238 "directory-prefix\0" Required_argument "P" 1299 "directory-prefix\0" Required_argument "P"
@@ -1244,6 +1305,7 @@ IF_FEATURE_WGET_TIMEOUT(
1244IF_DESKTOP( "tries\0" Required_argument "t") 1305IF_DESKTOP( "tries\0" Required_argument "t")
1245 "header\0" Required_argument "\xff" 1306 "header\0" Required_argument "\xff"
1246 "post-data\0" Required_argument "\xfe" 1307 "post-data\0" Required_argument "\xfe"
1308 "spider\0" No_argument "\xfd"
1247 /* Ignored (we always use PASV): */ 1309 /* Ignored (we always use PASV): */
1248IF_DESKTOP( "passive-ftp\0" No_argument "\xf0") 1310IF_DESKTOP( "passive-ftp\0" No_argument "\xf0")
1249 /* Ignored (we don't do ssl) */ 1311 /* Ignored (we don't do ssl) */
@@ -1275,7 +1337,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0")
1275#endif 1337#endif
1276 opt_complementary = "-1" /* at least one URL */ 1338 opt_complementary = "-1" /* at least one URL */
1277 IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */ 1339 IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */
1278 getopt32(argv, "csqO:P:Y:U:T:+" 1340 getopt32(argv, "cqO:P:Y:U:T:+"
1279 /*ignored:*/ "t:" 1341 /*ignored:*/ "t:"
1280 /*ignored:*/ "n::" 1342 /*ignored:*/ "n::"
1281 /* wget has exactly four -n<letter> opts, all of which we can ignore: 1343 /* wget has exactly four -n<letter> opts, all of which we can ignore:
@@ -1294,6 +1356,14 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0")
1294 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) 1356 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
1295 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data) 1357 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
1296 ); 1358 );
1359#if 0 /* option bits debug */
1360 if (option_mask32 & WGET_OPT_RETRIES) bb_error_msg("-t NUM");
1361 if (option_mask32 & WGET_OPT_nsomething) bb_error_msg("-nsomething");
1362 if (option_mask32 & WGET_OPT_HEADER) bb_error_msg("--header");
1363 if (option_mask32 & WGET_OPT_POST_DATA) bb_error_msg("--post-data");
1364 if (option_mask32 & WGET_OPT_SPIDER) bb_error_msg("--spider");
1365 exit(0);
1366#endif
1297 argv += optind; 1367 argv += optind;
1298 1368
1299#if ENABLE_FEATURE_WGET_LONG_OPTIONS 1369#if ENABLE_FEATURE_WGET_LONG_OPTIONS
diff --git a/procps/top.c b/procps/top.c
index 73cd285f0..6f7f7d382 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -728,12 +728,6 @@ static void reset_term(void)
728{ 728{
729 if (!OPT_BATCH_MODE) 729 if (!OPT_BATCH_MODE)
730 tcsetattr_stdin_TCSANOW(&initial_settings); 730 tcsetattr_stdin_TCSANOW(&initial_settings);
731 if (ENABLE_FEATURE_CLEAN_UP) {
732 clearmems();
733# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
734 free(prev_hist);
735# endif
736 }
737} 731}
738 732
739static void sig_catcher(int sig) 733static void sig_catcher(int sig)
@@ -1258,5 +1252,11 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1258#if ENABLE_FEATURE_USE_TERMIOS 1252#if ENABLE_FEATURE_USE_TERMIOS
1259 reset_term(); 1253 reset_term();
1260#endif 1254#endif
1255 if (ENABLE_FEATURE_CLEAN_UP) {
1256 clearmems();
1257#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1258 free(prev_hist);
1259#endif
1260 }
1261 return EXIT_SUCCESS; 1261 return EXIT_SUCCESS;
1262} 1262}
diff --git a/runit/runsvdir.c b/runit/runsvdir.c
index 2b7927542..84916e929 100644
--- a/runit/runsvdir.c
+++ b/runit/runsvdir.c
@@ -181,9 +181,9 @@ static NOINLINE int do_rescan(void)
181 continue; 181 continue;
182 /* Do we have this service listed already? */ 182 /* Do we have this service listed already? */
183 for (i = 0; i < svnum; i++) { 183 for (i = 0; i < svnum; i++) {
184 if ((sv[i].ino == s.st_ino) 184 if (sv[i].ino == s.st_ino
185#if CHECK_DEVNO_TOO 185#if CHECK_DEVNO_TOO
186 && (sv[i].dev == s.st_dev) 186 && sv[i].dev == s.st_dev
187#endif 187#endif
188 ) { 188 ) {
189 if (sv[i].pid == 0) /* restart if it has died */ 189 if (sv[i].pid == 0) /* restart if it has died */
diff --git a/runit/svlogd.c b/runit/svlogd.c
index 7cae81cb2..3ed13b67b 100644
--- a/runit/svlogd.c
+++ b/runit/svlogd.c
@@ -784,7 +784,7 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
784 case '!': 784 case '!':
785 if (s[1]) { 785 if (s[1]) {
786 free(ld->processor); 786 free(ld->processor);
787 ld->processor = wstrdup(s); 787 ld->processor = wstrdup(&s[1]);
788 } 788 }
789 break; 789 break;
790 } 790 }
diff --git a/scripts/trylink b/scripts/trylink
index 145df9959..c2a431626 100755
--- a/scripts/trylink
+++ b/scripts/trylink
@@ -46,7 +46,7 @@ try() {
46} 46}
47 47
48check_cc() { 48check_cc() {
49 local tempname="$(mktemp)" 49 local tempname="$(mktemp tmp.XXXXXXXXXX)"
50 local r 50 local r
51 echo "int main(int argc,char**argv){return argv?argc:0;}" >"$tempname".c 51 echo "int main(int argc,char**argv){return argv?argc:0;}" >"$tempname".c
52 # Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :( 52 # Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :(
@@ -61,7 +61,7 @@ check_cc() {
61} 61}
62 62
63check_libc_is_glibc() { 63check_libc_is_glibc() {
64 local tempname="$(mktemp)" 64 local tempname="$(mktemp tmp.XXXXXXXXXX)"
65 local r 65 local r
66 echo "\ 66 echo "\
67 #include <stdlib.h> 67 #include <stdlib.h>
diff --git a/shell/Config.src b/shell/Config.src
index b31e62dda..e4df35973 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -88,7 +88,7 @@ config FEATURE_SH_EXTRA_QUIET
88config FEATURE_SH_STANDALONE 88config FEATURE_SH_STANDALONE
89 bool "Standalone shell" 89 bool "Standalone shell"
90 default n 90 default n
91 depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS 91 depends on (HUSH || ASH)
92 help 92 help
93 This option causes busybox shells to use busybox applets 93 This option causes busybox shells to use busybox applets
94 in preference to executables in the PATH whenever possible. For 94 in preference to executables in the PATH whenever possible. For
@@ -121,7 +121,7 @@ config FEATURE_SH_STANDALONE
121config FEATURE_SH_NOFORK 121config FEATURE_SH_NOFORK
122 bool "Run 'nofork' applets directly" 122 bool "Run 'nofork' applets directly"
123 default n 123 default n
124 depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS 124 depends on (HUSH || ASH)
125 help 125 help
126 This option causes busybox shells to not execute typical 126 This option causes busybox shells to not execute typical
127 fork/exec/wait sequence, but call <applet>_main directly, 127 fork/exec/wait sequence, but call <applet>_main directly,
diff --git a/shell/ash.c b/shell/ash.c
index 750ca7b6d..02e76c0ae 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -57,8 +57,18 @@
57#include <sys/utsname.h> /* for setting $HOSTNAME */ 57#include <sys/utsname.h> /* for setting $HOSTNAME */
58 58
59#include "busybox.h" /* for applet_names */ 59#include "busybox.h" /* for applet_names */
60#include "unicode.h"
61 60
61#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
62/* Bionic at least up to version 24 has no glob() */
63# undef ENABLE_ASH_INTERNAL_GLOB
64# define ENABLE_ASH_INTERNAL_GLOB 1
65#endif
66
67#if !ENABLE_ASH_INTERNAL_GLOB
68# include <glob.h>
69#endif
70
71#include "unicode.h"
62#include "shell_common.h" 72#include "shell_common.h"
63#if ENABLE_SH_MATH_SUPPORT 73#if ENABLE_SH_MATH_SUPPORT
64# include "math.h" 74# include "math.h"
@@ -104,6 +114,42 @@
104//config: shell (by Herbert Xu), which was created by porting the 'ash' shell 114//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
105//config: (written by Kenneth Almquist) from NetBSD. 115//config: (written by Kenneth Almquist) from NetBSD.
106//config: 116//config:
117//config:config ASH_OPTIMIZE_FOR_SIZE
118//config: bool "Optimize for size instead of speed"
119//config: default y
120//config: depends on ASH
121//config: help
122//config: Compile ash for reduced size at the price of speed.
123//config:
124//config:config ASH_INTERNAL_GLOB
125//config: bool "Use internal glob() implementation"
126//config: default n
127//config: depends on ASH
128//config: help
129//config: Do not use glob() function from libc, use internal implementation.
130//config: Use this if you are getting "glob.h: No such file or directory"
131//config: or similar build errors.
132//config:
133//config:config ASH_RANDOM_SUPPORT
134//config: bool "Pseudorandom generator and $RANDOM variable"
135//config: default y
136//config: depends on ASH
137//config: help
138//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
139//config: Each read of "$RANDOM" will generate a new pseudorandom value.
140//config: You can reset the generator by using a specified start value.
141//config: After "unset RANDOM" the generator will switch off and this
142//config: variable will no longer have special treatment.
143//config:
144//config:config ASH_EXPAND_PRMT
145//config: bool "Expand prompt string"
146//config: default y
147//config: depends on ASH
148//config: help
149//config: "PS#" may contain volatile content, such as backquote commands.
150//config: This option recreates the prompt string from the environment
151//config: variable each time it is displayed.
152//config:
107//config:config ASH_BASH_COMPAT 153//config:config ASH_BASH_COMPAT
108//config: bool "bash-compatible extensions" 154//config: bool "bash-compatible extensions"
109//config: default y 155//config: default y
@@ -183,33 +229,6 @@
183//config: help 229//config: help
184//config: Enable "check for new mail" function in the ash shell. 230//config: Enable "check for new mail" function in the ash shell.
185//config: 231//config:
186//config:config ASH_OPTIMIZE_FOR_SIZE
187//config: bool "Optimize for size instead of speed"
188//config: default y
189//config: depends on ASH
190//config: help
191//config: Compile ash for reduced size at the price of speed.
192//config:
193//config:config ASH_RANDOM_SUPPORT
194//config: bool "Pseudorandom generator and $RANDOM variable"
195//config: default y
196//config: depends on ASH
197//config: help
198//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
199//config: Each read of "$RANDOM" will generate a new pseudorandom value.
200//config: You can reset the generator by using a specified start value.
201//config: After "unset RANDOM" the generator will switch off and this
202//config: variable will no longer have special treatment.
203//config:
204//config:config ASH_EXPAND_PRMT
205//config: bool "Expand prompt string"
206//config: default y
207//config: depends on ASH
208//config: help
209//config: "PS#" may contain volatile content, such as backquote commands.
210//config: This option recreates the prompt string from the environment
211//config: variable each time it is displayed.
212//config:
213 232
214//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 233//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
215//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) 234//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
@@ -324,8 +343,10 @@ struct jmploc {
324}; 343};
325 344
326struct globals_misc { 345struct globals_misc {
327 /* pid of main shell */ 346 uint8_t exitstatus; /* exit status of last command */
328 int rootpid; 347 uint8_t back_exitstatus;/* exit status of backquoted command */
348 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
349 int rootpid; /* pid of main shell */
329 /* shell level: 0 for the main shell, 1 for its children, and so on */ 350 /* shell level: 0 for the main shell, 1 for its children, and so on */
330 int shlvl; 351 int shlvl;
331#define rootshell (!shlvl) 352#define rootshell (!shlvl)
@@ -393,7 +414,7 @@ struct globals_misc {
393#define S_DFL 1 /* default signal handling (SIG_DFL) */ 414#define S_DFL 1 /* default signal handling (SIG_DFL) */
394#define S_CATCH 2 /* signal is caught */ 415#define S_CATCH 2 /* signal is caught */
395#define S_IGN 3 /* signal is ignored (SIG_IGN) */ 416#define S_IGN 3 /* signal is ignored (SIG_IGN) */
396#define S_HARD_IGN 4 /* signal is ignored permenantly */ 417#define S_HARD_IGN 4 /* signal is ignored permanently */
397 418
398 /* indicates specified signal received */ 419 /* indicates specified signal received */
399 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 420 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
@@ -406,10 +427,12 @@ struct globals_misc {
406 random_t random_gen; 427 random_t random_gen;
407#endif 428#endif
408 pid_t backgndpid; /* pid of last background process */ 429 pid_t backgndpid; /* pid of last background process */
409 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
410}; 430};
411extern struct globals_misc *const ash_ptr_to_globals_misc; 431extern struct globals_misc *const ash_ptr_to_globals_misc;
412#define G_misc (*ash_ptr_to_globals_misc) 432#define G_misc (*ash_ptr_to_globals_misc)
433#define exitstatus (G_misc.exitstatus )
434#define back_exitstatus (G_misc.back_exitstatus )
435#define job_warning (G_misc.job_warning)
413#define rootpid (G_misc.rootpid ) 436#define rootpid (G_misc.rootpid )
414#define shlvl (G_misc.shlvl ) 437#define shlvl (G_misc.shlvl )
415#define minusc (G_misc.minusc ) 438#define minusc (G_misc.minusc )
@@ -431,7 +454,6 @@ extern struct globals_misc *const ash_ptr_to_globals_misc;
431#define trap_ptr (G_misc.trap_ptr ) 454#define trap_ptr (G_misc.trap_ptr )
432#define random_gen (G_misc.random_gen ) 455#define random_gen (G_misc.random_gen )
433#define backgndpid (G_misc.backgndpid ) 456#define backgndpid (G_misc.backgndpid )
434#define job_warning (G_misc.job_warning)
435#define INIT_G_misc() do { \ 457#define INIT_G_misc() do { \
436 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ 458 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
437 barrier(); \ 459 barrier(); \
@@ -460,12 +482,11 @@ static void trace_vprintf(const char *fmt, va_list va);
460 482
461 483
462/* ============ Utility functions */ 484/* ============ Utility functions */
463#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
464
465#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) 485#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
466#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) 486#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
467 487
468static int isdigit_str9(const char *str) 488static int
489isdigit_str9(const char *str)
469{ 490{
470 int maxlen = 9 + 1; /* max 9 digits: 999999999 */ 491 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
471 while (--maxlen && isdigit(*str)) 492 while (--maxlen && isdigit(*str))
@@ -473,7 +494,8 @@ static int isdigit_str9(const char *str)
473 return (*str == '\0'); 494 return (*str == '\0');
474} 495}
475 496
476static const char *var_end(const char *var) 497static const char *
498var_end(const char *var)
477{ 499{
478 while (*var) 500 while (*var)
479 if (*var++ == '=') 501 if (*var++ == '=')
@@ -494,7 +516,7 @@ static void exitshell(void) NORETURN;
494 */ 516 */
495#define INT_OFF do { \ 517#define INT_OFF do { \
496 suppress_int++; \ 518 suppress_int++; \
497 xbarrier(); \ 519 barrier(); \
498} while (0) 520} while (0)
499 521
500/* 522/*
@@ -522,7 +544,7 @@ raise_exception(int e)
522#endif 544#endif
523 545
524/* 546/*
525 * Called from trap.c when a SIGINT is received. (If the user specifies 547 * Called when a SIGINT is received. (If the user specifies
526 * that SIGINT is to be trapped or ignored using the trap builtin, then 548 * that SIGINT is to be trapped or ignored using the trap builtin, then
527 * this routine is not called.) Suppressint is nonzero when interrupts 549 * this routine is not called.) Suppressint is nonzero when interrupts
528 * are held using the INT_OFF macro. (The test for iflag is just 550 * are held using the INT_OFF macro. (The test for iflag is just
@@ -549,6 +571,8 @@ raise_interrupt(void)
549 } 571 }
550 ex_type = EXINT; 572 ex_type = EXINT;
551 } 573 }
574 /* bash: ^C even on empty command line sets $? */
575 exitstatus = SIGINT + 128;
552 raise_exception(ex_type); 576 raise_exception(ex_type);
553 /* NOTREACHED */ 577 /* NOTREACHED */
554} 578}
@@ -562,7 +586,7 @@ raise_interrupt(void)
562static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void 586static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
563int_on(void) 587int_on(void)
564{ 588{
565 xbarrier(); 589 barrier();
566 if (--suppress_int == 0 && pending_int) { 590 if (--suppress_int == 0 && pending_int) {
567 raise_interrupt(); 591 raise_interrupt();
568 } 592 }
@@ -571,7 +595,7 @@ int_on(void)
571static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void 595static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
572force_int_on(void) 596force_int_on(void)
573{ 597{
574 xbarrier(); 598 barrier();
575 suppress_int = 0; 599 suppress_int = 0;
576 if (pending_int) 600 if (pending_int)
577 raise_interrupt(); 601 raise_interrupt();
@@ -581,7 +605,7 @@ force_int_on(void)
581#define SAVE_INT(v) ((v) = suppress_int) 605#define SAVE_INT(v) ((v) = suppress_int)
582 606
583#define RESTORE_INT(v) do { \ 607#define RESTORE_INT(v) do { \
584 xbarrier(); \ 608 barrier(); \
585 suppress_int = (v); \ 609 suppress_int = (v); \
586 if (suppress_int == 0 && pending_int) \ 610 if (suppress_int == 0 && pending_int) \
587 raise_interrupt(); \ 611 raise_interrupt(); \
@@ -1237,6 +1261,12 @@ struct strpush {
1237 struct alias *ap; /* if push was associated with an alias */ 1261 struct alias *ap; /* if push was associated with an alias */
1238#endif 1262#endif
1239 char *string; /* remember the string since it may change */ 1263 char *string; /* remember the string since it may change */
1264
1265 /* Remember last two characters for pungetc. */
1266 int lastc[2];
1267
1268 /* Number of outstanding calls to pungetc. */
1269 int unget;
1240}; 1270};
1241 1271
1242struct parsefile { 1272struct parsefile {
@@ -1249,6 +1279,12 @@ struct parsefile {
1249 char *buf; /* input buffer */ 1279 char *buf; /* input buffer */
1250 struct strpush *strpush; /* for pushing strings at this level */ 1280 struct strpush *strpush; /* for pushing strings at this level */
1251 struct strpush basestrpush; /* so pushing one is fast */ 1281 struct strpush basestrpush; /* so pushing one is fast */
1282
1283 /* Remember last two characters for pungetc. */
1284 int lastc[2];
1285
1286 /* Number of outstanding calls to pungetc. */
1287 int unget;
1252}; 1288};
1253 1289
1254static struct parsefile basepf; /* top level input file */ 1290static struct parsefile basepf; /* top level input file */
@@ -1256,7 +1292,6 @@ static struct parsefile *g_parsefile = &basepf; /* current input file */
1256static int startlinno; /* line # where last token started */ 1292static int startlinno; /* line # where last token started */
1257static char *commandname; /* currently executing command */ 1293static char *commandname; /* currently executing command */
1258static struct strlist *cmdenviron; /* environment for builtin command */ 1294static struct strlist *cmdenviron; /* environment for builtin command */
1259static uint8_t exitstatus; /* exit status of last command */
1260 1295
1261 1296
1262/* ============ Message printing */ 1297/* ============ Message printing */
@@ -1428,13 +1463,11 @@ struct stackmark {
1428 struct stack_block *stackp; 1463 struct stack_block *stackp;
1429 char *stacknxt; 1464 char *stacknxt;
1430 size_t stacknleft; 1465 size_t stacknleft;
1431 struct stackmark *marknext;
1432}; 1466};
1433 1467
1434 1468
1435struct globals_memstack { 1469struct globals_memstack {
1436 struct stack_block *g_stackp; // = &stackbase; 1470 struct stack_block *g_stackp; // = &stackbase;
1437 struct stackmark *markp;
1438 char *g_stacknxt; // = stackbase.space; 1471 char *g_stacknxt; // = stackbase.space;
1439 char *sstrend; // = stackbase.space + MINSIZE; 1472 char *sstrend; // = stackbase.space + MINSIZE;
1440 size_t g_stacknleft; // = MINSIZE; 1473 size_t g_stacknleft; // = MINSIZE;
@@ -1444,7 +1477,6 @@ struct globals_memstack {
1444extern struct globals_memstack *const ash_ptr_to_globals_memstack; 1477extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1445#define G_memstack (*ash_ptr_to_globals_memstack) 1478#define G_memstack (*ash_ptr_to_globals_memstack)
1446#define g_stackp (G_memstack.g_stackp ) 1479#define g_stackp (G_memstack.g_stackp )
1447#define markp (G_memstack.markp )
1448#define g_stacknxt (G_memstack.g_stacknxt ) 1480#define g_stacknxt (G_memstack.g_stacknxt )
1449#define sstrend (G_memstack.sstrend ) 1481#define sstrend (G_memstack.sstrend )
1450#define g_stacknleft (G_memstack.g_stacknleft) 1482#define g_stacknleft (G_memstack.g_stacknleft)
@@ -1528,20 +1560,33 @@ stunalloc(void *p)
1528 * Like strdup but works with the ash stack. 1560 * Like strdup but works with the ash stack.
1529 */ 1561 */
1530static char * 1562static char *
1531ststrdup(const char *p) 1563sstrdup(const char *p)
1532{ 1564{
1533 size_t len = strlen(p) + 1; 1565 size_t len = strlen(p) + 1;
1534 return memcpy(stalloc(len), p, len); 1566 return memcpy(stalloc(len), p, len);
1535} 1567}
1536 1568
1537static void 1569static void
1538setstackmark(struct stackmark *mark) 1570grabstackblock(size_t len)
1571{
1572 len = SHELL_ALIGN(len);
1573 g_stacknxt += len;
1574 g_stacknleft -= len;
1575}
1576
1577static void
1578pushstackmark(struct stackmark *mark, size_t len)
1539{ 1579{
1540 mark->stackp = g_stackp; 1580 mark->stackp = g_stackp;
1541 mark->stacknxt = g_stacknxt; 1581 mark->stacknxt = g_stacknxt;
1542 mark->stacknleft = g_stacknleft; 1582 mark->stacknleft = g_stacknleft;
1543 mark->marknext = markp; 1583 grabstackblock(len);
1544 markp = mark; 1584}
1585
1586static void
1587setstackmark(struct stackmark *mark)
1588{
1589 pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase);
1545} 1590}
1546 1591
1547static void 1592static void
@@ -1553,7 +1598,6 @@ popstackmark(struct stackmark *mark)
1553 return; 1598 return;
1554 1599
1555 INT_OFF; 1600 INT_OFF;
1556 markp = mark->marknext;
1557 while (g_stackp != mark->stackp) { 1601 while (g_stackp != mark->stackp) {
1558 sp = g_stackp; 1602 sp = g_stackp;
1559 g_stackp = sp->prev; 1603 g_stackp = sp->prev;
@@ -1586,14 +1630,11 @@ growstackblock(void)
1586 newlen += 128; 1630 newlen += 128;
1587 1631
1588 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { 1632 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
1589 struct stack_block *oldstackp;
1590 struct stackmark *xmark;
1591 struct stack_block *sp; 1633 struct stack_block *sp;
1592 struct stack_block *prevstackp; 1634 struct stack_block *prevstackp;
1593 size_t grosslen; 1635 size_t grosslen;
1594 1636
1595 INT_OFF; 1637 INT_OFF;
1596 oldstackp = g_stackp;
1597 sp = g_stackp; 1638 sp = g_stackp;
1598 prevstackp = sp->prev; 1639 prevstackp = sp->prev;
1599 grosslen = newlen + sizeof(struct stack_block) - MINSIZE; 1640 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
@@ -1603,18 +1644,6 @@ growstackblock(void)
1603 g_stacknxt = sp->space; 1644 g_stacknxt = sp->space;
1604 g_stacknleft = newlen; 1645 g_stacknleft = newlen;
1605 sstrend = sp->space + newlen; 1646 sstrend = sp->space + newlen;
1606
1607 /*
1608 * Stack marks pointing to the start of the old block
1609 * must be relocated to point to the new block
1610 */
1611 xmark = markp;
1612 while (xmark != NULL && xmark->stackp == oldstackp) {
1613 xmark->stackp = g_stackp;
1614 xmark->stacknxt = g_stacknxt;
1615 xmark->stacknleft = g_stacknleft;
1616 xmark = xmark->marknext;
1617 }
1618 INT_ON; 1647 INT_ON;
1619 } else { 1648 } else {
1620 char *oldspace = g_stacknxt; 1649 char *oldspace = g_stacknxt;
@@ -1627,14 +1656,6 @@ growstackblock(void)
1627 } 1656 }
1628} 1657}
1629 1658
1630static void
1631grabstackblock(size_t len)
1632{
1633 len = SHELL_ALIGN(len);
1634 g_stacknxt += len;
1635 g_stacknleft -= len;
1636}
1637
1638/* 1659/*
1639 * The following routines are somewhat easier to use than the above. 1660 * The following routines are somewhat easier to use than the above.
1640 * The user declares a variable of type STACKSTR, which may be declared 1661 * The user declares a variable of type STACKSTR, which may be declared
@@ -1671,7 +1692,7 @@ static char *
1671makestrspace(size_t newlen, char *p) 1692makestrspace(size_t newlen, char *p)
1672{ 1693{
1673 size_t len = p - g_stacknxt; 1694 size_t len = p - g_stacknxt;
1674 size_t size = stackblocksize(); 1695 size_t size;
1675 1696
1676 for (;;) { 1697 for (;;) {
1677 size_t nleft; 1698 size_t nleft;
@@ -1990,7 +2011,7 @@ static const struct {
1990 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, 2011 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1991 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, 2012 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
1992#if ENABLE_ASH_GETOPTS 2013#if ENABLE_ASH_GETOPTS
1993 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset }, 2014 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
1994#endif 2015#endif
1995#if ENABLE_ASH_RANDOM_SUPPORT 2016#if ENABLE_ASH_RANDOM_SUPPORT
1996 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, 2017 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
@@ -2193,7 +2214,8 @@ lookupvar(const char *name)
2193 return NULL; 2214 return NULL;
2194} 2215}
2195 2216
2196static void reinit_unicode_for_ash(void) 2217static void
2218reinit_unicode_for_ash(void)
2197{ 2219{
2198 /* Unicode support should be activated even if LANG is set 2220 /* Unicode support should be activated even if LANG is set
2199 * _during_ shell execution, not only if it was set when 2221 * _during_ shell execution, not only if it was set when
@@ -2560,8 +2582,7 @@ setprompt_if(smallint do_set, int whichprompt)
2560 prompt = nullstr; 2582 prompt = nullstr;
2561 } 2583 }
2562#if ENABLE_ASH_EXPAND_PRMT 2584#if ENABLE_ASH_EXPAND_PRMT
2563 setstackmark(&smark); 2585 pushstackmark(&smark, stackblocksize());
2564 stalloc(stackblocksize());
2565#endif 2586#endif
2566 putprompt(expandstr(prompt)); 2587 putprompt(expandstr(prompt));
2567#if ENABLE_ASH_EXPAND_PRMT 2588#if ENABLE_ASH_EXPAND_PRMT
@@ -2704,7 +2725,7 @@ updatepwd(const char *dir)
2704 char *cdcomppath; 2725 char *cdcomppath;
2705 const char *lim; 2726 const char *lim;
2706 2727
2707 cdcomppath = ststrdup(dir); 2728 cdcomppath = sstrdup(dir);
2708 STARTSTACKSTR(new); 2729 STARTSTACKSTR(new);
2709 if (*dir != '/') { 2730 if (*dir != '/') {
2710 if (curdir == nullstr) 2731 if (curdir == nullstr)
@@ -3000,18 +3021,27 @@ enum {
3000static int 3021static int
3001SIT(int c, int syntax) 3022SIT(int c, int syntax)
3002{ 3023{
3003 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; 3024 /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */
3025 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~";
3026 /*
3027 * This causes '/' to be prepended with CTLESC in dquoted string,
3028 * making "./file"* treated incorrectly because we feed
3029 * ".\/file*" string to glob(), confusing it (see expandmeta func).
3030 * The "homegrown" glob implementation is okay with that,
3031 * but glibc one isn't. With '/' always treated as CWORD,
3032 * both work fine.
3033 */
3004# if ENABLE_ASH_ALIAS 3034# if ENABLE_ASH_ALIAS
3005 static const uint8_t syntax_index_table[] ALIGN1 = { 3035 static const uint8_t syntax_index_table[] ALIGN1 = {
3006 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ 3036 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
3007 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ 3037 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */
3008 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ 3038 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
3009 11, 3 /* "}~" */ 3039 11, 3 /* "}~" */
3010 }; 3040 };
3011# else 3041# else
3012 static const uint8_t syntax_index_table[] ALIGN1 = { 3042 static const uint8_t syntax_index_table[] ALIGN1 = {
3013 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ 3043 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
3014 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ 3044 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
3015 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ 3045 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3016 10, 2 /* "}~" */ 3046 10, 2 /* "}~" */
3017 }; 3047 };
@@ -3093,7 +3123,8 @@ static const uint8_t syntax_index_table[] ALIGN1 = {
3093 /* 44 "," */ CWORD_CWORD_CWORD_CWORD, 3123 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
3094 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, 3124 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
3095 /* 46 "." */ CWORD_CWORD_CWORD_CWORD, 3125 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
3096 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD, 3126/* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */
3127 /* 47 "/" */ CWORD_CWORD_CWORD_CWORD,
3097 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, 3128 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
3098 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, 3129 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
3099 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, 3130 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
@@ -3335,7 +3366,8 @@ static struct alias **atab; // [ATABSIZE];
3335 3366
3336 3367
3337static struct alias ** 3368static struct alias **
3338__lookupalias(const char *name) { 3369__lookupalias(const char *name)
3370{
3339 unsigned int hashval; 3371 unsigned int hashval;
3340 struct alias **app; 3372 struct alias **app;
3341 const char *p; 3373 const char *p;
@@ -3517,8 +3549,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3517#endif /* ASH_ALIAS */ 3549#endif /* ASH_ALIAS */
3518 3550
3519 3551
3520/* ============ jobs.c */
3521
3522/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 3552/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3523#define FORK_FG 0 3553#define FORK_FG 0
3524#define FORK_BG 1 3554#define FORK_BG 1
@@ -3842,7 +3872,7 @@ getjob(const char *name, int getctl)
3842 3872
3843 if (is_number(p)) { 3873 if (is_number(p)) {
3844 num = atoi(p); 3874 num = atoi(p);
3845 if (num <= njobs) { 3875 if (num > 0 && num <= njobs) {
3846 jp = jobtab + num - 1; 3876 jp = jobtab + num - 1;
3847 if (jp->used) 3877 if (jp->used)
3848 goto gotit; 3878 goto gotit;
@@ -5419,6 +5449,19 @@ openredirect(union node *redir)
5419 char *fname; 5449 char *fname;
5420 int f; 5450 int f;
5421 5451
5452 switch (redir->nfile.type) {
5453/* Can't happen, our single caller does this itself */
5454// case NTOFD:
5455// case NFROMFD:
5456// return -1;
5457 case NHERE:
5458 case NXHERE:
5459 return openhere(redir);
5460 }
5461
5462 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
5463 * allocated space. Do it only when we know it is safe.
5464 */
5422 fname = redir->nfile.expfname; 5465 fname = redir->nfile.expfname;
5423#if ENABLE_PLATFORM_MINGW32 5466#if ENABLE_PLATFORM_MINGW32
5424 /* Support for /dev/null */ 5467 /* Support for /dev/null */
@@ -5445,7 +5488,12 @@ openredirect(union node *redir)
5445 break; 5488 break;
5446 } 5489 }
5447#endif 5490#endif
5491
5448 switch (redir->nfile.type) { 5492 switch (redir->nfile.type) {
5493 default:
5494#if DEBUG
5495 abort();
5496#endif
5449 case NFROM: 5497 case NFROM:
5450 f = open(fname, O_RDONLY); 5498 f = open(fname, O_RDONLY);
5451 if (f < 0) 5499 if (f < 0)
@@ -5481,20 +5529,6 @@ openredirect(union node *redir)
5481 lseek(f, 0, SEEK_END); 5529 lseek(f, 0, SEEK_END);
5482#endif 5530#endif
5483 break; 5531 break;
5484 default:
5485#if DEBUG
5486 abort();
5487#endif
5488 /* Fall through to eliminate warning. */
5489/* Our single caller does this itself */
5490// case NTOFD:
5491// case NFROMFD:
5492// f = -1;
5493// break;
5494 case NHERE:
5495 case NXHERE:
5496 f = openhere(redir);
5497 break;
5498 } 5532 }
5499 5533
5500 return f; 5534 return f;
@@ -5548,7 +5582,8 @@ struct redirtab {
5548}; 5582};
5549#define redirlist (G_var.redirlist) 5583#define redirlist (G_var.redirlist)
5550 5584
5551static int need_to_remember(struct redirtab *rp, int fd) 5585static int
5586need_to_remember(struct redirtab *rp, int fd)
5552{ 5587{
5553 int i; 5588 int i;
5554 5589
@@ -5565,7 +5600,8 @@ static int need_to_remember(struct redirtab *rp, int fd)
5565} 5600}
5566 5601
5567/* "hidden" fd is a fd used to read scripts, or a copy of such */ 5602/* "hidden" fd is a fd used to read scripts, or a copy of such */
5568static int is_hidden_fd(struct redirtab *rp, int fd) 5603static int
5604is_hidden_fd(struct redirtab *rp, int fd)
5569{ 5605{
5570 int i; 5606 int i;
5571 struct parsefile *pf; 5607 struct parsefile *pf;
@@ -5979,7 +6015,6 @@ rmescapes(char *str, int flag)
5979 while (*p) { 6015 while (*p) {
5980 if ((unsigned char)*p == CTLQUOTEMARK) { 6016 if ((unsigned char)*p == CTLQUOTEMARK) {
5981// Note: both inquotes and protect_against_glob only affect whether 6017// Note: both inquotes and protect_against_glob only affect whether
5982// CTLESC,<ch> gets converted to <ch> or to \<ch>
5983 inquotes = ~inquotes; 6018 inquotes = ~inquotes;
5984 p++; 6019 p++;
5985 protect_against_glob = globbing; 6020 protect_against_glob = globbing;
@@ -5987,6 +6022,10 @@ rmescapes(char *str, int flag)
5987 } 6022 }
5988 if ((unsigned char)*p == CTLESC) { 6023 if ((unsigned char)*p == CTLESC) {
5989 p++; 6024 p++;
6025#if DEBUG
6026 if (*p == '\0')
6027 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6028#endif
5990 if (protect_against_glob) { 6029 if (protect_against_glob) {
5991 *q++ = '\\'; 6030 *q++ = '\\';
5992 } 6031 }
@@ -6043,11 +6082,14 @@ memtodest(const char *p, size_t len, int syntax, int quotes)
6043 unsigned char c = *p++; 6082 unsigned char c = *p++;
6044 if (c) { 6083 if (c) {
6045 int n = SIT(c, syntax); 6084 int n = SIT(c, syntax);
6046 if ((quotes & QUOTES_ESC) && 6085 if ((quotes & QUOTES_ESC)
6047 ((n == CCTL) || 6086 && ((n == CCTL)
6048 (((quotes & EXP_FULL) || syntax != BASESYNTAX) && 6087 || (((quotes & EXP_FULL) || syntax != BASESYNTAX)
6049 n == CBACK))) 6088 && n == CBACK)
6089 )
6090 ) {
6050 USTPUTC(CTLESC, q); 6091 USTPUTC(CTLESC, q);
6092 }
6051 } else if (!(quotes & QUOTES_KEEPNUL)) 6093 } else if (!(quotes & QUOTES_KEEPNUL))
6052 continue; 6094 continue;
6053 USTPUTC(c, q); 6095 USTPUTC(c, q);
@@ -6188,9 +6230,8 @@ struct backcmd { /* result of evalbackcmd */
6188}; 6230};
6189 6231
6190/* These forward decls are needed to use "eval" code for backticks handling: */ 6232/* These forward decls are needed to use "eval" code for backticks handling: */
6191static uint8_t back_exitstatus; /* exit status of backquoted command */
6192#define EV_EXIT 01 /* exit after evaluating tree */ 6233#define EV_EXIT 01 /* exit after evaluating tree */
6193static void evaltree(union node *, int); 6234static int evaltree(union node *, int);
6194 6235
6195static void FAST_FUNC 6236static void FAST_FUNC
6196evalbackcmd(union node *n, struct backcmd *result) 6237evalbackcmd(union node *n, struct backcmd *result)
@@ -6262,10 +6303,8 @@ expbackq(union node *cmd, int flag)
6262 struct stackmark smark; 6303 struct stackmark smark;
6263 6304
6264 INT_OFF; 6305 INT_OFF;
6265 setstackmark(&smark); 6306 startloc = expdest - (char *)stackblock();
6266 dest = expdest; 6307 pushstackmark(&smark, startloc);
6267 startloc = dest - (char *)stackblock();
6268 grabstackstr(dest);
6269 evalbackcmd(cmd, &in); 6308 evalbackcmd(cmd, &in);
6270 popstackmark(&smark); 6309 popstackmark(&smark);
6271 6310
@@ -6681,6 +6720,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6681 6720
6682#if ENABLE_ASH_BASH_COMPAT 6721#if ENABLE_ASH_BASH_COMPAT
6683 case VSSUBSTR: 6722 case VSSUBSTR:
6723//TODO: support more general format ${v:EXPR:EXPR},
6724// where EXPR follows $(()) rules
6684 loc = str = stackblock() + strloc; 6725 loc = str = stackblock() + strloc;
6685 /* Read POS in ${var:POS:LEN} */ 6726 /* Read POS in ${var:POS:LEN} */
6686 pos = atoi(loc); /* number(loc) errors out on "1:4" */ 6727 pos = atoi(loc); /* number(loc) errors out on "1:4" */
@@ -6791,7 +6832,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6791 char *idx, *end; 6832 char *idx, *end;
6792 6833
6793 if (!repl) { 6834 if (!repl) {
6794 if ((repl=strchr(str, CTLESC))) 6835 repl = strchr(str, CTLESC);
6836 if (repl)
6795 *repl++ = '\0'; 6837 *repl++ = '\0';
6796 else 6838 else
6797 repl = nullstr; 6839 repl = nullstr;
@@ -6924,20 +6966,21 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6924 * ash -c 'echo ${#1#}' name:'1=#' 6966 * ash -c 'echo ${#1#}' name:'1=#'
6925 */ 6967 */
6926static NOINLINE ssize_t 6968static NOINLINE ssize_t
6927varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) 6969varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp)
6928{ 6970{
6929 const char *p; 6971 const char *p;
6930 int num; 6972 int num;
6931 int i; 6973 int i;
6932 ssize_t len = 0; 6974 ssize_t len = 0;
6933 int sep; 6975 int sep;
6934 int quoted = flags & EXP_QUOTED; 6976 int quoted = *quotedp;
6935 int subtype = varflags & VSTYPE; 6977 int subtype = varflags & VSTYPE;
6936 int discard = subtype == VSPLUS || subtype == VSLENGTH; 6978 int discard = subtype == VSPLUS || subtype == VSLENGTH;
6937 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; 6979 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
6938 int syntax = quoted ? DQSYNTAX : BASESYNTAX; 6980 int syntax;
6939 6981
6940 sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; 6982 sep = (flags & EXP_FULL) << CHAR_BIT;
6983 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6941 6984
6942 switch (*name) { 6985 switch (*name) {
6943 case '$': 6986 case '$':
@@ -6971,21 +7014,21 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6971 raise_error_syntax("bad substitution"); 7014 raise_error_syntax("bad substitution");
6972#endif 7015#endif
6973 break; 7016 break;
6974 case '@': { 7017 case '@':
7018 if (quoted && sep)
7019 goto param;
7020 /* fall through */
7021 case '*': {
6975 char **ap; 7022 char **ap;
6976 char sepc; 7023 char sepc;
6977 7024
6978 if (quoted && (flags & EXP_FULL)) { 7025 if (quoted)
6979 /* note: this is not meant as PEOF value */ 7026 sep = 0;
6980 sep = 1 << CHAR_BIT; 7027 sep |= ifsset() ? ifsval()[0] : ' ';
6981 goto param;
6982 }
6983 /* fall through */
6984 case '*':
6985 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
6986 param: 7028 param:
6987 ap = shellparam.p;
6988 sepc = sep; 7029 sepc = sep;
7030 *quotedp = !sepc;
7031 ap = shellparam.p;
6989 if (!ap) 7032 if (!ap)
6990 return -1; 7033 return -1;
6991 while ((p = *ap++) != NULL) { 7034 while ((p = *ap++) != NULL) {
@@ -6997,7 +7040,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6997 } 7040 }
6998 } 7041 }
6999 break; 7042 break;
7000 } /* case '@' and '*' */ 7043 } /* case '*' */
7001 case '0': 7044 case '0':
7002 case '1': 7045 case '1':
7003 case '2': 7046 case '2':
@@ -7069,7 +7112,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
7069 * input string. 7112 * input string.
7070 */ 7113 */
7071static char * 7114static char *
7072evalvar(char *p, int flags, struct strlist *var_str_list) 7115evalvar(char *p, int flag, struct strlist *var_str_list)
7073{ 7116{
7074 char varflags; 7117 char varflags;
7075 char subtype; 7118 char subtype;
@@ -7082,14 +7125,14 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7082 7125
7083 varflags = (unsigned char) *p++; 7126 varflags = (unsigned char) *p++;
7084 subtype = varflags & VSTYPE; 7127 subtype = varflags & VSTYPE;
7085 quoted = flags & EXP_QUOTED; 7128 quoted = flag & EXP_QUOTED;
7086 var = p; 7129 var = p;
7087 easy = (!quoted || (*var == '@' && shellparam.nparam)); 7130 easy = (!quoted || (*var == '@' && shellparam.nparam));
7088 startloc = expdest - (char *)stackblock(); 7131 startloc = expdest - (char *)stackblock();
7089 p = strchr(p, '=') + 1; //TODO: use var_end(p)? 7132 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
7090 7133
7091 again: 7134 again:
7092 varlen = varvalue(var, varflags, flags, var_str_list); 7135 varlen = varvalue(var, varflags, flag, var_str_list, &quoted);
7093 if (varflags & VSNUL) 7136 if (varflags & VSNUL)
7094 varlen--; 7137 varlen--;
7095 7138
@@ -7103,36 +7146,27 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7103 if (varlen < 0) { 7146 if (varlen < 0) {
7104 argstr( 7147 argstr(
7105 p, 7148 p,
7106 flags | EXP_TILDE | EXP_WORD, 7149 flag | EXP_TILDE | EXP_WORD,
7107 var_str_list 7150 var_str_list
7108 ); 7151 );
7109 goto end; 7152 goto end;
7110 } 7153 }
7111 if (easy) 7154 goto record;
7112 goto record;
7113 goto end;
7114 } 7155 }
7115 7156
7116 if (subtype == VSASSIGN || subtype == VSQUESTION) { 7157 if (subtype == VSASSIGN || subtype == VSQUESTION) {
7117 if (varlen < 0) { 7158 if (varlen >= 0)
7118 if (subevalvar(p, var, /* strloc: */ 0,
7119 subtype, startloc, varflags,
7120 /* quotes: */ flags & ~QUOTES_ESC,
7121 var_str_list)
7122 ) {
7123 varflags &= ~VSNUL;
7124 /*
7125 * Remove any recorded regions beyond
7126 * start of variable
7127 */
7128 removerecordregions(startloc);
7129 goto again;
7130 }
7131 goto end;
7132 }
7133 if (easy)
7134 goto record; 7159 goto record;
7135 goto end; 7160
7161 subevalvar(p, var, 0, subtype, startloc, varflags,
7162 flag & ~QUOTES_ESC, var_str_list);
7163 varflags &= ~VSNUL;
7164 /*
7165 * Remove any recorded regions beyond
7166 * start of variable
7167 */
7168 removerecordregions(startloc);
7169 goto again;
7136 } 7170 }
7137 7171
7138 if (varlen < 0 && uflag) 7172 if (varlen < 0 && uflag)
@@ -7144,8 +7178,10 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7144 } 7178 }
7145 7179
7146 if (subtype == VSNORMAL) { 7180 if (subtype == VSNORMAL) {
7147 if (easy) 7181 record:
7148 goto record; 7182 if (!easy)
7183 goto end;
7184 recordregion(startloc, expdest - (char *)stackblock(), quoted);
7149 goto end; 7185 goto end;
7150 } 7186 }
7151 7187
@@ -7174,7 +7210,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7174 STPUTC('\0', expdest); 7210 STPUTC('\0', expdest);
7175 patloc = expdest - (char *)stackblock(); 7211 patloc = expdest - (char *)stackblock();
7176 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, 7212 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
7177 startloc, varflags, flags, var_str_list)) { 7213 startloc, varflags, flag, var_str_list)) {
7178 int amount = expdest - ( 7214 int amount = expdest - (
7179 (char *)stackblock() + patloc - 1 7215 (char *)stackblock() + patloc - 1
7180 ); 7216 );
@@ -7182,8 +7218,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7182 } 7218 }
7183 /* Remove any recorded regions beyond start of variable */ 7219 /* Remove any recorded regions beyond start of variable */
7184 removerecordregions(startloc); 7220 removerecordregions(startloc);
7185 record: 7221 goto record;
7186 recordregion(startloc, expdest - (char *)stackblock(), quoted);
7187 } 7222 }
7188 7223
7189 end: 7224 end:
@@ -7327,11 +7362,90 @@ addfname(const char *name)
7327 struct strlist *sp; 7362 struct strlist *sp;
7328 7363
7329 sp = stzalloc(sizeof(*sp)); 7364 sp = stzalloc(sizeof(*sp));
7330 sp->text = ststrdup(name); 7365 sp->text = sstrdup(name);
7331 *exparg.lastp = sp; 7366 *exparg.lastp = sp;
7332 exparg.lastp = &sp->next; 7367 exparg.lastp = &sp->next;
7333} 7368}
7334 7369
7370/* If we want to use glob() from libc... */
7371#if !ENABLE_ASH_INTERNAL_GLOB
7372
7373/* Add the result of glob() to the list */
7374static void
7375addglob(const glob_t *pglob)
7376{
7377 char **p = pglob->gl_pathv;
7378
7379 do {
7380 addfname(*p);
7381 } while (*++p);
7382}
7383static void
7384expandmeta(struct strlist *str /*, int flag*/)
7385{
7386 /* TODO - EXP_REDIR */
7387
7388 while (str) {
7389 char *p;
7390 glob_t pglob;
7391 int i;
7392
7393 if (fflag)
7394 goto nometa;
7395 INT_OFF;
7396 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7397// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
7398// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
7399//
7400// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping:
7401// if you pass it "file\?", it returns "file\?", not "file?", if no match.
7402// Which means you need to unescape the string, right? Not so fast:
7403// if there _is_ a file named "file\?" (with backslash), it is returned
7404// as "file\?" too (whichever pattern you used to find it, say, "file*").
7405// You DONT KNOW by looking at the result whether you need to unescape it.
7406//
7407// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?",
7408// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file.
7409// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match).
7410// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly.
7411// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob);
7412// i = glob(p, GLOB_NOMAGIC, NULL, &pglob);
7413 i = glob(p, 0, NULL, &pglob);
7414 //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-");
7415 if (p != str->text)
7416 free(p);
7417 switch (i) {
7418 case 0:
7419#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR
7420 /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */
7421 if (!(pglob.gl_flags & GLOB_MAGCHAR))
7422 goto nometa2;
7423#endif
7424 addglob(&pglob);
7425 globfree(&pglob);
7426 INT_ON;
7427 break;
7428 case GLOB_NOMATCH:
7429 //nometa2:
7430 globfree(&pglob);
7431 INT_ON;
7432 nometa:
7433 *exparg.lastp = str;
7434 rmescapes(str->text, 0);
7435 exparg.lastp = &str->next;
7436 break;
7437 default: /* GLOB_NOSPACE */
7438 globfree(&pglob);
7439 INT_ON;
7440 ash_msg_and_raise_error(bb_msg_memory_exhausted);
7441 }
7442 str = str->next;
7443 }
7444}
7445
7446#else
7447/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
7448
7335/* 7449/*
7336 * Do metacharacter (i.e. *, ?, [...]) expansion. 7450 * Do metacharacter (i.e. *, ?, [...]) expansion.
7337 */ 7451 */
@@ -7535,7 +7649,8 @@ expandmeta(struct strlist *str /*, int flag*/)
7535 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); 7649 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7536 { 7650 {
7537 int i = strlen(str->text); 7651 int i = strlen(str->text);
7538 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 7652//BUGGY estimation of how long expanded name can be
7653 expdir = ckmalloc(i < 2048 ? 2048 : i+1);
7539 } 7654 }
7540 expmeta(expdir, expdir, p); 7655 expmeta(expdir, expdir, p);
7541 free(expdir); 7656 free(expdir);
@@ -7560,6 +7675,7 @@ expandmeta(struct strlist *str /*, int flag*/)
7560 str = str->next; 7675 str = str->next;
7561 } 7676 }
7562} 7677}
7678#endif /* ENABLE_ASH_INTERNAL_GLOB */
7563 7679
7564/* 7680/*
7565 * Perform variable substitution and command substitution on an argument, 7681 * Perform variable substitution and command substitution on an argument,
@@ -8126,49 +8242,86 @@ enum {
8126}; 8242};
8127typedef smallint token_id_t; 8243typedef smallint token_id_t;
8128 8244
8129/* first char is indicating which tokens mark the end of a list */ 8245/* Nth bit indicates if token marks the end of a list */
8246enum {
8247 tokendlist = 0
8248 /* 0 */ | (1u << TEOF)
8249 /* 1 */ | (0u << TNL)
8250 /* 2 */ | (0u << TREDIR)
8251 /* 3 */ | (0u << TWORD)
8252 /* 4 */ | (0u << TSEMI)
8253 /* 5 */ | (0u << TBACKGND)
8254 /* 6 */ | (0u << TAND)
8255 /* 7 */ | (0u << TOR)
8256 /* 8 */ | (0u << TPIPE)
8257 /* 9 */ | (0u << TLP)
8258 /* 10 */ | (1u << TRP)
8259 /* 11 */ | (1u << TENDCASE)
8260 /* 12 */ | (1u << TENDBQUOTE)
8261 /* 13 */ | (0u << TNOT)
8262 /* 14 */ | (0u << TCASE)
8263 /* 15 */ | (1u << TDO)
8264 /* 16 */ | (1u << TDONE)
8265 /* 17 */ | (1u << TELIF)
8266 /* 18 */ | (1u << TELSE)
8267 /* 19 */ | (1u << TESAC)
8268 /* 20 */ | (1u << TFI)
8269 /* 21 */ | (0u << TFOR)
8270#if ENABLE_ASH_BASH_COMPAT
8271 /* 22 */ | (0u << TFUNCTION)
8272#endif
8273 /* 23 */ | (0u << TIF)
8274 /* 24 */ | (0u << TIN)
8275 /* 25 */ | (1u << TTHEN)
8276 /* 26 */ | (0u << TUNTIL)
8277 /* 27 */ | (0u << TWHILE)
8278 /* 28 */ | (0u << TBEGIN)
8279 /* 29 */ | (1u << TEND)
8280 , /* thus far 29 bits used */
8281};
8282
8130static const char *const tokname_array[] = { 8283static const char *const tokname_array[] = {
8131 "\1end of file", 8284 "end of file",
8132 "\0newline", 8285 "newline",
8133 "\0redirection", 8286 "redirection",
8134 "\0word", 8287 "word",
8135 "\0;", 8288 ";",
8136 "\0&", 8289 "&",
8137 "\0&&", 8290 "&&",
8138 "\0||", 8291 "||",
8139 "\0|", 8292 "|",
8140 "\0(", 8293 "(",
8141 "\1)", 8294 ")",
8142 "\1;;", 8295 ";;",
8143 "\1`", 8296 "`",
8144#define KWDOFFSET 13 8297#define KWDOFFSET 13
8145 /* the following are keywords */ 8298 /* the following are keywords */
8146 "\0!", 8299 "!",
8147 "\0case", 8300 "case",
8148 "\1do", 8301 "do",
8149 "\1done", 8302 "done",
8150 "\1elif", 8303 "elif",
8151 "\1else", 8304 "else",
8152 "\1esac", 8305 "esac",
8153 "\1fi", 8306 "fi",
8154 "\0for", 8307 "for",
8155#if ENABLE_ASH_BASH_COMPAT 8308#if ENABLE_ASH_BASH_COMPAT
8156 "\0function", 8309 "function",
8157#endif 8310#endif
8158 "\0if", 8311 "if",
8159 "\0in", 8312 "in",
8160 "\1then", 8313 "then",
8161 "\0until", 8314 "until",
8162 "\0while", 8315 "while",
8163 "\0{", 8316 "{",
8164 "\1}", 8317 "}",
8165}; 8318};
8166 8319
8167/* Wrapper around strcmp for qsort/bsearch/... */ 8320/* Wrapper around strcmp for qsort/bsearch/... */
8168static int 8321static int
8169pstrcmp(const void *a, const void *b) 8322pstrcmp(const void *a, const void *b)
8170{ 8323{
8171 return strcmp((char*) a, (*(char**) b) + 1); 8324 return strcmp((char*)a, *(char**)b);
8172} 8325}
8173 8326
8174static const char *const * 8327static const char *const *
@@ -8297,9 +8450,45 @@ typecmd(int argc UNUSED_PARAM, char **argv)
8297} 8450}
8298 8451
8299#if ENABLE_ASH_CMDCMD 8452#if ENABLE_ASH_CMDCMD
8453/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
8454static char **
8455parse_command_args(char **argv, const char **path)
8456{
8457 char *cp, c;
8458
8459 for (;;) {
8460 cp = *++argv;
8461 if (!cp)
8462 return NULL;
8463 if (*cp++ != '-')
8464 break;
8465 c = *cp++;
8466 if (!c)
8467 break;
8468 if (c == '-' && !*cp) {
8469 if (!*++argv)
8470 return NULL;
8471 break;
8472 }
8473 do {
8474 switch (c) {
8475 case 'p':
8476 *path = bb_default_path;
8477 break;
8478 default:
8479 /* run 'typecmd' for other options */
8480 return NULL;
8481 }
8482 c = *cp++;
8483 } while (c);
8484 }
8485 return argv;
8486}
8487
8300static int FAST_FUNC 8488static int FAST_FUNC
8301commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 8489commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8302{ 8490{
8491 char *cmd;
8303 int c; 8492 int c;
8304 enum { 8493 enum {
8305 VERIFY_BRIEF = 1, 8494 VERIFY_BRIEF = 1,
@@ -8307,29 +8496,32 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8307 } verify = 0; 8496 } verify = 0;
8308 const char *path = NULL; 8497 const char *path = NULL;
8309 8498
8499 /* "command [-p] PROG ARGS" (that is, without -V or -v)
8500 * never reaches this function.
8501 */
8502
8310 while ((c = nextopt("pvV")) != '\0') 8503 while ((c = nextopt("pvV")) != '\0')
8311 if (c == 'V') 8504 if (c == 'V')
8312 verify |= VERIFY_VERBOSE; 8505 verify |= VERIFY_VERBOSE;
8313 else if (c == 'v') 8506 else if (c == 'v')
8314 verify |= VERIFY_BRIEF; 8507 /*verify |= VERIFY_BRIEF*/;
8315#if DEBUG 8508#if DEBUG
8316 else if (c != 'p') 8509 else if (c != 'p')
8317 abort(); 8510 abort();
8318#endif 8511#endif
8319 else 8512 else
8320 path = bb_default_path; 8513 path = bb_default_path;
8514
8321 /* Mimic bash: just "command -v" doesn't complain, it's a nop */ 8515 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
8322 if (verify && (*argptr != NULL)) { 8516 cmd = *argptr;
8323 return describe_command(*argptr, path, verify - VERIFY_BRIEF); 8517 if (/*verify && */ cmd)
8324 } 8518 return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/);
8325 8519
8326 return 0; 8520 return 0;
8327} 8521}
8328#endif 8522#endif
8329 8523
8330 8524
8331/* ============ eval.c */
8332
8333static int funcblocksize; /* size of structures in function */ 8525static int funcblocksize; /* size of structures in function */
8334static int funcstringsize; /* size of strings in node */ 8526static int funcstringsize; /* size of strings in node */
8335static void *funcblock; /* block to allocate function from */ 8527static void *funcblock; /* block to allocate function from */
@@ -8342,7 +8534,6 @@ static char **nodeptr;
8342/* flags in argument to evaltree */ 8534/* flags in argument to evaltree */
8343#define EV_EXIT 01 /* exit after evaluating tree */ 8535#define EV_EXIT 01 /* exit after evaluating tree */
8344#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ 8536#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
8345#define EV_BACKCMD 04 /* command executing within back quotes */
8346 8537
8347static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 8538static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8348 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 8539 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -8674,15 +8865,13 @@ defun(char *name, union node *func)
8674#define SKIPBREAK (1 << 0) 8865#define SKIPBREAK (1 << 0)
8675#define SKIPCONT (1 << 1) 8866#define SKIPCONT (1 << 1)
8676#define SKIPFUNC (1 << 2) 8867#define SKIPFUNC (1 << 2)
8677#define SKIPFILE (1 << 3)
8678#define SKIPEVAL (1 << 4)
8679static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 8868static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
8680static int skipcount; /* number of levels to skip */ 8869static int skipcount; /* number of levels to skip */
8681static int funcnest; /* depth of function calls */ 8870static int funcnest; /* depth of function calls */
8682static int loopnest; /* current loop nesting level */ 8871static int loopnest; /* current loop nesting level */
8683 8872
8684/* Forward decl way out to parsing code - dotrap needs it */ 8873/* Forward decl way out to parsing code - dotrap needs it */
8685static int evalstring(char *s, int mask); 8874static int evalstring(char *s, int flags);
8686 8875
8687/* Called to execute a trap. 8876/* Called to execute a trap.
8688 * Single callsite - at the end of evaltree(). 8877 * Single callsite - at the end of evaltree().
@@ -8691,69 +8880,71 @@ static int evalstring(char *s, int mask);
8691 * Perhaps we should avoid entering new trap handlers 8880 * Perhaps we should avoid entering new trap handlers
8692 * while we are executing a trap handler. [is it a TODO?] 8881 * while we are executing a trap handler. [is it a TODO?]
8693 */ 8882 */
8694static int 8883static void
8695dotrap(void) 8884dotrap(void)
8696{ 8885{
8697 uint8_t *g; 8886 uint8_t *g;
8698 int sig; 8887 int sig;
8699 uint8_t savestatus; 8888 uint8_t last_status;
8889
8890 if (!pending_sig)
8891 return;
8700 8892
8701 savestatus = exitstatus; 8893 last_status = exitstatus;
8702 pending_sig = 0; 8894 pending_sig = 0;
8703 xbarrier(); 8895 barrier();
8704 8896
8705 TRACE(("dotrap entered\n")); 8897 TRACE(("dotrap entered\n"));
8706 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { 8898 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8707 int want_exexit; 8899 char *p;
8708 char *t;
8709 8900
8710 if (*g == 0) 8901 if (!*g)
8711 continue; 8902 continue;
8712 t = trap[sig]; 8903
8904 if (evalskip) {
8905 pending_sig = sig;
8906 break;
8907 }
8908
8909 p = trap[sig];
8713 /* non-trapped SIGINT is handled separately by raise_interrupt, 8910 /* non-trapped SIGINT is handled separately by raise_interrupt,
8714 * don't upset it by resetting gotsig[SIGINT-1] */ 8911 * don't upset it by resetting gotsig[SIGINT-1] */
8715 if (sig == SIGINT && !t) 8912 if (sig == SIGINT && !p)
8716 continue; 8913 continue;
8717 8914
8718 TRACE(("sig %d is active, will run handler '%s'\n", sig, t)); 8915 TRACE(("sig %d is active, will run handler '%s'\n", sig, p));
8719 *g = 0; 8916 *g = 0;
8720 if (!t) 8917 if (!p)
8721 continue; 8918 continue;
8722 want_exexit = evalstring(t, SKIPEVAL); 8919 evalstring(p, 0);
8723 exitstatus = savestatus;
8724 if (want_exexit) {
8725 TRACE(("dotrap returns %d\n", want_exexit));
8726 return want_exexit;
8727 }
8728 } 8920 }
8729 8921 exitstatus = last_status;
8730 TRACE(("dotrap returns 0\n")); 8922 TRACE(("dotrap returns\n"));
8731 return 0;
8732} 8923}
8733 8924
8734/* forward declarations - evaluation is fairly recursive business... */ 8925/* forward declarations - evaluation is fairly recursive business... */
8735static void evalloop(union node *, int); 8926static int evalloop(union node *, int);
8736static void evalfor(union node *, int); 8927static int evalfor(union node *, int);
8737static void evalcase(union node *, int); 8928static int evalcase(union node *, int);
8738static void evalsubshell(union node *, int); 8929static int evalsubshell(union node *, int);
8739static void expredir(union node *); 8930static void expredir(union node *);
8740static void evalpipe(union node *, int); 8931static int evalpipe(union node *, int);
8741static void evalcommand(union node *, int); 8932static int evalcommand(union node *, int);
8742static int evalbltin(const struct builtincmd *, int, char **); 8933static int evalbltin(const struct builtincmd *, int, char **, int);
8743static void prehash(union node *); 8934static void prehash(union node *);
8744 8935
8745/* 8936/*
8746 * Evaluate a parse tree. The value is left in the global variable 8937 * Evaluate a parse tree. The value is left in the global variable
8747 * exitstatus. 8938 * exitstatus.
8748 */ 8939 */
8749static void 8940static int
8750evaltree(union node *n, int flags) 8941evaltree(union node *n, int flags)
8751{ 8942{
8752 struct jmploc *volatile savehandler = exception_handler; 8943 struct jmploc *volatile savehandler = exception_handler;
8753 struct jmploc jmploc; 8944 struct jmploc jmploc;
8754 int checkexit = 0; 8945 int checkexit = 0;
8755 void (*evalfn)(union node *, int); 8946 int (*evalfn)(union node *, int);
8756 int status; 8947 int status = 0;
8757 int int_level; 8948 int int_level;
8758 8949
8759 SAVE_INT(int_level); 8950 SAVE_INT(int_level);
@@ -8764,6 +8955,8 @@ evaltree(union node *n, int flags)
8764 } 8955 }
8765 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); 8956 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
8766 8957
8958 dotrap();
8959
8767 exception_handler = &jmploc; 8960 exception_handler = &jmploc;
8768 { 8961 {
8769 int err = setjmp(jmploc.loc); 8962 int err = setjmp(jmploc.loc);
@@ -8790,15 +8983,13 @@ evaltree(union node *n, int flags)
8790 break; 8983 break;
8791#endif 8984#endif
8792 case NNOT: 8985 case NNOT:
8793 evaltree(n->nnot.com, EV_TESTED); 8986 status = !evaltree(n->nnot.com, EV_TESTED);
8794 status = !exitstatus;
8795 goto setstatus; 8987 goto setstatus;
8796 case NREDIR: 8988 case NREDIR:
8797 expredir(n->nredir.redirect); 8989 expredir(n->nredir.redirect);
8798 status = redirectsafe(n->nredir.redirect, REDIR_PUSH); 8990 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8799 if (!status) { 8991 if (!status) {
8800 evaltree(n->nredir.n, flags & EV_TESTED); 8992 status = evaltree(n->nredir.n, flags & EV_TESTED);
8801 status = exitstatus;
8802 } 8993 }
8803 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); 8994 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
8804 goto setstatus; 8995 goto setstatus;
@@ -8816,6 +9007,8 @@ evaltree(union node *n, int flags)
8816 evalfn = evalloop; 9007 evalfn = evalloop;
8817 goto calleval; 9008 goto calleval;
8818 case NSUBSHELL: 9009 case NSUBSHELL:
9010 evalfn = evalsubshell;
9011 goto checkexit;
8819 case NBACKGND: 9012 case NBACKGND:
8820 evalfn = evalsubshell; 9013 evalfn = evalsubshell;
8821 goto calleval; 9014 goto calleval;
@@ -8836,27 +9029,24 @@ evaltree(union node *n, int flags)
8836#error NOR + 1 != NSEMI 9029#error NOR + 1 != NSEMI
8837#endif 9030#endif
8838 unsigned is_or = n->type - NAND; 9031 unsigned is_or = n->type - NAND;
8839 evaltree( 9032 status = evaltree(
8840 n->nbinary.ch1, 9033 n->nbinary.ch1,
8841 (flags | ((is_or >> 1) - 1)) & EV_TESTED 9034 (flags | ((is_or >> 1) - 1)) & EV_TESTED
8842 ); 9035 );
8843 if ((!exitstatus) == is_or) 9036 if ((!status) == is_or || evalskip)
8844 break; 9037 break;
8845 if (!evalskip) { 9038 n = n->nbinary.ch2;
8846 n = n->nbinary.ch2;
8847 evaln: 9039 evaln:
8848 evalfn = evaltree; 9040 evalfn = evaltree;
8849 calleval: 9041 calleval:
8850 evalfn(n, flags); 9042 status = evalfn(n, flags);
8851 break; 9043 goto setstatus;
8852 }
8853 break;
8854 } 9044 }
8855 case NIF: 9045 case NIF:
8856 evaltree(n->nif.test, EV_TESTED); 9046 status = evaltree(n->nif.test, EV_TESTED);
8857 if (evalskip) 9047 if (evalskip)
8858 break; 9048 break;
8859 if (exitstatus == 0) { 9049 if (!status) {
8860 n = n->nif.ifpart; 9050 n = n->nif.ifpart;
8861 goto evaln; 9051 goto evaln;
8862 } 9052 }
@@ -8864,11 +9054,14 @@ evaltree(union node *n, int flags)
8864 n = n->nif.elsepart; 9054 n = n->nif.elsepart;
8865 goto evaln; 9055 goto evaln;
8866 } 9056 }
8867 goto success; 9057 status = 0;
9058 goto setstatus;
8868 case NDEFUN: 9059 case NDEFUN:
8869 defun(n->narg.text, n->narg.next); 9060 defun(n->narg.text, n->narg.next);
8870 success: 9061 /* Not necessary. To test it:
8871 status = 0; 9062 * "false; f() { qwerty; }; echo $?" should print 0.
9063 */
9064 /* status = 0; */
8872 setstatus: 9065 setstatus:
8873 exitstatus = status; 9066 exitstatus = status;
8874 break; 9067 break;
@@ -8881,119 +9074,127 @@ evaltree(union node *n, int flags)
8881 /* Order of checks below is important: 9074 /* Order of checks below is important:
8882 * signal handlers trigger before exit caused by "set -e". 9075 * signal handlers trigger before exit caused by "set -e".
8883 */ 9076 */
8884 if (pending_sig && dotrap()) 9077 dotrap();
8885 goto exexit;
8886 if (checkexit & exitstatus)
8887 evalskip |= SKIPEVAL;
8888 9078
8889 if (flags & EV_EXIT) { 9079 if (checkexit & status)
8890 exexit: 9080 raise_exception(EXEXIT);
9081 if (flags & EV_EXIT)
8891 raise_exception(EXEXIT); 9082 raise_exception(EXEXIT);
8892 }
8893 9083
8894 RESTORE_INT(int_level); 9084 RESTORE_INT(int_level);
8895 TRACE(("leaving evaltree (no interrupts)\n")); 9085 TRACE(("leaving evaltree (no interrupts)\n"));
9086
9087 return exitstatus;
8896} 9088}
8897 9089
8898#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) 9090#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8899static 9091static
8900#endif 9092#endif
8901void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); 9093int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8902 9094
8903static void 9095static int
9096skiploop(void)
9097{
9098 int skip = evalskip;
9099
9100 switch (skip) {
9101 case 0:
9102 break;
9103 case SKIPBREAK:
9104 case SKIPCONT:
9105 if (--skipcount <= 0) {
9106 evalskip = 0;
9107 break;
9108 }
9109 skip = SKIPBREAK;
9110 break;
9111 }
9112 return skip;
9113}
9114
9115static int
8904evalloop(union node *n, int flags) 9116evalloop(union node *n, int flags)
8905{ 9117{
9118 int skip;
8906 int status; 9119 int status;
8907 9120
8908 loopnest++; 9121 loopnest++;
8909 status = 0; 9122 status = 0;
8910 flags &= EV_TESTED; 9123 flags &= EV_TESTED;
8911 for (;;) { 9124 do {
8912 int i; 9125 int i;
8913 9126
8914 evaltree(n->nbinary.ch1, EV_TESTED); 9127 i = evaltree(n->nbinary.ch1, EV_TESTED);
8915 if (evalskip) { 9128 skip = skiploop();
8916 skipping: 9129 if (skip == SKIPFUNC)
8917 if (evalskip == SKIPCONT && --skipcount <= 0) { 9130 status = i;
8918 evalskip = 0; 9131 if (skip)
8919 continue; 9132 continue;
8920 }
8921 if (evalskip == SKIPBREAK && --skipcount <= 0)
8922 evalskip = 0;
8923 break;
8924 }
8925 i = exitstatus;
8926 if (n->type != NWHILE) 9133 if (n->type != NWHILE)
8927 i = !i; 9134 i = !i;
8928 if (i != 0) 9135 if (i != 0)
8929 break; 9136 break;
8930 evaltree(n->nbinary.ch2, flags); 9137 status = evaltree(n->nbinary.ch2, flags);
8931 status = exitstatus; 9138 skip = skiploop();
8932 if (evalskip) 9139 } while (!(skip & ~SKIPCONT));
8933 goto skipping;
8934 }
8935 loopnest--; 9140 loopnest--;
8936 exitstatus = status; 9141
9142 return status;
8937} 9143}
8938 9144
8939static void 9145static int
8940evalfor(union node *n, int flags) 9146evalfor(union node *n, int flags)
8941{ 9147{
8942 struct arglist arglist; 9148 struct arglist arglist;
8943 union node *argp; 9149 union node *argp;
8944 struct strlist *sp; 9150 struct strlist *sp;
8945 struct stackmark smark; 9151 struct stackmark smark;
9152 int status = 0;
8946 9153
8947 setstackmark(&smark); 9154 setstackmark(&smark);
8948 arglist.list = NULL; 9155 arglist.list = NULL;
8949 arglist.lastp = &arglist.list; 9156 arglist.lastp = &arglist.list;
8950 for (argp = n->nfor.args; argp; argp = argp->narg.next) { 9157 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
8951 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 9158 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8952 /* XXX */
8953 if (evalskip)
8954 goto out;
8955 } 9159 }
8956 *arglist.lastp = NULL; 9160 *arglist.lastp = NULL;
8957 9161
8958 exitstatus = 0;
8959 loopnest++; 9162 loopnest++;
8960 flags &= EV_TESTED; 9163 flags &= EV_TESTED;
8961 for (sp = arglist.list; sp; sp = sp->next) { 9164 for (sp = arglist.list; sp; sp = sp->next) {
8962 setvar0(n->nfor.var, sp->text); 9165 setvar0(n->nfor.var, sp->text);
8963 evaltree(n->nfor.body, flags); 9166 status = evaltree(n->nfor.body, flags);
8964 if (evalskip) { 9167 if (skiploop() & ~SKIPCONT)
8965 if (evalskip == SKIPCONT && --skipcount <= 0) {
8966 evalskip = 0;
8967 continue;
8968 }
8969 if (evalskip == SKIPBREAK && --skipcount <= 0)
8970 evalskip = 0;
8971 break; 9168 break;
8972 }
8973 } 9169 }
8974 loopnest--; 9170 loopnest--;
8975 out:
8976 popstackmark(&smark); 9171 popstackmark(&smark);
9172
9173 return status;
8977} 9174}
8978 9175
8979static void 9176static int
8980evalcase(union node *n, int flags) 9177evalcase(union node *n, int flags)
8981{ 9178{
8982 union node *cp; 9179 union node *cp;
8983 union node *patp; 9180 union node *patp;
8984 struct arglist arglist; 9181 struct arglist arglist;
8985 struct stackmark smark; 9182 struct stackmark smark;
9183 int status = 0;
8986 9184
8987 setstackmark(&smark); 9185 setstackmark(&smark);
8988 arglist.list = NULL; 9186 arglist.list = NULL;
8989 arglist.lastp = &arglist.list; 9187 arglist.lastp = &arglist.list;
8990 expandarg(n->ncase.expr, &arglist, EXP_TILDE); 9188 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
8991 exitstatus = 0;
8992 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { 9189 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8993 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { 9190 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
8994 if (casematch(patp, arglist.list->text)) { 9191 if (casematch(patp, arglist.list->text)) {
8995 if (evalskip == 0) { 9192 /* Ensure body is non-empty as otherwise
8996 evaltree(cp->nclist.body, flags); 9193 * EV_EXIT may prevent us from setting the
9194 * exit status.
9195 */
9196 if (evalskip == 0 && cp->nclist.body) {
9197 status = evaltree(cp->nclist.body, flags);
8997 } 9198 }
8998 goto out; 9199 goto out;
8999 } 9200 }
@@ -9001,12 +9202,14 @@ evalcase(union node *n, int flags)
9001 } 9202 }
9002 out: 9203 out:
9003 popstackmark(&smark); 9204 popstackmark(&smark);
9205
9206 return status;
9004} 9207}
9005 9208
9006/* 9209/*
9007 * Kick off a subshell to evaluate a tree. 9210 * Kick off a subshell to evaluate a tree.
9008 */ 9211 */
9009static void 9212static int
9010evalsubshell(union node *n, int flags) 9213evalsubshell(union node *n, int flags)
9011{ 9214{
9012 IF_PLATFORM_MINGW32(struct forkshell fs;) 9215 IF_PLATFORM_MINGW32(struct forkshell fs;)
@@ -9043,8 +9246,8 @@ evalsubshell(union node *n, int flags)
9043 status = 0; 9246 status = 0;
9044 if (!backgnd) 9247 if (!backgnd)
9045 status = waitforjob(jp); 9248 status = waitforjob(jp);
9046 exitstatus = status;
9047 INT_ON; 9249 INT_ON;
9250 return status;
9048} 9251}
9049 9252
9050/* 9253/*
@@ -9117,7 +9320,7 @@ expredir(union node *n)
9117 * of the shell, which make the last process in a pipeline the parent 9320 * of the shell, which make the last process in a pipeline the parent
9118 * of all the rest.) 9321 * of all the rest.)
9119 */ 9322 */
9120static void 9323static int
9121evalpipe(union node *n, int flags) 9324evalpipe(union node *n, int flags)
9122{ 9325{
9123 IF_PLATFORM_MINGW32(struct forkshell fs;) 9326 IF_PLATFORM_MINGW32(struct forkshell fs;)
@@ -9126,6 +9329,7 @@ evalpipe(union node *n, int flags)
9126 int pipelen; 9329 int pipelen;
9127 int prevfd; 9330 int prevfd;
9128 int pip[2]; 9331 int pip[2];
9332 int status = 0;
9129 9333
9130 TRACE(("evalpipe(0x%lx) called\n", (long)n)); 9334 TRACE(("evalpipe(0x%lx) called\n", (long)n));
9131 pipelen = 0; 9335 pipelen = 0;
@@ -9180,10 +9384,12 @@ evalpipe(union node *n, int flags)
9180 close(pip[1]); 9384 close(pip[1]);
9181 } 9385 }
9182 if (n->npipe.pipe_backgnd == 0) { 9386 if (n->npipe.pipe_backgnd == 0) {
9183 exitstatus = waitforjob(jp); 9387 status = waitforjob(jp);
9184 TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 9388 TRACE(("evalpipe: job done exit status %d\n", status));
9185 } 9389 }
9186 INT_ON; 9390 INT_ON;
9391
9392 return status;
9187} 9393}
9188 9394
9189/* 9395/*
@@ -9282,12 +9488,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
9282 9488
9283 saveparam = shellparam; 9489 saveparam = shellparam;
9284 savelocalvars = localvars; 9490 savelocalvars = localvars;
9491 savehandler = exception_handler;
9285 e = setjmp(jmploc.loc); 9492 e = setjmp(jmploc.loc);
9286 if (e) { 9493 if (e) {
9287 goto funcdone; 9494 goto funcdone;
9288 } 9495 }
9289 INT_OFF; 9496 INT_OFF;
9290 savehandler = exception_handler;
9291 exception_handler = &jmploc; 9497 exception_handler = &jmploc;
9292 localvars = NULL; 9498 localvars = NULL;
9293 shellparam.malloced = 0; 9499 shellparam.malloced = 0;
@@ -9315,42 +9521,6 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
9315 return e; 9521 return e;
9316} 9522}
9317 9523
9318#if ENABLE_ASH_CMDCMD
9319static char **
9320parse_command_args(char **argv, const char **path)
9321{
9322 char *cp, c;
9323
9324 for (;;) {
9325 cp = *++argv;
9326 if (!cp)
9327 return NULL;
9328 if (*cp++ != '-')
9329 break;
9330 c = *cp++;
9331 if (!c)
9332 break;
9333 if (c == '-' && !*cp) {
9334 if (!*++argv)
9335 return NULL;
9336 break;
9337 }
9338 do {
9339 switch (c) {
9340 case 'p':
9341 *path = bb_default_path;
9342 break;
9343 default:
9344 /* run 'typecmd' for other options */
9345 return NULL;
9346 }
9347 c = *cp++;
9348 } while (c);
9349 }
9350 return argv;
9351}
9352#endif
9353
9354/* 9524/*
9355 * Make a variable a local variable. When a variable is made local, it's 9525 * Make a variable a local variable. When a variable is made local, it's
9356 * value and flags are saved in a localvar structure. The saved values 9526 * value and flags are saved in a localvar structure. The saved values
@@ -9458,7 +9628,20 @@ execcmd(int argc UNUSED_PARAM, char **argv)
9458 iflag = 0; /* exit on error */ 9628 iflag = 0; /* exit on error */
9459 mflag = 0; 9629 mflag = 0;
9460 optschanged(); 9630 optschanged();
9631 /* We should set up signals for "exec CMD"
9632 * the same way as for "CMD" without "exec".
9633 * But optschanged->setinteractive->setsignal
9634 * still thought we are a root shell. Therefore, for example,
9635 * SIGQUIT is still set to IGN. Fix it:
9636 */
9637 shlvl++;
9638 setsignal(SIGQUIT);
9639 /*setsignal(SIGTERM); - unnecessary because of iflag=0 */
9640 /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */
9641 /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */
9642
9461 shellexec(argv + 1, pathval(), 0); 9643 shellexec(argv + 1, pathval(), 0);
9644 /* NOTREACHED */
9462 } 9645 }
9463 return 0; 9646 return 0;
9464} 9647}
@@ -9473,14 +9656,14 @@ returncmd(int argc UNUSED_PARAM, char **argv)
9473 * If called outside a function, do what ksh does; 9656 * If called outside a function, do what ksh does;
9474 * skip the rest of the file. 9657 * skip the rest of the file.
9475 */ 9658 */
9476 evalskip = funcnest ? SKIPFUNC : SKIPFILE; 9659 evalskip = SKIPFUNC;
9477 return argv[1] ? number(argv[1]) : exitstatus; 9660 return argv[1] ? number(argv[1]) : exitstatus;
9478} 9661}
9479 9662
9480/* Forward declarations for builtintab[] */ 9663/* Forward declarations for builtintab[] */
9481static int breakcmd(int, char **) FAST_FUNC; 9664static int breakcmd(int, char **) FAST_FUNC;
9482static int dotcmd(int, char **) FAST_FUNC; 9665static int dotcmd(int, char **) FAST_FUNC;
9483static int evalcmd(int, char **) FAST_FUNC; 9666static int evalcmd(int, char **, int) FAST_FUNC;
9484static int exitcmd(int, char **) FAST_FUNC; 9667static int exitcmd(int, char **) FAST_FUNC;
9485static int exportcmd(int, char **) FAST_FUNC; 9668static int exportcmd(int, char **) FAST_FUNC;
9486#if ENABLE_ASH_GETOPTS 9669#if ENABLE_ASH_GETOPTS
@@ -9550,7 +9733,7 @@ static const struct builtincmd builtintab[] = {
9550#if ENABLE_ASH_BUILTIN_ECHO 9733#if ENABLE_ASH_BUILTIN_ECHO
9551 { BUILTIN_REGULAR "echo" , echocmd }, 9734 { BUILTIN_REGULAR "echo" , echocmd },
9552#endif 9735#endif
9553 { BUILTIN_SPEC_REG "eval" , evalcmd }, 9736 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
9554 { BUILTIN_SPEC_REG "exec" , execcmd }, 9737 { BUILTIN_SPEC_REG "exec" , execcmd },
9555 { BUILTIN_SPEC_REG "exit" , exitcmd }, 9738 { BUILTIN_SPEC_REG "exit" , exitcmd },
9556 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd }, 9739 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
@@ -9606,27 +9789,28 @@ static const struct builtincmd builtintab[] = {
9606 9789
9607/* Should match the above table! */ 9790/* Should match the above table! */
9608#define COMMANDCMD (builtintab + \ 9791#define COMMANDCMD (builtintab + \
9609 2 + \ 9792 /* . : */ 2 + \
9610 1 * ENABLE_ASH_BUILTIN_TEST + \ 9793 /* [ */ 1 * ENABLE_ASH_BUILTIN_TEST + \
9611 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ 9794 /* [[ */ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9612 1 * ENABLE_ASH_ALIAS + \ 9795 /* alias */ 1 * ENABLE_ASH_ALIAS + \
9613 1 * ENABLE_ASH_JOB_CONTROL + \ 9796 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
9614 3) 9797 /* break cd cddir */ 3)
9615#define EXECCMD (builtintab + \ 9798#define EVALCMD (COMMANDCMD + \
9616 2 + \ 9799 /* command */ 1 * ENABLE_ASH_CMDCMD + \
9617 1 * ENABLE_ASH_BUILTIN_TEST + \ 9800 /* continue */ 1 + \
9618 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ 9801 /* echo */ 1 * ENABLE_ASH_BUILTIN_ECHO + \
9619 1 * ENABLE_ASH_ALIAS + \ 9802 0)
9620 1 * ENABLE_ASH_JOB_CONTROL + \ 9803#define EXECCMD (EVALCMD + \
9621 3 + \ 9804 /* eval */ 1)
9622 1 * ENABLE_ASH_CMDCMD + \
9623 1 + \
9624 ENABLE_ASH_BUILTIN_ECHO + \
9625 1)
9626 9805
9627/* 9806/*
9628 * Search the table of builtin commands. 9807 * Search the table of builtin commands.
9629 */ 9808 */
9809static int
9810pstrcmp1(const void *a, const void *b)
9811{
9812 return strcmp((char*)a, *(char**)b + 1);
9813}
9630static struct builtincmd * 9814static struct builtincmd *
9631find_builtin(const char *name) 9815find_builtin(const char *name)
9632{ 9816{
@@ -9634,7 +9818,7 @@ find_builtin(const char *name)
9634 9818
9635 bp = bsearch( 9819 bp = bsearch(
9636 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), 9820 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
9637 pstrcmp 9821 pstrcmp1
9638 ); 9822 );
9639 return bp; 9823 return bp;
9640} 9824}
@@ -9658,7 +9842,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9658 return back_exitstatus; 9842 return back_exitstatus;
9659} 9843}
9660 9844
9661static void 9845static int
9662evalcommand(union node *cmd, int flags) 9846evalcommand(union node *cmd, int flags)
9663{ 9847{
9664 static const struct builtincmd null_bltin = { 9848 static const struct builtincmd null_bltin = {
@@ -9794,6 +9978,9 @@ evalcommand(union node *cmd, int flags)
9794 nargv = parse_command_args(argv, &path); 9978 nargv = parse_command_args(argv, &path);
9795 if (!nargv) 9979 if (!nargv)
9796 break; 9980 break;
9981 /* It's "command [-p] PROG ARGS" (that is, no -Vv).
9982 * nargv => "PROG". path is updated if -p.
9983 */
9797 argc -= nargv - argv; 9984 argc -= nargv - argv;
9798 argv = nargv; 9985 argv = nargv;
9799 cmd_flag |= DO_NOFUNC; 9986 cmd_flag |= DO_NOFUNC;
@@ -9862,9 +10049,9 @@ evalcommand(union node *cmd, int flags)
9862 jp = makejob(/*cmd,*/ 1); 10049 jp = makejob(/*cmd,*/ 1);
9863 if (forkshell(jp, cmd, FORK_FG) != 0) { 10050 if (forkshell(jp, cmd, FORK_FG) != 0) {
9864 /* parent */ 10051 /* parent */
9865 exitstatus = waitforjob(jp); 10052 status = waitforjob(jp);
9866 INT_ON; 10053 INT_ON;
9867 TRACE(("forked child exited with %d\n", exitstatus)); 10054 TRACE(("forked child exited with %d\n", status));
9868 break; 10055 break;
9869 } 10056 }
9870 /* child */ 10057 /* child */
@@ -9894,7 +10081,7 @@ evalcommand(union node *cmd, int flags)
9894 * to reap the zombie and make kill detect that it's gone: */ 10081 * to reap the zombie and make kill detect that it's gone: */
9895 dowait(DOWAIT_NONBLOCK, NULL); 10082 dowait(DOWAIT_NONBLOCK, NULL);
9896 10083
9897 if (evalbltin(cmdentry.u.cmd, argc, argv)) { 10084 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
9898 int exit_status; 10085 int exit_status;
9899 int i = exception_type; 10086 int i = exception_type;
9900 if (i == EXEXIT || i == EXEXEC) 10087 if (i == EXEXIT || i == EXEXEC)
@@ -9911,7 +10098,7 @@ evalcommand(union node *cmd, int flags)
9911 } 10098 }
9912 FORCE_INT_ON; 10099 FORCE_INT_ON;
9913 } 10100 }
9914 break; 10101 goto readstatus;
9915 10102
9916 case CMDFUNCTION: 10103 case CMDFUNCTION:
9917 listsetvar(varlist.list, 0); 10104 listsetvar(varlist.list, 0);
@@ -9919,6 +10106,8 @@ evalcommand(union node *cmd, int flags)
9919 dowait(DOWAIT_NONBLOCK, NULL); 10106 dowait(DOWAIT_NONBLOCK, NULL);
9920 if (evalfun(cmdentry.u.func, argc, argv, flags)) 10107 if (evalfun(cmdentry.u.func, argc, argv, flags))
9921 goto raise; 10108 goto raise;
10109 readstatus:
10110 status = exitstatus;
9922 break; 10111 break;
9923 } /* switch */ 10112 } /* switch */
9924 10113
@@ -9932,29 +10121,36 @@ evalcommand(union node *cmd, int flags)
9932 setvar0("_", lastarg); 10121 setvar0("_", lastarg);
9933 } 10122 }
9934 popstackmark(&smark); 10123 popstackmark(&smark);
10124
10125 return status;
9935} 10126}
9936 10127
9937static int 10128static int
9938evalbltin(const struct builtincmd *cmd, int argc, char **argv) 10129evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
9939{ 10130{
9940 char *volatile savecmdname; 10131 char *volatile savecmdname;
9941 struct jmploc *volatile savehandler; 10132 struct jmploc *volatile savehandler;
9942 struct jmploc jmploc; 10133 struct jmploc jmploc;
10134 int status;
9943 int i; 10135 int i;
9944 10136
9945 savecmdname = commandname; 10137 savecmdname = commandname;
10138 savehandler = exception_handler;
9946 i = setjmp(jmploc.loc); 10139 i = setjmp(jmploc.loc);
9947 if (i) 10140 if (i)
9948 goto cmddone; 10141 goto cmddone;
9949 savehandler = exception_handler;
9950 exception_handler = &jmploc; 10142 exception_handler = &jmploc;
9951 commandname = argv[0]; 10143 commandname = argv[0];
9952 argptr = argv + 1; 10144 argptr = argv + 1;
9953 optptr = NULL; /* initialize nextopt */ 10145 optptr = NULL; /* initialize nextopt */
9954 exitstatus = (*cmd->builtin)(argc, argv); 10146 if (cmd == EVALCMD)
10147 status = evalcmd(argc, argv, flags);
10148 else
10149 status = (*cmd->builtin)(argc, argv);
9955 flush_stdout_stderr(); 10150 flush_stdout_stderr();
10151 status |= ferror(stdout);
10152 exitstatus = status;
9956 cmddone: 10153 cmddone:
9957 exitstatus |= ferror(stdout);
9958 clearerr(stdout); 10154 clearerr(stdout);
9959 commandname = savecmdname; 10155 commandname = savecmdname;
9960 exception_handler = savehandler; 10156 exception_handler = savehandler;
@@ -10058,6 +10254,8 @@ pushstring(char *s, struct alias *ap)
10058 g_parsefile->strpush = sp; 10254 g_parsefile->strpush = sp;
10059 sp->prev_string = g_parsefile->next_to_pgetc; 10255 sp->prev_string = g_parsefile->next_to_pgetc;
10060 sp->prev_left_in_line = g_parsefile->left_in_line; 10256 sp->prev_left_in_line = g_parsefile->left_in_line;
10257 sp->unget = g_parsefile->unget;
10258 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
10061#if ENABLE_ASH_ALIAS 10259#if ENABLE_ASH_ALIAS
10062 sp->ap = ap; 10260 sp->ap = ap;
10063 if (ap) { 10261 if (ap) {
@@ -10067,6 +10265,7 @@ pushstring(char *s, struct alias *ap)
10067#endif 10265#endif
10068 g_parsefile->next_to_pgetc = s; 10266 g_parsefile->next_to_pgetc = s;
10069 g_parsefile->left_in_line = len; 10267 g_parsefile->left_in_line = len;
10268 g_parsefile->unget = 0;
10070 INT_ON; 10269 INT_ON;
10071} 10270}
10072 10271
@@ -10094,17 +10293,14 @@ popstring(void)
10094#endif 10293#endif
10095 g_parsefile->next_to_pgetc = sp->prev_string; 10294 g_parsefile->next_to_pgetc = sp->prev_string;
10096 g_parsefile->left_in_line = sp->prev_left_in_line; 10295 g_parsefile->left_in_line = sp->prev_left_in_line;
10296 g_parsefile->unget = sp->unget;
10297 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
10097 g_parsefile->strpush = sp->prev; 10298 g_parsefile->strpush = sp->prev;
10098 if (sp != &(g_parsefile->basestrpush)) 10299 if (sp != &(g_parsefile->basestrpush))
10099 free(sp); 10300 free(sp);
10100 INT_ON; 10301 INT_ON;
10101} 10302}
10102 10303
10103//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
10104//it peeks whether it is &>, and then pushes back both chars.
10105//This function needs to save last *next_to_pgetc to buf[0]
10106//to make two pungetc() reliable. Currently,
10107// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
10108static int 10304static int
10109preadfd(void) 10305preadfd(void)
10110{ 10306{
@@ -10189,13 +10385,14 @@ preadfd(void)
10189 */ 10385 */
10190//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) 10386//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
10191#define pgetc_debug(...) ((void)0) 10387#define pgetc_debug(...) ((void)0)
10388static int pgetc(void);
10192static int 10389static int
10193preadbuffer(void) 10390preadbuffer(void)
10194{ 10391{
10195 char *q; 10392 char *q;
10196 int more; 10393 int more;
10197 10394
10198 while (g_parsefile->strpush) { 10395 if (g_parsefile->strpush) {
10199#if ENABLE_ASH_ALIAS 10396#if ENABLE_ASH_ALIAS
10200 if (g_parsefile->left_in_line == -1 10397 if (g_parsefile->left_in_line == -1
10201 && g_parsefile->strpush->ap 10398 && g_parsefile->strpush->ap
@@ -10207,13 +10404,7 @@ preadbuffer(void)
10207 } 10404 }
10208#endif 10405#endif
10209 popstring(); 10406 popstring();
10210 /* try "pgetc" now: */ 10407 return pgetc();
10211 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
10212 g_parsefile->left_in_line,
10213 g_parsefile->next_to_pgetc,
10214 g_parsefile->next_to_pgetc);
10215 if (--g_parsefile->left_in_line >= 0)
10216 return (unsigned char)(*g_parsefile->next_to_pgetc++);
10217 } 10408 }
10218 /* on both branches above g_parsefile->left_in_line < 0. 10409 /* on both branches above g_parsefile->left_in_line < 0.
10219 * "pgetc" needs refilling. 10410 * "pgetc" needs refilling.
@@ -10292,27 +10483,41 @@ preadbuffer(void)
10292 return (unsigned char)*g_parsefile->next_to_pgetc++; 10483 return (unsigned char)*g_parsefile->next_to_pgetc++;
10293} 10484}
10294 10485
10295#define pgetc_as_macro() \ 10486static void
10296 (--g_parsefile->left_in_line >= 0 \ 10487nlprompt(void)
10297 ? (unsigned char)*g_parsefile->next_to_pgetc++ \ 10488{
10298 : preadbuffer() \ 10489 g_parsefile->linno++;
10299 ) 10490 setprompt_if(doprompt, 2);
10491}
10492static void
10493nlnoprompt(void)
10494{
10495 g_parsefile->linno++;
10496 needprompt = doprompt;
10497}
10300 10498
10301static int 10499static int
10302pgetc(void) 10500pgetc(void)
10303{ 10501{
10304 pgetc_debug("pgetc_fast at %d:%p'%s'", 10502 int c;
10503
10504 pgetc_debug("pgetc at %d:%p'%s'",
10305 g_parsefile->left_in_line, 10505 g_parsefile->left_in_line,
10306 g_parsefile->next_to_pgetc, 10506 g_parsefile->next_to_pgetc,
10307 g_parsefile->next_to_pgetc); 10507 g_parsefile->next_to_pgetc);
10308 return pgetc_as_macro(); 10508 if (g_parsefile->unget)
10309} 10509 return g_parsefile->lastc[--g_parsefile->unget];
10310 10510
10311#if ENABLE_ASH_OPTIMIZE_FOR_SIZE 10511 if (--g_parsefile->left_in_line >= 0)
10312# define pgetc_fast() pgetc() 10512 c = (signed char)*g_parsefile->next_to_pgetc++;
10313#else 10513 else
10314# define pgetc_fast() pgetc_as_macro() 10514 c = preadbuffer();
10315#endif 10515
10516 g_parsefile->lastc[1] = g_parsefile->lastc[0];
10517 g_parsefile->lastc[0] = c;
10518
10519 return c;
10520}
10316 10521
10317#if ENABLE_ASH_ALIAS 10522#if ENABLE_ASH_ALIAS
10318static int 10523static int
@@ -10320,11 +10525,11 @@ pgetc_without_PEOA(void)
10320{ 10525{
10321 int c; 10526 int c;
10322 do { 10527 do {
10323 pgetc_debug("pgetc_fast at %d:%p'%s'", 10528 pgetc_debug("pgetc at %d:%p'%s'",
10324 g_parsefile->left_in_line, 10529 g_parsefile->left_in_line,
10325 g_parsefile->next_to_pgetc, 10530 g_parsefile->next_to_pgetc,
10326 g_parsefile->next_to_pgetc); 10531 g_parsefile->next_to_pgetc);
10327 c = pgetc_fast(); 10532 c = pgetc();
10328 } while (c == PEOA); 10533 } while (c == PEOA);
10329 return c; 10534 return c;
10330} 10535}
@@ -10358,18 +10563,31 @@ pfgets(char *line, int len)
10358} 10563}
10359 10564
10360/* 10565/*
10361 * Undo the last call to pgetc. Only one character may be pushed back. 10566 * Undo a call to pgetc. Only two characters may be pushed back.
10362 * PEOF may be pushed back. 10567 * PEOF may be pushed back.
10363 */ 10568 */
10364static void 10569static void
10365pungetc(void) 10570pungetc(void)
10366{ 10571{
10367 g_parsefile->left_in_line++; 10572 g_parsefile->unget++;
10368 g_parsefile->next_to_pgetc--; 10573}
10369 pgetc_debug("pushed back to %d:%p'%s'", 10574
10370 g_parsefile->left_in_line, 10575/* This one eats backslash+newline */
10371 g_parsefile->next_to_pgetc, 10576static int
10372 g_parsefile->next_to_pgetc); 10577pgetc_eatbnl(void)
10578{
10579 int c;
10580
10581 while ((c = pgetc()) == '\\') {
10582 if (pgetc() != '\n') {
10583 pungetc();
10584 break;
10585 }
10586
10587 nlprompt();
10588 }
10589
10590 return c;
10373} 10591}
10374 10592
10375/* 10593/*
@@ -10386,6 +10604,7 @@ pushfile(void)
10386 pf->pf_fd = -1; 10604 pf->pf_fd = -1;
10387 /*pf->strpush = NULL; - ckzalloc did it */ 10605 /*pf->strpush = NULL; - ckzalloc did it */
10388 /*pf->basestrpush.prev = NULL;*/ 10606 /*pf->basestrpush.prev = NULL;*/
10607 /*pf->unget = 0;*/
10389 g_parsefile = pf; 10608 g_parsefile = pf;
10390} 10609}
10391 10610
@@ -10959,8 +11178,8 @@ static const char *
10959tokname(char *buf, int tok) 11178tokname(char *buf, int tok)
10960{ 11179{
10961 if (tok < TSEMI) 11180 if (tok < TSEMI)
10962 return tokname_array[tok] + 1; 11181 return tokname_array[tok];
10963 sprintf(buf, "\"%s\"", tokname_array[tok] + 1); 11182 sprintf(buf, "\"%s\"", tokname_array[tok]);
10964 return buf; 11183 return buf;
10965} 11184}
10966 11185
@@ -11017,7 +11236,7 @@ list(int nlflag)
11017 } 11236 }
11018 11237
11019 checkkwd = CHKNL | CHKKWD | CHKALIAS; 11238 checkkwd = CHKNL | CHKKWD | CHKALIAS;
11020 if (nlflag == 2 && tokname_array[peektoken()][0]) 11239 if (nlflag == 2 && ((1 << peektoken()) & tokendlist))
11021 return n1; 11240 return n1;
11022 nlflag |= 2; 11241 nlflag |= 2;
11023 11242
@@ -11399,7 +11618,7 @@ parse_command(void)
11399 n1->nbinary.ch1 = list(0); 11618 n1->nbinary.ch1 = list(0);
11400 got = readtoken(); 11619 got = readtoken();
11401 if (got != TDO) { 11620 if (got != TDO) {
11402 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1, 11621 TRACE(("expecting DO got '%s' %s\n", tokname_array[got],
11403 got == TWORD ? wordtext : "")); 11622 got == TWORD ? wordtext : ""));
11404 raise_error_unexpected_syntax(TDO); 11623 raise_error_unexpected_syntax(TDO);
11405 } 11624 }
@@ -11545,7 +11764,8 @@ parse_command(void)
11545} 11764}
11546 11765
11547#if ENABLE_ASH_BASH_COMPAT 11766#if ENABLE_ASH_BASH_COMPAT
11548static int decode_dollar_squote(void) 11767static int
11768decode_dollar_squote(void)
11549{ 11769{
11550 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; 11770 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11551 int c, cnt; 11771 int c, cnt;
@@ -11613,7 +11833,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11613 /* NB: syntax parameter fits into smallint */ 11833 /* NB: syntax parameter fits into smallint */
11614 /* c parameter is an unsigned char or PEOF or PEOA */ 11834 /* c parameter is an unsigned char or PEOF or PEOA */
11615 char *out; 11835 char *out;
11616 int len; 11836 size_t len;
11617 char line[EOFMARKLEN + 1]; 11837 char line[EOFMARKLEN + 1];
11618 struct nodelist *bqlist; 11838 struct nodelist *bqlist;
11619 smallint quotef; 11839 smallint quotef;
@@ -11656,25 +11876,31 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11656 if (syntax == BASESYNTAX) 11876 if (syntax == BASESYNTAX)
11657 goto endword; /* exit outer loop */ 11877 goto endword; /* exit outer loop */
11658 USTPUTC(c, out); 11878 USTPUTC(c, out);
11659 g_parsefile->linno++; 11879 nlprompt();
11660 setprompt_if(doprompt, 2);
11661 c = pgetc(); 11880 c = pgetc();
11662 goto loop; /* continue outer loop */ 11881 goto loop; /* continue outer loop */
11663 case CWORD: 11882 case CWORD:
11664 USTPUTC(c, out); 11883 USTPUTC(c, out);
11665 break; 11884 break;
11666 case CCTL: 11885 case CCTL:
11667 if (eofmark == NULL || dblquote)
11668 USTPUTC(CTLESC, out);
11669#if ENABLE_ASH_BASH_COMPAT 11886#if ENABLE_ASH_BASH_COMPAT
11670 if (c == '\\' && bash_dollar_squote) { 11887 if (c == '\\' && bash_dollar_squote) {
11671 c = decode_dollar_squote(); 11888 c = decode_dollar_squote();
11889 if (c == '\0') {
11890 /* skip $'\000', $'\x00' (like bash) */
11891 break;
11892 }
11672 if (c & 0x100) { 11893 if (c & 0x100) {
11673 USTPUTC('\\', out); 11894 /* Unknown escape. Encode as '\z' */
11674 c = (unsigned char)c; 11895 c = (unsigned char)c;
11896 if (eofmark == NULL || dblquote)
11897 USTPUTC(CTLESC, out);
11898 USTPUTC('\\', out);
11675 } 11899 }
11676 } 11900 }
11677#endif 11901#endif
11902 if (eofmark == NULL || dblquote)
11903 USTPUTC(CTLESC, out);
11678 USTPUTC(c, out); 11904 USTPUTC(c, out);
11679 break; 11905 break;
11680 case CBACK: /* backslash */ 11906 case CBACK: /* backslash */
@@ -11684,7 +11910,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11684 USTPUTC('\\', out); 11910 USTPUTC('\\', out);
11685 pungetc(); 11911 pungetc();
11686 } else if (c == '\n') { 11912 } else if (c == '\n') {
11687 setprompt_if(doprompt, 2); 11913 nlprompt();
11688 } else { 11914 } else {
11689#if ENABLE_ASH_EXPAND_PRMT 11915#if ENABLE_ASH_EXPAND_PRMT
11690 if (c == '$' && pssyntax) { 11916 if (c == '$' && pssyntax) {
@@ -11752,7 +11978,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11752 if (parenlevel > 0) { 11978 if (parenlevel > 0) {
11753 parenlevel--; 11979 parenlevel--;
11754 } else { 11980 } else {
11755 if (pgetc() == ')') { 11981 if (pgetc_eatbnl() == ')') {
11756 c = CTLENDARI; 11982 c = CTLENDARI;
11757 if (--arinest == 0) { 11983 if (--arinest == 0) {
11758 syntax = prevsyntax; 11984 syntax = prevsyntax;
@@ -11779,6 +12005,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11779 if (varnest == 0) { 12005 if (varnest == 0) {
11780#if ENABLE_ASH_BASH_COMPAT 12006#if ENABLE_ASH_BASH_COMPAT
11781 if (c == '&') { 12007 if (c == '&') {
12008//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
11782 if (pgetc() == '>') 12009 if (pgetc() == '>')
11783 c = 0x100 + '>'; /* flag &> */ 12010 c = 0x100 + '>'; /* flag &> */
11784 pungetc(); 12011 pungetc();
@@ -11789,7 +12016,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11789 IF_ASH_ALIAS(if (c != PEOA)) 12016 IF_ASH_ALIAS(if (c != PEOA))
11790 USTPUTC(c, out); 12017 USTPUTC(c, out);
11791 } 12018 }
11792 c = pgetc_fast(); 12019 c = pgetc();
11793 } /* for (;;) */ 12020 } /* for (;;) */
11794 endword: 12021 endword:
11795 12022
@@ -11854,8 +12081,7 @@ checkend: {
11854 continue; 12081 continue;
11855 if (*p == '\n' && *q == '\0') { 12082 if (*p == '\n' && *q == '\0') {
11856 c = PEOF; 12083 c = PEOF;
11857 g_parsefile->linno++; 12084 nlnoprompt();
11858 needprompt = doprompt;
11859 } else { 12085 } else {
11860 pushstring(line, NULL); 12086 pushstring(line, NULL);
11861 } 12087 }
@@ -11954,7 +12180,7 @@ parsesub: {
11954 int typeloc; 12180 int typeloc;
11955 int flags; 12181 int flags;
11956 12182
11957 c = pgetc(); 12183 c = pgetc_eatbnl();
11958 if (c > 255 /* PEOA or PEOF */ 12184 if (c > 255 /* PEOA or PEOF */
11959 || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) 12185 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
11960 ) { 12186 ) {
@@ -11967,7 +12193,7 @@ parsesub: {
11967 pungetc(); 12193 pungetc();
11968 } else if (c == '(') { 12194 } else if (c == '(') {
11969 /* $(command) or $((arith)) */ 12195 /* $(command) or $((arith)) */
11970 if (pgetc() == '(') { 12196 if (pgetc_eatbnl() == '(') {
11971#if ENABLE_SH_MATH_SUPPORT 12197#if ENABLE_SH_MATH_SUPPORT
11972 PARSEARITH(); 12198 PARSEARITH();
11973#else 12199#else
@@ -11984,9 +12210,9 @@ parsesub: {
11984 USTPUTC(VSNORMAL, out); 12210 USTPUTC(VSNORMAL, out);
11985 subtype = VSNORMAL; 12211 subtype = VSNORMAL;
11986 if (c == '{') { 12212 if (c == '{') {
11987 c = pgetc(); 12213 c = pgetc_eatbnl();
11988 if (c == '#') { 12214 if (c == '#') {
11989 c = pgetc(); 12215 c = pgetc_eatbnl();
11990 if (c == '}') 12216 if (c == '}')
11991 c = '#'; /* ${#} - same as $# */ 12217 c = '#'; /* ${#} - same as $# */
11992 else 12218 else
@@ -11999,18 +12225,18 @@ parsesub: {
11999 /* $[{[#]]NAME[}] */ 12225 /* $[{[#]]NAME[}] */
12000 do { 12226 do {
12001 STPUTC(c, out); 12227 STPUTC(c, out);
12002 c = pgetc(); 12228 c = pgetc_eatbnl();
12003 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); 12229 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
12004 } else if (isdigit(c)) { 12230 } else if (isdigit(c)) {
12005 /* $[{[#]]NUM[}] */ 12231 /* $[{[#]]NUM[}] */
12006 do { 12232 do {
12007 STPUTC(c, out); 12233 STPUTC(c, out);
12008 c = pgetc(); 12234 c = pgetc_eatbnl();
12009 } while (isdigit(c)); 12235 } while (isdigit(c));
12010 } else if (is_special(c)) { 12236 } else if (is_special(c)) {
12011 /* $[{[#]]<specialchar>[}] */ 12237 /* $[{[#]]<specialchar>[}] */
12012 USTPUTC(c, out); 12238 USTPUTC(c, out);
12013 c = pgetc(); 12239 c = pgetc_eatbnl();
12014 } else { 12240 } else {
12015 badsub: 12241 badsub:
12016 raise_error_syntax("bad substitution"); 12242 raise_error_syntax("bad substitution");
@@ -12023,15 +12249,18 @@ parsesub: {
12023 STPUTC('=', out); 12249 STPUTC('=', out);
12024 flags = 0; 12250 flags = 0;
12025 if (subtype == 0) { 12251 if (subtype == 0) {
12252 static const char types[] ALIGN1 = "}-+?=";
12026 /* ${VAR...} but not $VAR or ${#VAR} */ 12253 /* ${VAR...} but not $VAR or ${#VAR} */
12027 /* c == first char after VAR */ 12254 /* c == first char after VAR */
12028 switch (c) { 12255 switch (c) {
12029 case ':': 12256 case ':':
12030 c = pgetc(); 12257 c = pgetc_eatbnl();
12031#if ENABLE_ASH_BASH_COMPAT 12258#if ENABLE_ASH_BASH_COMPAT
12032 if (c == ':' || c == '$' || isdigit(c)) { 12259 /* This check is only needed to not misinterpret
12033//TODO: support more general format ${v:EXPR:EXPR}, 12260 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12034// where EXPR follows $(()) rules 12261 * constructs.
12262 */
12263 if (!strchr(types, c)) {
12035 subtype = VSSUBSTR; 12264 subtype = VSSUBSTR;
12036 pungetc(); 12265 pungetc();
12037 break; /* "goto do_pungetc" is bigger (!) */ 12266 break; /* "goto do_pungetc" is bigger (!) */
@@ -12040,7 +12269,6 @@ parsesub: {
12040 flags = VSNUL; 12269 flags = VSNUL;
12041 /*FALLTHROUGH*/ 12270 /*FALLTHROUGH*/
12042 default: { 12271 default: {
12043 static const char types[] ALIGN1 = "}-+?=";
12044 const char *p = strchr(types, c); 12272 const char *p = strchr(types, c);
12045 if (p == NULL) 12273 if (p == NULL)
12046 goto badsub; 12274 goto badsub;
@@ -12051,7 +12279,7 @@ parsesub: {
12051 case '#': { 12279 case '#': {
12052 int cc = c; 12280 int cc = c;
12053 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); 12281 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
12054 c = pgetc(); 12282 c = pgetc_eatbnl();
12055 if (c != cc) 12283 if (c != cc)
12056 goto do_pungetc; 12284 goto do_pungetc;
12057 subtype++; 12285 subtype++;
@@ -12063,7 +12291,7 @@ parsesub: {
12063//TODO: encode pattern and repl separately. 12291//TODO: encode pattern and repl separately.
12064// Currently ${v/$var_with_slash/repl} is horribly broken 12292// Currently ${v/$var_with_slash/repl} is horribly broken
12065 subtype = VSREPLACE; 12293 subtype = VSREPLACE;
12066 c = pgetc(); 12294 c = pgetc_eatbnl();
12067 if (c != '/') 12295 if (c != '/')
12068 goto do_pungetc; 12296 goto do_pungetc;
12069 subtype++; /* VSREPLACEALL */ 12297 subtype++; /* VSREPLACEALL */
@@ -12101,9 +12329,18 @@ parsebackq: {
12101 str = NULL; 12329 str = NULL;
12102 savelen = out - (char *)stackblock(); 12330 savelen = out - (char *)stackblock();
12103 if (savelen > 0) { 12331 if (savelen > 0) {
12332 /*
12333 * FIXME: this can allocate very large block on stack and SEGV.
12334 * Example:
12335 * echo "..<100kbytes>..`true` $(true) `true` ..."
12336 * allocates 100kb for every command subst. With about
12337 * a hundred command substitutions stack overflows.
12338 * With larger prepended string, SEGV happens sooner.
12339 */
12104 str = alloca(savelen); 12340 str = alloca(savelen);
12105 memcpy(str, stackblock(), savelen); 12341 memcpy(str, stackblock(), savelen);
12106 } 12342 }
12343
12107 if (oldstyle) { 12344 if (oldstyle) {
12108 /* We must read until the closing backquote, giving special 12345 /* We must read until the closing backquote, giving special
12109 * treatment to some slashes, and then push the string and 12346 * treatment to some slashes, and then push the string and
@@ -12126,8 +12363,7 @@ parsebackq: {
12126 case '\\': 12363 case '\\':
12127 pc = pgetc(); 12364 pc = pgetc();
12128 if (pc == '\n') { 12365 if (pc == '\n') {
12129 g_parsefile->linno++; 12366 nlprompt();
12130 setprompt_if(doprompt, 2);
12131 /* 12367 /*
12132 * If eating a newline, avoid putting 12368 * If eating a newline, avoid putting
12133 * the newline into the new character 12369 * the newline into the new character
@@ -12152,8 +12388,7 @@ parsebackq: {
12152 raise_error_syntax("EOF in backquote substitution"); 12388 raise_error_syntax("EOF in backquote substitution");
12153 12389
12154 case '\n': 12390 case '\n':
12155 g_parsefile->linno++; 12391 nlnoprompt();
12156 needprompt = doprompt;
12157 break; 12392 break;
12158 12393
12159 default: 12394 default:
@@ -12272,7 +12507,7 @@ xxreadtoken(void)
12272 setprompt_if(needprompt, 2); 12507 setprompt_if(needprompt, 2);
12273 startlinno = g_parsefile->linno; 12508 startlinno = g_parsefile->linno;
12274 for (;;) { /* until token or start of word found */ 12509 for (;;) { /* until token or start of word found */
12275 c = pgetc_fast(); 12510 c = pgetc();
12276 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) 12511 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
12277 continue; 12512 continue;
12278 12513
@@ -12285,16 +12520,14 @@ xxreadtoken(void)
12285 pungetc(); 12520 pungetc();
12286 break; /* return readtoken1(...) */ 12521 break; /* return readtoken1(...) */
12287 } 12522 }
12288 startlinno = ++g_parsefile->linno; 12523 nlprompt();
12289 setprompt_if(doprompt, 2);
12290 } else { 12524 } else {
12291 const char *p; 12525 const char *p;
12292 12526
12293 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; 12527 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
12294 if (c != PEOF) { 12528 if (c != PEOF) {
12295 if (c == '\n') { 12529 if (c == '\n') {
12296 g_parsefile->linno++; 12530 nlnoprompt();
12297 needprompt = doprompt;
12298 } 12531 }
12299 12532
12300 p = strchr(xxreadtoken_chars, c); 12533 p = strchr(xxreadtoken_chars, c);
@@ -12335,7 +12568,7 @@ xxreadtoken(void)
12335 setprompt_if(needprompt, 2); 12568 setprompt_if(needprompt, 2);
12336 startlinno = g_parsefile->linno; 12569 startlinno = g_parsefile->linno;
12337 for (;;) { /* until token or start of word found */ 12570 for (;;) { /* until token or start of word found */
12338 c = pgetc_fast(); 12571 c = pgetc();
12339 switch (c) { 12572 switch (c) {
12340 case ' ': case '\t': 12573 case ' ': case '\t':
12341 IF_ASH_ALIAS(case PEOA:) 12574 IF_ASH_ALIAS(case PEOA:)
@@ -12347,15 +12580,13 @@ xxreadtoken(void)
12347 continue; 12580 continue;
12348 case '\\': 12581 case '\\':
12349 if (pgetc() == '\n') { 12582 if (pgetc() == '\n') {
12350 startlinno = ++g_parsefile->linno; 12583 nlprompt();
12351 setprompt_if(doprompt, 2);
12352 continue; 12584 continue;
12353 } 12585 }
12354 pungetc(); 12586 pungetc();
12355 goto breakloop; 12587 goto breakloop;
12356 case '\n': 12588 case '\n':
12357 g_parsefile->linno++; 12589 nlnoprompt();
12358 needprompt = doprompt;
12359 RETURN(TNL); 12590 RETURN(TNL);
12360 case PEOF: 12591 case PEOF:
12361 RETURN(TEOF); 12592 RETURN(TEOF);
@@ -12426,7 +12657,7 @@ readtoken(void)
12426 pp = findkwd(wordtext); 12657 pp = findkwd(wordtext);
12427 if (pp) { 12658 if (pp) {
12428 lasttoken = t = pp - tokname_array; 12659 lasttoken = t = pp - tokname_array;
12429 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1)); 12660 TRACE(("keyword '%s' recognized\n", tokname_array[t]));
12430 goto out; 12661 goto out;
12431 } 12662 }
12432 } 12663 }
@@ -12447,9 +12678,9 @@ readtoken(void)
12447 checkkwd = 0; 12678 checkkwd = 0;
12448#if DEBUG 12679#if DEBUG
12449 if (!alreadyseen) 12680 if (!alreadyseen)
12450 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); 12681 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
12451 else 12682 else
12452 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); 12683 TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
12453#endif 12684#endif
12454 return t; 12685 return t;
12455} 12686}
@@ -12536,35 +12767,39 @@ expandstr(const char *ps)
12536 * Execute a command or commands contained in a string. 12767 * Execute a command or commands contained in a string.
12537 */ 12768 */
12538static int 12769static int
12539evalstring(char *s, int mask) 12770evalstring(char *s, int flags)
12540{ 12771{
12541 union node *n; 12772 union node *n;
12542 struct stackmark smark; 12773 struct stackmark smark;
12543 int skip; 12774 int status;
12544 12775
12776 s = sstrdup(s);
12545 setinputstring(s); 12777 setinputstring(s);
12546 setstackmark(&smark); 12778 setstackmark(&smark);
12547 12779
12548 skip = 0; 12780 status = 0;
12549 while ((n = parsecmd(0)) != NODE_EOF) { 12781 while ((n = parsecmd(0)) != NODE_EOF) {
12550 evaltree(n, 0); 12782 int i;
12783
12784 i = evaltree(n, flags);
12785 if (n)
12786 status = i;
12551 popstackmark(&smark); 12787 popstackmark(&smark);
12552 skip = evalskip; 12788 if (evalskip)
12553 if (skip)
12554 break; 12789 break;
12555 } 12790 }
12791 popstackmark(&smark);
12556 popfile(); 12792 popfile();
12793 stunalloc(s);
12557 12794
12558 skip &= mask; 12795 return status;
12559 evalskip = skip;
12560 return skip;
12561} 12796}
12562 12797
12563/* 12798/*
12564 * The eval command. 12799 * The eval command.
12565 */ 12800 */
12566static int FAST_FUNC 12801static int FAST_FUNC
12567evalcmd(int argc UNUSED_PARAM, char **argv) 12802evalcmd(int argc UNUSED_PARAM, char **argv, int flags)
12568{ 12803{
12569 char *p; 12804 char *p;
12570 char *concat; 12805 char *concat;
@@ -12584,9 +12819,9 @@ evalcmd(int argc UNUSED_PARAM, char **argv)
12584 STPUTC('\0', concat); 12819 STPUTC('\0', concat);
12585 p = grabstackstr(concat); 12820 p = grabstackstr(concat);
12586 } 12821 }
12587 evalstring(p, ~SKIPEVAL); 12822 return evalstring(p, flags & EV_TESTED);
12588 } 12823 }
12589 return exitstatus; 12824 return 0;
12590} 12825}
12591 12826
12592/* 12827/*
@@ -12600,6 +12835,7 @@ cmdloop(int top)
12600 union node *n; 12835 union node *n;
12601 struct stackmark smark; 12836 struct stackmark smark;
12602 int inter; 12837 int inter;
12838 int status = 0;
12603 int numeof = 0; 12839 int numeof = 0;
12604 12840
12605 TRACE(("cmdloop(%d) called\n", top)); 12841 TRACE(("cmdloop(%d) called\n", top));
@@ -12631,20 +12867,24 @@ cmdloop(int top)
12631 } 12867 }
12632 numeof++; 12868 numeof++;
12633 } else if (nflag == 0) { 12869 } else if (nflag == 0) {
12870 int i;
12871
12634 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 12872 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12635 job_warning >>= 1; 12873 job_warning >>= 1;
12636 numeof = 0; 12874 numeof = 0;
12637 evaltree(n, 0); 12875 i = evaltree(n, 0);
12876 if (n)
12877 status = i;
12638 } 12878 }
12639 popstackmark(&smark); 12879 popstackmark(&smark);
12640 skip = evalskip; 12880 skip = evalskip;
12641 12881
12642 if (skip) { 12882 if (skip) {
12643 evalskip = 0; 12883 evalskip &= ~SKIPFUNC;
12644 return skip & SKIPEVAL; 12884 break;
12645 } 12885 }
12646 } 12886 }
12647 return 0; 12887 return status;
12648} 12888}
12649 12889
12650/* 12890/*
@@ -12689,32 +12929,38 @@ find_dot_file(char *name)
12689} 12929}
12690 12930
12691static int FAST_FUNC 12931static int FAST_FUNC
12692dotcmd(int argc, char **argv) 12932dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
12693{ 12933{
12934 /* "false; . empty_file; echo $?" should print 0, not 1: */
12935 int status = 0;
12694 char *fullname; 12936 char *fullname;
12937 char **argv;
12695 struct strlist *sp; 12938 struct strlist *sp;
12696 volatile struct shparam saveparam; 12939 volatile struct shparam saveparam;
12697 12940
12698 for (sp = cmdenviron; sp; sp = sp->next) 12941 for (sp = cmdenviron; sp; sp = sp->next)
12699 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); 12942 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
12700 12943
12701 if (!argv[1]) { 12944 nextopt(nullstr); /* handle possible "--" */
12945 argv = argptr;
12946
12947 if (!argv[0]) {
12702 /* bash says: "bash: .: filename argument required" */ 12948 /* bash says: "bash: .: filename argument required" */
12703 return 2; /* bash compat */ 12949 return 2; /* bash compat */
12704 } 12950 }
12705 12951
12706 /* "false; . empty_file; echo $?" should print 0, not 1: */
12707 exitstatus = 0;
12708
12709 /* This aborts if file isn't found, which is POSIXly correct. 12952 /* This aborts if file isn't found, which is POSIXly correct.
12710 * bash returns exitcode 1 instead. 12953 * bash returns exitcode 1 instead.
12711 */ 12954 */
12712 fullname = find_dot_file(argv[1]); 12955 fullname = find_dot_file(argv[0]);
12713 argv += 2; 12956 argv++;
12714 argc -= 2; 12957 if (argv[0]) { /* . FILE ARGS, ARGS exist */
12715 if (argc) { /* argc > 0, argv[0] != NULL */ 12958 int argc;
12716 saveparam = shellparam; 12959 saveparam = shellparam;
12717 shellparam.malloced = 0; 12960 shellparam.malloced = 0;
12961 argc = 1;
12962 while (argv[argc])
12963 argc++;
12718 shellparam.nparam = argc; 12964 shellparam.nparam = argc;
12719 shellparam.p = argv; 12965 shellparam.p = argv;
12720 }; 12966 };
@@ -12724,15 +12970,15 @@ dotcmd(int argc, char **argv)
12724 */ 12970 */
12725 setinputfile(fullname, INPUT_PUSH_FILE); 12971 setinputfile(fullname, INPUT_PUSH_FILE);
12726 commandname = fullname; 12972 commandname = fullname;
12727 cmdloop(0); 12973 status = cmdloop(0);
12728 popfile(); 12974 popfile();
12729 12975
12730 if (argc) { 12976 if (argv[0]) {
12731 freeparam(&shellparam); 12977 freeparam(&shellparam);
12732 shellparam = saveparam; 12978 shellparam = saveparam;
12733 }; 12979 };
12734 12980
12735 return exitstatus; 12981 return status;
12736} 12982}
12737 12983
12738static int FAST_FUNC 12984static int FAST_FUNC
@@ -12994,8 +13240,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
12994} 13240}
12995 13241
12996 13242
12997/* ============ trap.c */
12998
12999/* 13243/*
13000 * The trap builtin. 13244 * The trap builtin.
13001 */ 13245 */
@@ -13101,7 +13345,7 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13101 } 13345 }
13102 } 13346 }
13103# endif 13347# endif
13104 out1fmt("\n\n"); 13348 newline_and_flush(stdout);
13105 return EXIT_SUCCESS; 13349 return EXIT_SUCCESS;
13106} 13350}
13107#endif 13351#endif
@@ -13421,15 +13665,10 @@ exitshell(void)
13421#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 13665#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
13422 save_history(line_input_state); 13666 save_history(line_input_state);
13423#endif 13667#endif
13424
13425 status = exitstatus; 13668 status = exitstatus;
13426 TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); 13669 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13427 if (setjmp(loc.loc)) { 13670 if (setjmp(loc.loc)) {
13428 if (exception_type == EXEXIT) 13671 if (exception_type == EXEXIT)
13429/* dash bug: it just does _exit(exitstatus) here
13430 * but we have to do setjobctl(0) first!
13431 * (bug is still not fixed in dash-0.5.3 - if you run dash
13432 * under Midnight Commander, on exit from dash MC is backgrounded) */
13433 status = exitstatus; 13672 status = exitstatus;
13434 goto out; 13673 goto out;
13435 } 13674 }
@@ -13437,11 +13676,15 @@ exitshell(void)
13437 p = trap[0]; 13676 p = trap[0];
13438 if (p) { 13677 if (p) {
13439 trap[0] = NULL; 13678 trap[0] = NULL;
13679 evalskip = 0;
13440 evalstring(p, 0); 13680 evalstring(p, 0);
13441 free(p); 13681 /*free(p); - we'll exit soon */
13442 } 13682 }
13443 flush_stdout_stderr(); 13683 flush_stdout_stderr();
13444 out: 13684 out:
13685 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
13686 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
13687 */
13445 setjobctl(0); 13688 setjobctl(0);
13446 _exit(status); 13689 _exit(status);
13447 /* NOTREACHED */ 13690 /* NOTREACHED */
@@ -13454,18 +13697,15 @@ init(int xp)
13454init(void) 13697init(void)
13455#endif 13698#endif
13456{ 13699{
13457 /* from input.c: */
13458 /* we will never free this */ 13700 /* we will never free this */
13459 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 13701 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
13460 13702
13461 /* from trap.c: */
13462 signal(SIGCHLD, SIG_DFL); 13703 signal(SIGCHLD, SIG_DFL);
13463 /* bash re-enables SIGHUP which is SIG_IGNed on entry. 13704 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
13464 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" 13705 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
13465 */ 13706 */
13466 signal(SIGHUP, SIG_DFL); 13707 signal(SIGHUP, SIG_DFL);
13467 13708
13468 /* from var.c: */
13469 { 13709 {
13470 char **envp; 13710 char **envp;
13471 const char *p; 13711 const char *p;
@@ -13553,11 +13793,14 @@ init(void)
13553 } 13793 }
13554#endif 13794#endif
13555 for (envp = environ; envp && *envp; envp++) { 13795 for (envp = environ; envp && *envp; envp++) {
13556 if (strchr(*envp, '=')) { 13796 p = endofname(*envp);
13797 if (p != *envp && *p == '=') {
13557 setvareq(*envp, VEXPORT|VTEXTFIXED); 13798 setvareq(*envp, VEXPORT|VTEXTFIXED);
13558 } 13799 }
13559 } 13800 }
13560 13801
13802 setvareq((char*)defoptindvar, VTEXTFIXED);
13803
13561 setvar0("PPID", utoa(getppid())); 13804 setvar0("PPID", utoa(getppid()));
13562#if ENABLE_ASH_BASH_COMPAT 13805#if ENABLE_ASH_BASH_COMPAT
13563 p = lookupvar("SHLVL"); 13806 p = lookupvar("SHLVL");
@@ -13570,10 +13813,10 @@ init(void)
13570#endif 13813#endif
13571 p = lookupvar("PWD"); 13814 p = lookupvar("PWD");
13572 if (p) { 13815 if (p) {
13573 if (*p != '/' || stat(p, &st1) || stat(".", &st2) 13816 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
13574 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino 13817 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
13575 ) { 13818 ) {
13576 p = '\0'; 13819 p = NULL;
13577 } 13820 }
13578 } 13821 }
13579 setpwd(p, 0); 13822 setpwd(p, 0);
@@ -13664,14 +13907,10 @@ procargs(char **argv)
13664static void 13907static void
13665read_profile(const char *name) 13908read_profile(const char *name)
13666{ 13909{
13667 int skip;
13668
13669 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) 13910 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13670 return; 13911 return;
13671 skip = cmdloop(0); 13912 cmdloop(0);
13672 popfile(); 13913 popfile();
13673 if (skip)
13674 exitshell();
13675} 13914}
13676 13915
13677/* 13916/*
diff --git a/shell/ash_test/ash-glob/glob_dir.right b/shell/ash_test/ash-glob/glob_dir.right
new file mode 100644
index 000000000..aa90514d5
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_dir.right
@@ -0,0 +1,19 @@
1dirtest/z.tmp
2dirtest/z.tmp
3dirtest/z.tmp
4dirtest/z.tmp
5dirtest/z.tmp
6dirtest/z.tmp
7dirtest/z.tmp
8dirtest/z.tmp
9dirtest/z.tmp
10
11*/z.tmp
12*/z.*
13*/?.*
14*/z*p
15d*r*e*t/z*p
16*\/z.tmp
17*/z.*
18*/z*p
19d*r*e*t/z*p
diff --git a/shell/ash_test/ash-glob/glob_dir.tests b/shell/ash_test/ash-glob/glob_dir.tests
new file mode 100755
index 000000000..dc4c4fdb5
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_dir.tests
@@ -0,0 +1,25 @@
1mkdir dirtest
2 >dirtest/z.tmp
3
4echo */z.tmp
5echo */z.*
6echo */?.*
7echo */z*p
8echo d*r*e*t/z*p
9echo *"/z.t"mp
10echo */z"."*
11echo *"/z"*"p"
12echo "d"*r*e*t"/"z*p
13echo
14echo \*/z.tmp
15echo "*"/z.*
16echo */"?".*
17echo */z"*p"
18echo d*r*e\*t/z*p
19echo *"\\/z.t"mp
20echo */z".*"
21echo *"/z"\*"p"
22echo "d*"r*e*t"/"z*p
23
24rm dirtest/z.tmp
25rmdir dirtest
diff --git a/shell/hush_test/hush-misc/heredoc2.right b/shell/ash_test/ash-heredoc/heredoc5.right
index 74110e3b5..74110e3b5 100644
--- a/shell/hush_test/hush-misc/heredoc2.right
+++ b/shell/ash_test/ash-heredoc/heredoc5.right
diff --git a/shell/hush_test/hush-misc/heredoc2.tests b/shell/ash_test/ash-heredoc/heredoc5.tests
index e619bded1..e619bded1 100755
--- a/shell/hush_test/hush-misc/heredoc2.tests
+++ b/shell/ash_test/ash-heredoc/heredoc5.tests
diff --git a/shell/hush_test/hush-misc/heredoc_huge.right b/shell/ash_test/ash-heredoc/heredoc_huge.right
index 11740f674..11740f674 100644
--- a/shell/hush_test/hush-misc/heredoc_huge.right
+++ b/shell/ash_test/ash-heredoc/heredoc_huge.right
diff --git a/shell/hush_test/hush-misc/heredoc_huge.tests b/shell/ash_test/ash-heredoc/heredoc_huge.tests
index c2ec2817b..c2ec2817b 100755
--- a/shell/hush_test/hush-misc/heredoc_huge.tests
+++ b/shell/ash_test/ash-heredoc/heredoc_huge.tests
diff --git a/shell/ash_test/ash-misc/and-or.right b/shell/ash_test/ash-misc/and-or.right
new file mode 100644
index 000000000..f9fa5fbf8
--- /dev/null
+++ b/shell/ash_test/ash-misc/and-or.right
@@ -0,0 +1,18 @@
1a1
2a4
3b1
4b3
5b4
6b6
7c4
8c5
9c7
10c8
11ff1
12ff3
13ft2
14ft3
15tf2
16tf3
17tt2
18tt4
diff --git a/shell/ash_test/ash-misc/and-or.tests b/shell/ash_test/ash-misc/and-or.tests
new file mode 100755
index 000000000..485458abe
--- /dev/null
+++ b/shell/ash_test/ash-misc/and-or.tests
@@ -0,0 +1,34 @@
1false || echo a1
2false && echo a2
3true || echo a3
4true && echo a4
5
6false || echo b1 || echo b2
7false || echo b3 && echo b4
8false && echo b5 || echo b6
9false && echo b7 && echo b8
10
11true || echo c1 || echo c2
12true || echo c3 && echo c4
13true && echo c5 || echo c6
14true && echo c7 && echo c8
15
16false || false || echo ff1
17false || false && echo ff2
18false && false || echo ff3
19false && false && echo ff4
20
21false || true || echo ft1
22false || true && echo ft2
23false && true || echo ft3
24false && true && echo ft4
25
26true || false || echo tf1
27true || false && echo tf2
28true && false || echo tf3
29true && false && echo tf4
30
31true || true || echo tt1
32true || true && echo tt2
33true && true || echo tt3
34true && true && echo tt4
diff --git a/shell/ash_test/ash-misc/assignment1.right b/shell/ash_test/ash-misc/assignment1.right
new file mode 100644
index 000000000..d0a13d3d8
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment1.right
@@ -0,0 +1,9 @@
1if1:0
2while1:0
3until1:0
4if2:0
5while2:0
6until2:0
7if3:0
8while3:0
9until3:0
diff --git a/shell/ash_test/ash-misc/assignment1.tests b/shell/ash_test/ash-misc/assignment1.tests
new file mode 100755
index 000000000..033b35250
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment1.tests
@@ -0,0 +1,42 @@
1# Assignments after some keywords should still work
2
3if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi
4echo if1:$?
5while a=1 true; do a=1 true; break; done
6echo while1:$?
7until a=1 false; do a=1 true; break; done
8echo until1:$?
9
10if a=1 true
11 then a=1 true
12 elif a=1 true
13 then a=1 true
14 else a=1 true
15 fi
16echo if2:$?
17while a=1 true
18 do a=1 true
19 break
20 done
21echo while2:$?
22until a=1 false
23 do a=1 true
24 break
25 done
26echo until2:$?
27
28if
29 a=1 true; then
30 a=1 true; elif
31 a=1 true; then
32 a=1 true; else
33 a=1 true; fi
34echo if3:$?
35while
36 a=1 true; do
37 a=1 true; break; done
38echo while3:$?
39until
40 a=1 false; do
41 a=1 true; break; done
42echo until3:$?
diff --git a/shell/ash_test/ash-misc/assignment3.right b/shell/ash_test/ash-misc/assignment3.right
new file mode 100644
index 000000000..0f02d7cbc
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment3.right
@@ -0,0 +1,2 @@
1Done:0
2abc=123
diff --git a/shell/ash_test/ash-misc/assignment3.tests b/shell/ash_test/ash-misc/assignment3.tests
new file mode 100755
index 000000000..790129be1
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment3.tests
@@ -0,0 +1,5 @@
1# This must be interpreted as assignments
2a=1 b\
3=2 c=3
4echo Done:$?
5echo abc=$a$b$c
diff --git a/shell/ash_test/ash-misc/assignment4.right b/shell/ash_test/ash-misc/assignment4.right
new file mode 100644
index 000000000..31c896f62
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment4.right
@@ -0,0 +1 @@
Done:0
diff --git a/shell/ash_test/ash-misc/assignment4.tests b/shell/ash_test/ash-misc/assignment4.tests
new file mode 100755
index 000000000..6f46d0a33
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment4.tests
@@ -0,0 +1,3 @@
1# There was a bug where we misinterpreted assignments after 'do':
2for i in 1; do eval b=; done
3echo Done:$?
diff --git a/shell/ash_test/ash-misc/break1.right b/shell/ash_test/ash-misc/break1.right
new file mode 100644
index 000000000..04a4b1757
--- /dev/null
+++ b/shell/ash_test/ash-misc/break1.right
@@ -0,0 +1,2 @@
1A
2OK:0
diff --git a/shell/ash_test/ash-misc/break1.tests b/shell/ash_test/ash-misc/break1.tests
new file mode 100755
index 000000000..3a6b060d9
--- /dev/null
+++ b/shell/ash_test/ash-misc/break1.tests
@@ -0,0 +1,2 @@
1while true; do echo A; break; echo B; done
2echo OK:$?
diff --git a/shell/ash_test/ash-misc/break2.right b/shell/ash_test/ash-misc/break2.right
new file mode 100644
index 000000000..8a15cb95f
--- /dev/null
+++ b/shell/ash_test/ash-misc/break2.right
@@ -0,0 +1,3 @@
1A
2AA
3OK:0
diff --git a/shell/ash_test/ash-misc/break2.tests b/shell/ash_test/ash-misc/break2.tests
new file mode 100755
index 000000000..7da9faf34
--- /dev/null
+++ b/shell/ash_test/ash-misc/break2.tests
@@ -0,0 +1,6 @@
1while true; do
2 echo A
3 while true; do echo AA; break 2; echo BB; done
4 echo B
5done
6echo OK:$?
diff --git a/shell/ash_test/ash-misc/break3.right b/shell/ash_test/ash-misc/break3.right
new file mode 100644
index 000000000..04a4b1757
--- /dev/null
+++ b/shell/ash_test/ash-misc/break3.right
@@ -0,0 +1,2 @@
1A
2OK:0
diff --git a/shell/ash_test/ash-misc/break3.tests b/shell/ash_test/ash-misc/break3.tests
new file mode 100755
index 000000000..d138dcac5
--- /dev/null
+++ b/shell/ash_test/ash-misc/break3.tests
@@ -0,0 +1,2 @@
1v=break; while true; do echo A; $v; echo B; break; echo C; done
2echo OK:$?
diff --git a/shell/ash_test/ash-misc/break4.right b/shell/ash_test/ash-misc/break4.right
new file mode 100644
index 000000000..6f41c141d
--- /dev/null
+++ b/shell/ash_test/ash-misc/break4.right
@@ -0,0 +1,6 @@
1A
2AA
3TRUE
4A
5AA
6OK:0
diff --git a/shell/ash_test/ash-misc/break4.tests b/shell/ash_test/ash-misc/break4.tests
new file mode 100755
index 000000000..67da2889c
--- /dev/null
+++ b/shell/ash_test/ash-misc/break4.tests
@@ -0,0 +1,12 @@
1cond=true
2while $cond; do
3 echo A
4 if test "$cond" = true; then
5 cond='echo TRUE'
6 else
7 cond=false
8 fi
9 while true; do echo AA; continue 2; echo BB; done
10 echo B
11done
12echo OK:$?
diff --git a/shell/ash_test/ash-misc/break5.right b/shell/ash_test/ash-misc/break5.right
new file mode 100644
index 000000000..0b9df2a4f
--- /dev/null
+++ b/shell/ash_test/ash-misc/break5.right
@@ -0,0 +1,13 @@
1A
2B
30
4A:a
5B
6D
7A:b
8B
9D
10A:c
11B
12D
130
diff --git a/shell/ash_test/ash-misc/break5.tests b/shell/ash_test/ash-misc/break5.tests
new file mode 100755
index 000000000..273e040ec
--- /dev/null
+++ b/shell/ash_test/ash-misc/break5.tests
@@ -0,0 +1,4 @@
1while true; do echo A; { echo B; break; echo C; }; echo D; done
2echo $?
3for v in a b c; do echo A:$v; (echo B; break; echo C); echo D; done
4echo $?
diff --git a/shell/ash_test/ash-misc/builtin1.right b/shell/ash_test/ash-misc/builtin1.right
new file mode 100644
index 000000000..2e55ecb09
--- /dev/null
+++ b/shell/ash_test/ash-misc/builtin1.right
@@ -0,0 +1,2 @@
1VARIABLE=export
2OK:0
diff --git a/shell/ash_test/ash-misc/builtin1.tests b/shell/ash_test/ash-misc/builtin1.tests
new file mode 100755
index 000000000..1a2941faa
--- /dev/null
+++ b/shell/ash_test/ash-misc/builtin1.tests
@@ -0,0 +1,6 @@
1# builtins, unlike keywords like "while", can be constructed
2# with substitutions
3VARIABLE=export
4$VARIABLE VARIABLE
5env | grep ^VARIABLE
6echo OK:$?
diff --git a/shell/ash_test/ash-misc/case1.right b/shell/ash_test/ash-misc/case1.right
new file mode 100644
index 000000000..4afb2f51c
--- /dev/null
+++ b/shell/ash_test/ash-misc/case1.right
@@ -0,0 +1,22 @@
1OK_1
2OK_1
3OK_21
4OK_22
5OK_23
6OK_31
7OK_32
8OK_41
9OK_42
10OK_43
11OK_44
12OK_51
13OK_52
14OK_53
15OK_sub1
16OK_sub2
17OK_sub3
18OK_sub4
19OK_sub5
20OK_sub6
21OK_esac1
22Done
diff --git a/shell/ash_test/ash-misc/case1.tests b/shell/ash_test/ash-misc/case1.tests
new file mode 100755
index 000000000..d72b57f53
--- /dev/null
+++ b/shell/ash_test/ash-misc/case1.tests
@@ -0,0 +1,40 @@
1case w in a) echo SKIP;; w) echo OK_1;; w) echo WRONG;; esac
2
3case w in
4 a) echo SKIP;;
5 w)echo OK_1 ;;
6 w)
7 echo WRONG
8 ;;
9esac
10
11t=w
12case $t in a) echo SKIP;; w) echo OK_21;; w) echo WRONG;; esac;
13case "$t" in a) echo SKIP;; w) echo OK_22;; w) echo WRONG;; esac;
14case w in a) echo SKIP;; $t) echo OK_23;; "$t") echo WRONG;; esac;
15
16case '' in a) echo SKIP;; w) echo WRONG;; *) echo OK_31;; esac;
17case '' in a) echo SKIP;; '') echo OK_32;; *) echo WRONG;; esac;
18
19case `echo w` in a) echo SKIP;; w) echo OK_41;; w) echo WRONG;; esac;
20case "`echo w`" in a) echo SKIP;; w) echo OK_42;; w) echo WRONG;; esac;
21case `echo w w` in a) echo SKIP;; w) echo WRONG;; 'w w') echo OK_43;; esac;
22case `echo w w` in a) echo SKIP;; w) echo WRONG;; w*) echo OK_44;; esac;
23
24case w in `echo w`) echo OK_51;; `echo WRONG >&2`w) echo WRONG;; esac;
25case w in `echo OK_52 >&2`) echo SKIP;; `echo`w) echo OK_53;; esac;
26
27# parsing cases in subshells can easily get messy
28 case m in m) echo OK_sub1;; esac
29 case m in (m) echo OK_sub2;; esac
30(case m in m) echo OK_sub3;; esac)
31(case m in (m) echo OK_sub4;; esac)
32(
33 case m in m) echo OK_sub5;; esac
34)
35(
36 case m in (m) echo OK_sub6;; esac
37)
38(case esac in "esac") echo OK_esac1;; esac)
39
40echo Done
diff --git a/shell/ash_test/ash-misc/colon.right b/shell/ash_test/ash-misc/colon.right
new file mode 100644
index 000000000..2a87d0293
--- /dev/null
+++ b/shell/ash_test/ash-misc/colon.right
@@ -0,0 +1,2 @@
10
2OK: 0
diff --git a/shell/ash_test/ash-misc/colon.tests b/shell/ash_test/ash-misc/colon.tests
new file mode 100755
index 000000000..cb8ab5306
--- /dev/null
+++ b/shell/ash_test/ash-misc/colon.tests
@@ -0,0 +1,5 @@
1false
2:
3echo $?
4(while :; do exit; done)
5echo OK: $?
diff --git a/shell/ash_test/ash-misc/compound.right b/shell/ash_test/ash-misc/compound.right
new file mode 100644
index 000000000..757d42fe4
--- /dev/null
+++ b/shell/ash_test/ash-misc/compound.right
@@ -0,0 +1,14 @@
1new group
20
31
42
53
64
75
86
9new group
10new group
110
121
132
143
diff --git a/shell/ash_test/ash-misc/compound.tests b/shell/ash_test/ash-misc/compound.tests
new file mode 100755
index 000000000..a5e85c3d5
--- /dev/null
+++ b/shell/ash_test/ash-misc/compound.tests
@@ -0,0 +1,21 @@
1echo new group
2echo 0; { :; }
3echo 1; { : ;}
4echo 2; ({ :; })
5echo 3; ({ : ;})
6echo 4; ( : )
7echo 5; ( :; )
8echo 6; ( : ;)
9# not sure if POSIX requires these, but bash accepts them ...
10#echo 7; {( : )}
11#echo 8; {( :; )}
12#echo 9; {( : ;)}
13
14echo new group
15#echo 0; {(:);}
16
17echo new group
18echo 0; (:)
19echo 1; (:;)
20echo 2; (:);
21echo 3; (:;);
diff --git a/shell/ash_test/ash-misc/continue1.right b/shell/ash_test/ash-misc/continue1.right
new file mode 100644
index 000000000..c4a5565bc
--- /dev/null
+++ b/shell/ash_test/ash-misc/continue1.right
@@ -0,0 +1,8 @@
1A:a
2A:b
3A:c
4OK1
5A:a
6A:b
7A:c
8OK2
diff --git a/shell/ash_test/ash-misc/continue1.tests b/shell/ash_test/ash-misc/continue1.tests
new file mode 100755
index 000000000..72d356660
--- /dev/null
+++ b/shell/ash_test/ash-misc/continue1.tests
@@ -0,0 +1,4 @@
1for v in a b c; do echo A:$v; continue 666; done
2echo OK1
3for v in a b c; do echo A:$v; continue 666; done
4echo OK2
diff --git a/shell/ash_test/ash-misc/continue2.right b/shell/ash_test/ash-misc/continue2.right
new file mode 100644
index 000000000..49d3ebd3a
--- /dev/null
+++ b/shell/ash_test/ash-misc/continue2.right
@@ -0,0 +1 @@
Ok:1
diff --git a/shell/ash_test/ash-misc/continue2.tests b/shell/ash_test/ash-misc/continue2.tests
new file mode 100755
index 000000000..c2df07195
--- /dev/null
+++ b/shell/ash_test/ash-misc/continue2.tests
@@ -0,0 +1,3 @@
1e=''
2(while test $e && exit 1; true; do e=1; continue; done)
3echo Ok:$?
diff --git a/shell/ash_test/ash-misc/continue3.right b/shell/ash_test/ash-misc/continue3.right
new file mode 100644
index 000000000..aa47d0d46
--- /dev/null
+++ b/shell/ash_test/ash-misc/continue3.right
@@ -0,0 +1,2 @@
10
20
diff --git a/shell/ash_test/ash-misc/continue3.tests b/shell/ash_test/ash-misc/continue3.tests
new file mode 100755
index 000000000..0aff867cd
--- /dev/null
+++ b/shell/ash_test/ash-misc/continue3.tests
@@ -0,0 +1,3 @@
1# Test that "continue" does affect exitcode (sets to 0)
2e=''
3while echo $?; test $e && exit; true; do e=1; false; continue; done
diff --git a/shell/ash_test/ash-misc/empty_for.right b/shell/ash_test/ash-misc/empty_for.right
new file mode 100644
index 000000000..290d39b7e
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_for.right
@@ -0,0 +1 @@
OK: 0
diff --git a/shell/ash_test/ash-misc/empty_for.tests b/shell/ash_test/ash-misc/empty_for.tests
new file mode 100755
index 000000000..0cb52e849
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_for.tests
@@ -0,0 +1,3 @@
1false
2for a in; do echo "HELLO"; done
3echo OK: $?
diff --git a/shell/ash_test/ash-misc/empty_for2.right b/shell/ash_test/ash-misc/empty_for2.right
new file mode 100644
index 000000000..1acee9eb8
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_for2.right
@@ -0,0 +1,4 @@
1PARAM:abc
2PARAM:d e
3PARAM:123
4OK: 0
diff --git a/shell/ash_test/ash-misc/empty_for2.tests b/shell/ash_test/ash-misc/empty_for2.tests
new file mode 100755
index 000000000..2b12ec2c1
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_for2.tests
@@ -0,0 +1,6 @@
1if test $# = 0; then
2 exec "$THIS_SH" $0 abc "d e" 123
3fi
4false
5for v; do echo "PARAM:$v"; done
6echo OK: $?
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/ash_test/ash-misc/errexit1.right
index d86bac9de..d86bac9de 100644
--- a/shell/msh_test/msh-execution/many_continues.right
+++ b/shell/ash_test/ash-misc/errexit1.right
diff --git a/shell/ash_test/ash-misc/errexit1.tests b/shell/ash_test/ash-misc/errexit1.tests
new file mode 100755
index 000000000..7b4a15634
--- /dev/null
+++ b/shell/ash_test/ash-misc/errexit1.tests
@@ -0,0 +1,5 @@
1set -e
2(true)
3echo OK
4(false)
5echo FAIL
diff --git a/shell/ash_test/ash-misc/eval1.right b/shell/ash_test/ash-misc/eval1.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/ash_test/ash-misc/eval1.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/ash_test/ash-misc/eval1.tests b/shell/ash_test/ash-misc/eval1.tests
new file mode 100755
index 000000000..b78c6cc94
--- /dev/null
+++ b/shell/ash_test/ash-misc/eval1.tests
@@ -0,0 +1,4 @@
1# empty eval nevertheless sets $? = 0
2false
3eval
4echo Ok:$?
diff --git a/shell/ash_test/ash-misc/eval2.right b/shell/ash_test/ash-misc/eval2.right
new file mode 100644
index 000000000..a7ce6ccc7
--- /dev/null
+++ b/shell/ash_test/ash-misc/eval2.right
@@ -0,0 +1,3 @@
1Zero:0
2Zero:0
3Zero:0
diff --git a/shell/ash_test/ash-misc/eval2.tests b/shell/ash_test/ash-misc/eval2.tests
new file mode 100755
index 000000000..6bfb87aa7
--- /dev/null
+++ b/shell/ash_test/ash-misc/eval2.tests
@@ -0,0 +1,4 @@
1false; eval; echo Zero:$?
2false; eval ""; echo Zero:$?
3false; eval "
4"; echo Zero:$?
diff --git a/shell/ash_test/ash-misc/exit1.right b/shell/ash_test/ash-misc/exit1.right
new file mode 100644
index 000000000..dd2cfc279
--- /dev/null
+++ b/shell/ash_test/ash-misc/exit1.right
@@ -0,0 +1 @@
Once
diff --git a/shell/ash_test/ash-misc/exit1.tests b/shell/ash_test/ash-misc/exit1.tests
new file mode 100755
index 000000000..41e0d092d
--- /dev/null
+++ b/shell/ash_test/ash-misc/exit1.tests
@@ -0,0 +1,4 @@
1trap "echo Not shown" EXIT
2(exit) # must be silent
3trap "echo Once; exit" EXIT
4{ exit; }
diff --git a/shell/ash_test/ash-misc/exitcode1.right b/shell/ash_test/ash-misc/exitcode1.right
new file mode 100644
index 000000000..e5fefefda
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode1.right
@@ -0,0 +1,2 @@
1One:1
2Zero:0
diff --git a/shell/ash_test/ash-misc/exitcode1.tests b/shell/ash_test/ash-misc/exitcode1.tests
new file mode 100755
index 000000000..dc8619d8b
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode1.tests
@@ -0,0 +1,2 @@
1false || case a in a) echo One:$?;; esac
2echo Zero:$?
diff --git a/shell/ash_test/ash-misc/exitcode_EACCES.right b/shell/ash_test/ash-misc/exitcode_EACCES.right
new file mode 100644
index 000000000..0a332f56c
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_EACCES.right
@@ -0,0 +1,2 @@
1exitcode_EACCES.tests: line 1: ./: Permission denied
2126
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.tests b/shell/ash_test/ash-misc/exitcode_EACCES.tests
index 26b5c6116..26b5c6116 100755
--- a/shell/msh_test/msh-execution/exitcode_EACCES.tests
+++ b/shell/ash_test/ash-misc/exitcode_EACCES.tests
diff --git a/shell/ash_test/ash-misc/exitcode_ENOENT.right b/shell/ash_test/ash-misc/exitcode_ENOENT.right
new file mode 100644
index 000000000..45b27b32e
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_ENOENT.right
@@ -0,0 +1,2 @@
1exitcode_ENOENT.tests: line 1: ./does_not_exist_for_sure: not found
2127
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.tests b/shell/ash_test/ash-misc/exitcode_ENOENT.tests
index 7f1b88a99..7f1b88a99 100755
--- a/shell/msh_test/msh-execution/exitcode_ENOENT.tests
+++ b/shell/ash_test/ash-misc/exitcode_ENOENT.tests
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.right b/shell/ash_test/ash-misc/for_with_bslashes.right
new file mode 100644
index 000000000..02d96692c
--- /dev/null
+++ b/shell/ash_test/ash-misc/for_with_bslashes.right
@@ -0,0 +1,8 @@
1a
2b\c
3b\\c
4b"c
5b'c
6b$c
7b`true`c
8Zero:0
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.tests b/shell/ash_test/ash-misc/for_with_bslashes.tests
new file mode 100755
index 000000000..363f3d85b
--- /dev/null
+++ b/shell/ash_test/ash-misc/for_with_bslashes.tests
@@ -0,0 +1,10 @@
1# UNFIXED BUG.
2# commented-out words contain ^C character.
3# It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it.
4# When it is fixed, update this test.
5
6for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c'
7do
8 echo $a
9done
10echo Zero:$?
diff --git a/shell/ash_test/ash-misc/for_with_keywords.right b/shell/ash_test/ash-misc/for_with_keywords.right
new file mode 100644
index 000000000..eb04e9af9
--- /dev/null
+++ b/shell/ash_test/ash-misc/for_with_keywords.right
@@ -0,0 +1,4 @@
1do
2done
3then
4OK: 0
diff --git a/shell/ash_test/ash-misc/for_with_keywords.tests b/shell/ash_test/ash-misc/for_with_keywords.tests
new file mode 100755
index 000000000..a8b8e4264
--- /dev/null
+++ b/shell/ash_test/ash-misc/for_with_keywords.tests
@@ -0,0 +1,2 @@
1for if in do done then; do echo $if; done
2echo OK: $?
diff --git a/shell/ash_test/ash-misc/func6.right b/shell/ash_test/ash-misc/func6.right
new file mode 100644
index 000000000..0ebd8e5a3
--- /dev/null
+++ b/shell/ash_test/ash-misc/func6.right
@@ -0,0 +1,2 @@
1Two:2
2Two:2
diff --git a/shell/ash_test/ash-misc/func6.tests b/shell/ash_test/ash-misc/func6.tests
new file mode 100755
index 000000000..029c3e85e
--- /dev/null
+++ b/shell/ash_test/ash-misc/func6.tests
@@ -0,0 +1,11 @@
1f1() {
2 while return 2; do :; done
3}
4f1
5echo Two:$?
6
7f2() {
8 while :; do return 2; done
9}
10f2
11echo Two:$?
diff --git a/shell/ash_test/ash-misc/if_false_exitcode.right b/shell/ash_test/ash-misc/if_false_exitcode.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/ash_test/ash-misc/if_false_exitcode.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/ash_test/ash-misc/if_false_exitcode.tests b/shell/ash_test/ash-misc/if_false_exitcode.tests
new file mode 100755
index 000000000..01b36b100
--- /dev/null
+++ b/shell/ash_test/ash-misc/if_false_exitcode.tests
@@ -0,0 +1,2 @@
1if false; then echo Bad; fi
2echo Ok:$?
diff --git a/shell/ash_test/ash-misc/nommu1.right b/shell/ash_test/ash-misc/nommu1.right
new file mode 100644
index 000000000..d206a854b
--- /dev/null
+++ b/shell/ash_test/ash-misc/nommu1.right
@@ -0,0 +1,7 @@
1Ok
2Ok
3Ok
4Ok
5Ok
6Ok
7Done
diff --git a/shell/ash_test/ash-misc/nommu1.tests b/shell/ash_test/ash-misc/nommu1.tests
new file mode 100755
index 000000000..e14ada5f9
--- /dev/null
+++ b/shell/ash_test/ash-misc/nommu1.tests
@@ -0,0 +1,12 @@
1(echo \
2Ok)
3( (echo \
4Ok) )
5( ( (echo \
6Ok) ) )
7
8(echo \Ok)
9( (echo \Ok) )
10( ( (echo \Ok) ) )
11
12echo Done
diff --git a/shell/ash_test/ash-misc/nommu2.right b/shell/ash_test/ash-misc/nommu2.right
new file mode 100644
index 000000000..fb8ba8b5c
--- /dev/null
+++ b/shell/ash_test/ash-misc/nommu2.right
@@ -0,0 +1,5 @@
1Ok
2Ok
3Ok
4Ok
5Done
diff --git a/shell/ash_test/ash-misc/nommu2.tests b/shell/ash_test/ash-misc/nommu2.tests
new file mode 100755
index 000000000..61ed5ce5b
--- /dev/null
+++ b/shell/ash_test/ash-misc/nommu2.tests
@@ -0,0 +1,5 @@
1echo Not shown | if true; then echo $(echo Ok); fi
2echo Not shown | if true; then echo `echo Ok`; fi
3echo Not shown | ( if true; then echo $(echo Ok); fi )
4echo Not shown | ( if true; then echo `echo Ok`; fi )
5echo Done
diff --git a/shell/ash_test/ash-misc/nommu3.right b/shell/ash_test/ash-misc/nommu3.right
new file mode 100644
index 000000000..da1534bef
--- /dev/null
+++ b/shell/ash_test/ash-misc/nommu3.right
@@ -0,0 +1,2 @@
1Ok
20
diff --git a/shell/ash_test/ash-misc/nommu3.tests b/shell/ash_test/ash-misc/nommu3.tests
new file mode 100755
index 000000000..ac82a6a11
--- /dev/null
+++ b/shell/ash_test/ash-misc/nommu3.tests
@@ -0,0 +1,15 @@
1#!/bin/sh
2
3func()
4{
5 while read p; do echo "$p"; done
6}
7
8pipe_to_func()
9{
10 # We had a NOMMU bug which caused "echo Ok |" part to be lost
11 echo Ok | func
12}
13
14pipe_to_func | cat
15echo $?
diff --git a/shell/ash_test/ash-misc/opts1.right b/shell/ash_test/ash-misc/opts1.right
new file mode 100644
index 000000000..4da75737d
--- /dev/null
+++ b/shell/ash_test/ash-misc/opts1.right
@@ -0,0 +1,2 @@
1Param1: >-10qwertyuiop<
2Done
diff --git a/shell/ash_test/ash-misc/opts1.tests b/shell/ash_test/ash-misc/opts1.tests
new file mode 100755
index 000000000..45a23d66b
--- /dev/null
+++ b/shell/ash_test/ash-misc/opts1.tests
@@ -0,0 +1,5 @@
1if test $# = 0; then
2 exec "$THIS_SH" $0 -10qwertyuiop
3fi
4echo "Param1: >$1<"
5echo Done
diff --git a/shell/ash_test/ash-misc/pid.right b/shell/ash_test/ash-misc/pid.right
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/shell/ash_test/ash-misc/pid.right
@@ -0,0 +1 @@
0
diff --git a/shell/ash_test/ash-misc/pid.tests b/shell/ash_test/ash-misc/pid.tests
new file mode 100755
index 000000000..eaeaa717b
--- /dev/null
+++ b/shell/ash_test/ash-misc/pid.tests
@@ -0,0 +1 @@
test `(echo $$)` = `echo $$`; echo $?
diff --git a/shell/ash_test/ash-misc/pipefail.right b/shell/ash_test/ash-misc/pipefail.right
new file mode 100644
index 000000000..5845d8939
--- /dev/null
+++ b/shell/ash_test/ash-misc/pipefail.right
@@ -0,0 +1,40 @@
1Default:
2true | true:
30
41
5true | false:
61
70
8false | true:
90
101
11exit 2 | exit 3 | exit 4:
124
130
14Pipefail on:
15true | true:
160
171
18true | false:
191
200
21false | true:
221
230
24exit 2 | exit 3 | exit 4:
254
260
27Pipefail off:
28true | true:
290
301
31true | false:
321
330
34false | true:
350
361
37exit 2 | exit 3 | exit 4:
384
390
40Done
diff --git a/shell/ash_test/ash-misc/pipefail.tests b/shell/ash_test/ash-misc/pipefail.tests
new file mode 100755
index 000000000..9df841861
--- /dev/null
+++ b/shell/ash_test/ash-misc/pipefail.tests
@@ -0,0 +1,45 @@
1echo Default:
2echo "true | true:"
3 true | true; echo $?
4! true | true; echo $?
5echo "true | false:"
6 true | false; echo $?
7! true | false; echo $?
8echo "false | true:"
9 false | true; echo $?
10! false | true; echo $?
11echo "exit 2 | exit 3 | exit 4:"
12 exit 2 | exit 3 | exit 4; echo $?
13! exit 2 | exit 3 | exit 4; echo $?
14
15echo Pipefail on:
16set -o pipefail
17echo "true | true:"
18 true | true; echo $?
19! true | true; echo $?
20echo "true | false:"
21 true | false; echo $?
22! true | false; echo $?
23echo "false | true:"
24 false | true; echo $?
25! false | true; echo $?
26echo "exit 2 | exit 3 | exit 4:"
27 exit 2 | exit 3 | exit 4; echo $?
28! exit 2 | exit 3 | exit 4; echo $?
29
30echo Pipefail off:
31set +o pipefail
32echo "true | true:"
33 true | true; echo $?
34! true | true; echo $?
35echo "true | false:"
36 true | false; echo $?
37! true | false; echo $?
38echo "false | true:"
39 false | true; echo $?
40! false | true; echo $?
41echo "exit 2 | exit 3 | exit 4:"
42 exit 2 | exit 3 | exit 4; echo $?
43! exit 2 | exit 3 | exit 4; echo $?
44
45echo Done
diff --git a/shell/msh_test/msh-bugs/read.right b/shell/ash_test/ash-misc/read.right
index 0e50e2a23..0e50e2a23 100644
--- a/shell/msh_test/msh-bugs/read.right
+++ b/shell/ash_test/ash-misc/read.right
diff --git a/shell/msh_test/msh-bugs/read.tests b/shell/ash_test/ash-misc/read.tests
index ff1acbde1..ff1acbde1 100755
--- a/shell/msh_test/msh-bugs/read.tests
+++ b/shell/ash_test/ash-misc/read.tests
diff --git a/shell/ash_test/ash-misc/return1.right b/shell/ash_test/ash-misc/return1.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/ash_test/ash-misc/return1.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/ash_test/ash-misc/return1.tests b/shell/ash_test/ash-misc/return1.tests
new file mode 100755
index 000000000..eeb92ef3f
--- /dev/null
+++ b/shell/ash_test/ash-misc/return1.tests
@@ -0,0 +1,4 @@
1echo "true && return; echo Should not be printed" >return_sourced
2. ./return_sourced
3rm return_sourced
4echo Ok:$?
diff --git a/shell/msh_test/msh-bugs/shift.right b/shell/ash_test/ash-misc/shift.right
index d281e358c..d281e358c 100644
--- a/shell/msh_test/msh-bugs/shift.right
+++ b/shell/ash_test/ash-misc/shift.right
diff --git a/shell/msh_test/msh-bugs/shift.tests b/shell/ash_test/ash-misc/shift.tests
index 53ef249f2..53ef249f2 100755
--- a/shell/msh_test/msh-bugs/shift.tests
+++ b/shell/ash_test/ash-misc/shift.tests
diff --git a/shell/ash_test/ash-misc/sigint1.right b/shell/ash_test/ash-misc/sigint1.right
new file mode 100644
index 000000000..a9094b056
--- /dev/null
+++ b/shell/ash_test/ash-misc/sigint1.right
@@ -0,0 +1 @@
Sending SIGINT to main shell PID
diff --git a/shell/ash_test/ash-misc/sigint1.tests b/shell/ash_test/ash-misc/sigint1.tests
new file mode 100755
index 000000000..3d483d32a
--- /dev/null
+++ b/shell/ash_test/ash-misc/sigint1.tests
@@ -0,0 +1,41 @@
1# What should happen if non-interactive shell gets SIGINT?
2
3(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
4
5# We create a child which exits with 0 even on SIGINT
6# (The complex command is necessary only if SIGINT is generated by ^C,
7# in this testcase even bare "sleep 2" would do because
8# in the testcase we don't send SIGINT *to the child*...)
9$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
10
11# In one second, we (main shell) get SIGINT here.
12# The question is whether we should, or should not, exit.
13
14# bash will not stop here. It will execute next command(s).
15
16# The rationale for this is described here:
17# http://www.cons.org/cracauer/sigint.html
18#
19# Basically, bash will not exit on SIGINT immediately if it waits
20# for a child. It will wait for the child to exit.
21# If child exits NOT by dying on SIGINT, then bash will not exit.
22#
23# The idea is that the following script:
24# | emacs file.txt
25# | more cmds
26# User may use ^C to interrupt editor's ops like search. But then
27# emacs exits normally. User expects that script doesn't stop.
28#
29# This is a nice idea, but detecting "did process really exit
30# with SIGINT?" is racy. Consider:
31# | bash -c 'while true; do /bin/true; done'
32# When ^C is pressed while bash waits for /bin/true to exit,
33# it may happen that /bin/true exits with exitcode 0 before
34# ^C is delivered to it as SIGINT. bash will see SIGINT, then
35# it will see that child exited with 0, and bash will NOT EXIT.
36
37# Therefore we do not implement bash behavior.
38# I'd say that emacs need to put itself into a separate pgrp
39# to isolate shell from getting stray SIGINTs from ^C.
40
41echo Next command after SIGINT was executed
diff --git a/shell/ash_test/ash-misc/source3.right b/shell/ash_test/ash-misc/source3.right
new file mode 100644
index 000000000..bdf9001a5
--- /dev/null
+++ b/shell/ash_test/ash-misc/source3.right
@@ -0,0 +1,2 @@
1Zero:0
2Zero:0
diff --git a/shell/ash_test/ash-misc/source3.tests b/shell/ash_test/ash-misc/source3.tests
new file mode 100755
index 000000000..1abf156ed
--- /dev/null
+++ b/shell/ash_test/ash-misc/source3.tests
@@ -0,0 +1,6 @@
1# Test both empty file, and one-empty-line file
2echo >sourced1
3true >sourced2
4false; . ./sourced1; echo Zero:$?
5false; . ./sourced2; echo Zero:$?
6rm sourced1 sourced2
diff --git a/shell/ash_test/ash-misc/source5.right b/shell/ash_test/ash-misc/source5.right
new file mode 100644
index 000000000..0587bad67
--- /dev/null
+++ b/shell/ash_test/ash-misc/source5.right
@@ -0,0 +1,4 @@
10:arg0 1:arg1 2:arg2
2Ok1:0
30:arg0 1:q 2:w
4Ok2:0
diff --git a/shell/ash_test/ash-misc/source5.tests b/shell/ash_test/ash-misc/source5.tests
new file mode 100755
index 000000000..40b6b83cd
--- /dev/null
+++ b/shell/ash_test/ash-misc/source5.tests
@@ -0,0 +1,8 @@
1echo 'echo "0:$0 1:$1 2:$2"' >sourced1
2set -- 1 2 3
3"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
4echo Ok1:$?
5"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
6echo Ok2:$?
7
8rm sourced1
diff --git a/shell/msh_test/msh-misc/tick.right b/shell/ash_test/ash-misc/unicode1.right
index 6ed281c75..d3bbbf697 100644
--- a/shell/msh_test/msh-misc/tick.right
+++ b/shell/ash_test/ash-misc/unicode1.right
@@ -1,2 +1,3 @@
11 11
21 21
3Ok
diff --git a/shell/ash_test/ash-misc/unicode1.tests b/shell/ash_test/ash-misc/unicode1.tests
new file mode 100755
index 000000000..8788ba910
--- /dev/null
+++ b/shell/ash_test/ash-misc/unicode1.tests
@@ -0,0 +1,13 @@
1LANG=en_US.UTF-8
2
3# A combining character U+300
4a=`printf "\xcc\x80"`
5# Should print 1
6echo ${#a}
7
8# A Japanese katakana charachter U+30a3
9a=`printf "\xe3\x82\xa3"`
10# Should print 1
11echo ${#a}
12
13echo Ok
diff --git a/shell/ash_test/ash-misc/until1.right b/shell/ash_test/ash-misc/until1.right
new file mode 100644
index 000000000..be2daad95
--- /dev/null
+++ b/shell/ash_test/ash-misc/until1.right
@@ -0,0 +1,3 @@
11
21
3Ok:0
diff --git a/shell/ash_test/ash-misc/until1.tests b/shell/ash_test/ash-misc/until1.tests
new file mode 100755
index 000000000..10ab28381
--- /dev/null
+++ b/shell/ash_test/ash-misc/until1.tests
@@ -0,0 +1,11 @@
1x=1
2until test "$x" = 4; do echo $x; x=4; done
3
4# We had a bug in multi-line form
5x=1
6until test "$x" = 4; do
7 echo $x
8 x=4
9done
10
11echo Ok:$?
diff --git a/shell/ash_test/ash-misc/while1.right b/shell/ash_test/ash-misc/while1.right
new file mode 100644
index 000000000..7c4d7beb0
--- /dev/null
+++ b/shell/ash_test/ash-misc/while1.right
@@ -0,0 +1 @@
OK:0
diff --git a/shell/ash_test/ash-misc/while1.tests b/shell/ash_test/ash-misc/while1.tests
new file mode 100755
index 000000000..11e201e6a
--- /dev/null
+++ b/shell/ash_test/ash-misc/while1.tests
@@ -0,0 +1,2 @@
1while false; do echo NOT SHOWN; done
2echo OK:$?
diff --git a/shell/ash_test/ash-misc/while2.right b/shell/ash_test/ash-misc/while2.right
new file mode 100644
index 000000000..07207cc84
--- /dev/null
+++ b/shell/ash_test/ash-misc/while2.right
@@ -0,0 +1,2 @@
1Hello
2OK:0
diff --git a/shell/ash_test/ash-misc/while2.tests b/shell/ash_test/ash-misc/while2.tests
new file mode 100755
index 000000000..2247adc74
--- /dev/null
+++ b/shell/ash_test/ash-misc/while2.tests
@@ -0,0 +1,2 @@
1while echo Hello; false; do echo NOT SHOWN; done
2echo OK:$?
diff --git a/shell/ash_test/ash-misc/while4.right b/shell/ash_test/ash-misc/while4.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/ash_test/ash-misc/while4.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/ash_test/ash-misc/while4.tests b/shell/ash_test/ash-misc/while4.tests
new file mode 100755
index 000000000..ba80e603a
--- /dev/null
+++ b/shell/ash_test/ash-misc/while4.tests
@@ -0,0 +1,6 @@
1false
2while false && echo Not reached; do
3 echo BUG
4 break
5done
6echo Ok:$?
diff --git a/shell/ash_test/ash-misc/while_in_subshell.right b/shell/ash_test/ash-misc/while_in_subshell.right
new file mode 100644
index 000000000..290d39b7e
--- /dev/null
+++ b/shell/ash_test/ash-misc/while_in_subshell.right
@@ -0,0 +1 @@
OK: 0
diff --git a/shell/ash_test/ash-misc/while_in_subshell.tests b/shell/ash_test/ash-misc/while_in_subshell.tests
new file mode 100755
index 000000000..def8e092b
--- /dev/null
+++ b/shell/ash_test/ash-misc/while_in_subshell.tests
@@ -0,0 +1,2 @@
1(while true; do exit; done)
2echo OK: $?
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
index 3fa2f186d..74dca1cc0 100755
--- a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
+++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
@@ -1,5 +1,5 @@
1# The bug here was triggered by: 1# The bug here was triggered by:
2# * performin pathname expansion because we see [ 2# * performing pathname expansion because we see [
3# * replace operator did not escape \ in replace string 3# * replace operator did not escape \ in replace string
4 4
5IP=192.168.0.1 5IP=192.168.0.1
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash2.right b/shell/ash_test/ash-quoting/dollar_squote_bash2.right
new file mode 100644
index 000000000..f7a1731dd
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_squote_bash2.right
@@ -0,0 +1,6 @@
1strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
2strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
380:\€
481:\
582:\‚
6Done:0
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash2.tests b/shell/ash_test/ash-quoting/dollar_squote_bash2.tests
new file mode 100755
index 000000000..449772813
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_squote_bash2.tests
@@ -0,0 +1,10 @@
1# Embedded NULs
2echo $'str\x00'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
3echo $'str\000'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
4
5# The chars after '\' are hex 0x80,81,82...
6echo 80:$'\€'
7echo 81:$'\'
8echo 82:$'\‚'
9
10echo Done:$?
diff --git a/shell/ash_test/ash-read/read_r.tests b/shell/ash_test/ash-read/read_r.tests
index 2c4cc6106..1f0a18afc 100755
--- a/shell/ash_test/ash-read/read_r.tests
+++ b/shell/ash_test/ash-read/read_r.tests
@@ -1,2 +1,4 @@
1echo -e 'test\\\nbest' | (read reply; echo "$reply") 1echo 'test\
2echo -e 'test\\\nbest' | (read -r reply; echo "$reply") 2best' | (read reply; echo "$reply")
3echo 'test\
4best' | (read -r reply; echo "$reply")
diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/ash_test/ash-redir/redir1.right
index 15515d1af..15515d1af 100644
--- a/shell/hush_test/hush-misc/redir1.right
+++ b/shell/ash_test/ash-redir/redir1.right
diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/ash_test/ash-redir/redir1.tests
index ef2fbfb77..ef2fbfb77 100755
--- a/shell/hush_test/hush-misc/redir1.tests
+++ b/shell/ash_test/ash-redir/redir1.tests
diff --git a/shell/ash_test/ash-redir/redir7.tests b/shell/ash_test/ash-redir/redir7.tests
index ca3979a81..e873a4622 100755
--- a/shell/ash_test/ash-redir/redir7.tests
+++ b/shell/ash_test/ash-redir/redir7.tests
@@ -4,9 +4,9 @@
4# was out of sync for redirect filenames. 4# was out of sync for redirect filenames.
5 5
6>unicode.sh 6>unicode.sh
7echo -e 'echo Ok >uni\x81code' >>unicode.sh 7printf 'echo Ok >uni\x81code\n' >>unicode.sh
8echo -e 'cat uni\x81code' >>unicode.sh 8printf 'cat uni\x81code\n' >>unicode.sh
9echo -e 'cat uni?code' >>unicode.sh 9printf 'cat uni?code\n' >>unicode.sh
10. ./unicode.sh 10. ./unicode.sh
11rm uni*code* 11rm uni*code*
12echo Done 12echo Done
diff --git a/shell/ash_test/ash-redir/redir8.tests b/shell/ash_test/ash-redir/redir8.tests
index 8cb42c0d3..2bd386749 100755
--- a/shell/ash_test/ash-redir/redir8.tests
+++ b/shell/ash_test/ash-redir/redir8.tests
@@ -6,10 +6,10 @@
6# Subcase when redirect filename is specified in a variable. 6# Subcase when redirect filename is specified in a variable.
7 7
8>unicode.sh 8>unicode.sh
9echo -e 'v=uni\x81code' >>unicode.sh 9printf 'v=uni\x81code\n' >>unicode.sh
10echo -e 'echo Ok >"$v"' >>unicode.sh 10printf 'echo Ok >"$v"\n' >>unicode.sh
11echo -e 'cat uni\x81code' >>unicode.sh 11printf 'cat uni\x81code\n' >>unicode.sh
12echo -e 'cat uni?code' >>unicode.sh 12printf 'cat uni?code\n' >>unicode.sh
13. ./unicode.sh 13. ./unicode.sh
14rm uni*code* 14rm uni*code*
15echo Done 15echo Done
diff --git a/shell/hush_test/hush-misc/redir2.right b/shell/ash_test/ash-redir/redir_escapednum.right
index 7326d9603..7326d9603 100644
--- a/shell/hush_test/hush-misc/redir2.right
+++ b/shell/ash_test/ash-redir/redir_escapednum.right
diff --git a/shell/hush_test/hush-misc/redir2.tests b/shell/ash_test/ash-redir/redir_escapednum.tests
index 81983cae2..81983cae2 100755
--- a/shell/hush_test/hush-misc/redir2.tests
+++ b/shell/ash_test/ash-redir/redir_escapednum.tests
diff --git a/shell/hush_test/hush-misc/redir4.right b/shell/ash_test/ash-redir/redir_expand.right
index ead25f603..ead25f603 100644
--- a/shell/hush_test/hush-misc/redir4.right
+++ b/shell/ash_test/ash-redir/redir_expand.right
diff --git a/shell/hush_test/hush-misc/redir4.tests b/shell/ash_test/ash-redir/redir_expand.tests
index c50b8cedf..c50b8cedf 100755
--- a/shell/hush_test/hush-misc/redir4.tests
+++ b/shell/ash_test/ash-redir/redir_expand.tests
diff --git a/shell/hush_test/hush-misc/redir6.right b/shell/ash_test/ash-redir/redir_multi.right
index a97c4bdf1..a97c4bdf1 100644
--- a/shell/hush_test/hush-misc/redir6.right
+++ b/shell/ash_test/ash-redir/redir_multi.right
diff --git a/shell/hush_test/hush-misc/redir6.tests b/shell/ash_test/ash-redir/redir_multi.tests
index c639ebb2d..c639ebb2d 100755
--- a/shell/hush_test/hush-misc/redir6.tests
+++ b/shell/ash_test/ash-redir/redir_multi.tests
diff --git a/shell/ash_test/ash-redir/redir_script.right b/shell/ash_test/ash-redir/redir_script.right
new file mode 100644
index 000000000..6694ed334
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_script.right
@@ -0,0 +1 @@
Ok: script fd is not closed
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests
new file mode 100755
index 000000000..ccc497d7b
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_script.tests
@@ -0,0 +1,29 @@
1# Builds a " 3>&- 4>&-" string.
2# Note: one of these fds is a directory opened to /proc/self/fd
3# for globbing. It is unwanted, but I don't know how to filter it out.
4find_fds() {
5 fds=""
6 for f in /proc/self/fd/*; do
7 test "$f" = "/proc/self/fd/0" && continue
8 test "$f" = "/proc/self/fd/1" && continue
9 test "$f" = "/proc/self/fd/2" && continue
10 fds="$fds ${f##*/}>&-"
11 done
12}
13
14find_fds
15fds1="$fds"
16
17# One of the fds is open to the script body
18# Close it while executing something.
19eval "find_fds $fds"
20
21# Shell should not lose that fd. Did it?
22find_fds
23test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; }
24
25echo "Bug: script fd is closed"
26echo "fds1:$fds1"
27echo "fds2:$fds"
28exit 1
29
diff --git a/shell/hush_test/hush-parsing/redir_space.right b/shell/ash_test/ash-redir/redir_space.right
index 084295204..084295204 100644
--- a/shell/hush_test/hush-parsing/redir_space.right
+++ b/shell/ash_test/ash-redir/redir_space.right
diff --git a/shell/hush_test/hush-parsing/redir_space.tests b/shell/ash_test/ash-redir/redir_space.tests
index c0b543098..c0b543098 100755
--- a/shell/hush_test/hush-parsing/redir_space.tests
+++ b/shell/ash_test/ash-redir/redir_space.tests
diff --git a/shell/ash_test/ash-signals/continue_and_trap1.right b/shell/ash_test/ash-signals/continue_and_trap1.right
new file mode 100644
index 000000000..d2dd0af72
--- /dev/null
+++ b/shell/ash_test/ash-signals/continue_and_trap1.right
@@ -0,0 +1 @@
Exiting
diff --git a/shell/ash_test/ash-signals/continue_and_trap1.tests b/shell/ash_test/ash-signals/continue_and_trap1.tests
new file mode 100755
index 000000000..2a5c147b1
--- /dev/null
+++ b/shell/ash_test/ash-signals/continue_and_trap1.tests
@@ -0,0 +1,7 @@
1trap "echo Exiting; exit" INT
2
3(sleep 1; kill -s INT $$) &
4
5while continue; do
6 continue;
7done
diff --git a/shell/ash_test/ash-signals/return_in_trap1.right b/shell/ash_test/ash-signals/return_in_trap1.right
new file mode 100644
index 000000000..a6e637885
--- /dev/null
+++ b/shell/ash_test/ash-signals/return_in_trap1.right
@@ -0,0 +1,4 @@
1a:2
2b:0
3Trap
4d:3
diff --git a/shell/ash_test/ash-signals/return_in_trap1.tests b/shell/ash_test/ash-signals/return_in_trap1.tests
new file mode 100755
index 000000000..f2498024f
--- /dev/null
+++ b/shell/ash_test/ash-signals/return_in_trap1.tests
@@ -0,0 +1,18 @@
1a() {
2 (exit 2)
3 echo a:$?
4 (kill -s USR1 $$; echo b:$?; exit 3)
5 echo c:$? # does not execute
6 (exit 4)
7}
8
9trap "echo Trap; return" USR1
10a
11
12echo d:$?
13# It's debatable what is the correct value above.
14# Does 'return' in trap sees $? == 2 or $? == 3?
15# IOW: after (kill..), does shell first wait for its completion
16# and sets $?, then checks pending signals and runs a trap handler,
17# or does it first checks pending signals and runs handler?
18# hush does the former, and prints 3.
diff --git a/shell/hush_test/hush-trap/save-ret.right b/shell/ash_test/ash-signals/save-ret.right
index a3e12ce5e..a3e12ce5e 100644
--- a/shell/hush_test/hush-trap/save-ret.right
+++ b/shell/ash_test/ash-signals/save-ret.right
diff --git a/shell/hush_test/hush-trap/save-ret.tests b/shell/ash_test/ash-signals/save-ret.tests
index 0786b6d96..0786b6d96 100755
--- a/shell/hush_test/hush-trap/save-ret.tests
+++ b/shell/ash_test/ash-signals/save-ret.tests
diff --git a/shell/ash_test/ash-signals/signal1.tests b/shell/ash_test/ash-signals/signal1.tests
index 28bfc6a46..61943467a 100755
--- a/shell/ash_test/ash-signals/signal1.tests
+++ b/shell/ash_test/ash-signals/signal1.tests
@@ -19,7 +19,7 @@ while $sleeping; do
19 if wait %%; then 19 if wait %%; then
20 echo "sleep completed" 20 echo "sleep completed"
21 sleeping=false 21 sleeping=false
22 elif [ $? == 127 ]; then 22 elif [ $? = 127 ]; then
23 echo "BUG: no processes to wait for?!" 23 echo "BUG: no processes to wait for?!"
24 sleeping=false 24 sleeping=false
25 else 25 else
diff --git a/shell/ash_test/ash-signals/sigquit_exec.right b/shell/ash_test/ash-signals/sigquit_exec.right
new file mode 100644
index 000000000..a8041929a
--- /dev/null
+++ b/shell/ash_test/ash-signals/sigquit_exec.right
@@ -0,0 +1,2 @@
1SigIgn: 0000000000000000
2SigIgn: 0000000000000000
diff --git a/shell/ash_test/ash-signals/sigquit_exec.tests b/shell/ash_test/ash-signals/sigquit_exec.tests
new file mode 100755
index 000000000..24bda6921
--- /dev/null
+++ b/shell/ash_test/ash-signals/sigquit_exec.tests
@@ -0,0 +1,4 @@
1# Should show no masked signals in both cases.
2# We had a bug where SIGQUIT was masked on exec.
3grep SigIgn: /proc/self/status
4exec grep SigIgn: /proc/self/status
diff --git a/shell/ash_test/ash-vars/empty.right b/shell/ash_test/ash-vars/empty.right
new file mode 100644
index 000000000..2cb3c70f2
--- /dev/null
+++ b/shell/ash_test/ash-vars/empty.right
@@ -0,0 +1,3 @@
1a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
2a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
3a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
diff --git a/shell/ash_test/ash-vars/empty.tests b/shell/ash_test/ash-vars/empty.tests
new file mode 100755
index 000000000..a9c247e31
--- /dev/null
+++ b/shell/ash_test/ash-vars/empty.tests
@@ -0,0 +1,5 @@
1e=
2
3echo a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
4echo a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F
5echo $e a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F
diff --git a/shell/ash_test/ash-vars/glob_and_vars.right b/shell/ash_test/ash-vars/glob_and_vars.right
new file mode 100644
index 000000000..3ac7ec5ff
--- /dev/null
+++ b/shell/ash_test/ash-vars/glob_and_vars.right
@@ -0,0 +1 @@
./glob_and_vars.right ./glob_and_vars.tests
diff --git a/shell/ash_test/ash-vars/glob_and_vars.tests b/shell/ash_test/ash-vars/glob_and_vars.tests
new file mode 100755
index 000000000..482cf9d8a
--- /dev/null
+++ b/shell/ash_test/ash-vars/glob_and_vars.tests
@@ -0,0 +1,2 @@
1v=.
2echo $v/glob_and_vars.[tr]*
diff --git a/shell/ash_test/ash-vars/param_expand_len.right b/shell/ash_test/ash-vars/param_expand_len.right
new file mode 100644
index 000000000..96e8cb59b
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_len.right
@@ -0,0 +1,9 @@
10
20
31
4Make sure len parsing doesnt break arg count
50 0
64 4
7Testing len op
84 3 2 1 0 0
90 3 0
diff --git a/shell/ash_test/ash-vars/param_expand_len.tests b/shell/ash_test/ash-vars/param_expand_len.tests
new file mode 100755
index 000000000..fe20a45e9
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_len.tests
@@ -0,0 +1,17 @@
1"$THIS_SH" -c 'echo $#'
2"$THIS_SH" -c 'echo $#' arg0
3"$THIS_SH" -c 'echo $#' arg0 arg1
4
5echo Make sure len parsing doesnt break arg count
6set --
7echo $# ${#}
8set -- aaaa bbb cc d
9echo $# ${#}
10
11echo Testing len op
12echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6}
13
14unset e
15f=abc
16g=
17echo ${#e} ${#f} ${#g}
diff --git a/shell/ash_test/ash-vars/param_glob.right b/shell/ash_test/ash-vars/param_glob.right
new file mode 100644
index 000000000..bdee8fe0f
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_glob.right
@@ -0,0 +1,4 @@
1param_glob.tests
2param_glob.tests
3param_glob.t*
4param_glob.t*
diff --git a/shell/ash_test/ash-vars/param_glob.tests b/shell/ash_test/ash-vars/param_glob.tests
new file mode 100755
index 000000000..4d74fee02
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_glob.tests
@@ -0,0 +1,9 @@
1if test $# = 0; then
2 exec "$THIS_SH" "$0" 'param_glob.t*'
3 echo NOT SHOWN
4 exit
5fi
6echo $*
7echo $@
8echo "$*"
9echo "$@"
diff --git a/shell/ash_test/ash-vars/param_subshell.right b/shell/ash_test/ash-vars/param_subshell.right
new file mode 100644
index 000000000..f3c3767de
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_subshell.right
@@ -0,0 +1,7 @@
11=1
22=2
33=3
44=4
55=5
66=6
77=7
diff --git a/shell/ash_test/ash-vars/param_subshell.tests b/shell/ash_test/ash-vars/param_subshell.tests
new file mode 100755
index 000000000..27fdc5b9b
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_subshell.tests
@@ -0,0 +1,15 @@
1if test $# = 0; then
2 "$THIS_SH" "$0" 1 2 3 4 5 6 7 8 9
3 exit
4fi
5echo 1=$1
6{ echo 2=$2; }
7{ echo 3=$3; } &
8# cant use usleep as it isnt standard in $PATH --
9# we fail when testing busybox compiled solely as "hush"
10wait
11( echo 4=$4 )
12( echo 5=$5 ) &
13wait
14true | echo 6=$6 | cat
15true | { echo 7=$7; } | cat
diff --git a/shell/msh_test/msh-vars/star.right b/shell/ash_test/ash-vars/star.right
index 0ecc55bc2..0ecc55bc2 100644
--- a/shell/msh_test/msh-vars/star.right
+++ b/shell/ash_test/ash-vars/star.right
diff --git a/shell/msh_test/msh-vars/star.tests b/shell/ash_test/ash-vars/star.tests
index 5554c4090..5554c4090 100755
--- a/shell/msh_test/msh-vars/star.tests
+++ b/shell/ash_test/ash-vars/star.tests
diff --git a/shell/ash_test/ash-vars/var1.right b/shell/ash_test/ash-vars/var1.right
index 2a01291bb..194e7dbe2 100644
--- a/shell/ash_test/ash-vars/var1.right
+++ b/shell/ash_test/ash-vars/var1.right
@@ -1,6 +1,4 @@
1a=a A=a 1http://busybox.net
2a=a A=a 2http://busybox.net_abc
3a= A= 31 1
4a= A= 41 1
5a=a A=a
6a=a A=a
diff --git a/shell/ash_test/ash-vars/var1.tests b/shell/ash_test/ash-vars/var1.tests
index 802e489bd..48a6782ba 100755
--- a/shell/ash_test/ash-vars/var1.tests
+++ b/shell/ash_test/ash-vars/var1.tests
@@ -1,14 +1,9 @@
1# check that first assignment has proper effect on second one 1URL=http://busybox.net
2 2
3( 3echo $URL
4a=a A=$a 4echo ${URL}_abc
5echo a=$a A=$A
6)
7(a=a A=$a; echo a=$a A=$A)
8(a=a A=$a echo a=$a A=$A)
9(a=a A=$a /bin/echo a=$a A=$A)
10 5
11f() { echo a=$a A=$A; } 6true
12 7false; echo $? ${?}
13(a=a A=$a f) 8true
14(a=a A=$a; f) 9{ false; echo $? ${?}; }
diff --git a/shell/ash_test/ash-vars/var2.right b/shell/ash_test/ash-vars/var2.right
index 8fed138eb..40bf4bff4 100644
--- a/shell/ash_test/ash-vars/var2.right
+++ b/shell/ash_test/ash-vars/var2.right
@@ -1 +1,2 @@
1bus/usb/1/2 1http://busybox.net
2http://busybox.net_abc
diff --git a/shell/ash_test/ash-vars/var2.tests b/shell/ash_test/ash-vars/var2.tests
index 07feaeb8b..1292410f3 100755
--- a/shell/ash_test/ash-vars/var2.tests
+++ b/shell/ash_test/ash-vars/var2.tests
@@ -1 +1,4 @@
1X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D 1_1=http://busybox.net
2
3echo $_1
4echo ${_1}_abc
diff --git a/shell/ash_test/ash-vars/var4.right b/shell/ash_test/ash-vars/var4.right
new file mode 100644
index 000000000..8fed138eb
--- /dev/null
+++ b/shell/ash_test/ash-vars/var4.right
@@ -0,0 +1 @@
bus/usb/1/2
diff --git a/shell/ash_test/ash-vars/var4.tests b/shell/ash_test/ash-vars/var4.tests
new file mode 100755
index 000000000..07feaeb8b
--- /dev/null
+++ b/shell/ash_test/ash-vars/var4.tests
@@ -0,0 +1 @@
X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
diff --git a/shell/ash_test/ash-vars/var5.right b/shell/ash_test/ash-vars/var5.right
new file mode 100644
index 000000000..2a01291bb
--- /dev/null
+++ b/shell/ash_test/ash-vars/var5.right
@@ -0,0 +1,6 @@
1a=a A=a
2a=a A=a
3a= A=
4a= A=
5a=a A=a
6a=a A=a
diff --git a/shell/ash_test/ash-vars/var5.tests b/shell/ash_test/ash-vars/var5.tests
new file mode 100755
index 000000000..802e489bd
--- /dev/null
+++ b/shell/ash_test/ash-vars/var5.tests
@@ -0,0 +1,14 @@
1# check that first assignment has proper effect on second one
2
3(
4a=a A=$a
5echo a=$a A=$A
6)
7(a=a A=$a; echo a=$a A=$A)
8(a=a A=$a echo a=$a A=$A)
9(a=a A=$a /bin/echo a=$a A=$A)
10
11f() { echo a=$a A=$A; }
12
13(a=a A=$a f)
14(a=a A=$a; f)
diff --git a/shell/ash_test/ash-vars/var_bash1a.right b/shell/ash_test/ash-vars/var_bash1a.right
new file mode 100644
index 000000000..1965b5c6c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash1a.right
@@ -0,0 +1,6 @@
1parameter 'abcdef'
2varoffset2 'cdef'
3varoffset-2 'ef'
4literal '2' 'cdef'
5literal '-2' 'abcdef'
6literal ' -2' 'ef'
diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests
new file mode 100755
index 000000000..551dd9acc
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash1a.tests
@@ -0,0 +1,11 @@
1parameter=abcdef
2offset=2
3noffset=-2
4echo "parameter '${parameter}'"
5echo "varoffset2 '${parameter:${offset}}'"
6echo "varoffset-2 '${parameter:${noffset}}'"
7echo "literal '2' '${parameter:2}'"
8# This is not inrpreted as ${VAR:POS{:LEN}},
9# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD
10echo "literal '-2' '${parameter:-2}'"
11echo "literal ' -2' '${parameter: -2}'"
diff --git a/shell/ash_test/ash-vars/var_bash4.right b/shell/ash_test/ash-vars/var_bash4.right
index 600e8532f..0ef1bf661 100644
--- a/shell/ash_test/ash-vars/var_bash4.right
+++ b/shell/ash_test/ash-vars/var_bash4.right
@@ -1,23 +1,40 @@
1Source: a*b\*c 1Source: a*b\*c
2Replace str: _\\_\z_ 2Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star" 3Pattern: single backslash and star: "replace literal star"
4In assignment: a_\_z_b\*c
5Unquoted: a_\_z_b\*c 4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c 6Quoted: a_\_\z_b\*c
7Quoted =: a_\_\z_b\*c
7Pattern: double backslash and star: "replace backslash and everything after it" 8Pattern: double backslash and star: "replace backslash and everything after it"
8In assignment: a*b_\_z_
9Unquoted: a*b_\_z_ 9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_
10Quoted: a*b_\_\z_ 11Quoted: a*b_\_\z_
12Quoted =: a*b_\_\z_
11 13
12Source: a\bc 14Source: a\bc
13Replace str: _\\_\z_ 15Replace str: _\\_\z_
14Pattern: single backslash and b: "replace b" 16Pattern: single backslash and b: "replace b"
15In assignment: a\_\_z_c
16Unquoted: a\_\_z_c 17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c
17Quoted: a\_\_\z_c 19Quoted: a\_\_\z_c
20Quoted =: a\_\_\z_c
18Pattern: double backslash and b: "replace backslash and b" 21Pattern: double backslash and b: "replace backslash and b"
19In assignment: a_\_z_c
20Unquoted: a_\_z_c 22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c
21Quoted: a_\_\z_c 24Quoted: a_\_\z_c
25Quoted =: a_\_\z_c
26
27Source: a\bc
28Replace str: _\\_\z_ (as variable $s)
29Pattern: single backslash and b: "replace b"
30Unquoted: a\_\\_\z_c
31Unquoted =: a\_\\_\z_c
32Quoted: a\_\\_\z_c
33Quoted =: a\_\\_\z_c
34Pattern: double backslash and b: "replace backslash and b"
35Unquoted: a_\\_\z_c
36Unquoted =: a_\\_\z_c
37Quoted: a_\\_\z_c
38Quoted =: a_\\_\z_c
22 39
23Done: 0 40Done: 0
diff --git a/shell/ash_test/ash-vars/var_bash4.tests b/shell/ash_test/ash-vars/var_bash4.tests
index d5470614b..32aa2b34c 100755
--- a/shell/ash_test/ash-vars/var_bash4.tests
+++ b/shell/ash_test/ash-vars/var_bash4.tests
@@ -6,23 +6,30 @@
6# even in quotes. 6# even in quotes.
7# 7#
8# bash4 (and probably bash3 too): "Quoted:" results are different from 8# bash4 (and probably bash3 too): "Quoted:" results are different from
9# unquoted and assignment expansions - they have a backslash before z. 9# unquoted expansions - they have a backslash before z.
10#
11# The difference only exists if repl is a literal. If it is a variable:
12# ${v/.../$s}, then all backslashes are preserved in both cases.
10 13
11v='a*b\*c' 14v='a*b\*c'
12echo 'Source: ' "$v" 15echo 'Source: ' "$v"
13echo 'Replace str: ' '_\\_\z_' 16echo 'Replace str: ' '_\\_\z_'
14 17
15echo 'Pattern: ' 'single backslash and star: "replace literal star"' 18echo 'Pattern: ' 'single backslash and star: "replace literal star"'
16r=${v/\*/_\\_\z_}
17echo 'In assignment:' "$r"
18echo 'Unquoted: ' ${v/\*/_\\_\z_} 19echo 'Unquoted: ' ${v/\*/_\\_\z_}
20r=${v/\*/_\\_\z_}
21echo 'Unquoted =: ' "$r"
19echo 'Quoted: ' "${v/\*/_\\_\z_}" 22echo 'Quoted: ' "${v/\*/_\\_\z_}"
23r="${v/\*/_\\_\z_}"
24echo 'Quoted =: ' "$r"
20 25
21echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' 26echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"'
22r=${v/\\*/_\\_\z_}
23echo 'In assignment:' "$r"
24echo 'Unquoted: ' ${v/\\*/_\\_\z_} 27echo 'Unquoted: ' ${v/\\*/_\\_\z_}
28r=${v/\\*/_\\_\z_}
29echo 'Unquoted =: ' "$r"
25echo 'Quoted: ' "${v/\\*/_\\_\z_}" 30echo 'Quoted: ' "${v/\\*/_\\_\z_}"
31r="${v/\\*/_\\_\z_}"
32echo 'Quoted =: ' "$r"
26 33
27echo 34echo
28 35
@@ -31,16 +38,43 @@ echo 'Source: ' "$v"
31echo 'Replace str: ' '_\\_\z_' 38echo 'Replace str: ' '_\\_\z_'
32 39
33echo 'Pattern: ' 'single backslash and b: "replace b"' 40echo 'Pattern: ' 'single backslash and b: "replace b"'
34r=${v/\b/_\\_\z_}
35echo 'In assignment:' "$r"
36echo 'Unquoted: ' ${v/\b/_\\_\z_} 41echo 'Unquoted: ' ${v/\b/_\\_\z_}
42r=${v/\b/_\\_\z_}
43echo 'Unquoted =: ' "$r"
37echo 'Quoted: ' "${v/\b/_\\_\z_}" 44echo 'Quoted: ' "${v/\b/_\\_\z_}"
45r="${v/\b/_\\_\z_}"
46echo 'Quoted =: ' "$r"
38 47
39echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' 48echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
40r=${v/\\b/_\\_\z_}
41echo 'In assignment:' "$r"
42echo 'Unquoted: ' ${v/\\b/_\\_\z_} 49echo 'Unquoted: ' ${v/\\b/_\\_\z_}
50r=${v/\\b/_\\_\z_}
51echo 'Unquoted =: ' "$r"
43echo 'Quoted: ' "${v/\\b/_\\_\z_}" 52echo 'Quoted: ' "${v/\\b/_\\_\z_}"
53r="${v/\\b/_\\_\z_}"
54echo 'Quoted =: ' "$r"
55
56echo
57
58v='a\bc'
59s='_\\_\z_'
60echo 'Source: ' "$v"
61echo 'Replace str: ' "$s" '(as variable $s)'
62
63echo 'Pattern: ' 'single backslash and b: "replace b"'
64echo 'Unquoted: ' ${v/\b/$s}
65r=${v/\b/$s}
66echo 'Unquoted =: ' "$r"
67echo 'Quoted: ' "${v/\b/$s}"
68r="${v/\b/$s}"
69echo 'Quoted =: ' "$r"
70
71echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
72echo 'Unquoted: ' ${v/\\b/$s}
73r=${v/\\b/$s}
74echo 'Unquoted =: ' "$r"
75echo 'Quoted: ' "${v/\\b/$s}"
76r="${v/\\b/$s}"
77echo 'Quoted =: ' "$r"
44 78
45echo 79echo
46 80
diff --git a/shell/ash_test/ash-vars/var_bash5.right b/shell/ash_test/ash-vars/var_bash5.right
index 278ed3228..1990902b2 100644
--- a/shell/ash_test/ash-vars/var_bash5.right
+++ b/shell/ash_test/ash-vars/var_bash5.right
@@ -1,4 +1,11 @@
1a/ 11 a/
2a/d 22 a/d
3a/e/f 33 a/e/f
44 a\
55 a\d
66 a\e\f
77 a\\
88 a\\d
99 a\\e\\f
10a ab
4Done: 0 11Done: 0
diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests
index 7f482a554..5748b4ac7 100755
--- a/shell/ash_test/ash-vars/var_bash5.tests
+++ b/shell/ash_test/ash-vars/var_bash5.tests
@@ -4,8 +4,26 @@
4v='a/b/c' 4v='a/b/c'
5s='b/c' 5s='b/c'
6r='e/f' 6r='e/f'
7echo "${v/$s}" 7echo "1 ${v/$s}"
8echo "${v/$s/d}" 8echo "2 ${v/$s/d}"
9echo "${v/$s/$r}" 9echo "3 ${v/$s/$r}"
10
11v='a\b\c'
12s='b\\c'
13r='e\f'
14echo "4 ${v/$s}"
15echo "5 ${v/$s/d}"
16echo "6 ${v/$s/$r}"
17
18v='a\\b\\c'
19s='b\\\\c'
20r='e\\f'
21echo "7 ${v/$s}"
22echo "8 ${v/$s/d}"
23echo "9 ${v/$s/$r}"
24
25v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b'
26s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-'
27echo "a ${v/$s}"
10 28
11echo Done: $? 29echo Done: $?
diff --git a/shell/msh_test/msh-bugs/var_expand_in_assign.right b/shell/ash_test/ash-vars/var_expand_in_assign.right
index 352210d7e..352210d7e 100644
--- a/shell/msh_test/msh-bugs/var_expand_in_assign.right
+++ b/shell/ash_test/ash-vars/var_expand_in_assign.right
diff --git a/shell/msh_test/msh-bugs/var_expand_in_assign.tests b/shell/ash_test/ash-vars/var_expand_in_assign.tests
index 18cdc74c0..18cdc74c0 100755
--- a/shell/msh_test/msh-bugs/var_expand_in_assign.tests
+++ b/shell/ash_test/ash-vars/var_expand_in_assign.tests
diff --git a/shell/msh_test/msh-bugs/var_expand_in_redir.right b/shell/ash_test/ash-vars/var_expand_in_redir.right
index 423299c97..423299c97 100644
--- a/shell/msh_test/msh-bugs/var_expand_in_redir.right
+++ b/shell/ash_test/ash-vars/var_expand_in_redir.right
diff --git a/shell/msh_test/msh-bugs/var_expand_in_redir.tests b/shell/ash_test/ash-vars/var_expand_in_redir.tests
index bda6bdd7f..bda6bdd7f 100755
--- a/shell/msh_test/msh-bugs/var_expand_in_redir.tests
+++ b/shell/ash_test/ash-vars/var_expand_in_redir.tests
diff --git a/shell/ash_test/ash-vars/var_expand_on_ifs.right b/shell/ash_test/ash-vars/var_expand_on_ifs.right
new file mode 100644
index 000000000..2ed2069f7
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_expand_on_ifs.right
@@ -0,0 +1,9 @@
11 a b c
22 a + b c
33 a b c
44 a b c
55 a b c
66 a b + c
77 a b c
88 a b c
99 a b c
diff --git a/shell/ash_test/ash-vars/var_expand_on_ifs.tests b/shell/ash_test/ash-vars/var_expand_on_ifs.tests
new file mode 100755
index 000000000..a12ff8ec8
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_expand_on_ifs.tests
@@ -0,0 +1,11 @@
1b=' b '
2e=''
3echo 1 a $b c
4echo 2 a +$b c
5echo 3 a $e$b c
6echo 4 a "$e"$b c
7echo 5 a ""$b c
8echo 6 a $b+ c
9echo 7 a $b$e c
10echo 8 a $b"$e" c
11echo 9 a $b"" c
diff --git a/shell/ash_test/ash-vars/var_in_pipes.right b/shell/ash_test/ash-vars/var_in_pipes.right
new file mode 100644
index 000000000..faf65bed4
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_in_pipes.right
@@ -0,0 +1,6 @@
1b=1
2b=2
3b=3
4b=4
5b=5
6b=6
diff --git a/shell/ash_test/ash-vars/var_in_pipes.tests b/shell/ash_test/ash-vars/var_in_pipes.tests
new file mode 100755
index 000000000..3f8cd2729
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_in_pipes.tests
@@ -0,0 +1,7 @@
1b=1 env | grep ^b=
2true | b=2 env | grep ^b=
3a=1 true | b=3 env | grep ^b=
4
5(b=4 env) | grep ^b=
6(true | b=5 env) | grep ^b=
7(a=1 true | b=6 env) | grep ^b=
diff --git a/shell/msh_test/msh-parsing/argv0.right b/shell/ash_test/ash-vars/var_leaks.right
index d86bac9de..d86bac9de 100644
--- a/shell/msh_test/msh-parsing/argv0.right
+++ b/shell/ash_test/ash-vars/var_leaks.right
diff --git a/shell/ash_test/ash-vars/var_leaks.tests b/shell/ash_test/ash-vars/var_leaks.tests
new file mode 100755
index 000000000..27c8c6504
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_leaks.tests
@@ -0,0 +1,14 @@
1# external program
2a=b /bin/true
3env | grep ^a=
4
5# builtin
6a=b true
7env | grep ^a=
8
9# exec with redirection only
10# in bash, this leaks!
11a=b exec 1>&1
12env | grep ^a=
13
14echo OK
diff --git a/shell/ash_test/ash-vars/var_posix1.right b/shell/ash_test/ash-vars/var_posix1.right
index 55f35798a..7ff618ad0 100644
--- a/shell/ash_test/ash-vars/var_posix1.right
+++ b/shell/ash_test/ash-vars/var_posix1.right
@@ -1,3 +1,19 @@
1Empty:
2Empty:
3Empty:
4Empty:
5Empty:
6Empty:
7Empty:
8Empty:
9Empty:
10Empty:
11Empty:
12Empty:
13Empty:
14Empty:
15Empty:
16Empty:
1abcdcd 17abcdcd
2abcdcd 18abcdcd
3abcdcd 19abcdcd
@@ -5,7 +21,9 @@ cdcd
5babcdcd 21babcdcd
6babcdcd 22babcdcd
7ababcdcd 23ababcdcd
8 24Empty:
25ababcdcd}_tail
26ababcdcd_tail
9ababcd 27ababcd
10ababcd 28ababcd
11ababcd 29ababcd
@@ -13,5 +31,11 @@ abab
13ababcdc 31ababcdc
14ababcdc 32ababcdc
15ababcdcd 33ababcdcd
16 34Empty:
17end 35ababcdcd}_tail
36ababcdcd_tail
37ababcdcd
38ab
39ab
40ab
41End
diff --git a/shell/ash_test/ash-vars/var_posix1.tests b/shell/ash_test/ash-vars/var_posix1.tests
index 4139e2cc3..82abe8198 100755
--- a/shell/ash_test/ash-vars/var_posix1.tests
+++ b/shell/ash_test/ash-vars/var_posix1.tests
@@ -1,3 +1,25 @@
1unset var
2
3echo Empty:${var#}
4echo Empty:${var##}
5echo Empty:${var#*}
6echo Empty:${var##*}
7echo Empty:${var%}
8echo Empty:${var%%}
9echo Empty:${var%*}
10echo Empty:${var%%*}
11
12var=
13
14echo Empty:${var#}
15echo Empty:${var##}
16echo Empty:${var#*}
17echo Empty:${var##*}
18echo Empty:${var%}
19echo Empty:${var%%}
20echo Empty:${var%*}
21echo Empty:${var%%*}
22
1var=ababcdcd 23var=ababcdcd
2 24
3echo ${var#ab} 25echo ${var#ab}
@@ -7,7 +29,9 @@ echo ${var##a*b}
7echo ${var#?} 29echo ${var#?}
8echo ${var##?} 30echo ${var##?}
9echo ${var#*} 31echo ${var#*}
10echo ${var##*} 32echo Empty:${var##*}
33echo ${var#}}_tail
34echo ${var#\}}_tail
11 35
12echo ${var%cd} 36echo ${var%cd}
13echo ${var%%cd} 37echo ${var%%cd}
@@ -16,6 +40,13 @@ echo ${var%%c*d}
16echo ${var%?} 40echo ${var%?}
17echo ${var%%?} 41echo ${var%%?}
18echo ${var%*} 42echo ${var%*}
19echo ${var%%*} 43echo Empty:${var%%*}
44echo ${var#}}_tail
45echo ${var#\}}_tail
46echo ${var%\\*}
47
48a=ab}; echo ${a%\}};
49a=abc; c=c; echo ${a%${c}}
50a=ab{{c; echo ${a%`echo {{c`}
20 51
21echo end 52echo End
diff --git a/shell/ash_test/ash-vars/var_serial.right b/shell/ash_test/ash-vars/var_serial.right
new file mode 100644
index 000000000..42aa33057
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_serial.right
@@ -0,0 +1,5 @@
1Assignments only: c=a
2Assignments and a command: c=a
3Assignments and a builtin: c=a
4Assignments and a function: c=a
5Done
diff --git a/shell/ash_test/ash-vars/var_serial.tests b/shell/ash_test/ash-vars/var_serial.tests
new file mode 100755
index 000000000..6b4a4cdf7
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_serial.tests
@@ -0,0 +1,22 @@
1a=a
2
3b=b
4c=c
5# Second assignment depends on the first:
6b=$a c=$b
7echo Assignments only: c=$c
8
9b=b
10c=c
11b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c'
12
13b=b
14c=c
15b=$a c=$b eval 'echo Assignments and a builtin: c=$c'
16
17b=b
18c=c
19f() { echo Assignments and a function: c=$c; }
20b=$a c=$b f
21
22echo Done
diff --git a/shell/msh_test/msh-vars/var_subst_in_for.right b/shell/ash_test/ash-vars/var_subst_in_for.right
index c8aca1c12..c8aca1c12 100644
--- a/shell/msh_test/msh-vars/var_subst_in_for.right
+++ b/shell/ash_test/ash-vars/var_subst_in_for.right
diff --git a/shell/msh_test/msh-vars/var_subst_in_for.tests b/shell/ash_test/ash-vars/var_subst_in_for.tests
index 4d1c11201..433c60627 100755
--- a/shell/msh_test/msh-vars/var_subst_in_for.tests
+++ b/shell/ash_test/ash-vars/var_subst_in_for.tests
@@ -1,5 +1,5 @@
1if test $# = 0; then 1if test $# = 0; then
2 exec "$THIS_SH" var_subst_in_for.tests abc "d e" 2 exec "$THIS_SH" "$0" abc "d e"
3fi 3fi
4 4
5echo 'Testing: in x y z' 5echo 'Testing: in x y z'
diff --git a/shell/ash_test/ash-vars/var_unbackslash.right b/shell/ash_test/ash-vars/var_unbackslash.right
new file mode 100644
index 000000000..8bc834711
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash.right
@@ -0,0 +1,11 @@
1b1=-qwerty-t-\-"-`---z-*-?-
2b1=-qwerty-t-\-"-`---z-*-?-
3b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
4b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
5b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
6b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
7c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
8c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
9c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
10c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
11Done: 0
diff --git a/shell/ash_test/ash-vars/var_unbackslash.tests b/shell/ash_test/ash-vars/var_unbackslash.tests
new file mode 100755
index 000000000..bb52af3d0
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash.tests
@@ -0,0 +1,23 @@
1# Test for correct handling of backslashes
2a=qwerty
3
4b=-$a-\t-\\-\"-\`-\--\z-\*-\?-
5echo b1=$b
6echo "b1=$b"
7b="-$a-\t-\\-\"-\`-\--\z-\*-\?-"
8echo b2=$b
9echo "b2=$b"
10b='-$a-\t-\\-\"-\`-\--\z-\*-\?-'
11echo b3=$b
12echo "b3=$b"
13
14c=$b
15echo "c=$c"
16c=${b}
17echo "c=$c"
18c="$b"
19echo "c=$c"
20c="${b}"
21echo "c=$c"
22
23echo "Done: $?"
diff --git a/shell/ash_test/ash-vars/var_unbackslash1.right b/shell/ash_test/ash-vars/var_unbackslash1.right
new file mode 100644
index 000000000..3e0c0e2af
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash1.right
@@ -0,0 +1,7 @@
1Ok
2Ba d
3Ok
4Ok
5Ok
6Forty two:42
7Forty two:42
diff --git a/shell/ash_test/ash-vars/var_unbackslash1.tests b/shell/ash_test/ash-vars/var_unbackslash1.tests
new file mode 100755
index 000000000..a4665e423
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash1.tests
@@ -0,0 +1,35 @@
1ad="Ok"
2a="Ba"
3
4# "Ok"
5echo $a\
6d
7
8# This variable contains backslash+newline!
9e='echo $a\
10d'
11
12# "Ba d"
13eval $e
14# "Ok"
15eval "$e"
16
17echo $\
18(echo Ok\
19)
20echo "$\
21(echo Ok\
22)"
23
24echo Forty two:$\
25(\
26(\
2742\
28)\
29)
30echo "Forty two:$\
31(\
32(\
3342\
34)\
35)"
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.right b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right
new file mode 100644
index 000000000..cf583d0aa
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right
@@ -0,0 +1,41 @@
1Testing: !IFS $*
2.abc.
3.d.
4.e.
5Testing: !IFS $@
6.abc.
7.d.
8.e.
9Testing: !IFS "$*"
10.abc d e.
11Testing: !IFS "$@"
12.abc.
13.d e.
14Testing: IFS="" $*
15.abc.
16.d e.
17Testing: IFS="" $@
18.abc.
19.d e.
20Testing: IFS="" "$*"
21.abcd e.
22Testing: IFS="" "$@"
23.abc.
24.d e.
25Testing: !IFS v=$*
26v='abc d e'
27Testing: !IFS v=$@
28v='abc d e'
29Testing: !IFS v="$*"
30v='abc d e'
31Testing: !IFS v="$@"
32v='abc d e'
33Testing: IFS="" v=$*
34v='abcd e'
35Testing: IFS="" v=$@
36v='abcd e'
37Testing: IFS="" v="$*"
38v='abcd e'
39Testing: IFS="" v="$@"
40v='abcd e'
41Finished
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests
new file mode 100755
index 000000000..a62afc6fd
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests
@@ -0,0 +1,42 @@
1set -- abc "d e"
2
3echo 'Testing: !IFS $*'
4unset IFS; for a in $*; do echo ".$a."; done
5echo 'Testing: !IFS $@'
6unset IFS; for a in $@; do echo ".$a."; done
7echo 'Testing: !IFS "$*"'
8unset IFS; for a in "$*"; do echo ".$a."; done
9echo 'Testing: !IFS "$@"'
10unset IFS; for a in "$@"; do echo ".$a."; done
11
12echo 'Testing: IFS="" $*'
13IFS=""; for a in $*; do echo ".$a."; done
14echo 'Testing: IFS="" $@'
15IFS=""; for a in $@; do echo ".$a."; done
16echo 'Testing: IFS="" "$*"'
17IFS=""; for a in "$*"; do echo ".$a."; done
18echo 'Testing: IFS="" "$@"'
19IFS=""; for a in "$@"; do echo ".$a."; done
20
21echo 'Testing: !IFS v=$*'
22unset IFS; v=$*; echo "v='$v'"
23echo 'Testing: !IFS v=$@'
24unset IFS; v=$@; echo "v='$v'"
25echo 'Testing: !IFS v="$*"'
26unset IFS; v="$*"; echo "v='$v'"
27echo 'Testing: !IFS v="$@"'
28unset IFS; v="$@"; echo "v='$v'"
29
30echo 'Testing: IFS="" v=$*'
31IFS=""; v=$*; echo "v='$v'"
32echo 'Testing: IFS="" v=$@'
33IFS=""; v=$@; echo "v='$v'"
34echo 'Testing: IFS="" v="$*"'
35IFS=""; v="$*"; echo "v='$v'"
36echo 'Testing: IFS="" v="$@"'
37IFS=""; v="$@"; echo "v='$v'"
38
39# Note: in IFS="" v=$@ and IFS="" v="$@" cases, bash produces "abc d e"
40# We produce "abcd e"
41
42echo Finished
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs2.right b/shell/ash_test/ash-vars/var_wordsplit_ifs2.right
new file mode 100644
index 000000000..c234193fe
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs2.right
@@ -0,0 +1,3 @@
1Unquoted:<1>
2Unquoted:<3>
3Quoted:<123>
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests
new file mode 100755
index 000000000..47523549c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests
@@ -0,0 +1,13 @@
1# 123 chars long
2a="\
301234567890123456789\
401234567890123456789\
501234567890123456789\
601234567890123456789\
701234567890123456789\
80123456789\
90123456789\
10012"
11
12IFS=2; for v in ${#a}; do echo Unquoted:"<$v>"; done
13IFS=2; for v in "${#a}"; do echo Quoted:"<$v>"; done
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs3.right b/shell/ash_test/ash-vars/var_wordsplit_ifs3.right
new file mode 100644
index 000000000..5ab72e1cd
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs3.right
@@ -0,0 +1,12 @@
1Unquoted%:<q>
2Unquoted%:<w>
3Unquoted%:<e>
4Unquoted%:<r>
5Unquoted%:<t>
6Unquoted#:<w>
7Unquoted#:<e>
8Unquoted#:<r>
9Unquoted#:<t>
10Unquoted#:<y>
11Quoted%:<q w e r t >
12Quoted#:< w e r t y>
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests
new file mode 100755
index 000000000..4aa65574a
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests
@@ -0,0 +1,5 @@
1a="q w e r t y"
2for v in ${a%y}; do echo Unquoted%:"<$v>"; done
3for v in ${a#q}; do echo Unquoted#:"<$v>"; done
4for v in "${a%y}"; do echo Quoted%:"<$v>"; done
5for v in "${a#q}"; do echo Quoted#:"<$v>"; done
diff --git a/shell/hush.c b/shell/hush.c
index eabe83ac6..9b51f389e 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -103,6 +103,9 @@
103#else 103#else
104# define CLEAR_RANDOM_T(rnd) ((void)0) 104# define CLEAR_RANDOM_T(rnd) ((void)0)
105#endif 105#endif
106#ifndef F_DUPFD_CLOEXEC
107# define F_DUPFD_CLOEXEC F_DUPFD
108#endif
106#ifndef PIPE_BUF 109#ifndef PIPE_BUF
107# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 110# define PIPE_BUF 4096 /* amount of buffering in a pipe */
108#endif 111#endif
@@ -459,19 +462,17 @@ static const char *const assignment_flag[] = {
459 462
460typedef struct in_str { 463typedef struct in_str {
461 const char *p; 464 const char *p;
462 /* eof_flag=1: last char in ->p is really an EOF */
463 char eof_flag; /* meaningless if ->p == NULL */
464 char peek_buf[2];
465#if ENABLE_HUSH_INTERACTIVE 465#if ENABLE_HUSH_INTERACTIVE
466 smallint promptmode; /* 0: PS1, 1: PS2 */ 466 smallint promptmode; /* 0: PS1, 1: PS2 */
467#endif 467#endif
468 int peek_buf[2];
468 int last_char; 469 int last_char;
469 FILE *file; 470 FILE *file;
470 int (*get) (struct in_str *) FAST_FUNC; 471 int (*get) (struct in_str *) FAST_FUNC;
471 int (*peek) (struct in_str *) FAST_FUNC; 472 int (*peek) (struct in_str *) FAST_FUNC;
472} in_str; 473} in_str;
473#define i_getch(input) ((input)->get(input)) 474#define i_getch(input) ((input)->get(input))
474#define i_peek(input) ((input)->peek(input)) 475#define i_peek(input) ((input)->peek(input))
475 476
476/* The descrip member of this structure is only used to make 477/* The descrip member of this structure is only used to make
477 * debugging output pretty */ 478 * debugging output pretty */
@@ -711,6 +712,13 @@ enum {
711}; 712};
712 713
713 714
715struct FILE_list {
716 struct FILE_list *next;
717 FILE *fp;
718 int fd;
719};
720
721
714/* "Globals" within this file */ 722/* "Globals" within this file */
715/* Sorted roughly by size (smaller offsets == smaller code) */ 723/* Sorted roughly by size (smaller offsets == smaller code) */
716struct globals { 724struct globals {
@@ -769,6 +777,9 @@ struct globals {
769 * 1: return is invoked, skip all till end of func 777 * 1: return is invoked, skip all till end of func
770 */ 778 */
771 smallint flag_return_in_progress; 779 smallint flag_return_in_progress;
780# define G_flag_return_in_progress (G.flag_return_in_progress)
781#else
782# define G_flag_return_in_progress 0
772#endif 783#endif
773 smallint exiting; /* used to prevent EXIT trap recursion */ 784 smallint exiting; /* used to prevent EXIT trap recursion */
774 /* These four support $?, $#, and $1 */ 785 /* These four support $?, $#, and $1 */
@@ -802,6 +813,7 @@ struct globals {
802 unsigned handled_SIGCHLD; 813 unsigned handled_SIGCHLD;
803 smallint we_have_children; 814 smallint we_have_children;
804#endif 815#endif
816 struct FILE_list *FILE_list;
805 /* Which signals have non-DFL handler (even with no traps set)? 817 /* Which signals have non-DFL handler (even with no traps set)?
806 * Set at the start to: 818 * Set at the start to:
807 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) 819 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
@@ -823,7 +835,9 @@ struct globals {
823 int debug_indent; 835 int debug_indent;
824#endif 836#endif
825 struct sigaction sa; 837 struct sigaction sa;
826 char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; 838#if ENABLE_FEATURE_EDITING
839 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
840#endif
827}; 841};
828#define G (*ptr_to_globals) 842#define G (*ptr_to_globals)
829/* Not #defining name to G.name - this quickly gets unwieldy 843/* Not #defining name to G.name - this quickly gets unwieldy
@@ -1252,6 +1266,91 @@ static void free_strings(char **strings)
1252} 1266}
1253 1267
1254 1268
1269static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC)
1270{
1271 /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */
1272 int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10);
1273 if (newfd < 0) {
1274 /* fd was not open? */
1275 if (errno == EBADF)
1276 return fd;
1277 xfunc_die();
1278 }
1279 close(fd);
1280 return newfd;
1281}
1282
1283
1284/* Manipulating the list of open FILEs */
1285static FILE *remember_FILE(FILE *fp)
1286{
1287 if (fp) {
1288 struct FILE_list *n = xmalloc(sizeof(*n));
1289 n->next = G.FILE_list;
1290 G.FILE_list = n;
1291 n->fp = fp;
1292 n->fd = fileno(fp);
1293 close_on_exec_on(n->fd);
1294 }
1295 return fp;
1296}
1297static void fclose_and_forget(FILE *fp)
1298{
1299 struct FILE_list **pp = &G.FILE_list;
1300 while (*pp) {
1301 struct FILE_list *cur = *pp;
1302 if (cur->fp == fp) {
1303 *pp = cur->next;
1304 free(cur);
1305 break;
1306 }
1307 pp = &cur->next;
1308 }
1309 fclose(fp);
1310}
1311static int save_FILEs_on_redirect(int fd)
1312{
1313 struct FILE_list *fl = G.FILE_list;
1314 while (fl) {
1315 if (fd == fl->fd) {
1316 /* We use it only on script files, they are all CLOEXEC */
1317 fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC);
1318 return 1;
1319 }
1320 fl = fl->next;
1321 }
1322 return 0;
1323}
1324static void restore_redirected_FILEs(void)
1325{
1326 struct FILE_list *fl = G.FILE_list;
1327 while (fl) {
1328 int should_be = fileno(fl->fp);
1329 if (fl->fd != should_be) {
1330 xmove_fd(fl->fd, should_be);
1331 fl->fd = should_be;
1332 }
1333 fl = fl->next;
1334 }
1335}
1336#if ENABLE_FEATURE_SH_STANDALONE
1337static void close_all_FILE_list(void)
1338{
1339 struct FILE_list *fl = G.FILE_list;
1340 while (fl) {
1341 /* fclose would also free FILE object.
1342 * It is disastrous if we share memory with a vforked parent.
1343 * I'm not sure we never come here after vfork.
1344 * Therefore just close fd, nothing more.
1345 */
1346 /*fclose(fl->fp); - unsafe */
1347 close(fl->fd);
1348 fl = fl->next;
1349 }
1350}
1351#endif
1352
1353
1255/* Helpers for setting new $n and restoring them back 1354/* Helpers for setting new $n and restoring them back
1256 */ 1355 */
1257typedef struct save_arg_t { 1356typedef struct save_arg_t {
@@ -1477,19 +1576,50 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
1477 return old_sa.sa_handler; 1576 return old_sa.sa_handler;
1478} 1577}
1479 1578
1579static void hush_exit(int exitcode) NORETURN;
1580static void fflush_and__exit(void) NORETURN;
1581static void restore_ttypgrp_and__exit(void) NORETURN;
1582
1583static void restore_ttypgrp_and__exit(void)
1584{
1585 /* xfunc has failed! die die die */
1586 /* no EXIT traps, this is an escape hatch! */
1587 G.exiting = 1;
1588 hush_exit(xfunc_error_retval);
1589}
1590
1591/* Needed only on some libc:
1592 * It was observed that on exit(), fgetc'ed buffered data
1593 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
1594 * With the net effect that even after fork(), not vfork(),
1595 * exit() in NOEXECed applet in "sh SCRIPT":
1596 * noexec_applet_here
1597 * echo END_OF_SCRIPT
1598 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
1599 * This makes "echo END_OF_SCRIPT" executed twice.
1600 * Similar problems can be seen with die_if_script() -> xfunc_die()
1601 * and in `cmd` handling.
1602 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
1603 */
1604static void fflush_and__exit(void)
1605{
1606 fflush_all();
1607 _exit(xfunc_error_retval);
1608}
1609
1480#if ENABLE_HUSH_JOB 1610#if ENABLE_HUSH_JOB
1481 1611
1482static void xfunc_has_died(void);
1483/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 1612/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
1484# define disable_restore_tty_pgrp_on_exit() (die_func = NULL) 1613# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
1485/* After [v]fork, in parent: restore tty pgrp on xfunc death */ 1614/* After [v]fork, in parent: restore tty pgrp on xfunc death */
1486# define enable_restore_tty_pgrp_on_exit() (die_func = xfunc_has_died) 1615# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
1487 1616
1488/* Restores tty foreground process group, and exits. 1617/* Restores tty foreground process group, and exits.
1489 * May be called as signal handler for fatal signal 1618 * May be called as signal handler for fatal signal
1490 * (will resend signal to itself, producing correct exit state) 1619 * (will resend signal to itself, producing correct exit state)
1491 * or called directly with -EXITCODE. 1620 * or called directly with -EXITCODE.
1492 * We also call it if xfunc is exiting. */ 1621 * We also call it if xfunc is exiting.
1622 */
1493static void sigexit(int sig) NORETURN; 1623static void sigexit(int sig) NORETURN;
1494static void sigexit(int sig) 1624static void sigexit(int sig)
1495{ 1625{
@@ -1544,7 +1674,6 @@ static sighandler_t pick_sighandler(unsigned sig)
1544} 1674}
1545 1675
1546/* Restores tty foreground process group, and exits. */ 1676/* Restores tty foreground process group, and exits. */
1547static void hush_exit(int exitcode) NORETURN;
1548static void hush_exit(int exitcode) 1677static void hush_exit(int exitcode)
1549{ 1678{
1550#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 1679#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
@@ -1580,23 +1709,14 @@ static void hush_exit(int exitcode)
1580 } 1709 }
1581#endif 1710#endif
1582 1711
1583#if ENABLE_HUSH_JOB
1584 fflush_all(); 1712 fflush_all();
1713#if ENABLE_HUSH_JOB
1585 sigexit(- (exitcode & 0xff)); 1714 sigexit(- (exitcode & 0xff));
1586#else 1715#else
1587 exit(exitcode); 1716 _exit(exitcode);
1588#endif 1717#endif
1589} 1718}
1590 1719
1591static void xfunc_has_died(void) NORETURN;
1592static void xfunc_has_died(void)
1593{
1594 /* xfunc has failed! die die die */
1595 /* no EXIT traps, this is an escape hatch! */
1596 G.exiting = 1;
1597 hush_exit(xfunc_error_retval);
1598}
1599
1600 1720
1601//TODO: return a mask of ALL handled sigs? 1721//TODO: return a mask of ALL handled sigs?
1602static int check_and_run_traps(void) 1722static int check_and_run_traps(void)
@@ -1619,6 +1739,7 @@ static int check_and_run_traps(void)
1619 break; 1739 break;
1620 got_sig: 1740 got_sig:
1621 if (G.traps && G.traps[sig]) { 1741 if (G.traps && G.traps[sig]) {
1742 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
1622 if (G.traps[sig][0]) { 1743 if (G.traps[sig][0]) {
1623 /* We have user-defined handler */ 1744 /* We have user-defined handler */
1624 smalluint save_rcode; 1745 smalluint save_rcode;
@@ -1636,6 +1757,7 @@ static int check_and_run_traps(void)
1636 /* not a trap: special action */ 1757 /* not a trap: special action */
1637 switch (sig) { 1758 switch (sig) {
1638 case SIGINT: 1759 case SIGINT:
1760 debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig);
1639 /* Builtin was ^C'ed, make it look prettier: */ 1761 /* Builtin was ^C'ed, make it look prettier: */
1640 bb_putchar('\n'); 1762 bb_putchar('\n');
1641 G.flag_SIGINT = 1; 1763 G.flag_SIGINT = 1;
@@ -1644,6 +1766,7 @@ static int check_and_run_traps(void)
1644#if ENABLE_HUSH_JOB 1766#if ENABLE_HUSH_JOB
1645 case SIGHUP: { 1767 case SIGHUP: {
1646 struct pipe *job; 1768 struct pipe *job;
1769 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
1647 /* bash is observed to signal whole process groups, 1770 /* bash is observed to signal whole process groups,
1648 * not individual processes */ 1771 * not individual processes */
1649 for (job = G.job_list; job; job = job->next) { 1772 for (job = G.job_list; job; job = job->next) {
@@ -1658,6 +1781,7 @@ static int check_and_run_traps(void)
1658#endif 1781#endif
1659#if ENABLE_HUSH_FAST 1782#if ENABLE_HUSH_FAST
1660 case SIGCHLD: 1783 case SIGCHLD:
1784 debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig);
1661 G.count_SIGCHLD++; 1785 G.count_SIGCHLD++;
1662//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); 1786//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
1663 /* Note: 1787 /* Note:
@@ -1667,6 +1791,7 @@ static int check_and_run_traps(void)
1667 break; 1791 break;
1668#endif 1792#endif
1669 default: /* ignored: */ 1793 default: /* ignored: */
1794 debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig);
1670 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ 1795 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
1671 /* Note: 1796 /* Note:
1672 * We dont do 'last_sig = sig' here -> NOT returning this sig. 1797 * We dont do 'last_sig = sig' here -> NOT returning this sig.
@@ -1766,6 +1891,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
1766{ 1891{
1767 struct variable **var_pp; 1892 struct variable **var_pp;
1768 struct variable *cur; 1893 struct variable *cur;
1894 char *free_me = NULL;
1769 char *eq_sign; 1895 char *eq_sign;
1770 int name_len; 1896 int name_len;
1771 1897
@@ -1782,6 +1908,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
1782 var_pp = &cur->next; 1908 var_pp = &cur->next;
1783 continue; 1909 continue;
1784 } 1910 }
1911
1785 /* We found an existing var with this name */ 1912 /* We found an existing var with this name */
1786 if (cur->flg_read_only) { 1913 if (cur->flg_read_only) {
1787#if !BB_MMU 1914#if !BB_MMU
@@ -1830,12 +1957,17 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
1830 strcpy(cur->varstr, str); 1957 strcpy(cur->varstr, str);
1831 goto free_and_exp; 1958 goto free_and_exp;
1832 } 1959 }
1833 } else { 1960 /* Can't reuse */
1834 /* max_len == 0 signifies "malloced" var, which we can 1961 cur->max_len = 0;
1835 * (and has to) free */ 1962 goto set_str_and_exp;
1836 free(cur->varstr); 1963 }
1837 } 1964 /* max_len == 0 signifies "malloced" var, which we can
1838 cur->max_len = 0; 1965 * (and have to) free. But we can't free(cur->varstr) here:
1966 * if cur->flg_export is 1, it is in the environment.
1967 * We should either unsetenv+free, or wait until putenv,
1968 * then putenv(new)+free(old).
1969 */
1970 free_me = cur->varstr;
1839 goto set_str_and_exp; 1971 goto set_str_and_exp;
1840 } 1972 }
1841 1973
@@ -1862,10 +1994,15 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
1862 cur->flg_export = 0; 1994 cur->flg_export = 0;
1863 /* unsetenv was already done */ 1995 /* unsetenv was already done */
1864 } else { 1996 } else {
1997 int i;
1865 debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); 1998 debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr);
1866 return putenv(cur->varstr); 1999 i = putenv(cur->varstr);
2000 /* only now we can free old exported malloced string */
2001 free(free_me);
2002 return i;
1867 } 2003 }
1868 } 2004 }
2005 free(free_me);
1869 return 0; 2006 return 0;
1870} 2007}
1871 2008
@@ -2005,28 +2142,19 @@ static void reinit_unicode_for_hush(void)
2005 } 2142 }
2006} 2143}
2007 2144
2008
2009/* 2145/*
2010 * in_str support 2146 * in_str support (strings, and "strings" read from files).
2011 */ 2147 */
2012static int FAST_FUNC static_get(struct in_str *i)
2013{
2014 int ch = *i->p;
2015 if (ch != '\0') {
2016 i->p++;
2017 i->last_char = ch;
2018 return ch;
2019 }
2020 return EOF;
2021}
2022
2023static int FAST_FUNC static_peek(struct in_str *i)
2024{
2025 return *i->p;
2026}
2027 2148
2028#if ENABLE_HUSH_INTERACTIVE 2149#if ENABLE_HUSH_INTERACTIVE
2029 2150/* To test correct lineedit/interactive behavior, type from command line:
2151 * echo $P\
2152 * \
2153 * AT\
2154 * H\
2155 * \
2156 * It excercises a lot of corner cases.
2157 */
2030static void cmdedit_update_prompt(void) 2158static void cmdedit_update_prompt(void)
2031{ 2159{
2032 if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { 2160 if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
@@ -2040,7 +2168,6 @@ static void cmdedit_update_prompt(void)
2040 if (G.PS2 == NULL) 2168 if (G.PS2 == NULL)
2041 G.PS2 = "> "; 2169 G.PS2 = "> ";
2042} 2170}
2043
2044static const char *setup_prompt_string(int promptmode) 2171static const char *setup_prompt_string(int promptmode)
2045{ 2172{
2046 const char *prompt_str; 2173 const char *prompt_str;
@@ -2058,33 +2185,36 @@ static const char *setup_prompt_string(int promptmode)
2058 prompt_str = G.PS2; 2185 prompt_str = G.PS2;
2059 } else 2186 } else
2060 prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; 2187 prompt_str = (promptmode == 0) ? G.PS1 : G.PS2;
2061 debug_printf("result '%s'\n", prompt_str); 2188 debug_printf("prompt_str '%s'\n", prompt_str);
2062 return prompt_str; 2189 return prompt_str;
2063} 2190}
2064 2191static int get_user_input(struct in_str *i)
2065static void get_user_input(struct in_str *i)
2066{ 2192{
2067 int r; 2193 int r;
2068 const char *prompt_str; 2194 const char *prompt_str;
2069 2195
2070 prompt_str = setup_prompt_string(i->promptmode); 2196 prompt_str = setup_prompt_string(i->promptmode);
2071# if ENABLE_FEATURE_EDITING 2197# if ENABLE_FEATURE_EDITING
2072 /* Enable command line editing only while a command line
2073 * is actually being read */
2074 do { 2198 do {
2075 reinit_unicode_for_hush(); 2199 reinit_unicode_for_hush();
2076 G.flag_SIGINT = 0; 2200 G.flag_SIGINT = 0;
2077 /* buglet: SIGINT will not make new prompt to appear _at once_, 2201 /* buglet: SIGINT will not make new prompt to appear _at once_,
2078 * only after <Enter>. (^C will work) */ 2202 * only after <Enter>. (^C will work) */
2079 r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); 2203 r = read_line_input(G.line_input_state, prompt_str,
2204 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1,
2205 /*timeout*/ -1
2206 );
2080 /* catch *SIGINT* etc (^C is handled by read_line_input) */ 2207 /* catch *SIGINT* etc (^C is handled by read_line_input) */
2081 check_and_run_traps(); 2208 check_and_run_traps();
2082 } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ 2209 } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
2083 i->eof_flag = (r < 0); 2210 if (r < 0) {
2084 if (i->eof_flag) { /* EOF/error detected */ 2211 /* EOF/error detected */
2085 G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ 2212 i->p = NULL;
2086 G.user_input_buf[1] = '\0'; 2213 i->peek_buf[0] = r = EOF;
2214 return r;
2087 } 2215 }
2216 i->p = G.user_input_buf;
2217 return (unsigned char)*i->p++;
2088# else 2218# else
2089 do { 2219 do {
2090 G.flag_SIGINT = 0; 2220 G.flag_SIGINT = 0;
@@ -2098,76 +2228,149 @@ static void get_user_input(struct in_str *i)
2098 fputs(prompt_str, stdout); 2228 fputs(prompt_str, stdout);
2099 } 2229 }
2100 fflush_all(); 2230 fflush_all();
2101 G.user_input_buf[0] = r = fgetc(i->file); 2231 r = fgetc(i->file);
2102 /*G.user_input_buf[1] = '\0'; - already is and never changed */ 2232 } while (G.flag_SIGINT || r == '\0');
2103 } while (G.flag_SIGINT); 2233 return r;
2104 i->eof_flag = (r == EOF);
2105# endif 2234# endif
2106 i->p = G.user_input_buf;
2107} 2235}
2108
2109#endif /* INTERACTIVE */
2110
2111/* This is the magic location that prints prompts 2236/* This is the magic location that prints prompts
2112 * and gets data back from the user */ 2237 * and gets data back from the user */
2238static int fgetc_interactive(struct in_str *i)
2239{
2240 int ch;
2241 /* If it's interactive stdin, get new line. */
2242 if (G_interactive_fd && i->file == stdin) {
2243 /* Returns first char (or EOF), the rest is in i->p[] */
2244 ch = get_user_input(i);
2245 i->promptmode = 1; /* PS2 */
2246 } else {
2247 /* Not stdin: script file, sourced file, etc */
2248 do ch = fgetc(i->file); while (ch == '\0');
2249 }
2250 return ch;
2251}
2252#else
2253static inline int fgetc_interactive(struct in_str *i)
2254{
2255 int ch;
2256 do ch = fgetc(i->file); while (ch == '\0');
2257 return ch;
2258}
2259#endif /* INTERACTIVE */
2260
2113static int FAST_FUNC file_get(struct in_str *i) 2261static int FAST_FUNC file_get(struct in_str *i)
2114{ 2262{
2115 int ch; 2263 int ch;
2116 2264
2117 /* If there is data waiting, eat it up */ 2265#if ENABLE_FEATURE_EDITING
2118 if (i->p && *i->p) { 2266 /* This can be stdin, check line editing char[] buffer */
2119#if ENABLE_HUSH_INTERACTIVE 2267 if (i->p && *i->p != '\0') {
2120 take_cached: 2268 ch = (unsigned char)*i->p++;
2121#endif 2269 goto out;
2122 ch = *i->p++; 2270 }
2123 if (i->eof_flag && !*i->p)
2124 ch = EOF;
2125 /* note: ch is never NUL */
2126 } else {
2127 /* need to double check i->file because we might be doing something
2128 * more complicated by now, like sourcing or substituting. */
2129#if ENABLE_HUSH_INTERACTIVE
2130 if (G_interactive_fd && i->file == stdin) {
2131 do {
2132 get_user_input(i);
2133 } while (!*i->p); /* need non-empty line */
2134 i->promptmode = 1; /* PS2 */
2135 goto take_cached;
2136 }
2137#endif 2271#endif
2138 do ch = fgetc(i->file); while (ch == '\0'); 2272 /* peek_buf[] is an int array, not char. Can contain EOF. */
2273 ch = i->peek_buf[0];
2274 if (ch != 0) {
2275 int ch2 = i->peek_buf[1];
2276 i->peek_buf[0] = ch2;
2277 if (ch2 == 0) /* very likely, avoid redundant write */
2278 goto out;
2279 i->peek_buf[1] = 0;
2280 goto out;
2139 } 2281 }
2282
2283 ch = fgetc_interactive(i);
2284 out:
2140 debug_printf("file_get: got '%c' %d\n", ch, ch); 2285 debug_printf("file_get: got '%c' %d\n", ch, ch);
2141 i->last_char = ch; 2286 i->last_char = ch;
2142 return ch; 2287 return ch;
2143} 2288}
2144 2289
2145/* All callers guarantee this routine will never
2146 * be used right after a newline, so prompting is not needed.
2147 */
2148static int FAST_FUNC file_peek(struct in_str *i) 2290static int FAST_FUNC file_peek(struct in_str *i)
2149{ 2291{
2150 int ch; 2292 int ch;
2151 if (i->p && *i->p) { 2293
2152 if (i->eof_flag && !i->p[1]) 2294#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
2153 return EOF; 2295 /* This can be stdin, check line editing char[] buffer */
2154 return *i->p; 2296 if (i->p && *i->p != '\0')
2155 /* note: ch is never NUL */ 2297 return (unsigned char)*i->p;
2298#endif
2299 /* peek_buf[] is an int array, not char. Can contain EOF. */
2300 ch = i->peek_buf[0];
2301 if (ch != 0)
2302 return ch;
2303
2304 /* Need to get a new char */
2305 ch = fgetc_interactive(i);
2306 debug_printf("file_peek: got '%c' %d\n", ch, ch);
2307
2308 /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
2309#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
2310 if (i->p) {
2311 i->p -= 1;
2312 return ch;
2156 } 2313 }
2157 do ch = fgetc(i->file); while (ch == '\0'); 2314#endif
2158 i->eof_flag = (ch == EOF);
2159 i->peek_buf[0] = ch; 2315 i->peek_buf[0] = ch;
2160 i->peek_buf[1] = '\0'; 2316 /*i->peek_buf[1] = 0; - already is */
2161 i->p = i->peek_buf; 2317 return ch;
2162 debug_printf("file_peek: got '%c' %d\n", ch, ch); 2318}
2319
2320static int FAST_FUNC static_get(struct in_str *i)
2321{
2322 int ch = (unsigned char)*i->p;
2323 if (ch != '\0') {
2324 i->p++;
2325 i->last_char = ch;
2326 return ch;
2327 }
2328 return EOF;
2329}
2330
2331static int FAST_FUNC static_peek(struct in_str *i)
2332{
2333 /* Doesn't report EOF on NUL. None of the callers care. */
2334 return (unsigned char)*i->p;
2335}
2336
2337/* Only ever called if i_peek() was called, and did not return EOF.
2338 * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL,
2339 * not end-of-line. Therefore we never need to read a new editing line here.
2340 */
2341static int i_peek2(struct in_str *i)
2342{
2343 int ch;
2344
2345 /* There are two cases when i->p[] buffer exists.
2346 * (1) it's a string in_str.
2347 * (2) It's a file, and we have a saved line editing buffer.
2348 * In both cases, we know that i->p[0] exists and not NUL, and
2349 * the peek2 result is in i->p[1].
2350 */
2351 if (i->p)
2352 return (unsigned char)i->p[1];
2353
2354 /* Now we know it is a file-based in_str. */
2355
2356 /* peek_buf[] is an int array, not char. Can contain EOF. */
2357 /* Is there 2nd char? */
2358 ch = i->peek_buf[1];
2359 if (ch == 0) {
2360 /* We did not read it yet, get it now */
2361 do ch = fgetc(i->file); while (ch == '\0');
2362 i->peek_buf[1] = ch;
2363 }
2364
2365 debug_printf("file_peek2: got '%c' %d\n", ch, ch);
2163 return ch; 2366 return ch;
2164} 2367}
2165 2368
2166static void setup_file_in_str(struct in_str *i, FILE *f) 2369static void setup_file_in_str(struct in_str *i, FILE *f)
2167{ 2370{
2168 memset(i, 0, sizeof(*i)); 2371 memset(i, 0, sizeof(*i));
2169 i->peek = file_peek;
2170 i->get = file_get; 2372 i->get = file_get;
2373 i->peek = file_peek;
2171 /* i->promptmode = 0; - PS1 (memset did it) */ 2374 /* i->promptmode = 0; - PS1 (memset did it) */
2172 i->file = f; 2375 i->file = f;
2173 /* i->p = NULL; */ 2376 /* i->p = NULL; */
@@ -2176,11 +2379,10 @@ static void setup_file_in_str(struct in_str *i, FILE *f)
2176static void setup_string_in_str(struct in_str *i, const char *s) 2379static void setup_string_in_str(struct in_str *i, const char *s)
2177{ 2380{
2178 memset(i, 0, sizeof(*i)); 2381 memset(i, 0, sizeof(*i));
2179 i->peek = static_peek;
2180 i->get = static_get; 2382 i->get = static_get;
2383 i->peek = static_peek;
2181 /* i->promptmode = 0; - PS1 (memset did it) */ 2384 /* i->promptmode = 0; - PS1 (memset did it) */
2182 i->p = s; 2385 i->p = s;
2183 /* i->eof_flag = 0; */
2184} 2386}
2185 2387
2186 2388
@@ -2211,7 +2413,7 @@ static ALWAYS_INLINE void o_free_unsafe(o_string *o)
2211static void o_grow_by(o_string *o, int len) 2413static void o_grow_by(o_string *o, int len)
2212{ 2414{
2213 if (o->length + len > o->maxlen) { 2415 if (o->length + len > o->maxlen) {
2214 o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); 2416 o->maxlen += (2 * len) | (B_CHUNK-1);
2215 o->data = xrealloc(o->data, 1 + o->maxlen); 2417 o->data = xrealloc(o->data, 1 + o->maxlen);
2216 } 2418 }
2217} 2419}
@@ -2219,11 +2421,26 @@ static void o_grow_by(o_string *o, int len)
2219static void o_addchr(o_string *o, int ch) 2421static void o_addchr(o_string *o, int ch)
2220{ 2422{
2221 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); 2423 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
2424 if (o->length < o->maxlen) {
2425 /* likely. avoid o_grow_by() call */
2426 add:
2427 o->data[o->length] = ch;
2428 o->length++;
2429 o->data[o->length] = '\0';
2430 return;
2431 }
2222 o_grow_by(o, 1); 2432 o_grow_by(o, 1);
2223 o->data[o->length] = ch; 2433 goto add;
2224 o->length++; 2434}
2435
2436#if 0
2437/* Valid only if we know o_string is not empty */
2438static void o_delchr(o_string *o)
2439{
2440 o->length--;
2225 o->data[o->length] = '\0'; 2441 o->data[o->length] = '\0';
2226} 2442}
2443#endif
2227 2444
2228static void o_addblock(o_string *o, const char *str, int len) 2445static void o_addblock(o_string *o, const char *str, int len)
2229{ 2446{
@@ -2937,6 +3154,14 @@ static int done_command(struct parse_context *ctx)
2937 struct pipe *pi = ctx->pipe; 3154 struct pipe *pi = ctx->pipe;
2938 struct command *command = ctx->command; 3155 struct command *command = ctx->command;
2939 3156
3157#if 0 /* Instead we emit error message at run time */
3158 if (ctx->pending_redirect) {
3159 /* For example, "cmd >" (no filename to redirect to) */
3160 die_if_script("syntax error: %s", "invalid redirect");
3161 ctx->pending_redirect = NULL;
3162 }
3163#endif
3164
2940 if (command) { 3165 if (command) {
2941 if (IS_NULL_CMD(command)) { 3166 if (IS_NULL_CMD(command)) {
2942 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); 3167 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
@@ -3459,6 +3684,12 @@ static int parse_redirect(struct parse_context *ctx,
3459 debug_printf_parse("duplicating redirect '%d>&%d'\n", 3684 debug_printf_parse("duplicating redirect '%d>&%d'\n",
3460 redir->rd_fd, redir->rd_dup); 3685 redir->rd_fd, redir->rd_dup);
3461 } else { 3686 } else {
3687#if 0 /* Instead we emit error message at run time */
3688 if (ctx->pending_redirect) {
3689 /* For example, "cmd > <file" */
3690 die_if_script("syntax error: %s", "invalid redirect");
3691 }
3692#endif
3462 /* Set ctx->pending_redirect, so we know what to do at the 3693 /* Set ctx->pending_redirect, so we know what to do at the
3463 * end of the next parsed word. */ 3694 * end of the next parsed word. */
3464 ctx->pending_redirect = redir; 3695 ctx->pending_redirect = redir;
@@ -3724,6 +3955,39 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
3724 /* command remains "open", available for possible redirects */ 3955 /* command remains "open", available for possible redirects */
3725} 3956}
3726 3957
3958static int i_getch_and_eat_bkslash_nl(struct in_str *input)
3959{
3960 for (;;) {
3961 int ch, ch2;
3962
3963 ch = i_getch(input);
3964 if (ch != '\\')
3965 return ch;
3966 ch2 = i_peek(input);
3967 if (ch2 != '\n')
3968 return ch;
3969 /* backslash+newline, skip it */
3970 i_getch(input);
3971 }
3972}
3973
3974static int i_peek_and_eat_bkslash_nl(struct in_str *input)
3975{
3976 for (;;) {
3977 int ch, ch2;
3978
3979 ch = i_peek(input);
3980 if (ch != '\\')
3981 return ch;
3982 ch2 = i_peek2(input);
3983 if (ch2 != '\n')
3984 return ch;
3985 /* backslash+newline, skip it */
3986 i_getch(input);
3987 i_getch(input);
3988 }
3989}
3990
3727#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS 3991#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
3728/* Subroutines for copying $(...) and `...` things */ 3992/* Subroutines for copying $(...) and `...` things */
3729static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); 3993static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
@@ -3841,7 +4105,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
3841 if (!dbl) 4105 if (!dbl)
3842 break; 4106 break;
3843 /* we look for closing )) of $((EXPR)) */ 4107 /* we look for closing )) of $((EXPR)) */
3844 if (i_peek(input) == end_ch) { 4108 if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
3845 i_getch(input); /* eat second ')' */ 4109 i_getch(input); /* eat second ')' */
3846 break; 4110 break;
3847 } 4111 }
@@ -3879,6 +4143,13 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
3879 syntax_error_unterm_ch(')'); 4143 syntax_error_unterm_ch(')');
3880 return 0; 4144 return 0;
3881 } 4145 }
4146#if 0
4147 if (ch == '\n') {
4148 /* "backslash+newline", ignore both */
4149 o_delchr(dest); /* undo insertion of '\' */
4150 continue;
4151 }
4152#endif
3882 o_addchr(dest, ch); 4153 o_addchr(dest, ch);
3883 continue; 4154 continue;
3884 } 4155 }
@@ -3897,7 +4168,7 @@ static int parse_dollar(o_string *as_string,
3897 o_string *dest, 4168 o_string *dest,
3898 struct in_str *input, unsigned char quote_mask) 4169 struct in_str *input, unsigned char quote_mask)
3899{ 4170{
3900 int ch = i_peek(input); /* first character after the $ */ 4171 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
3901 4172
3902 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); 4173 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
3903 if (isalpha(ch)) { 4174 if (isalpha(ch)) {
@@ -3909,9 +4180,11 @@ static int parse_dollar(o_string *as_string,
3909 debug_printf_parse(": '%c'\n", ch); 4180 debug_printf_parse(": '%c'\n", ch);
3910 o_addchr(dest, ch | quote_mask); 4181 o_addchr(dest, ch | quote_mask);
3911 quote_mask = 0; 4182 quote_mask = 0;
3912 ch = i_peek(input); 4183 ch = i_peek_and_eat_bkslash_nl(input);
3913 if (!isalnum(ch) && ch != '_') 4184 if (!isalnum(ch) && ch != '_') {
4185 /* End of variable name reached */
3914 break; 4186 break;
4187 }
3915 ch = i_getch(input); 4188 ch = i_getch(input);
3916 nommu_addchr(as_string, ch); 4189 nommu_addchr(as_string, ch);
3917 } 4190 }
@@ -3938,7 +4211,7 @@ static int parse_dollar(o_string *as_string,
3938 ch = i_getch(input); /* eat '{' */ 4211 ch = i_getch(input); /* eat '{' */
3939 nommu_addchr(as_string, ch); 4212 nommu_addchr(as_string, ch);
3940 4213
3941 ch = i_getch(input); /* first char after '{' */ 4214 ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
3942 /* It should be ${?}, or ${#var}, 4215 /* It should be ${?}, or ${#var},
3943 * or even ${?+subst} - operator acting on a special variable, 4216 * or even ${?+subst} - operator acting on a special variable,
3944 * or the beginning of variable name. 4217 * or the beginning of variable name.
@@ -4044,7 +4317,7 @@ static int parse_dollar(o_string *as_string,
4044 ch = i_getch(input); 4317 ch = i_getch(input);
4045 nommu_addchr(as_string, ch); 4318 nommu_addchr(as_string, ch);
4046# if ENABLE_SH_MATH_SUPPORT 4319# if ENABLE_SH_MATH_SUPPORT
4047 if (i_peek(input) == '(') { 4320 if (i_peek_and_eat_bkslash_nl(input) == '(') {
4048 ch = i_getch(input); 4321 ch = i_getch(input);
4049 nommu_addchr(as_string, ch); 4322 nommu_addchr(as_string, ch);
4050 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4323 o_addchr(dest, SPECIAL_VAR_SYMBOL);
@@ -4081,7 +4354,7 @@ static int parse_dollar(o_string *as_string,
4081 case '_': 4354 case '_':
4082 ch = i_getch(input); 4355 ch = i_getch(input);
4083 nommu_addchr(as_string, ch); 4356 nommu_addchr(as_string, ch);
4084 ch = i_peek(input); 4357 ch = i_peek_and_eat_bkslash_nl(input);
4085 if (isalnum(ch)) { /* it's $_name or $_123 */ 4358 if (isalnum(ch)) { /* it's $_name or $_123 */
4086 ch = '_'; 4359 ch = '_';
4087 goto make_var; 4360 goto make_var;
@@ -5548,7 +5821,7 @@ static char* expand_strvec_to_string(char **argv)
5548 n++; 5821 n++;
5549 } 5822 }
5550 } 5823 }
5551 overlapping_strcpy((char*)list, list[0]); 5824 overlapping_strcpy((char*)list, list[0] ? list[0] : "");
5552 debug_printf_expand("strvec_to_string='%s'\n", (char*)list); 5825 debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
5553 return (char*)list; 5826 return (char*)list;
5554} 5827}
@@ -5825,10 +6098,8 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
5825 debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); 6098 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
5826 run_and_free_list(pipe_list); 6099 run_and_free_list(pipe_list);
5827 empty = 0; 6100 empty = 0;
5828#if ENABLE_HUSH_FUNCTIONS 6101 if (G_flag_return_in_progress == 1)
5829 if (G.flag_return_in_progress == 1)
5830 break; 6102 break;
5831#endif
5832 } 6103 }
5833} 6104}
5834 6105
@@ -5913,7 +6184,8 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5913 ) { 6184 ) {
5914 static const char *const argv[] = { NULL, NULL }; 6185 static const char *const argv[] = { NULL, NULL };
5915 builtin_trap((char**)argv); 6186 builtin_trap((char**)argv);
5916 exit(0); /* not _exit() - we need to fflush */ 6187 fflush_all(); /* important */
6188 _exit(0);
5917 } 6189 }
5918# if BB_MMU 6190# if BB_MMU
5919 reset_traps_to_defaults(); 6191 reset_traps_to_defaults();
@@ -5946,8 +6218,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5946 free(to_free); 6218 free(to_free);
5947# endif 6219# endif
5948 close(channel[1]); 6220 close(channel[1]);
5949 close_on_exec_on(channel[0]); 6221 return remember_FILE(xfdopen_for_read(channel[0]));
5950 return xfdopen_for_read(channel[0]);
5951} 6222}
5952 6223
5953/* Return code is exit status of the process that is run. */ 6224/* Return code is exit status of the process that is run. */
@@ -5976,7 +6247,7 @@ static int process_command_subs(o_string *dest, const char *s)
5976 } 6247 }
5977 6248
5978 debug_printf("done reading from `cmd` pipe, closing it\n"); 6249 debug_printf("done reading from `cmd` pipe, closing it\n");
5979 fclose(fp); 6250 fclose_and_forget(fp);
5980 /* We need to extract exitcode. Test case 6251 /* We need to extract exitcode. Test case
5981 * "true; echo `sleep 1; false` $?" 6252 * "true; echo `sleep 1; false` $?"
5982 * should print 1 */ 6253 * should print 1 */
@@ -6068,6 +6339,74 @@ static void setup_heredoc(struct redir_struct *redir)
6068 wait(NULL); /* wait till child has died */ 6339 wait(NULL); /* wait till child has died */
6069} 6340}
6070 6341
6342/* fd: redirect wants this fd to be used (e.g. 3>file).
6343 * Move all conflicting internally used fds,
6344 * and remember them so that we can restore them later.
6345 */
6346static int save_fds_on_redirect(int fd, int squirrel[3])
6347{
6348 if (squirrel) {
6349 /* Handle redirects of fds 0,1,2 */
6350
6351 /* If we collide with an already moved stdio fd... */
6352 if (fd == squirrel[0]) {
6353 squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD);
6354 return 1;
6355 }
6356 if (fd == squirrel[1]) {
6357 squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD);
6358 return 1;
6359 }
6360 if (fd == squirrel[2]) {
6361 squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD);
6362 return 1;
6363 }
6364 /* If we are about to redirect stdio fd, and did not yet move it... */
6365 if (fd <= 2 && squirrel[fd] < 0) {
6366 /* We avoid taking stdio fds */
6367 squirrel[fd] = fcntl(fd, F_DUPFD, 10);
6368 if (squirrel[fd] < 0 && errno != EBADF)
6369 xfunc_die();
6370 return 0; /* "we did not close fd" */
6371 }
6372 }
6373
6374#if ENABLE_HUSH_INTERACTIVE
6375 if (fd != 0 && fd == G.interactive_fd) {
6376 G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC);
6377 return 1;
6378 }
6379#endif
6380
6381 /* Are we called from setup_redirects(squirrel==NULL)? Two cases:
6382 * (1) Redirect in a forked child. No need to save FILEs' fds,
6383 * we aren't going to use them anymore, ok to trash.
6384 * (2) "exec 3>FILE". Bummer. We can save FILEs' fds,
6385 * but how are we doing to use them?
6386 * "fileno(fd) = new_fd" can't be done.
6387 */
6388 if (!squirrel)
6389 return 0;
6390
6391 return save_FILEs_on_redirect(fd);
6392}
6393
6394static void restore_redirects(int squirrel[3])
6395{
6396 int i, fd;
6397 for (i = 0; i <= 2; i++) {
6398 fd = squirrel[i];
6399 if (fd != -1) {
6400 /* We simply die on error */
6401 xmove_fd(fd, i);
6402 }
6403 }
6404
6405 /* Moved G.interactive_fd stays on new fd, not doing anything for it */
6406
6407 restore_redirected_FILEs();
6408}
6409
6071/* squirrel != NULL means we squirrel away copies of stdin, stdout, 6410/* squirrel != NULL means we squirrel away copies of stdin, stdout,
6072 * and stderr if they are redirected. */ 6411 * and stderr if they are redirected. */
6073static int setup_redirects(struct command *prog, int squirrel[]) 6412static int setup_redirects(struct command *prog, int squirrel[])
@@ -6077,12 +6416,8 @@ static int setup_redirects(struct command *prog, int squirrel[])
6077 6416
6078 for (redir = prog->redirects; redir; redir = redir->next) { 6417 for (redir = prog->redirects; redir; redir = redir->next) {
6079 if (redir->rd_type == REDIRECT_HEREDOC2) { 6418 if (redir->rd_type == REDIRECT_HEREDOC2) {
6080 /* rd_fd<<HERE case */ 6419 /* "rd_fd<<HERE" case */
6081 if (squirrel && redir->rd_fd < 3 6420 save_fds_on_redirect(redir->rd_fd, squirrel);
6082 && squirrel[redir->rd_fd] < 0
6083 ) {
6084 squirrel[redir->rd_fd] = dup(redir->rd_fd);
6085 }
6086 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ 6421 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
6087 * of the heredoc */ 6422 * of the heredoc */
6088 debug_printf_parse("set heredoc '%s'\n", 6423 debug_printf_parse("set heredoc '%s'\n",
@@ -6092,12 +6427,15 @@ static int setup_redirects(struct command *prog, int squirrel[])
6092 } 6427 }
6093 6428
6094 if (redir->rd_dup == REDIRFD_TO_FILE) { 6429 if (redir->rd_dup == REDIRFD_TO_FILE) {
6095 /* rd_fd<*>file case (<*> is <,>,>>,<>) */ 6430 /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
6096 char *p; 6431 char *p;
6097 if (redir->rd_filename == NULL) { 6432 if (redir->rd_filename == NULL) {
6098 /* Something went wrong in the parse. 6433 /*
6099 * Pretend it didn't happen */ 6434 * Examples:
6100 bb_error_msg("bug in redirect parse"); 6435 * "cmd >" (no filename)
6436 * "cmd > <file" (2nd redirect starts too early)
6437 */
6438 die_if_script("syntax error: %s", "invalid redirect");
6101 continue; 6439 continue;
6102 } 6440 }
6103 mode = redir_table[redir->rd_type].mode; 6441 mode = redir_table[redir->rd_type].mode;
@@ -6105,47 +6443,39 @@ static int setup_redirects(struct command *prog, int squirrel[])
6105 openfd = open_or_warn(p, mode); 6443 openfd = open_or_warn(p, mode);
6106 free(p); 6444 free(p);
6107 if (openfd < 0) { 6445 if (openfd < 0) {
6108 /* this could get lost if stderr has been redirected, but 6446 /* Error message from open_or_warn can be lost
6109 * bash and ash both lose it as well (though zsh doesn't!) */ 6447 * if stderr has been redirected, but bash
6110//what the above comment tries to say? 6448 * and ash both lose it as well
6449 * (though zsh doesn't!)
6450 */
6111 return 1; 6451 return 1;
6112 } 6452 }
6113 } else { 6453 } else {
6114 /* rd_fd<*>rd_dup or rd_fd<*>- cases */ 6454 /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */
6115 openfd = redir->rd_dup; 6455 openfd = redir->rd_dup;
6116 } 6456 }
6117 6457
6118 if (openfd != redir->rd_fd) { 6458 if (openfd != redir->rd_fd) {
6119 if (squirrel && redir->rd_fd < 3 6459 int closed = save_fds_on_redirect(redir->rd_fd, squirrel);
6120 && squirrel[redir->rd_fd] < 0
6121 ) {
6122 squirrel[redir->rd_fd] = dup(redir->rd_fd);
6123 }
6124 if (openfd == REDIRFD_CLOSE) { 6460 if (openfd == REDIRFD_CLOSE) {
6125 /* "n>-" means "close me" */ 6461 /* "rd_fd >&-" means "close me" */
6126 close(redir->rd_fd); 6462 if (!closed) {
6463 /* ^^^ optimization: saving may already
6464 * have closed it. If not... */
6465 close(redir->rd_fd);
6466 }
6127 } else { 6467 } else {
6128 xdup2(openfd, redir->rd_fd); 6468 xdup2(openfd, redir->rd_fd);
6129 if (redir->rd_dup == REDIRFD_TO_FILE) 6469 if (redir->rd_dup == REDIRFD_TO_FILE)
6470 /* "rd_fd > FILE" */
6130 close(openfd); 6471 close(openfd);
6472 /* else: "rd_fd > rd_dup" */
6131 } 6473 }
6132 } 6474 }
6133 } 6475 }
6134 return 0; 6476 return 0;
6135} 6477}
6136 6478
6137static void restore_redirects(int squirrel[])
6138{
6139 int i, fd;
6140 for (i = 0; i < 3; i++) {
6141 fd = squirrel[i];
6142 if (fd != -1) {
6143 /* We simply die on error */
6144 xmove_fd(fd, i);
6145 }
6146 }
6147}
6148
6149static char *find_in_path(const char *arg) 6479static char *find_in_path(const char *arg)
6150{ 6480{
6151 char *ret = NULL; 6481 char *ret = NULL;
@@ -6329,8 +6659,8 @@ static int run_function(const struct function *funcp, char **argv)
6329 save_and_replace_G_args(&sv, argv); 6659 save_and_replace_G_args(&sv, argv);
6330 6660
6331 /* "we are in function, ok to use return" */ 6661 /* "we are in function, ok to use return" */
6332 sv_flg = G.flag_return_in_progress; 6662 sv_flg = G_flag_return_in_progress;
6333 G.flag_return_in_progress = -1; 6663 G_flag_return_in_progress = -1;
6334# if ENABLE_HUSH_LOCAL 6664# if ENABLE_HUSH_LOCAL
6335 G.func_nest_level++; 6665 G.func_nest_level++;
6336# endif 6666# endif
@@ -6371,7 +6701,7 @@ static int run_function(const struct function *funcp, char **argv)
6371 G.func_nest_level--; 6701 G.func_nest_level--;
6372 } 6702 }
6373# endif 6703# endif
6374 G.flag_return_in_progress = sv_flg; 6704 G_flag_return_in_progress = sv_flg;
6375 6705
6376 restore_G_args(&sv, argv); 6706 restore_G_args(&sv, argv);
6377 6707
@@ -6417,13 +6747,17 @@ static void exec_builtin(char ***to_free,
6417static void execvp_or_die(char **argv) NORETURN; 6747static void execvp_or_die(char **argv) NORETURN;
6418static void execvp_or_die(char **argv) 6748static void execvp_or_die(char **argv)
6419{ 6749{
6750 int e;
6420 debug_printf_exec("execing '%s'\n", argv[0]); 6751 debug_printf_exec("execing '%s'\n", argv[0]);
6421 /* Don't propagate SIG_IGN to the child */ 6752 /* Don't propagate SIG_IGN to the child */
6422 if (SPECIAL_JOBSTOP_SIGS != 0) 6753 if (SPECIAL_JOBSTOP_SIGS != 0)
6423 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); 6754 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
6424 execvp(argv[0], argv); 6755 execvp(argv[0], argv);
6756 e = 2;
6757 if (errno == EACCES) e = 126;
6758 if (errno == ENOENT) e = 127;
6425 bb_perror_msg("can't execute '%s'", argv[0]); 6759 bb_perror_msg("can't execute '%s'", argv[0]);
6426 _exit(127); /* bash compat */ 6760 _exit(e);
6427} 6761}
6428 6762
6429#if ENABLE_HUSH_MODE_X 6763#if ENABLE_HUSH_MODE_X
@@ -6466,7 +6800,8 @@ static void dump_cmd_in_x_mode(char **argv)
6466 * Never returns. 6800 * Never returns.
6467 * Don't exit() here. If you don't exec, use _exit instead. 6801 * Don't exit() here. If you don't exec, use _exit instead.
6468 * The at_exit handlers apparently confuse the calling process, 6802 * The at_exit handlers apparently confuse the calling process,
6469 * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ 6803 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
6804 */
6470static void pseudo_exec_argv(nommu_save_t *nommu_save, 6805static void pseudo_exec_argv(nommu_save_t *nommu_save,
6471 char **argv, int assignment_cnt, 6806 char **argv, int assignment_cnt,
6472 char **argv_expanded) NORETURN; 6807 char **argv_expanded) NORETURN;
@@ -6546,6 +6881,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
6546 if (a >= 0) { 6881 if (a >= 0) {
6547# if BB_MMU /* see above why on NOMMU it is not allowed */ 6882# if BB_MMU /* see above why on NOMMU it is not allowed */
6548 if (APPLET_IS_NOEXEC(a)) { 6883 if (APPLET_IS_NOEXEC(a)) {
6884 /* Do not leak open fds from opened script files etc */
6885 close_all_FILE_list();
6549 debug_printf_exec("running applet '%s'\n", argv[0]); 6886 debug_printf_exec("running applet '%s'\n", argv[0]);
6550 run_applet_no_and_exit(a, argv); 6887 run_applet_no_and_exit(a, argv);
6551 } 6888 }
@@ -7121,6 +7458,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7121 if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { 7458 if (x->b_function == builtin_exec && argv_expanded[1] == NULL) {
7122 debug_printf("exec with redirects only\n"); 7459 debug_printf("exec with redirects only\n");
7123 rcode = setup_redirects(command, NULL); 7460 rcode = setup_redirects(command, NULL);
7461 /* rcode=1 can be if redir file can't be opened */
7124 goto clean_up_and_ret1; 7462 goto clean_up_and_ret1;
7125 } 7463 }
7126 } 7464 }
@@ -7247,9 +7585,20 @@ static NOINLINE int run_pipe(struct pipe *pi)
7247 if (pipefds.rd > 1) 7585 if (pipefds.rd > 1)
7248 close(pipefds.rd); 7586 close(pipefds.rd);
7249 /* Like bash, explicit redirects override pipes, 7587 /* Like bash, explicit redirects override pipes,
7250 * and the pipe fd is available for dup'ing. */ 7588 * and the pipe fd (fd#1) is available for dup'ing:
7251 if (setup_redirects(command, NULL)) 7589 * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
7590 * of cmd1 goes into pipe.
7591 */
7592 if (setup_redirects(command, NULL)) {
7593 /* Happens when redir file can't be opened:
7594 * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
7595 * FOO
7596 * hush: can't open '/qwe/rty': No such file or directory
7597 * BAZ
7598 * (echo BAR is not executed, it hits _exit(1) below)
7599 */
7252 _exit(1); 7600 _exit(1);
7601 }
7253 7602
7254 /* Stores to nommu_save list of env vars putenv'ed 7603 /* Stores to nommu_save list of env vars putenv'ed
7255 * (NOMMU, on MMU we don't need that) */ 7604 * (NOMMU, on MMU we don't need that) */
@@ -7380,6 +7729,8 @@ static int run_list(struct pipe *pi)
7380 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { 7729 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
7381 if (G.flag_SIGINT) 7730 if (G.flag_SIGINT)
7382 break; 7731 break;
7732 if (G_flag_return_in_progress == 1)
7733 break;
7383 7734
7384 IF_HAS_KEYWORDS(rword = pi->res_word;) 7735 IF_HAS_KEYWORDS(rword = pi->res_word;)
7385 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", 7736 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
@@ -7550,12 +7901,10 @@ static int run_list(struct pipe *pi)
7550 continue; 7901 continue;
7551 } 7902 }
7552#endif 7903#endif
7553#if ENABLE_HUSH_FUNCTIONS 7904 if (G_flag_return_in_progress == 1) {
7554 if (G.flag_return_in_progress == 1) {
7555 checkjobs(NULL); 7905 checkjobs(NULL);
7556 break; 7906 break;
7557 } 7907 }
7558#endif
7559 } else if (pi->followup == PIPE_BG) { 7908 } else if (pi->followup == PIPE_BG) {
7560 /* What does bash do with attempts to background builtins? */ 7909 /* What does bash do with attempts to background builtins? */
7561 /* even bash 3.2 doesn't do that well with nested bg: 7910 /* even bash 3.2 doesn't do that well with nested bg:
@@ -7787,6 +8136,7 @@ int hush_main(int argc, char **argv)
7787 INIT_G(); 8136 INIT_G();
7788 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 8137 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
7789 G.last_exitcode = EXIT_SUCCESS; 8138 G.last_exitcode = EXIT_SUCCESS;
8139
7790#if ENABLE_HUSH_FAST 8140#if ENABLE_HUSH_FAST
7791 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 8141 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
7792#endif 8142#endif
@@ -7876,7 +8226,7 @@ int hush_main(int argc, char **argv)
7876 /* Initialize some more globals to non-zero values */ 8226 /* Initialize some more globals to non-zero values */
7877 cmdedit_update_prompt(); 8227 cmdedit_update_prompt();
7878 8228
7879 die_func = xfunc_has_died; 8229 die_func = restore_ttypgrp_and__exit;
7880 8230
7881 /* Shell is non-interactive at first. We need to call 8231 /* Shell is non-interactive at first. We need to call
7882 * install_special_sighandlers() if we are going to execute "sh <script>", 8232 * install_special_sighandlers() if we are going to execute "sh <script>",
@@ -8035,10 +8385,10 @@ int hush_main(int argc, char **argv)
8035 debug_printf("sourcing /etc/profile\n"); 8385 debug_printf("sourcing /etc/profile\n");
8036 input = fopen_for_read("/etc/profile"); 8386 input = fopen_for_read("/etc/profile");
8037 if (input != NULL) { 8387 if (input != NULL) {
8038 close_on_exec_on(fileno(input)); 8388 remember_FILE(input);
8039 install_special_sighandlers(); 8389 install_special_sighandlers();
8040 parse_and_run_file(input); 8390 parse_and_run_file(input);
8041 fclose(input); 8391 fclose_and_forget(input);
8042 } 8392 }
8043 /* bash: after sourcing /etc/profile, 8393 /* bash: after sourcing /etc/profile,
8044 * tries to source (in the given order): 8394 * tries to source (in the given order):
@@ -8060,11 +8410,11 @@ int hush_main(int argc, char **argv)
8060 G.global_argv++; 8410 G.global_argv++;
8061 debug_printf("running script '%s'\n", G.global_argv[0]); 8411 debug_printf("running script '%s'\n", G.global_argv[0]);
8062 input = xfopen_for_read(G.global_argv[0]); 8412 input = xfopen_for_read(G.global_argv[0]);
8063 close_on_exec_on(fileno(input)); 8413 remember_FILE(input);
8064 install_special_sighandlers(); 8414 install_special_sighandlers();
8065 parse_and_run_file(input); 8415 parse_and_run_file(input);
8066#if ENABLE_FEATURE_CLEAN_UP 8416#if ENABLE_FEATURE_CLEAN_UP
8067 fclose(input); 8417 fclose_and_forget(input);
8068#endif 8418#endif
8069 goto final_return; 8419 goto final_return;
8070 } 8420 }
@@ -8205,7 +8555,7 @@ int hush_main(int argc, char **argv)
8205int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 8555int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
8206int msh_main(int argc, char **argv) 8556int msh_main(int argc, char **argv)
8207{ 8557{
8208 //bb_error_msg("msh is deprecated, please use hush instead"); 8558 bb_error_msg("msh is deprecated, please use hush instead");
8209 return hush_main(argc, argv); 8559 return hush_main(argc, argv);
8210} 8560}
8211#endif 8561#endif
@@ -8399,6 +8749,14 @@ static void helper_export_local(char **argv, int exp, int lvl)
8399 continue; 8749 continue;
8400 } 8750 }
8401 } 8751 }
8752#if ENABLE_HUSH_LOCAL
8753 if (exp == 0 /* local? */
8754 && var && var->func_nest_level == lvl
8755 ) {
8756 /* "local x=abc; ...; local x" - ignore second local decl */
8757 continue;
8758 }
8759#endif
8402 /* Exporting non-existing variable. 8760 /* Exporting non-existing variable.
8403 * bash does not put it in environment, 8761 * bash does not put it in environment,
8404 * but remembers that it is exported, 8762 * but remembers that it is exported,
@@ -8473,6 +8831,212 @@ static int FAST_FUNC builtin_local(char **argv)
8473} 8831}
8474#endif 8832#endif
8475 8833
8834/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
8835static int FAST_FUNC builtin_unset(char **argv)
8836{
8837 int ret;
8838 unsigned opts;
8839
8840 /* "!": do not abort on errors */
8841 /* "+": stop at 1st non-option */
8842 opts = getopt32(argv, "!+vf");
8843 if (opts == (unsigned)-1)
8844 return EXIT_FAILURE;
8845 if (opts == 3) {
8846 bb_error_msg("unset: -v and -f are exclusive");
8847 return EXIT_FAILURE;
8848 }
8849 argv += optind;
8850
8851 ret = EXIT_SUCCESS;
8852 while (*argv) {
8853 if (!(opts & 2)) { /* not -f */
8854 if (unset_local_var(*argv)) {
8855 /* unset <nonexistent_var> doesn't fail.
8856 * Error is when one tries to unset RO var.
8857 * Message was printed by unset_local_var. */
8858 ret = EXIT_FAILURE;
8859 }
8860 }
8861#if ENABLE_HUSH_FUNCTIONS
8862 else {
8863 unset_func(*argv);
8864 }
8865#endif
8866 argv++;
8867 }
8868 return ret;
8869}
8870
8871/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
8872 * built-in 'set' handler
8873 * SUSv3 says:
8874 * set [-abCefhmnuvx] [-o option] [argument...]
8875 * set [+abCefhmnuvx] [+o option] [argument...]
8876 * set -- [argument...]
8877 * set -o
8878 * set +o
8879 * Implementations shall support the options in both their hyphen and
8880 * plus-sign forms. These options can also be specified as options to sh.
8881 * Examples:
8882 * Write out all variables and their values: set
8883 * Set $1, $2, and $3 and set "$#" to 3: set c a b
8884 * Turn on the -x and -v options: set -xv
8885 * Unset all positional parameters: set --
8886 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
8887 * Set the positional parameters to the expansion of x, even if x expands
8888 * with a leading '-' or '+': set -- $x
8889 *
8890 * So far, we only support "set -- [argument...]" and some of the short names.
8891 */
8892static int FAST_FUNC builtin_set(char **argv)
8893{
8894 int n;
8895 char **pp, **g_argv;
8896 char *arg = *++argv;
8897
8898 if (arg == NULL) {
8899 struct variable *e;
8900 for (e = G.top_var; e; e = e->next)
8901 puts(e->varstr);
8902 return EXIT_SUCCESS;
8903 }
8904
8905 do {
8906 if (strcmp(arg, "--") == 0) {
8907 ++argv;
8908 goto set_argv;
8909 }
8910 if (arg[0] != '+' && arg[0] != '-')
8911 break;
8912 for (n = 1; arg[n]; ++n) {
8913 if (set_mode((arg[0] == '-'), arg[n], argv[1]))
8914 goto error;
8915 if (arg[n] == 'o' && argv[1])
8916 argv++;
8917 }
8918 } while ((arg = *++argv) != NULL);
8919 /* Now argv[0] is 1st argument */
8920
8921 if (arg == NULL)
8922 return EXIT_SUCCESS;
8923 set_argv:
8924
8925 /* NB: G.global_argv[0] ($0) is never freed/changed */
8926 g_argv = G.global_argv;
8927 if (G.global_args_malloced) {
8928 pp = g_argv;
8929 while (*++pp)
8930 free(*pp);
8931 g_argv[1] = NULL;
8932 } else {
8933 G.global_args_malloced = 1;
8934 pp = xzalloc(sizeof(pp[0]) * 2);
8935 pp[0] = g_argv[0]; /* retain $0 */
8936 g_argv = pp;
8937 }
8938 /* This realloc's G.global_argv */
8939 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
8940
8941 n = 1;
8942 while (*++pp)
8943 n++;
8944 G.global_argc = n;
8945
8946 return EXIT_SUCCESS;
8947
8948 /* Nothing known, so abort */
8949 error:
8950 bb_error_msg("set: %s: invalid option", arg);
8951 return EXIT_FAILURE;
8952}
8953
8954static int FAST_FUNC builtin_shift(char **argv)
8955{
8956 int n = 1;
8957 argv = skip_dash_dash(argv);
8958 if (argv[0]) {
8959 n = atoi(argv[0]);
8960 }
8961 if (n >= 0 && n < G.global_argc) {
8962 if (G.global_args_malloced) {
8963 int m = 1;
8964 while (m <= n)
8965 free(G.global_argv[m++]);
8966 }
8967 G.global_argc -= n;
8968 memmove(&G.global_argv[1], &G.global_argv[n+1],
8969 G.global_argc * sizeof(G.global_argv[0]));
8970 return EXIT_SUCCESS;
8971 }
8972 return EXIT_FAILURE;
8973}
8974
8975/* Interruptibility of read builtin in bash
8976 * (tested on bash-4.2.8 by sending signals (not by ^C)):
8977 *
8978 * Empty trap makes read ignore corresponding signal, for any signal.
8979 *
8980 * SIGINT:
8981 * - terminates non-interactive shell;
8982 * - interrupts read in interactive shell;
8983 * if it has non-empty trap:
8984 * - executes trap and returns to command prompt in interactive shell;
8985 * - executes trap and returns to read in non-interactive shell;
8986 * SIGTERM:
8987 * - is ignored (does not interrupt) read in interactive shell;
8988 * - terminates non-interactive shell;
8989 * if it has non-empty trap:
8990 * - executes trap and returns to read;
8991 * SIGHUP:
8992 * - terminates shell (regardless of interactivity);
8993 * if it has non-empty trap:
8994 * - executes trap and returns to read;
8995 */
8996static int FAST_FUNC builtin_read(char **argv)
8997{
8998 const char *r;
8999 char *opt_n = NULL;
9000 char *opt_p = NULL;
9001 char *opt_t = NULL;
9002 char *opt_u = NULL;
9003 const char *ifs;
9004 int read_flags;
9005
9006 /* "!": do not abort on errors.
9007 * Option string must start with "sr" to match BUILTIN_READ_xxx
9008 */
9009 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
9010 if (read_flags == (uint32_t)-1)
9011 return EXIT_FAILURE;
9012 argv += optind;
9013 ifs = get_local_var_value("IFS"); /* can be NULL */
9014
9015 again:
9016 r = shell_builtin_read(set_local_var_from_halves,
9017 argv,
9018 ifs,
9019 read_flags,
9020 opt_n,
9021 opt_p,
9022 opt_t,
9023 opt_u
9024 );
9025
9026 if ((uintptr_t)r == 1 && errno == EINTR) {
9027 unsigned sig = check_and_run_traps();
9028 if (sig && sig != SIGINT)
9029 goto again;
9030 }
9031
9032 if ((uintptr_t)r > 1) {
9033 bb_error_msg("%s", r);
9034 r = (char*)(uintptr_t)1;
9035 }
9036
9037 return (uintptr_t)r;
9038}
9039
8476static int FAST_FUNC builtin_trap(char **argv) 9040static int FAST_FUNC builtin_trap(char **argv)
8477{ 9041{
8478 int sig; 9042 int sig;
@@ -8671,7 +9235,6 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
8671 if (x->b_descr) 9235 if (x->b_descr)
8672 printf("%-10s%s\n", x->b_cmd, x->b_descr); 9236 printf("%-10s%s\n", x->b_cmd, x->b_descr);
8673 } 9237 }
8674 bb_putchar('\n');
8675 return EXIT_SUCCESS; 9238 return EXIT_SUCCESS;
8676} 9239}
8677#endif 9240#endif
@@ -8721,6 +9284,15 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
8721 if (l < (unsigned long)p) l = (unsigned long)p; 9284 if (l < (unsigned long)p) l = (unsigned long)p;
8722 free(p); 9285 free(p);
8723 9286
9287
9288# if 0 /* debug */
9289 {
9290 struct mallinfo mi = mallinfo();
9291 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
9292 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
9293 }
9294# endif
9295
8724 if (!G.memleak_value) 9296 if (!G.memleak_value)
8725 G.memleak_value = l; 9297 G.memleak_value = l;
8726 9298
@@ -8742,175 +9314,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
8742 return EXIT_SUCCESS; 9314 return EXIT_SUCCESS;
8743} 9315}
8744 9316
8745/* Interruptibility of read builtin in bash
8746 * (tested on bash-4.2.8 by sending signals (not by ^C)):
8747 *
8748 * Empty trap makes read ignore corresponding signal, for any signal.
8749 *
8750 * SIGINT:
8751 * - terminates non-interactive shell;
8752 * - interrupts read in interactive shell;
8753 * if it has non-empty trap:
8754 * - executes trap and returns to command prompt in interactive shell;
8755 * - executes trap and returns to read in non-interactive shell;
8756 * SIGTERM:
8757 * - is ignored (does not interrupt) read in interactive shell;
8758 * - terminates non-interactive shell;
8759 * if it has non-empty trap:
8760 * - executes trap and returns to read;
8761 * SIGHUP:
8762 * - terminates shell (regardless of interactivity);
8763 * if it has non-empty trap:
8764 * - executes trap and returns to read;
8765 */
8766static int FAST_FUNC builtin_read(char **argv)
8767{
8768 const char *r;
8769 char *opt_n = NULL;
8770 char *opt_p = NULL;
8771 char *opt_t = NULL;
8772 char *opt_u = NULL;
8773 const char *ifs;
8774 int read_flags;
8775
8776 /* "!": do not abort on errors.
8777 * Option string must start with "sr" to match BUILTIN_READ_xxx
8778 */
8779 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
8780 if (read_flags == (uint32_t)-1)
8781 return EXIT_FAILURE;
8782 argv += optind;
8783 ifs = get_local_var_value("IFS"); /* can be NULL */
8784
8785 again:
8786 r = shell_builtin_read(set_local_var_from_halves,
8787 argv,
8788 ifs,
8789 read_flags,
8790 opt_n,
8791 opt_p,
8792 opt_t,
8793 opt_u
8794 );
8795
8796 if ((uintptr_t)r == 1 && errno == EINTR) {
8797 unsigned sig = check_and_run_traps();
8798 if (sig && sig != SIGINT)
8799 goto again;
8800 }
8801
8802 if ((uintptr_t)r > 1) {
8803 bb_error_msg("%s", r);
8804 r = (char*)(uintptr_t)1;
8805 }
8806
8807 return (uintptr_t)r;
8808}
8809
8810/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
8811 * built-in 'set' handler
8812 * SUSv3 says:
8813 * set [-abCefhmnuvx] [-o option] [argument...]
8814 * set [+abCefhmnuvx] [+o option] [argument...]
8815 * set -- [argument...]
8816 * set -o
8817 * set +o
8818 * Implementations shall support the options in both their hyphen and
8819 * plus-sign forms. These options can also be specified as options to sh.
8820 * Examples:
8821 * Write out all variables and their values: set
8822 * Set $1, $2, and $3 and set "$#" to 3: set c a b
8823 * Turn on the -x and -v options: set -xv
8824 * Unset all positional parameters: set --
8825 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
8826 * Set the positional parameters to the expansion of x, even if x expands
8827 * with a leading '-' or '+': set -- $x
8828 *
8829 * So far, we only support "set -- [argument...]" and some of the short names.
8830 */
8831static int FAST_FUNC builtin_set(char **argv)
8832{
8833 int n;
8834 char **pp, **g_argv;
8835 char *arg = *++argv;
8836
8837 if (arg == NULL) {
8838 struct variable *e;
8839 for (e = G.top_var; e; e = e->next)
8840 puts(e->varstr);
8841 return EXIT_SUCCESS;
8842 }
8843
8844 do {
8845 if (strcmp(arg, "--") == 0) {
8846 ++argv;
8847 goto set_argv;
8848 }
8849 if (arg[0] != '+' && arg[0] != '-')
8850 break;
8851 for (n = 1; arg[n]; ++n) {
8852 if (set_mode((arg[0] == '-'), arg[n], argv[1]))
8853 goto error;
8854 if (arg[n] == 'o' && argv[1])
8855 argv++;
8856 }
8857 } while ((arg = *++argv) != NULL);
8858 /* Now argv[0] is 1st argument */
8859
8860 if (arg == NULL)
8861 return EXIT_SUCCESS;
8862 set_argv:
8863
8864 /* NB: G.global_argv[0] ($0) is never freed/changed */
8865 g_argv = G.global_argv;
8866 if (G.global_args_malloced) {
8867 pp = g_argv;
8868 while (*++pp)
8869 free(*pp);
8870 g_argv[1] = NULL;
8871 } else {
8872 G.global_args_malloced = 1;
8873 pp = xzalloc(sizeof(pp[0]) * 2);
8874 pp[0] = g_argv[0]; /* retain $0 */
8875 g_argv = pp;
8876 }
8877 /* This realloc's G.global_argv */
8878 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
8879
8880 n = 1;
8881 while (*++pp)
8882 n++;
8883 G.global_argc = n;
8884
8885 return EXIT_SUCCESS;
8886
8887 /* Nothing known, so abort */
8888 error:
8889 bb_error_msg("set: %s: invalid option", arg);
8890 return EXIT_FAILURE;
8891}
8892
8893static int FAST_FUNC builtin_shift(char **argv)
8894{
8895 int n = 1;
8896 argv = skip_dash_dash(argv);
8897 if (argv[0]) {
8898 n = atoi(argv[0]);
8899 }
8900 if (n >= 0 && n < G.global_argc) {
8901 if (G.global_args_malloced) {
8902 int m = 1;
8903 while (m <= n)
8904 free(G.global_argv[m++]);
8905 }
8906 G.global_argc -= n;
8907 memmove(&G.global_argv[1], &G.global_argv[n+1],
8908 G.global_argc * sizeof(G.global_argv[0]));
8909 return EXIT_SUCCESS;
8910 }
8911 return EXIT_FAILURE;
8912}
8913
8914static int FAST_FUNC builtin_source(char **argv) 9317static int FAST_FUNC builtin_source(char **argv)
8915{ 9318{
8916 char *arg_path, *filename; 9319 char *arg_path, *filename;
@@ -8932,7 +9335,7 @@ static int FAST_FUNC builtin_source(char **argv)
8932 if (arg_path) 9335 if (arg_path)
8933 filename = arg_path; 9336 filename = arg_path;
8934 } 9337 }
8935 input = fopen_or_warn(filename, "r"); 9338 input = remember_FILE(fopen_or_warn(filename, "r"));
8936 free(arg_path); 9339 free(arg_path);
8937 if (!input) { 9340 if (!input) {
8938 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ 9341 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
@@ -8941,23 +9344,24 @@ static int FAST_FUNC builtin_source(char **argv)
8941 */ 9344 */
8942 return EXIT_FAILURE; 9345 return EXIT_FAILURE;
8943 } 9346 }
8944 close_on_exec_on(fileno(input));
8945 9347
8946#if ENABLE_HUSH_FUNCTIONS 9348#if ENABLE_HUSH_FUNCTIONS
8947 sv_flg = G.flag_return_in_progress; 9349 sv_flg = G_flag_return_in_progress;
8948 /* "we are inside sourced file, ok to use return" */ 9350 /* "we are inside sourced file, ok to use return" */
8949 G.flag_return_in_progress = -1; 9351 G_flag_return_in_progress = -1;
8950#endif 9352#endif
8951 if (argv[1]) 9353 if (argv[1])
8952 save_and_replace_G_args(&sv, argv); 9354 save_and_replace_G_args(&sv, argv);
8953 9355
9356 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
9357 G.last_exitcode = 0;
8954 parse_and_run_file(input); 9358 parse_and_run_file(input);
8955 fclose(input); 9359 fclose_and_forget(input);
8956 9360
8957 if (argv[1]) 9361 if (argv[1])
8958 restore_G_args(&sv, argv); 9362 restore_G_args(&sv, argv);
8959#if ENABLE_HUSH_FUNCTIONS 9363#if ENABLE_HUSH_FUNCTIONS
8960 G.flag_return_in_progress = sv_flg; 9364 G_flag_return_in_progress = sv_flg;
8961#endif 9365#endif
8962 9366
8963 return G.last_exitcode; 9367 return G.last_exitcode;
@@ -9000,43 +9404,6 @@ static int FAST_FUNC builtin_umask(char **argv)
9000 return !rc; /* rc != 0 - success */ 9404 return !rc; /* rc != 0 - success */
9001} 9405}
9002 9406
9003/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
9004static int FAST_FUNC builtin_unset(char **argv)
9005{
9006 int ret;
9007 unsigned opts;
9008
9009 /* "!": do not abort on errors */
9010 /* "+": stop at 1st non-option */
9011 opts = getopt32(argv, "!+vf");
9012 if (opts == (unsigned)-1)
9013 return EXIT_FAILURE;
9014 if (opts == 3) {
9015 bb_error_msg("unset: -v and -f are exclusive");
9016 return EXIT_FAILURE;
9017 }
9018 argv += optind;
9019
9020 ret = EXIT_SUCCESS;
9021 while (*argv) {
9022 if (!(opts & 2)) { /* not -f */
9023 if (unset_local_var(*argv)) {
9024 /* unset <nonexistent_var> doesn't fail.
9025 * Error is when one tries to unset RO var.
9026 * Message was printed by unset_local_var. */
9027 ret = EXIT_FAILURE;
9028 }
9029 }
9030#if ENABLE_HUSH_FUNCTIONS
9031 else {
9032 unset_func(*argv);
9033 }
9034#endif
9035 argv++;
9036 }
9037 return ret;
9038}
9039
9040/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ 9407/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
9041static int FAST_FUNC builtin_wait(char **argv) 9408static int FAST_FUNC builtin_wait(char **argv)
9042{ 9409{
@@ -9153,9 +9520,11 @@ static int FAST_FUNC builtin_break(char **argv)
9153 unsigned depth; 9520 unsigned depth;
9154 if (G.depth_of_loop == 0) { 9521 if (G.depth_of_loop == 0) {
9155 bb_error_msg("%s: only meaningful in a loop", argv[0]); 9522 bb_error_msg("%s: only meaningful in a loop", argv[0]);
9523 /* if we came from builtin_continue(), need to undo "= 1" */
9524 G.flag_break_continue = 0;
9156 return EXIT_SUCCESS; /* bash compat */ 9525 return EXIT_SUCCESS; /* bash compat */
9157 } 9526 }
9158 G.flag_break_continue++; /* BC_BREAK = 1 */ 9527 G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
9159 9528
9160 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); 9529 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
9161 if (depth == UINT_MAX) 9530 if (depth == UINT_MAX)
@@ -9178,12 +9547,12 @@ static int FAST_FUNC builtin_return(char **argv)
9178{ 9547{
9179 int rc; 9548 int rc;
9180 9549
9181 if (G.flag_return_in_progress != -1) { 9550 if (G_flag_return_in_progress != -1) {
9182 bb_error_msg("%s: not in a function or sourced script", argv[0]); 9551 bb_error_msg("%s: not in a function or sourced script", argv[0]);
9183 return EXIT_FAILURE; /* bash compat */ 9552 return EXIT_FAILURE; /* bash compat */
9184 } 9553 }
9185 9554
9186 G.flag_return_in_progress = 1; 9555 G_flag_return_in_progress = 1;
9187 9556
9188 /* bash: 9557 /* bash:
9189 * out of range: wraps around at 256, does not error out 9558 * out of range: wraps around at 256, does not error out
diff --git a/shell/hush_test/hush-bugs/var3.right b/shell/hush_test/hush-bugs/var3.right
deleted file mode 100644
index 8eb0e3337..000000000
--- a/shell/hush_test/hush-bugs/var3.right
+++ /dev/null
@@ -1,5 +0,0 @@
11
21
3
4
50
diff --git a/shell/hush_test/hush-bugs/var3.tests b/shell/hush_test/hush-bugs/var3.tests
deleted file mode 100755
index 97b102cbe..000000000
--- a/shell/hush_test/hush-bugs/var3.tests
+++ /dev/null
@@ -1 +0,0 @@
1x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x
diff --git a/shell/hush_test/hush-glob/bash_brace1.tests b/shell/hush_test/hush-glob/bash_brace1.tests
index eb2f0e974..eae2e2a84 100755
--- a/shell/hush_test/hush-glob/bash_brace1.tests
+++ b/shell/hush_test/hush-glob/bash_brace1.tests
@@ -4,7 +4,7 @@ v='*brace1.t*'; echo $v
4# ...but not brace expanded: 4# ...but not brace expanded:
5v='*{b,b}race1.t*'; echo $v 5v='*{b,b}race1.t*'; echo $v
6 6
7# whereas direct brces are expanded: 7# whereas direct braces are expanded:
8echo *{b,b}race1.t* 8echo *{b,b}race1.t*
9 9
10echo Done: $? 10echo Done: $?
diff --git a/shell/hush_test/hush-glob/glob_dir.right b/shell/hush_test/hush-glob/glob_dir.right
new file mode 100644
index 000000000..aa90514d5
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob_dir.right
@@ -0,0 +1,19 @@
1dirtest/z.tmp
2dirtest/z.tmp
3dirtest/z.tmp
4dirtest/z.tmp
5dirtest/z.tmp
6dirtest/z.tmp
7dirtest/z.tmp
8dirtest/z.tmp
9dirtest/z.tmp
10
11*/z.tmp
12*/z.*
13*/?.*
14*/z*p
15d*r*e*t/z*p
16*\/z.tmp
17*/z.*
18*/z*p
19d*r*e*t/z*p
diff --git a/shell/hush_test/hush-glob/glob_dir.tests b/shell/hush_test/hush-glob/glob_dir.tests
new file mode 100755
index 000000000..dc4c4fdb5
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob_dir.tests
@@ -0,0 +1,25 @@
1mkdir dirtest
2 >dirtest/z.tmp
3
4echo */z.tmp
5echo */z.*
6echo */?.*
7echo */z*p
8echo d*r*e*t/z*p
9echo *"/z.t"mp
10echo */z"."*
11echo *"/z"*"p"
12echo "d"*r*e*t"/"z*p
13echo
14echo \*/z.tmp
15echo "*"/z.*
16echo */"?".*
17echo */z"*p"
18echo d*r*e\*t/z*p
19echo *"\\/z.t"mp
20echo */z".*"
21echo *"/z"\*"p"
22echo "d*"r*e*t"/"z*p
23
24rm dirtest/z.tmp
25rmdir dirtest
diff --git a/shell/hush_test/hush-misc/heredoc1.right b/shell/hush_test/hush-heredoc/heredoc1.right
index 7fc68f3e1..7fc68f3e1 100644
--- a/shell/hush_test/hush-misc/heredoc1.right
+++ b/shell/hush_test/hush-heredoc/heredoc1.right
diff --git a/shell/hush_test/hush-misc/heredoc1.tests b/shell/hush_test/hush-heredoc/heredoc1.tests
index 2eeb4726b..2eeb4726b 100755
--- a/shell/hush_test/hush-misc/heredoc1.tests
+++ b/shell/hush_test/hush-heredoc/heredoc1.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc2.right b/shell/hush_test/hush-heredoc/heredoc2.right
new file mode 100644
index 000000000..a486f1ac4
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc2.right
@@ -0,0 +1,2 @@
1bar
2bar
diff --git a/shell/hush_test/hush-heredoc/heredoc2.tests b/shell/hush_test/hush-heredoc/heredoc2.tests
new file mode 100755
index 000000000..6d9ccb6cc
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc2.tests
@@ -0,0 +1,7 @@
1foo () {
2cat <<EOF && { echo "$1" ; }
3$1
4EOF
5}
6
7foo "bar"
diff --git a/shell/hush_test/hush-misc/heredoc3.right b/shell/hush_test/hush-heredoc/heredoc3.right
index 6ed517f74..6ed517f74 100644
--- a/shell/hush_test/hush-misc/heredoc3.right
+++ b/shell/hush_test/hush-heredoc/heredoc3.right
diff --git a/shell/hush_test/hush-misc/heredoc3.tests b/shell/hush_test/hush-heredoc/heredoc3.tests
index 938577a89..938577a89 100755
--- a/shell/hush_test/hush-misc/heredoc3.tests
+++ b/shell/hush_test/hush-heredoc/heredoc3.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc4.right b/shell/hush_test/hush-heredoc/heredoc4.right
new file mode 100644
index 000000000..371b092e2
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc4.right
@@ -0,0 +1 @@
'$'
diff --git a/shell/hush_test/hush-heredoc/heredoc4.tests b/shell/hush_test/hush-heredoc/heredoc4.tests
new file mode 100755
index 000000000..642ddb324
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc4.tests
@@ -0,0 +1,3 @@
1cat <<EOF
2'$'
3EOF
diff --git a/shell/hush_test/hush-heredoc/heredoc5.right b/shell/hush_test/hush-heredoc/heredoc5.right
new file mode 100644
index 000000000..74110e3b5
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc5.right
@@ -0,0 +1,9 @@
1exit EOF-f
2"
3echo 1
4echo Hello World
5moo
6 EOF-f
7EOF-f f
8EOF-f
9Ok
diff --git a/shell/hush_test/hush-heredoc/heredoc5.tests b/shell/hush_test/hush-heredoc/heredoc5.tests
new file mode 100755
index 000000000..e619bded1
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc5.tests
@@ -0,0 +1,12 @@
1f=1
2 cat <<- EOF-f
3 exit EOF-f
4"
5echo $f
6echo `echo Hello World`
7 moo
8 EOF-f
9EOF-f f
10EOF-f
11EOF-f
12echo Ok
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-heredoc/heredoc_backslash1.right
index 6a6114821..6a6114821 100644
--- a/shell/hush_test/hush-misc/heredoc_backslash1.right
+++ b/shell/hush_test/hush-heredoc/heredoc_backslash1.right
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-heredoc/heredoc_backslash1.tests
index 501af5490..501af5490 100755
--- a/shell/hush_test/hush-misc/heredoc_backslash1.tests
+++ b/shell/hush_test/hush-heredoc/heredoc_backslash1.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc_huge.right b/shell/hush_test/hush-heredoc/heredoc_huge.right
new file mode 100644
index 000000000..11740f674
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_huge.right
@@ -0,0 +1,3 @@
1546ed3f5c81c780d3ab86ada14824237 -
2546ed3f5c81c780d3ab86ada14824237 -
3End
diff --git a/shell/hush_test/hush-heredoc/heredoc_huge.tests b/shell/hush_test/hush-heredoc/heredoc_huge.tests
new file mode 100755
index 000000000..c2ec2817b
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_huge.tests
@@ -0,0 +1,9 @@
1# This creates 120k heredoc
2echo 'cat <<HERE | md5sum' >"$0.tmp"
3yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp"
4echo 'HERE' >>"$0.tmp"
5
6yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum
7. "$0.tmp"
8rm "$0.tmp"
9echo End
diff --git a/shell/hush_test/hush-invert/invert.right b/shell/hush_test/hush-invert/invert.right
new file mode 100644
index 000000000..5a9239aa6
--- /dev/null
+++ b/shell/hush_test/hush-invert/invert.right
@@ -0,0 +1,10 @@
11
21
31
40
50
61
70
81
90
101
diff --git a/shell/hush_test/hush-invert/invert.tests b/shell/hush_test/hush-invert/invert.tests
new file mode 100755
index 000000000..8393d95a4
--- /dev/null
+++ b/shell/hush_test/hush-invert/invert.tests
@@ -0,0 +1,19 @@
1# tests of return value inversion
2# placeholder for future expansion
3
4# user subshells (...) did this wrong in bash versions before 2.04
5
6! ( echo hello | grep h >/dev/null 2>&1 ); echo $?
7! echo hello | grep h >/dev/null 2>&1 ; echo $?
8
9! true ; echo $?
10! false; echo $?
11
12! (false) ; echo $?
13! (true); echo $?
14
15! true | false ; echo $?
16! false | true ; echo $?
17
18! (true | false) ; echo $?
19! (false | true) ; echo $?
diff --git a/shell/hush_test/hush-misc/assignment2.right b/shell/hush_test/hush-misc/assignment2.right
new file mode 100644
index 000000000..9d1860be7
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment2.right
@@ -0,0 +1,2 @@
1hush: can't execute 'a=b': No such file or directory
2127
diff --git a/shell/hush_test/hush-misc/assignment2.rigth b/shell/hush_test/hush-misc/assignment2.rigth
deleted file mode 100644
index 591552cde..000000000
--- a/shell/hush_test/hush-misc/assignment2.rigth
+++ /dev/null
@@ -1,2 +0,0 @@
1hush: can't exec 'a=b': No such file or directory
21
diff --git a/shell/hush_test/hush-misc/assignment2.tests b/shell/hush_test/hush-misc/assignment2.tests
index 540e01ec2..f6938434c 100755
--- a/shell/hush_test/hush-misc/assignment2.tests
+++ b/shell/hush_test/hush-misc/assignment2.tests
@@ -1,4 +1,3 @@
1# This must not be interpreted as an assignment 1# This must not be interpreted as an assignment
2a''=b true 2a''=b true
3echo $? 3echo $?
4# (buglet: $? should be 127. it is currently 1)
diff --git a/shell/hush_test/hush-misc/eval1.right b/shell/hush_test/hush-misc/eval1.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/hush_test/hush-misc/eval1.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/hush_test/hush-misc/eval1.tests b/shell/hush_test/hush-misc/eval1.tests
new file mode 100755
index 000000000..b78c6cc94
--- /dev/null
+++ b/shell/hush_test/hush-misc/eval1.tests
@@ -0,0 +1,4 @@
1# empty eval nevertheless sets $? = 0
2false
3eval
4echo Ok:$?
diff --git a/shell/hush_test/hush-misc/eval2.right b/shell/hush_test/hush-misc/eval2.right
new file mode 100644
index 000000000..a7ce6ccc7
--- /dev/null
+++ b/shell/hush_test/hush-misc/eval2.right
@@ -0,0 +1,3 @@
1Zero:0
2Zero:0
3Zero:0
diff --git a/shell/hush_test/hush-misc/eval2.tests b/shell/hush_test/hush-misc/eval2.tests
new file mode 100755
index 000000000..6bfb87aa7
--- /dev/null
+++ b/shell/hush_test/hush-misc/eval2.tests
@@ -0,0 +1,4 @@
1false; eval; echo Zero:$?
2false; eval ""; echo Zero:$?
3false; eval "
4"; echo Zero:$?
diff --git a/shell/hush_test/hush-misc/exitcode1.right b/shell/hush_test/hush-misc/exitcode1.right
new file mode 100644
index 000000000..e5fefefda
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode1.right
@@ -0,0 +1,2 @@
1One:1
2Zero:0
diff --git a/shell/hush_test/hush-misc/exitcode1.tests b/shell/hush_test/hush-misc/exitcode1.tests
new file mode 100755
index 000000000..dc8619d8b
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode1.tests
@@ -0,0 +1,2 @@
1false || case a in a) echo One:$?;; esac
2echo Zero:$?
diff --git a/shell/hush_test/hush-misc/exitcode_EACCES.right b/shell/hush_test/hush-misc/exitcode_EACCES.right
new file mode 100644
index 000000000..a80d55137
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_EACCES.right
@@ -0,0 +1,2 @@
1hush: can't execute './': Permission denied
2126
diff --git a/shell/hush_test/hush-misc/exitcode_EACCES.tests b/shell/hush_test/hush-misc/exitcode_EACCES.tests
new file mode 100755
index 000000000..26b5c6116
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_EACCES.tests
@@ -0,0 +1,2 @@
1./
2echo $?
diff --git a/shell/hush_test/hush-misc/exitcode_ENOENT.right b/shell/hush_test/hush-misc/exitcode_ENOENT.right
new file mode 100644
index 000000000..d8315503f
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_ENOENT.right
@@ -0,0 +1,2 @@
1hush: can't execute './does_not_exist_for_sure': No such file or directory
2127
diff --git a/shell/hush_test/hush-misc/exitcode_ENOENT.tests b/shell/hush_test/hush-misc/exitcode_ENOENT.tests
new file mode 100755
index 000000000..7f1b88a99
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_ENOENT.tests
@@ -0,0 +1,2 @@
1./does_not_exist_for_sure
2echo $?
diff --git a/shell/hush_test/hush-misc/func6.right b/shell/hush_test/hush-misc/func6.right
new file mode 100644
index 000000000..0ebd8e5a3
--- /dev/null
+++ b/shell/hush_test/hush-misc/func6.right
@@ -0,0 +1,2 @@
1Two:2
2Two:2
diff --git a/shell/hush_test/hush-misc/func6.tests b/shell/hush_test/hush-misc/func6.tests
new file mode 100755
index 000000000..029c3e85e
--- /dev/null
+++ b/shell/hush_test/hush-misc/func6.tests
@@ -0,0 +1,11 @@
1f1() {
2 while return 2; do :; done
3}
4f1
5echo Two:$?
6
7f2() {
8 while :; do return 2; done
9}
10f2
11echo Two:$?
diff --git a/shell/hush_test/hush-misc/last_amp.right b/shell/hush_test/hush-misc/last_amp.right
new file mode 100644
index 000000000..3da21aec2
--- /dev/null
+++ b/shell/hush_test/hush-misc/last_amp.right
@@ -0,0 +1,2 @@
13
2End
diff --git a/shell/hush_test/hush-misc/last_amp.tests b/shell/hush_test/hush-misc/last_amp.tests
new file mode 100755
index 000000000..160937644
--- /dev/null
+++ b/shell/hush_test/hush-misc/last_amp.tests
@@ -0,0 +1,8 @@
1$THIS_SH -c 'echo 3&'
2d=`date`
3while test "`date`" = "$d"; do true; done
4d1=`date`
5$THIS_SH -c 'sleep 1&'
6d2=`date`
7test "$d1" = "$d2" || echo BAD
8echo End
diff --git a/shell/hush_test/hush-misc/local1.right b/shell/hush_test/hush-misc/local1.right
new file mode 100644
index 000000000..a2d121df6
--- /dev/null
+++ b/shell/hush_test/hush-misc/local1.right
@@ -0,0 +1,4 @@
1A1:'A'
2A2:''
3A3:''
4A4:'A'
diff --git a/shell/hush_test/hush-misc/local1.tests b/shell/hush_test/hush-misc/local1.tests
new file mode 100755
index 000000000..b1e675059
--- /dev/null
+++ b/shell/hush_test/hush-misc/local1.tests
@@ -0,0 +1,11 @@
1a=A
2f() {
3 local a
4 # the above line unsets $a
5 echo "A2:'$a'"
6 unset a
7 echo "A3:'$a'"
8}
9echo "A1:'$a'"
10f
11echo "A4:'$a'"
diff --git a/shell/hush_test/hush-misc/nulltick1.right b/shell/hush_test/hush-misc/nulltick1.right
new file mode 100644
index 000000000..f90b8209e
--- /dev/null
+++ b/shell/hush_test/hush-misc/nulltick1.right
@@ -0,0 +1,3 @@
1Test 1
2Test 2
3Done
diff --git a/shell/hush_test/hush-misc/nulltick1.tests b/shell/hush_test/hush-misc/nulltick1.tests
new file mode 100755
index 000000000..f81923de3
--- /dev/null
+++ b/shell/hush_test/hush-misc/nulltick1.tests
@@ -0,0 +1,3 @@
1echo Test ` ` 1
2echo Test `</dev/null` 2
3echo Done
diff --git a/shell/hush_test/hush-misc/source1.right b/shell/hush_test/hush-misc/source1.right
index d4256034b..0ab7c548c 100644
--- a/shell/hush_test/hush-misc/source1.right
+++ b/shell/hush_test/hush-misc/source1.right
@@ -1,5 +1,2 @@
1hush: syntax error: unterminated ${name} 1Sourced ok
2line2 2Done
3Ok1:0
4hush: syntax error: unterminated '
5Ok2:1
diff --git a/shell/hush_test/hush-misc/source1.tests b/shell/hush_test/hush-misc/source1.tests
index c13888359..e2e75b227 100755
--- a/shell/hush_test/hush-misc/source1.tests
+++ b/shell/hush_test/hush-misc/source1.tests
@@ -1,10 +1,5 @@
1echo 'echo ${^} 1echo "echo Sourced ok" >../sourced.sh
2echo line2' >sourced1 2PATH="..:$PATH"
3. ./sourced1 3. sourced.sh
4echo Ok1:$? 4rm ../sourced.sh
5 5echo Done
6echo "echo '" >sourced1
7. ./sourced1
8echo Ok2:$?
9
10rm sourced1
diff --git a/shell/hush_test/hush-misc/source2.right b/shell/hush_test/hush-misc/source2.right
index 0587bad67..ce7171c87 100644
--- a/shell/hush_test/hush-misc/source2.right
+++ b/shell/hush_test/hush-misc/source2.right
@@ -1,4 +1 @@
10:arg0 1:arg1 2:arg2 Done: 0
2Ok1:0
30:arg0 1:q 2:w
4Ok2:0
diff --git a/shell/hush_test/hush-misc/source2.tests b/shell/hush_test/hush-misc/source2.tests
index 40b6b83cd..1870cdf7e 100755
--- a/shell/hush_test/hush-misc/source2.tests
+++ b/shell/hush_test/hush-misc/source2.tests
@@ -1,8 +1,3 @@
1echo 'echo "0:$0 1:$1 2:$2"' >sourced1 1false
2set -- 1 2 3 2. /dev/null
3"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2 3echo Done: $?
4echo Ok1:$?
5"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
6echo Ok2:$?
7
8rm sourced1
diff --git a/shell/hush_test/hush-misc/source3.right b/shell/hush_test/hush-misc/source3.right
new file mode 100644
index 000000000..bdf9001a5
--- /dev/null
+++ b/shell/hush_test/hush-misc/source3.right
@@ -0,0 +1,2 @@
1Zero:0
2Zero:0
diff --git a/shell/hush_test/hush-misc/source3.tests b/shell/hush_test/hush-misc/source3.tests
new file mode 100755
index 000000000..1abf156ed
--- /dev/null
+++ b/shell/hush_test/hush-misc/source3.tests
@@ -0,0 +1,6 @@
1# Test both empty file, and one-empty-line file
2echo >sourced1
3true >sourced2
4false; . ./sourced1; echo Zero:$?
5false; . ./sourced2; echo Zero:$?
6rm sourced1 sourced2
diff --git a/shell/hush_test/hush-misc/source4.right b/shell/hush_test/hush-misc/source4.right
new file mode 100644
index 000000000..d4256034b
--- /dev/null
+++ b/shell/hush_test/hush-misc/source4.right
@@ -0,0 +1,5 @@
1hush: syntax error: unterminated ${name}
2line2
3Ok1:0
4hush: syntax error: unterminated '
5Ok2:1
diff --git a/shell/hush_test/hush-misc/source4.tests b/shell/hush_test/hush-misc/source4.tests
new file mode 100755
index 000000000..c13888359
--- /dev/null
+++ b/shell/hush_test/hush-misc/source4.tests
@@ -0,0 +1,10 @@
1echo 'echo ${^}
2echo line2' >sourced1
3. ./sourced1
4echo Ok1:$?
5
6echo "echo '" >sourced1
7. ./sourced1
8echo Ok2:$?
9
10rm sourced1
diff --git a/shell/hush_test/hush-misc/source5.right b/shell/hush_test/hush-misc/source5.right
new file mode 100644
index 000000000..0587bad67
--- /dev/null
+++ b/shell/hush_test/hush-misc/source5.right
@@ -0,0 +1,4 @@
10:arg0 1:arg1 2:arg2
2Ok1:0
30:arg0 1:q 2:w
4Ok2:0
diff --git a/shell/hush_test/hush-misc/source5.tests b/shell/hush_test/hush-misc/source5.tests
new file mode 100755
index 000000000..40b6b83cd
--- /dev/null
+++ b/shell/hush_test/hush-misc/source5.tests
@@ -0,0 +1,8 @@
1echo 'echo "0:$0 1:$1 2:$2"' >sourced1
2set -- 1 2 3
3"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
4echo Ok1:$?
5"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
6echo Ok2:$?
7
8rm sourced1
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right
new file mode 100644
index 000000000..b212c246c
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right
@@ -0,0 +1,10 @@
1192\.168\.0\.1
2192\.168\.0\.1[
3192\.168\.0\.1[
4192\\.168\\.0\\.1[
5192\.168\.0\.1[
6192\.168\.0\.1
7192\.168\.0\.1[
8192\.168\.0\.1[
9192\\.168\\.0\\.1[
10192\.168\.0\.1[
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests
new file mode 100755
index 000000000..74dca1cc0
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests
@@ -0,0 +1,21 @@
1# The bug here was triggered by:
2# * performing pathname expansion because we see [
3# * replace operator did not escape \ in replace string
4
5IP=192.168.0.1
6
7rm -f '192.168.0.1['
8echo "${IP//./\\.}"
9echo "${IP//./\\.}"'[' # bug was here
10echo "${IP//./\\.}[" # bug was here
11echo "${IP//./\\\\.}[" # bug was here
12echo "192\.168\.0\.1["
13
14echo >'192.168.0.1['
15echo "${IP//./\\.}"
16echo "${IP//./\\.}"'[' # bug was here
17echo "${IP//./\\.}[" # bug was here
18echo "${IP//./\\\\.}[" # bug was here
19echo "192\.168\.0\.1["
20
21rm -f '192.168.0.1['
diff --git a/shell/hush_test/hush-read/read_r.tests b/shell/hush_test/hush-read/read_r.tests
index 2c4cc6106..1f0a18afc 100755
--- a/shell/hush_test/hush-read/read_r.tests
+++ b/shell/hush_test/hush-read/read_r.tests
@@ -1,2 +1,4 @@
1echo -e 'test\\\nbest' | (read reply; echo "$reply") 1echo 'test\
2echo -e 'test\\\nbest' | (read -r reply; echo "$reply") 2best' | (read reply; echo "$reply")
3echo 'test\
4best' | (read -r reply; echo "$reply")
diff --git a/shell/hush_test/hush-redir/redir1.right b/shell/hush_test/hush-redir/redir1.right
new file mode 100644
index 000000000..15515d1af
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir1.right
@@ -0,0 +1,12 @@
1Test 0: var:ok
2File created:ok
3Test 1: var:ok
4File created:ok
5Test 2: var:ok
6File created:ok
7Test 3: var:ok
8File created:ok
9Test 4: var:ok
10File created:ok
11Test 5: var:ok
12File created:ok
diff --git a/shell/hush_test/hush-redir/redir1.tests b/shell/hush_test/hush-redir/redir1.tests
new file mode 100755
index 000000000..ef2fbfb77
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir1.tests
@@ -0,0 +1,40 @@
1rm shell_test_$$ 2>/dev/null
2var=bad
3>shell_test_$$ var=ok
4echo "Test 0: var:$var"
5test -f shell_test_$$ && echo "File created:ok"
6
7rm shell_test_$$ 2>/dev/null
8var=bad
9var=ok >shell_test_$$
10echo "Test 1: var:$var"
11test -f shell_test_$$ && echo "File created:ok"
12
13rm shell_test_$$ 2>/dev/null
14var=ok
15true | var=bad >shell_test_$$
16echo "Test 2: var:$var"
17test -f shell_test_$$ && echo "File created:ok"
18
19rm shell_test_$$ 2>/dev/null
20var=bad
21{ var=ok >shell_test_$$; }
22echo "Test 3: var:$var"
23test -f shell_test_$$ && echo "File created:ok"
24
25rm shell_test_$$ 2>/dev/null
26var=ok
27{ var=bad >shell_test_$$; } &
28# cant use usleep as it isnt standard in $PATH --
29# we fail when testing busybox compiled solely as "hush"
30wait
31echo "Test 4: var:$var"
32test -f shell_test_$$ && echo "File created:ok"
33
34rm shell_test_$$ 2>/dev/null
35var=ok
36( var=bad >shell_test_$$ )
37echo "Test 5: var:$var"
38test -f shell_test_$$ && echo "File created:ok"
39
40rm shell_test_$$ 2>/dev/null
diff --git a/shell/hush_test/hush-redir/redir2.right b/shell/hush_test/hush-redir/redir2.right
new file mode 100644
index 000000000..d86bac9de
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir2.right
@@ -0,0 +1 @@
OK
diff --git a/shell/hush_test/hush-redir/redir2.tests b/shell/hush_test/hush-redir/redir2.tests
new file mode 100755
index 000000000..61ccea30c
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir2.tests
@@ -0,0 +1,5 @@
1# ash once couldn't redirect above fd#9
2exec 1>/dev/null
3(echo LOST1 >&22) 22>&1
4(echo LOST2 >&22) 22>&1
5(echo OK >&22) 22>&2
diff --git a/shell/hush_test/hush-misc/redir3.right b/shell/hush_test/hush-redir/redir3.right
index 3d20bbf68..3d20bbf68 100644
--- a/shell/hush_test/hush-misc/redir3.right
+++ b/shell/hush_test/hush-redir/redir3.right
diff --git a/shell/hush_test/hush-misc/redir3.tests b/shell/hush_test/hush-redir/redir3.tests
index 7c28e4324..7c28e4324 100755
--- a/shell/hush_test/hush-misc/redir3.tests
+++ b/shell/hush_test/hush-redir/redir3.tests
diff --git a/shell/hush_test/hush-redir/redir4.right b/shell/hush_test/hush-redir/redir4.right
new file mode 100644
index 000000000..d86bac9de
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir4.right
@@ -0,0 +1 @@
OK
diff --git a/shell/hush_test/hush-redir/redir4.tests b/shell/hush_test/hush-redir/redir4.tests
new file mode 100755
index 000000000..4bdf5ae27
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir4.tests
@@ -0,0 +1,72 @@
1# ash uses fd 10 (usually) for reading the script
2exec 13>&-
3exec 12>&-
4exec 11>&-
5exec 10>&-
6# some amount of input is prefetched.
7# make sure final echo is far enough to not be prefetched.
8###############################################################
9###############################################################
10###############################################################
11###############################################################
12###############################################################
13###############################################################
14###############################################################
15###############################################################
16###############################################################
17###############################################################
18###############################################################
19###############################################################
20###############################################################
21###############################################################
22###############################################################
23###############################################################
24###############################################################
25###############################################################
26###############################################################
27###############################################################
28###############################################################
29###############################################################
30###############################################################
31###############################################################
32###############################################################
33###############################################################
34###############################################################
35###############################################################
36###############################################################
37###############################################################
38###############################################################
39###############################################################
40###############################################################
41###############################################################
42###############################################################
43###############################################################
44###############################################################
45###############################################################
46###############################################################
47###############################################################
48###############################################################
49###############################################################
50###############################################################
51###############################################################
52###############################################################
53###############################################################
54###############################################################
55###############################################################
56###############################################################
57###############################################################
58###############################################################
59###############################################################
60###############################################################
61###############################################################
62###############################################################
63###############################################################
64###############################################################
65###############################################################
66###############################################################
67###############################################################
68###############################################################
69###############################################################
70###############################################################
71###############################################################
72echo "OK"
diff --git a/shell/hush_test/hush-misc/redir5.right b/shell/hush_test/hush-redir/redir5.right
index 52cce4feb..52cce4feb 100644
--- a/shell/hush_test/hush-misc/redir5.right
+++ b/shell/hush_test/hush-redir/redir5.right
diff --git a/shell/hush_test/hush-misc/redir5.tests b/shell/hush_test/hush-redir/redir5.tests
index 957f9c81f..957f9c81f 100755
--- a/shell/hush_test/hush-misc/redir5.tests
+++ b/shell/hush_test/hush-redir/redir5.tests
diff --git a/shell/hush_test/hush-redir/redir6.right b/shell/hush_test/hush-redir/redir6.right
new file mode 100644
index 000000000..ed754df78
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir6.right
@@ -0,0 +1,2 @@
1Hello
2OK
diff --git a/shell/hush_test/hush-redir/redir6.tests b/shell/hush_test/hush-redir/redir6.tests
new file mode 100755
index 000000000..33b6d4cd4
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir6.tests
@@ -0,0 +1,3 @@
1# we had a bug where this would hang
2(head -n 1 <redir6.right)
3echo OK
diff --git a/shell/hush_test/hush-redir/redir7.right b/shell/hush_test/hush-redir/redir7.right
new file mode 100644
index 000000000..6430b0211
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir7.right
@@ -0,0 +1,3 @@
1Ok
2Ok
3Done
diff --git a/shell/hush_test/hush-redir/redir7.tests b/shell/hush_test/hush-redir/redir7.tests
new file mode 100755
index 000000000..e873a4622
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir7.tests
@@ -0,0 +1,12 @@
1# Chars above 0x7f are used as special codes.
2# 0x81 is CTLESC (see ash.c).
3# The bug was that quoting and unquoting of them
4# was out of sync for redirect filenames.
5
6>unicode.sh
7printf 'echo Ok >uni\x81code\n' >>unicode.sh
8printf 'cat uni\x81code\n' >>unicode.sh
9printf 'cat uni?code\n' >>unicode.sh
10. ./unicode.sh
11rm uni*code*
12echo Done
diff --git a/shell/hush_test/hush-redir/redir8.right b/shell/hush_test/hush-redir/redir8.right
new file mode 100644
index 000000000..6430b0211
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir8.right
@@ -0,0 +1,3 @@
1Ok
2Ok
3Done
diff --git a/shell/hush_test/hush-redir/redir8.tests b/shell/hush_test/hush-redir/redir8.tests
new file mode 100755
index 000000000..2bd386749
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir8.tests
@@ -0,0 +1,15 @@
1# Chars above 0x7f are used as special codes.
2# 0x81 is CTLESC (see ash.c).
3# The bug was that quoting and unquoting of them
4# was out of sync for redirect filenames.
5
6# Subcase when redirect filename is specified in a variable.
7
8>unicode.sh
9printf 'v=uni\x81code\n' >>unicode.sh
10printf 'echo Ok >"$v"\n' >>unicode.sh
11printf 'cat uni\x81code\n' >>unicode.sh
12printf 'cat uni?code\n' >>unicode.sh
13. ./unicode.sh
14rm uni*code*
15echo Done
diff --git a/shell/hush_test/hush-redir/redir9.right b/shell/hush_test/hush-redir/redir9.right
new file mode 100644
index 000000000..34c2512e4
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir9.right
@@ -0,0 +1,2 @@
1Ok
2Done:0
diff --git a/shell/hush_test/hush-redir/redir9.tests b/shell/hush_test/hush-redir/redir9.tests
new file mode 100755
index 000000000..8befa611c
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir9.tests
@@ -0,0 +1,4 @@
1echo Ok >file.tmp
2cat 0<>file.tmp
3echo Done:$?
4rm file.tmp
diff --git a/shell/hush_test/hush-redir/redirA.right b/shell/hush_test/hush-redir/redirA.right
new file mode 100644
index 000000000..31406e336
--- /dev/null
+++ b/shell/hush_test/hush-redir/redirA.right
@@ -0,0 +1,2 @@
1tmp11
2tmp11
diff --git a/shell/hush_test/hush-redir/redirA.tests b/shell/hush_test/hush-redir/redirA.tests
new file mode 100755
index 000000000..56833f938
--- /dev/null
+++ b/shell/hush_test/hush-redir/redirA.tests
@@ -0,0 +1,11 @@
1x="tmp11:tmp22"
2
3# Bug was incorrectly expanding variables in >redir
4echo "${x%:*}" >"${x%:*}"
5echo tmp1*
6rm tmp1*
7
8# Also try unquoted
9echo "${x%:*}" >${x%:*}
10echo tmp1*
11rm tmp1*
diff --git a/shell/hush_test/hush-redir/redir_escapednum.right b/shell/hush_test/hush-redir/redir_escapednum.right
new file mode 100644
index 000000000..7326d9603
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_escapednum.right
@@ -0,0 +1 @@
Ok
diff --git a/shell/hush_test/hush-redir/redir_escapednum.tests b/shell/hush_test/hush-redir/redir_escapednum.tests
new file mode 100755
index 000000000..81983cae2
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_escapednum.tests
@@ -0,0 +1,2 @@
1echo NOT SHOWN \2>/dev/null
2echo Ok
diff --git a/shell/hush_test/hush-redir/redir_expand.right b/shell/hush_test/hush-redir/redir_expand.right
new file mode 100644
index 000000000..ead25f603
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_expand.right
@@ -0,0 +1,18 @@
1shell_test
2\shell_test
3\shell_test
4\shell_test
5Here1
6Ok1
7Here2
8Ok2
9Here3
10Ok3
11Here4
12Ok4
13Now with variable refs
14shell_test_1
15\shell_test_1
16\shell_test_1
17\shell_test_1
18Done
diff --git a/shell/hush_test/hush-redir/redir_expand.tests b/shell/hush_test/hush-redir/redir_expand.tests
new file mode 100755
index 000000000..c50b8cedf
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_expand.tests
@@ -0,0 +1,85 @@
1rm *shell_test* 2>/dev/null
2
3>\shell_test
4echo *shell_test*
5rm *shell_test*
6
7>\\shell_test
8echo *shell_test*
9rm *shell_test*
10
11>"\shell_test"
12echo *shell_test*
13rm *shell_test*
14
15>"\\shell_test"
16echo *shell_test*
17rm *shell_test*
18
19
20cat <<\shell_test
21Here1
22shell_test
23echo Ok1
24
25cat <<\\shell_test
26Here2
27\shell_test
28echo Ok2
29
30cat <<"\shell_test"
31Here3
32\shell_test
33echo Ok3
34
35cat <<"\\shell_test"
36Here4
37\shell_test
38echo Ok4
39
40
41echo Now with variable refs
42i=1
43
44
45>\shell_test_$i
46echo *shell_test*
47rm *shell_test*
48
49>\\shell_test_$i
50echo *shell_test*
51rm *shell_test*
52
53>"\shell_test_$i"
54echo *shell_test*
55rm *shell_test*
56
57>"\\shell_test_$i"
58echo *shell_test*
59rm *shell_test*
60
61echo Done;exit
62# UNFIXED BUG. bash apparently will expand $i even in terminating delimiter.
63# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
64# does not mandate this behavior.
65# This is not likely to be used much in real-world.
66
67cat <<\shell_test_$i
68Here1
69shell_test_$i
70echo Ok1
71
72cat <<\\shell_test_$i
73Here2
74\shell_test_$i
75echo Ok2
76
77cat <<"\shell_test_$i"
78Here3
79\shell_test_$i
80echo Ok3
81
82cat <<"\\shell_test_$i"
83Here4
84\shell_test_$i
85echo Ok4
diff --git a/shell/hush_test/hush-redir/redir_multi.right b/shell/hush_test/hush-redir/redir_multi.right
new file mode 100644
index 000000000..a97c4bdf1
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_multi.right
@@ -0,0 +1,4 @@
1Testing multiple redirections to same fd
2Hello
3Done1
4Done2
diff --git a/shell/hush_test/hush-redir/redir_multi.tests b/shell/hush_test/hush-redir/redir_multi.tests
new file mode 100755
index 000000000..c639ebb2d
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_multi.tests
@@ -0,0 +1,5 @@
1echo "Testing multiple redirections to same fd"
2# bug was making us lose fd #1 after this:
3echo Hello >/dev/null 1>&2
4echo Done1
5echo Done2 >&2
diff --git a/shell/hush_test/hush-redir/redir_script.right b/shell/hush_test/hush-redir/redir_script.right
new file mode 100644
index 000000000..6694ed334
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_script.right
@@ -0,0 +1 @@
Ok: script fd is not closed
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests
new file mode 100755
index 000000000..ccc497d7b
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_script.tests
@@ -0,0 +1,29 @@
1# Builds a " 3>&- 4>&-" string.
2# Note: one of these fds is a directory opened to /proc/self/fd
3# for globbing. It is unwanted, but I don't know how to filter it out.
4find_fds() {
5 fds=""
6 for f in /proc/self/fd/*; do
7 test "$f" = "/proc/self/fd/0" && continue
8 test "$f" = "/proc/self/fd/1" && continue
9 test "$f" = "/proc/self/fd/2" && continue
10 fds="$fds ${f##*/}>&-"
11 done
12}
13
14find_fds
15fds1="$fds"
16
17# One of the fds is open to the script body
18# Close it while executing something.
19eval "find_fds $fds"
20
21# Shell should not lose that fd. Did it?
22find_fds
23test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; }
24
25echo "Bug: script fd is closed"
26echo "fds1:$fds1"
27echo "fds2:$fds"
28exit 1
29
diff --git a/shell/hush_test/hush-redir/redir_space.right b/shell/hush_test/hush-redir/redir_space.right
new file mode 100644
index 000000000..084295204
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_space.right
@@ -0,0 +1,3 @@
1z1.tmp: 1
2z2.tmp: 1
3"z1.tmp z2.tmp": TEST 0
diff --git a/shell/hush_test/hush-redir/redir_space.tests b/shell/hush_test/hush-redir/redir_space.tests
new file mode 100755
index 000000000..c0b543098
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_space.tests
@@ -0,0 +1,6 @@
1v='z1.tmp z2.tmp'
2echo TEST >$v
3echo 'z1.tmp:' `cat 'z1.tmp' 2>/dev/null; echo $?`
4echo 'z2.tmp:' `cat 'z2.tmp' 2>/dev/null; echo $?`
5echo '"z1.tmp z2.tmp":' `cat 'z1.tmp z2.tmp' 2>/dev/null; echo $?`
6rm z*.tmp
diff --git a/shell/hush_test/hush-trap/catch.right b/shell/hush_test/hush-signals/catch.right
index 80a062c4b..80a062c4b 100644
--- a/shell/hush_test/hush-trap/catch.right
+++ b/shell/hush_test/hush-signals/catch.right
diff --git a/shell/hush_test/hush-trap/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d17e..d2a21d17e 100755
--- a/shell/hush_test/hush-trap/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
diff --git a/shell/hush_test/hush-signals/continue_and_trap1.right b/shell/hush_test/hush-signals/continue_and_trap1.right
new file mode 100644
index 000000000..d2dd0af72
--- /dev/null
+++ b/shell/hush_test/hush-signals/continue_and_trap1.right
@@ -0,0 +1 @@
Exiting
diff --git a/shell/hush_test/hush-signals/continue_and_trap1.tests b/shell/hush_test/hush-signals/continue_and_trap1.tests
new file mode 100755
index 000000000..2a5c147b1
--- /dev/null
+++ b/shell/hush_test/hush-signals/continue_and_trap1.tests
@@ -0,0 +1,7 @@
1trap "echo Exiting; exit" INT
2
3(sleep 1; kill -s INT $$) &
4
5while continue; do
6 continue;
7done
diff --git a/shell/hush_test/hush-trap/exit.right b/shell/hush_test/hush-signals/exit.right
index 3d0072564..3d0072564 100644
--- a/shell/hush_test/hush-trap/exit.right
+++ b/shell/hush_test/hush-signals/exit.right
diff --git a/shell/hush_test/hush-trap/exit.tests b/shell/hush_test/hush-signals/exit.tests
index 2061105dd..2061105dd 100755
--- a/shell/hush_test/hush-trap/exit.tests
+++ b/shell/hush_test/hush-signals/exit.tests
diff --git a/shell/hush_test/hush-signals/reap1.right b/shell/hush_test/hush-signals/reap1.right
new file mode 100644
index 000000000..7326d9603
--- /dev/null
+++ b/shell/hush_test/hush-signals/reap1.right
@@ -0,0 +1 @@
Ok
diff --git a/shell/hush_test/hush-signals/reap1.tests b/shell/hush_test/hush-signals/reap1.tests
new file mode 100755
index 000000000..bf1a1f908
--- /dev/null
+++ b/shell/hush_test/hush-signals/reap1.tests
@@ -0,0 +1,14 @@
1#!/bin/sh
2
3# Must not find us alive
4{ sleep 2; kill -9 $$; } 2>/dev/null &
5
6sleep 1 &
7PID=$!
8
9# We must exit the loop in one second.
10# We had bug 5304: builtins never waited for exited children
11while kill -0 $PID >/dev/null 2>&1; do
12 true
13done
14echo Ok
diff --git a/shell/hush_test/hush-signals/return_in_trap1.right b/shell/hush_test/hush-signals/return_in_trap1.right
new file mode 100644
index 000000000..a6e637885
--- /dev/null
+++ b/shell/hush_test/hush-signals/return_in_trap1.right
@@ -0,0 +1,4 @@
1a:2
2b:0
3Trap
4d:3
diff --git a/shell/hush_test/hush-signals/return_in_trap1.tests b/shell/hush_test/hush-signals/return_in_trap1.tests
new file mode 100755
index 000000000..f2498024f
--- /dev/null
+++ b/shell/hush_test/hush-signals/return_in_trap1.tests
@@ -0,0 +1,18 @@
1a() {
2 (exit 2)
3 echo a:$?
4 (kill -s USR1 $$; echo b:$?; exit 3)
5 echo c:$? # does not execute
6 (exit 4)
7}
8
9trap "echo Trap; return" USR1
10a
11
12echo d:$?
13# It's debatable what is the correct value above.
14# Does 'return' in trap sees $? == 2 or $? == 3?
15# IOW: after (kill..), does shell first wait for its completion
16# and sets $?, then checks pending signals and runs a trap handler,
17# or does it first checks pending signals and runs handler?
18# hush does the former, and prints 3.
diff --git a/shell/hush_test/hush-signals/save-ret.right b/shell/hush_test/hush-signals/save-ret.right
new file mode 100644
index 000000000..a3e12ce5e
--- /dev/null
+++ b/shell/hush_test/hush-signals/save-ret.right
@@ -0,0 +1,2 @@
1YEAH
20
diff --git a/shell/hush_test/hush-signals/save-ret.tests b/shell/hush_test/hush-signals/save-ret.tests
new file mode 100755
index 000000000..0786b6d96
--- /dev/null
+++ b/shell/hush_test/hush-signals/save-ret.tests
@@ -0,0 +1,4 @@
1# make sure we do not corrupt $? across traps
2trap "echo YEAH; false" USR1
3kill -USR1 $$
4echo $?
diff --git a/shell/hush_test/hush-trap/savetrap.right b/shell/hush_test/hush-signals/savetrap.right
index a59225be3..a59225be3 100644
--- a/shell/hush_test/hush-trap/savetrap.right
+++ b/shell/hush_test/hush-signals/savetrap.right
diff --git a/shell/hush_test/hush-trap/savetrap.tests b/shell/hush_test/hush-signals/savetrap.tests
index c2b312fb8..c2b312fb8 100755
--- a/shell/hush_test/hush-trap/savetrap.tests
+++ b/shell/hush_test/hush-signals/savetrap.tests
diff --git a/shell/hush_test/hush-signals/sigint1.right b/shell/hush_test/hush-signals/sigint1.right
new file mode 100644
index 000000000..a9094b056
--- /dev/null
+++ b/shell/hush_test/hush-signals/sigint1.right
@@ -0,0 +1 @@
Sending SIGINT to main shell PID
diff --git a/shell/hush_test/hush-signals/sigint1.tests b/shell/hush_test/hush-signals/sigint1.tests
new file mode 100755
index 000000000..3d483d32a
--- /dev/null
+++ b/shell/hush_test/hush-signals/sigint1.tests
@@ -0,0 +1,41 @@
1# What should happen if non-interactive shell gets SIGINT?
2
3(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
4
5# We create a child which exits with 0 even on SIGINT
6# (The complex command is necessary only if SIGINT is generated by ^C,
7# in this testcase even bare "sleep 2" would do because
8# in the testcase we don't send SIGINT *to the child*...)
9$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
10
11# In one second, we (main shell) get SIGINT here.
12# The question is whether we should, or should not, exit.
13
14# bash will not stop here. It will execute next command(s).
15
16# The rationale for this is described here:
17# http://www.cons.org/cracauer/sigint.html
18#
19# Basically, bash will not exit on SIGINT immediately if it waits
20# for a child. It will wait for the child to exit.
21# If child exits NOT by dying on SIGINT, then bash will not exit.
22#
23# The idea is that the following script:
24# | emacs file.txt
25# | more cmds
26# User may use ^C to interrupt editor's ops like search. But then
27# emacs exits normally. User expects that script doesn't stop.
28#
29# This is a nice idea, but detecting "did process really exit
30# with SIGINT?" is racy. Consider:
31# | bash -c 'while true; do /bin/true; done'
32# When ^C is pressed while bash waits for /bin/true to exit,
33# it may happen that /bin/true exits with exitcode 0 before
34# ^C is delivered to it as SIGINT. bash will see SIGINT, then
35# it will see that child exited with 0, and bash will NOT EXIT.
36
37# Therefore we do not implement bash behavior.
38# I'd say that emacs need to put itself into a separate pgrp
39# to isolate shell from getting stray SIGINTs from ^C.
40
41echo Next command after SIGINT was executed
diff --git a/shell/hush_test/hush-signals/signal2.right b/shell/hush_test/hush-signals/signal2.right
new file mode 100644
index 000000000..a2af919c0
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal2.right
@@ -0,0 +1,3 @@
1child sleeps
2child exits as expected
3parent exits
diff --git a/shell/hush_test/hush-signals/signal2.tests b/shell/hush_test/hush-signals/signal2.tests
new file mode 100755
index 000000000..df639ca2c
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal2.tests
@@ -0,0 +1,18 @@
1#!/bin/sh
2
3$THIS_SH -c '
4cleanup() {
5 echo "child exits as expected"
6 exit
7}
8trap cleanup HUP
9echo "child sleeps"
10sleep 1
11echo "BAD exit from child!"
12' &
13
14child=$!
15sleep 0.1 # let child install handler first
16kill -HUP $child
17wait
18echo "parent exits"
diff --git a/shell/hush_test/hush-signals/signal3.right b/shell/hush_test/hush-signals/signal3.right
new file mode 100644
index 000000000..3113ba5cf
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal3.right
@@ -0,0 +1,4 @@
1child sleeps
2child got HUP
3child exits
4parent exits
diff --git a/shell/hush_test/hush-signals/signal3.tests b/shell/hush_test/hush-signals/signal3.tests
new file mode 100755
index 000000000..b56c2d97e
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal3.tests
@@ -0,0 +1,17 @@
1#!/bin/sh
2
3$THIS_SH -c '
4hup() {
5 echo "child got HUP"
6}
7trap hup HUP
8echo "child sleeps"
9sleep 1
10echo "child exits"
11' &
12
13child=$!
14sleep 0.1 # let child install handler first
15kill -HUP $child
16wait
17echo "parent exits"
diff --git a/shell/hush_test/hush-signals/signal5.right b/shell/hush_test/hush-signals/signal5.right
new file mode 100644
index 000000000..7cfd4110e
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal5.right
@@ -0,0 +1,12 @@
1Sleeping
2Sleeping
3Waiting
42 sec passed, sending USR1 to parent
5USR1 received
6Wait exit code: 138
7Waiting
83 sec passed, sending USR1 to parent
9USR1 received
10Wait exit code: 138
11Waiting
12Wait returned 0
diff --git a/shell/hush_test/hush-signals/signal5.tests b/shell/hush_test/hush-signals/signal5.tests
new file mode 100755
index 000000000..179bcdd80
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal5.tests
@@ -0,0 +1,14 @@
1trap "echo USR1 received" USR1
2stub() {
3 echo "Sleeping"
4 sleep $1
5 echo "$1 sec passed, sending USR1 to parent"
6 kill -USR1 $$
7}
8stub 3 &
9stub 2 &
10sleep 1
11until { echo "Waiting"; wait; } do
12 echo "Wait exit code: $?"
13done
14echo "Wait returned 0"
diff --git a/shell/hush_test/hush-signals/signal6.right b/shell/hush_test/hush-signals/signal6.right
new file mode 100644
index 000000000..df4d9306a
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal6.right
@@ -0,0 +1,2 @@
1got TERM
2Done: 0
diff --git a/shell/hush_test/hush-signals/signal6.tests b/shell/hush_test/hush-signals/signal6.tests
new file mode 100755
index 000000000..3ce151060
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal6.tests
@@ -0,0 +1,2 @@
1{ trap "echo got TERM" TERM; sleep 3; }& sleep 1; kill $!; wait
2echo Done: $?
diff --git a/shell/hush_test/hush-trap/signal7.right b/shell/hush_test/hush-signals/signal7.right
index ba7453e42..ba7453e42 100644
--- a/shell/hush_test/hush-trap/signal7.right
+++ b/shell/hush_test/hush-signals/signal7.right
diff --git a/shell/hush_test/hush-trap/signal7.tests b/shell/hush_test/hush-signals/signal7.tests
index c2b1381f9..c2b1381f9 100755
--- a/shell/hush_test/hush-trap/signal7.tests
+++ b/shell/hush_test/hush-signals/signal7.tests
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-signals/signal_read1.right
index 2870a8e70..2870a8e70 100644
--- a/shell/hush_test/hush-trap/signal_read1.right
+++ b/shell/hush_test/hush-signals/signal_read1.right
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-signals/signal_read1.tests
index 1105479a3..1105479a3 100755
--- a/shell/hush_test/hush-trap/signal_read1.tests
+++ b/shell/hush_test/hush-signals/signal_read1.tests
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-signals/signal_read2.right
index 71a6bc16d..71a6bc16d 100644
--- a/shell/hush_test/hush-trap/signal_read2.right
+++ b/shell/hush_test/hush-signals/signal_read2.right
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b5b..eab5b9b5b 100755
--- a/shell/hush_test/hush-trap/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
diff --git a/shell/hush_test/hush-signals/sigquit_exec.right b/shell/hush_test/hush-signals/sigquit_exec.right
new file mode 100644
index 000000000..a8041929a
--- /dev/null
+++ b/shell/hush_test/hush-signals/sigquit_exec.right
@@ -0,0 +1,2 @@
1SigIgn: 0000000000000000
2SigIgn: 0000000000000000
diff --git a/shell/hush_test/hush-signals/sigquit_exec.tests b/shell/hush_test/hush-signals/sigquit_exec.tests
new file mode 100755
index 000000000..24bda6921
--- /dev/null
+++ b/shell/hush_test/hush-signals/sigquit_exec.tests
@@ -0,0 +1,4 @@
1# Should show no masked signals in both cases.
2# We had a bug where SIGQUIT was masked on exec.
3grep SigIgn: /proc/self/status
4exec grep SigIgn: /proc/self/status
diff --git a/shell/hush_test/hush-trap/subshell.right b/shell/hush_test/hush-signals/subshell.right
index f865b932b..f865b932b 100644
--- a/shell/hush_test/hush-trap/subshell.right
+++ b/shell/hush_test/hush-signals/subshell.right
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b82..d877f2b82 100755
--- a/shell/hush_test/hush-trap/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
diff --git a/shell/hush_test/hush-trap/usage.right b/shell/hush_test/hush-signals/usage.right
index c0dbd6c3c..c0dbd6c3c 100644
--- a/shell/hush_test/hush-trap/usage.right
+++ b/shell/hush_test/hush-signals/usage.right
diff --git a/shell/hush_test/hush-trap/usage.tests b/shell/hush_test/hush-signals/usage.tests
index d29c6e74a..d29c6e74a 100755
--- a/shell/hush_test/hush-trap/usage.tests
+++ b/shell/hush_test/hush-signals/usage.tests
diff --git a/shell/hush_test/hush-standalone/noexec_gets_no_env.right b/shell/hush_test/hush-standalone/noexec_gets_no_env.right
new file mode 100644
index 000000000..8522dff31
--- /dev/null
+++ b/shell/hush_test/hush-standalone/noexec_gets_no_env.right
@@ -0,0 +1,4 @@
1VAR7=VAL
20
3VAR8=VAL
40
diff --git a/shell/hush_test/hush-standalone/noexec_gets_no_env.tests b/shell/hush_test/hush-standalone/noexec_gets_no_env.tests
new file mode 100755
index 000000000..0d347bdcd
--- /dev/null
+++ b/shell/hush_test/hush-standalone/noexec_gets_no_env.tests
@@ -0,0 +1,5 @@
1export VAR7=VAL
2env | grep ^VAR7=
3echo $?
4VAR8=VAL env | grep ^VAR8=
5echo $?
diff --git a/shell/hush_test/hush-standalone/nofork_trashes_getopt.right b/shell/hush_test/hush-standalone/nofork_trashes_getopt.right
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/shell/hush_test/hush-standalone/nofork_trashes_getopt.right
@@ -0,0 +1 @@
0
diff --git a/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests b/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests
new file mode 100755
index 000000000..f42c50730
--- /dev/null
+++ b/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests
@@ -0,0 +1,6 @@
1# In this test, rm is NOFORK and it modifies getopt internal state
2rm -f non_existent_file
3# Subsequent hexdump is run as NOEXEC, and thus still uses this state
4hexdump </dev/null
5# Did hexdump segfault etc?
6echo $?
diff --git a/shell/hush_test/hush-standalone/var_standalone1.right b/shell/hush_test/hush-standalone/var_standalone1.right
new file mode 100644
index 000000000..37457fd74
--- /dev/null
+++ b/shell/hush_test/hush-standalone/var_standalone1.right
@@ -0,0 +1 @@
Done: 1
diff --git a/shell/hush_test/hush-standalone/var_standalone1.tests b/shell/hush_test/hush-standalone/var_standalone1.tests
new file mode 100755
index 000000000..1e905ba75
--- /dev/null
+++ b/shell/hush_test/hush-standalone/var_standalone1.tests
@@ -0,0 +1,2 @@
1VAR=42 $THIS_SH -c 'unset VAR; env | grep ^VAR'
2echo Done: $?
diff --git a/shell/hush_test/hush-vars/var3.right b/shell/hush_test/hush-vars/var3.right
index 40e67fdf5..8eb0e3337 100644
--- a/shell/hush_test/hush-vars/var3.right
+++ b/shell/hush_test/hush-vars/var3.right
@@ -1,2 +1,5 @@
1hush: invalid number '1q' 11
2hush: syntax error: unterminated ${name} 21
3
4
50
diff --git a/shell/hush_test/hush-vars/var3.tests b/shell/hush_test/hush-vars/var3.tests
index aea36d62d..97b102cbe 100755
--- a/shell/hush_test/hush-vars/var3.tests
+++ b/shell/hush_test/hush-vars/var3.tests
@@ -1,4 +1 @@
1# reject invalid vars x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x
2"$THIS_SH" -c 'echo ${1q}'
3"$THIS_SH" -c 'echo ${&}'
4#"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$
diff --git a/shell/hush_test/hush-vars/var4.right b/shell/hush_test/hush-vars/var4.right
new file mode 100644
index 000000000..8fed138eb
--- /dev/null
+++ b/shell/hush_test/hush-vars/var4.right
@@ -0,0 +1 @@
bus/usb/1/2
diff --git a/shell/hush_test/hush-vars/var4.tests b/shell/hush_test/hush-vars/var4.tests
new file mode 100755
index 000000000..07feaeb8b
--- /dev/null
+++ b/shell/hush_test/hush-vars/var4.tests
@@ -0,0 +1 @@
X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
diff --git a/shell/hush_test/hush-vars/var5.right b/shell/hush_test/hush-vars/var5.right
new file mode 100644
index 000000000..2a01291bb
--- /dev/null
+++ b/shell/hush_test/hush-vars/var5.right
@@ -0,0 +1,6 @@
1a=a A=a
2a=a A=a
3a= A=
4a= A=
5a=a A=a
6a=a A=a
diff --git a/shell/hush_test/hush-vars/var5.tests b/shell/hush_test/hush-vars/var5.tests
new file mode 100755
index 000000000..802e489bd
--- /dev/null
+++ b/shell/hush_test/hush-vars/var5.tests
@@ -0,0 +1,14 @@
1# check that first assignment has proper effect on second one
2
3(
4a=a A=$a
5echo a=$a A=$A
6)
7(a=a A=$a; echo a=$a A=$A)
8(a=a A=$a echo a=$a A=$A)
9(a=a A=$a /bin/echo a=$a A=$A)
10
11f() { echo a=$a A=$A; }
12
13(a=a A=$a f)
14(a=a A=$a; f)
diff --git a/shell/hush_test/hush-vars/var6.right b/shell/hush_test/hush-vars/var6.right
new file mode 100644
index 000000000..40e67fdf5
--- /dev/null
+++ b/shell/hush_test/hush-vars/var6.right
@@ -0,0 +1,2 @@
1hush: invalid number '1q'
2hush: syntax error: unterminated ${name}
diff --git a/shell/hush_test/hush-vars/var6.tests b/shell/hush_test/hush-vars/var6.tests
new file mode 100755
index 000000000..aea36d62d
--- /dev/null
+++ b/shell/hush_test/hush-vars/var6.tests
@@ -0,0 +1,4 @@
1# reject invalid vars
2"$THIS_SH" -c 'echo ${1q}'
3"$THIS_SH" -c 'echo ${&}'
4#"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$
diff --git a/shell/hush_test/hush-vars/var_unbackslash1.right b/shell/hush_test/hush-vars/var_unbackslash1.right
new file mode 100644
index 000000000..3e0c0e2af
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash1.right
@@ -0,0 +1,7 @@
1Ok
2Ba d
3Ok
4Ok
5Ok
6Forty two:42
7Forty two:42
diff --git a/shell/hush_test/hush-vars/var_unbackslash1.tests b/shell/hush_test/hush-vars/var_unbackslash1.tests
new file mode 100755
index 000000000..a4665e423
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash1.tests
@@ -0,0 +1,35 @@
1ad="Ok"
2a="Ba"
3
4# "Ok"
5echo $a\
6d
7
8# This variable contains backslash+newline!
9e='echo $a\
10d'
11
12# "Ba d"
13eval $e
14# "Ok"
15eval "$e"
16
17echo $\
18(echo Ok\
19)
20echo "$\
21(echo Ok\
22)"
23
24echo Forty two:$\
25(\
26(\
2742\
28)\
29)
30echo "Forty two:$\
31(\
32(\
3342\
34)\
35)"
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.right b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right
new file mode 100644
index 000000000..cf583d0aa
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right
@@ -0,0 +1,41 @@
1Testing: !IFS $*
2.abc.
3.d.
4.e.
5Testing: !IFS $@
6.abc.
7.d.
8.e.
9Testing: !IFS "$*"
10.abc d e.
11Testing: !IFS "$@"
12.abc.
13.d e.
14Testing: IFS="" $*
15.abc.
16.d e.
17Testing: IFS="" $@
18.abc.
19.d e.
20Testing: IFS="" "$*"
21.abcd e.
22Testing: IFS="" "$@"
23.abc.
24.d e.
25Testing: !IFS v=$*
26v='abc d e'
27Testing: !IFS v=$@
28v='abc d e'
29Testing: !IFS v="$*"
30v='abc d e'
31Testing: !IFS v="$@"
32v='abc d e'
33Testing: IFS="" v=$*
34v='abcd e'
35Testing: IFS="" v=$@
36v='abcd e'
37Testing: IFS="" v="$*"
38v='abcd e'
39Testing: IFS="" v="$@"
40v='abcd e'
41Finished
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests
new file mode 100755
index 000000000..a62afc6fd
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests
@@ -0,0 +1,42 @@
1set -- abc "d e"
2
3echo 'Testing: !IFS $*'
4unset IFS; for a in $*; do echo ".$a."; done
5echo 'Testing: !IFS $@'
6unset IFS; for a in $@; do echo ".$a."; done
7echo 'Testing: !IFS "$*"'
8unset IFS; for a in "$*"; do echo ".$a."; done
9echo 'Testing: !IFS "$@"'
10unset IFS; for a in "$@"; do echo ".$a."; done
11
12echo 'Testing: IFS="" $*'
13IFS=""; for a in $*; do echo ".$a."; done
14echo 'Testing: IFS="" $@'
15IFS=""; for a in $@; do echo ".$a."; done
16echo 'Testing: IFS="" "$*"'
17IFS=""; for a in "$*"; do echo ".$a."; done
18echo 'Testing: IFS="" "$@"'
19IFS=""; for a in "$@"; do echo ".$a."; done
20
21echo 'Testing: !IFS v=$*'
22unset IFS; v=$*; echo "v='$v'"
23echo 'Testing: !IFS v=$@'
24unset IFS; v=$@; echo "v='$v'"
25echo 'Testing: !IFS v="$*"'
26unset IFS; v="$*"; echo "v='$v'"
27echo 'Testing: !IFS v="$@"'
28unset IFS; v="$@"; echo "v='$v'"
29
30echo 'Testing: IFS="" v=$*'
31IFS=""; v=$*; echo "v='$v'"
32echo 'Testing: IFS="" v=$@'
33IFS=""; v=$@; echo "v='$v'"
34echo 'Testing: IFS="" v="$*"'
35IFS=""; v="$*"; echo "v='$v'"
36echo 'Testing: IFS="" v="$@"'
37IFS=""; v="$@"; echo "v='$v'"
38
39# Note: in IFS="" v=$@ and IFS="" v="$@" cases, bash produces "abc d e"
40# We produce "abcd e"
41
42echo Finished
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs2.right b/shell/hush_test/hush-vars/var_wordsplit_ifs2.right
new file mode 100644
index 000000000..c234193fe
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs2.right
@@ -0,0 +1,3 @@
1Unquoted:<1>
2Unquoted:<3>
3Quoted:<123>
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests
new file mode 100755
index 000000000..47523549c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests
@@ -0,0 +1,13 @@
1# 123 chars long
2a="\
301234567890123456789\
401234567890123456789\
501234567890123456789\
601234567890123456789\
701234567890123456789\
80123456789\
90123456789\
10012"
11
12IFS=2; for v in ${#a}; do echo Unquoted:"<$v>"; done
13IFS=2; for v in "${#a}"; do echo Quoted:"<$v>"; done
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs3.right b/shell/hush_test/hush-vars/var_wordsplit_ifs3.right
new file mode 100644
index 000000000..5ab72e1cd
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs3.right
@@ -0,0 +1,12 @@
1Unquoted%:<q>
2Unquoted%:<w>
3Unquoted%:<e>
4Unquoted%:<r>
5Unquoted%:<t>
6Unquoted#:<w>
7Unquoted#:<e>
8Unquoted#:<r>
9Unquoted#:<t>
10Unquoted#:<y>
11Quoted%:<q w e r t >
12Quoted#:< w e r t y>
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests
new file mode 100755
index 000000000..4aa65574a
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests
@@ -0,0 +1,5 @@
1a="q w e r t y"
2for v in ${a%y}; do echo Unquoted%:"<$v>"; done
3for v in ${a#q}; do echo Unquoted#:"<$v>"; done
4for v in "${a%y}"; do echo Quoted%:"<$v>"; done
5for v in "${a#q}"; do echo Quoted#:"<$v>"; done
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 64a7abc47..837b3f7da 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -64,11 +64,12 @@ do_test()
64 echo -n "$1/$x:" 64 echo -n "$1/$x:"
65 ( 65 (
66 "$THIS_SH" "./$x" >"$name.xx" 2>&1 66 "$THIS_SH" "./$x" >"$name.xx" 2>&1
67 r=$?
67 # filter C library differences 68 # filter C library differences
68 sed -i \ 69 sed -i \
69 -e "/: invalid option /s:'::g" \ 70 -e "/: invalid option /s:'::g" \
70 "$name.xx" 71 "$name.xx"
71 test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77 72 test $r -eq 77 && rm -f "../$1-$x.fail" && exit 77
72 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" 73 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
73 ) 74 )
74 case $? in 75 case $? in
diff --git a/shell/msh_test/msh-bugs/noeol3.right b/shell/msh_test/msh-bugs/noeol3.right
deleted file mode 100644
index 56f8515b7..000000000
--- a/shell/msh_test/msh-bugs/noeol3.right
+++ /dev/null
@@ -1 +0,0 @@
1hush: syntax error: unterminated "
diff --git a/shell/msh_test/msh-bugs/noeol3.tests b/shell/msh_test/msh-bugs/noeol3.tests
deleted file mode 100755
index ec958ed7a..000000000
--- a/shell/msh_test/msh-bugs/noeol3.tests
+++ /dev/null
@@ -1,2 +0,0 @@
1# last line has no EOL!
2echo "unterminated \ No newline at end of file
diff --git a/shell/msh_test/msh-bugs/process_subst.right b/shell/msh_test/msh-bugs/process_subst.right
deleted file mode 100644
index 397bc8067..000000000
--- a/shell/msh_test/msh-bugs/process_subst.right
+++ /dev/null
@@ -1,3 +0,0 @@
1TESTzzBEST
2TEST$(echo zz)BEST
3TEST'BEST
diff --git a/shell/msh_test/msh-bugs/process_subst.tests b/shell/msh_test/msh-bugs/process_subst.tests
deleted file mode 100755
index 21996bc0e..000000000
--- a/shell/msh_test/msh-bugs/process_subst.tests
+++ /dev/null
@@ -1,3 +0,0 @@
1echo "TEST`echo zz;echo;echo`BEST"
2echo "TEST`echo '$(echo zz)'`BEST"
3echo "TEST`echo "'"`BEST"
diff --git a/shell/msh_test/msh-bugs/starquoted.right b/shell/msh_test/msh-bugs/starquoted.right
deleted file mode 100644
index b56323fe1..000000000
--- a/shell/msh_test/msh-bugs/starquoted.right
+++ /dev/null
@@ -1,8 +0,0 @@
1.1 abc d e f.
2.1.
3.abc.
4.d e f.
5.-1 abc d e f-.
6.-1.
7.abc.
8.d e f-.
diff --git a/shell/msh_test/msh-bugs/starquoted.tests b/shell/msh_test/msh-bugs/starquoted.tests
deleted file mode 100755
index 2fe49b1cd..000000000
--- a/shell/msh_test/msh-bugs/starquoted.tests
+++ /dev/null
@@ -1,8 +0,0 @@
1if test $# = 0; then
2 exec "$THIS_SH" "$0" 1 abc 'd e f'
3fi
4
5for a in "$*"; do echo ".$a."; done
6for a in "$@"; do echo ".$a."; done
7for a in "-$*-"; do echo ".$a."; done
8for a in "-$@-"; do echo ".$a."; done
diff --git a/shell/msh_test/msh-bugs/syntax_err.right b/shell/msh_test/msh-bugs/syntax_err.right
deleted file mode 100644
index 08a270c31..000000000
--- a/shell/msh_test/msh-bugs/syntax_err.right
+++ /dev/null
@@ -1,2 +0,0 @@
1shown
2hush: syntax error: unterminated '
diff --git a/shell/msh_test/msh-bugs/syntax_err.tests b/shell/msh_test/msh-bugs/syntax_err.tests
deleted file mode 100755
index d10ed42e9..000000000
--- a/shell/msh_test/msh-bugs/syntax_err.tests
+++ /dev/null
@@ -1,3 +0,0 @@
1echo shown
2echo test `echo 'aa`
3echo not shown
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.right b/shell/msh_test/msh-execution/exitcode_EACCES.right
deleted file mode 100644
index 6e5480b9d..000000000
--- a/shell/msh_test/msh-execution/exitcode_EACCES.right
+++ /dev/null
@@ -1,2 +0,0 @@
1./: can't execute
2126
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.right b/shell/msh_test/msh-execution/exitcode_ENOENT.right
deleted file mode 100644
index dd49d2c0c..000000000
--- a/shell/msh_test/msh-execution/exitcode_ENOENT.right
+++ /dev/null
@@ -1,2 +0,0 @@
1./does_not_exist_for_sure: not found
2127
diff --git a/shell/msh_test/msh-execution/many_continues.tests b/shell/msh_test/msh-execution/many_continues.tests
deleted file mode 100755
index 86c729abc..000000000
--- a/shell/msh_test/msh-execution/many_continues.tests
+++ /dev/null
@@ -1,15 +0,0 @@
1if test $# = 0; then
2 # Child will kill us in 1 second
3 "$THIS_SH" "$0" $$ &
4
5 # Loop many, many times
6 trap 'echo OK; exit 0' 15
7 while true; do
8 continue
9 done
10 echo BAD
11 exit 1
12fi
13
14sleep 1
15kill $1
diff --git a/shell/msh_test/msh-execution/nested_break.right b/shell/msh_test/msh-execution/nested_break.right
deleted file mode 100644
index 4e8b6b0f2..000000000
--- a/shell/msh_test/msh-execution/nested_break.right
+++ /dev/null
@@ -1,8 +0,0 @@
1A
2B
3iteration
4C
5A
6B
7iteration
8D
diff --git a/shell/msh_test/msh-execution/nested_break.tests b/shell/msh_test/msh-execution/nested_break.tests
deleted file mode 100755
index 1a954d227..000000000
--- a/shell/msh_test/msh-execution/nested_break.tests
+++ /dev/null
@@ -1,17 +0,0 @@
1# Testcase for http://bugs.busybox.net/view.php?id=846
2
3n=0
4while :
5do
6 echo A
7 while :
8 do
9 echo B
10 break
11 done
12 echo iteration
13 [ $n = 1 ] && break
14 echo C
15 n=`expr $n + 1`
16done
17echo D
diff --git a/shell/msh_test/msh-misc/tick.tests b/shell/msh_test/msh-misc/tick.tests
deleted file mode 100755
index 1f749a9cd..000000000
--- a/shell/msh_test/msh-misc/tick.tests
+++ /dev/null
@@ -1,4 +0,0 @@
1true
2false; echo `echo $?`
3true
4{ false; echo `echo $?`; }
diff --git a/shell/msh_test/msh-parsing/argv0.tests b/shell/msh_test/msh-parsing/argv0.tests
deleted file mode 100755
index f5c40f6fe..000000000
--- a/shell/msh_test/msh-parsing/argv0.tests
+++ /dev/null
@@ -1,4 +0,0 @@
1if test $# = 0; then
2 exec "$THIS_SH" "$0" arg
3fi
4echo OK
diff --git a/shell/msh_test/msh-parsing/noeol.right b/shell/msh_test/msh-parsing/noeol.right
deleted file mode 100644
index e427984d4..000000000
--- a/shell/msh_test/msh-parsing/noeol.right
+++ /dev/null
@@ -1 +0,0 @@
1HELLO
diff --git a/shell/msh_test/msh-parsing/noeol.tests b/shell/msh_test/msh-parsing/noeol.tests
deleted file mode 100755
index a93113a03..000000000
--- a/shell/msh_test/msh-parsing/noeol.tests
+++ /dev/null
@@ -1,2 +0,0 @@
1# next line has no EOL!
2echo HELLO \ No newline at end of file
diff --git a/shell/msh_test/msh-parsing/noeol2.right b/shell/msh_test/msh-parsing/noeol2.right
deleted file mode 100644
index d00491fd7..000000000
--- a/shell/msh_test/msh-parsing/noeol2.right
+++ /dev/null
@@ -1 +0,0 @@
11
diff --git a/shell/msh_test/msh-parsing/noeol2.tests b/shell/msh_test/msh-parsing/noeol2.tests
deleted file mode 100755
index 1220f056f..000000000
--- a/shell/msh_test/msh-parsing/noeol2.tests
+++ /dev/null
@@ -1,7 +0,0 @@
1# last line has no EOL!
2if true
3then
4 echo 1
5else
6 echo 2
7fi \ No newline at end of file
diff --git a/shell/msh_test/msh-parsing/quote1.right b/shell/msh_test/msh-parsing/quote1.right
deleted file mode 100644
index cb382054c..000000000
--- a/shell/msh_test/msh-parsing/quote1.right
+++ /dev/null
@@ -1 +0,0 @@
1'1'
diff --git a/shell/msh_test/msh-parsing/quote1.tests b/shell/msh_test/msh-parsing/quote1.tests
deleted file mode 100755
index f55895466..000000000
--- a/shell/msh_test/msh-parsing/quote1.tests
+++ /dev/null
@@ -1,2 +0,0 @@
1a=1
2echo "'$a'"
diff --git a/shell/msh_test/msh-parsing/quote2.right b/shell/msh_test/msh-parsing/quote2.right
deleted file mode 100644
index 3bc9edcd6..000000000
--- a/shell/msh_test/msh-parsing/quote2.right
+++ /dev/null
@@ -1 +0,0 @@
1>1
diff --git a/shell/msh_test/msh-parsing/quote2.tests b/shell/msh_test/msh-parsing/quote2.tests
deleted file mode 100755
index bd966f30b..000000000
--- a/shell/msh_test/msh-parsing/quote2.tests
+++ /dev/null
@@ -1,2 +0,0 @@
1a=1
2echo ">$a"
diff --git a/shell/msh_test/msh-parsing/quote3.right b/shell/msh_test/msh-parsing/quote3.right
deleted file mode 100644
index 069a46e8f..000000000
--- a/shell/msh_test/msh-parsing/quote3.right
+++ /dev/null
@@ -1,3 +0,0 @@
1Testing: in $empty""
2..
3Finished
diff --git a/shell/msh_test/msh-parsing/quote3.tests b/shell/msh_test/msh-parsing/quote3.tests
deleted file mode 100755
index 075e78570..000000000
--- a/shell/msh_test/msh-parsing/quote3.tests
+++ /dev/null
@@ -1,8 +0,0 @@
1if test $# = 0; then
2 exec "$THIS_SH" quote3.tests abc "d e"
3fi
4
5echo 'Testing: in $empty""'
6empty=''
7for a in $empty""; do echo ".$a."; done
8echo Finished
diff --git a/shell/msh_test/msh-parsing/quote4.right b/shell/msh_test/msh-parsing/quote4.right
deleted file mode 100644
index b2901ea97..000000000
--- a/shell/msh_test/msh-parsing/quote4.right
+++ /dev/null
@@ -1 +0,0 @@
1a b
diff --git a/shell/msh_test/msh-parsing/quote4.tests b/shell/msh_test/msh-parsing/quote4.tests
deleted file mode 100755
index f1dabfa54..000000000
--- a/shell/msh_test/msh-parsing/quote4.tests
+++ /dev/null
@@ -1,2 +0,0 @@
1a_b='a b'
2echo "$a_b"
diff --git a/shell/msh_test/msh-vars/var.right b/shell/msh_test/msh-vars/var.right
deleted file mode 100644
index 14b2314d9..000000000
--- a/shell/msh_test/msh-vars/var.right
+++ /dev/null
@@ -1,4 +0,0 @@
1http://busybox.net
2http://busybox.net_abc
31
41
diff --git a/shell/msh_test/msh-vars/var.tests b/shell/msh_test/msh-vars/var.tests
deleted file mode 100755
index 0a63696c9..000000000
--- a/shell/msh_test/msh-vars/var.tests
+++ /dev/null
@@ -1,9 +0,0 @@
1URL=http://busybox.net
2
3echo $URL
4echo ${URL}_abc
5
6true
7false; echo $?
8true
9{ false; echo $?; }
diff --git a/shell/msh_test/run-all b/shell/msh_test/run-all
deleted file mode 100755
index 29f62a5e2..000000000
--- a/shell/msh_test/run-all
+++ /dev/null
@@ -1,64 +0,0 @@
1#!/bin/sh
2
3test -x msh || {
4 echo "No ./msh - creating a link to ../../busybox"
5 ln -s ../../busybox msh
6}
7
8PATH="$PWD:$PATH" # for msh
9export PATH
10
11THIS_SH="$PWD/msh"
12export THIS_SH
13
14do_test()
15{
16 test -d "$1" || return 0
17# echo Running tests in directory "$1"
18 (
19 cd "$1" || { echo "cannot cd $1!"; exit 1; }
20 for x in run-*; do
21 test -f "$x" || continue
22 case "$x" in
23 "$0"|run-minimal|run-gprof) ;;
24 *.orig|*~) ;;
25 #*) echo $x ; sh $x ;;
26 *)
27 sh "$x" >"../$1-$x.fail" 2>&1 && \
28 { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
29 ;;
30 esac
31 done
32 # Many bash run-XXX scripts just do this,
33 # no point in duplication it all over the place
34 for x in *.tests; do
35 test -x "$x" || continue
36 name="${x%%.tests}"
37 test -f "$name.right" || continue
38# echo Running test: "$name.right"
39 {
40 "$THIS_SH" "./$x" >"$name.xx" 2>&1
41 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
42 } && echo "$1/$x: ok" || echo "$1/$x: fail"
43 done
44 )
45}
46
47# Main part of this script
48# Usage: run-all [directories]
49
50if [ $# -lt 1 ]; then
51 # All sub directories
52 modules=`ls -d msh-*`
53
54 for module in $modules; do
55 do_test $module
56 done
57else
58 while [ $# -ge 1 ]; do
59 if [ -d $1 ]; then
60 do_test $1
61 fi
62 shift
63 done
64fi
diff --git a/shell/shell_common.c b/shell/shell_common.c
index c3bff477d..5a5b1780d 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -25,6 +25,7 @@ const char defifsvar[] ALIGN1 = "IFS= \t\n";
25#else 25#else
26const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; 26const char defifsvar[] ALIGN1 = "IFS= \t\n\r";
27#endif 27#endif
28const char defoptindvar[] ALIGN1 = "OPTIND=1";
28 29
29 30
30int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) 31int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
diff --git a/shell/shell_common.h b/shell/shell_common.h
index 993ed5951..a82535c86 100644
--- a/shell/shell_common.h
+++ b/shell/shell_common.h
@@ -24,6 +24,8 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
24extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */ 24extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */
25#define defifs (defifsvar + 4) 25#define defifs (defifsvar + 4)
26 26
27extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */
28
27int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); 29int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
28 30
29/* Builtins */ 31/* Builtins */
diff --git a/testsuite/gzip/gzip-compression-levels b/testsuite/gzip/gzip-compression-levels
new file mode 100644
index 000000000..6d9a13d08
--- /dev/null
+++ b/testsuite/gzip/gzip-compression-levels
@@ -0,0 +1,5 @@
1# FEATURE: CONFIG_FEATURE_GZIP_LEVELS
2
3level1=$(busybox gzip -c -1 $(which busybox) | wc -c)
4level9=$(busybox gzip -c -9 $(which busybox) | wc -c)
5test $level1 -gt $level9
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests
index 6c75b6d1c..cca26dc64 100755
--- a/testsuite/md5sum.tests
+++ b/testsuite/md5sum.tests
@@ -23,6 +23,8 @@ test -f "$bindir/.config" && . "$bindir/.config"
23test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \ 23test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \
24&& { echo "SKIPPED: $sum"; exit 0; } 24&& { echo "SKIPPED: $sum"; exit 0; }
25 25
26FAILCOUNT=0
27
26text="The quick brown fox jumps over the lazy dog" 28text="The quick brown fox jumps over the lazy dog"
27text=`yes "$text" | head -c 9999` 29text=`yes "$text" | head -c 9999`
28 30
@@ -33,11 +35,21 @@ while test $n -le 999; do
33 n=$(($n+1)) 35 n=$(($n+1))
34done | "$sum" 36done | "$sum"
35)` 37)`
36 38if test x"$result" != x"$expected -"; then
37if test x"$result" = x"$expected -"; then 39 echo "FAIL: $sum (r:$result exp:$expected)"
40 : $((FAILCOUNT++))
41else
38 echo "PASS: $sum" 42 echo "PASS: $sum"
39 exit 0
40fi 43fi
41 44
42echo "FAIL: $sum (r:$result exp:$expected)" 45# GNU compat: -c EMPTY must fail (exitcode 1)!
43exit 1 46>EMPTY
47if "$sum" -c EMPTY 2>/dev/null; then
48 echo "FAIL: $sum -c EMPTY"
49 : $((FAILCOUNT++))
50else
51 echo "PASS: $sum -c EMPTY"
52fi
53rm EMPTY
54
55exit $FAILCOUNT
diff --git a/testsuite/sed.tests b/testsuite/sed.tests
index a71f8b1f0..05c00a99b 100755
--- a/testsuite/sed.tests
+++ b/testsuite/sed.tests
@@ -73,13 +73,9 @@ testing "sed t (test/branch clears test bit)" "sed -e 's/a/b/;:loop;t loop'" \
73testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \ 73testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \
74 "1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n" 74 "1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n"
75 75
76test x"$SKIP_KNOWN_BUGS" = x"" && {
77# Normal sed end-of-script doesn't print "c" because n flushed the pattern
78# space. If n hits EOF, pattern space is empty when script ends.
79# Query: how does this interact with no newline at EOF?
80testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \ 76testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \
81 "a\nb\nb\nc\n" "" "a\nb\nc\n" 77 "a\nb\nb\nc\n" "" "a\nb\nc\n"
82} 78
83# non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end 79# non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end
84# GNU sed: N flushes pattern space, therefore c is printed too @ script end 80# GNU sed: N flushes pattern space, therefore c is printed too @ script end
85testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \ 81testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests
index 82fada633..2cd8e3bf2 100755
--- a/testsuite/sha3sum.tests
+++ b/testsuite/sha3sum.tests
@@ -1,3 +1,3 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./md5sum.tests sha3sum c29d77bc548fa2b20a04c861400a5360879c52156e2a54a3415b99a9a3123e1d5f36714a24eca8c1f05a8e2d8ba859c930d41141f64a255c6794436fc99c486a 3. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index 6391f9bd9..32a66d03d 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -363,7 +363,6 @@ struct globals {
363 363
364 jmp_buf listingbuf; 364 jmp_buf listingbuf;
365 char line_buffer[80]; 365 char line_buffer[80];
366 char partname_buffer[80];
367 /* Raw disk label. For DOS-type partition tables the MBR, 366 /* Raw disk label. For DOS-type partition tables the MBR,
368 * with descriptions of the primary partitions. */ 367 * with descriptions of the primary partitions. */
369 char MBRbuffer[MAX_SECTOR_SIZE]; 368 char MBRbuffer[MAX_SECTOR_SIZE];
@@ -399,7 +398,6 @@ struct globals {
399#define total_number_of_sectors (G.total_number_of_sectors) 398#define total_number_of_sectors (G.total_number_of_sectors)
400#define listingbuf (G.listingbuf ) 399#define listingbuf (G.listingbuf )
401#define line_buffer (G.line_buffer ) 400#define line_buffer (G.line_buffer )
402#define partname_buffer (G.partname_buffer)
403#define MBRbuffer (G.MBRbuffer ) 401#define MBRbuffer (G.MBRbuffer )
404#define ptes (G.ptes ) 402#define ptes (G.ptes )
405#define INIT_G() do { \ 403#define INIT_G() do { \
@@ -468,9 +466,6 @@ static sector_t bb_BLKGETSIZE_sectors(int fd)
468 466
469#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) 467#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
470 468
471#define hsc2sector(h,s,c) \
472 (sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c)))
473
474static void 469static void
475close_dev_fd(void) 470close_dev_fd(void)
476{ 471{
@@ -478,9 +473,7 @@ close_dev_fd(void)
478 xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd); 473 xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd);
479} 474}
480 475
481/* 476/* Return partition name */
482 * Return partition name - uses static storage
483 */
484static const char * 477static const char *
485partname(const char *dev, int pno, int lth) 478partname(const char *dev, int pno, int lth)
486{ 479{
@@ -489,8 +482,8 @@ partname(const char *dev, int pno, int lth)
489 int bufsiz; 482 int bufsiz;
490 char *bufp; 483 char *bufp;
491 484
492 bufp = partname_buffer; 485 bufp = auto_string(xzalloc(80));
493 bufsiz = sizeof(partname_buffer); 486 bufsiz = 80;
494 487
495 w = strlen(dev); 488 w = strlen(dev);
496 p = ""; 489 p = "";
@@ -1898,14 +1891,14 @@ check_consistency(const struct partition *p, int partition)
1898 return; /* do not check extended partitions */ 1891 return; /* do not check extended partitions */
1899 1892
1900/* physical beginning c, h, s */ 1893/* physical beginning c, h, s */
1901 pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300); 1894 pbc = cylinder(p->sector, p->cyl);
1902 pbh = p->head; 1895 pbh = p->head;
1903 pbs = p->sector & 0x3f; 1896 pbs = sector(p->sector);
1904 1897
1905/* physical ending c, h, s */ 1898/* physical ending c, h, s */
1906 pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300); 1899 pec = cylinder(p->end_sector, p->end_cyl);
1907 peh = p->end_head; 1900 peh = p->end_head;
1908 pes = p->end_sector & 0x3f; 1901 pes = sector(p->end_sector);
1909 1902
1910/* compute logical beginning (c, h, s) */ 1903/* compute logical beginning (c, h, s) */
1911 linear2chs(get_start_sect(p), &lbc, &lbh, &lbs); 1904 linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
@@ -1916,17 +1909,17 @@ check_consistency(const struct partition *p, int partition)
1916/* Same physical / logical beginning? */ 1909/* Same physical / logical beginning? */
1917 if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { 1910 if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1918 printf("Partition %u has different physical/logical " 1911 printf("Partition %u has different physical/logical "
1919 "beginnings (non-Linux?):\n", partition + 1); 1912 "start (non-Linux?):\n", partition + 1);
1920 printf(" phys=(%u, %u, %u) ", pbc, pbh, pbs); 1913 printf(" phys=(%u,%u,%u) ", pbc, pbh, pbs);
1921 printf("logical=(%u, %u, %u)\n", lbc, lbh, lbs); 1914 printf("logical=(%u,%u,%u)\n", lbc, lbh, lbs);
1922 } 1915 }
1923 1916
1924/* Same physical / logical ending? */ 1917/* Same physical / logical ending? */
1925 if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { 1918 if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
1926 printf("Partition %u has different physical/logical " 1919 printf("Partition %u has different physical/logical "
1927 "endings:\n", partition + 1); 1920 "end:\n", partition + 1);
1928 printf(" phys=(%u, %u, %u) ", pec, peh, pes); 1921 printf(" phys=(%u,%u,%u) ", pec, peh, pes);
1929 printf("logical=(%u, %u, %u)\n", lec, leh, les); 1922 printf("logical=(%u,%u,%u)\n", lec, leh, les);
1930 } 1923 }
1931 1924
1932/* Ending on cylinder boundary? */ 1925/* Ending on cylinder boundary? */
@@ -1940,22 +1933,23 @@ static void
1940list_disk_geometry(void) 1933list_disk_geometry(void)
1941{ 1934{
1942 ullong bytes = ((ullong)total_number_of_sectors << 9); 1935 ullong bytes = ((ullong)total_number_of_sectors << 9);
1943 long megabytes = bytes / 1000000; 1936 ullong xbytes = bytes / (1024*1024);
1944 1937 char x = 'M';
1945 if (megabytes < 10000) 1938
1946 printf("\nDisk %s: %lu MB, %llu bytes\n", 1939 if (xbytes >= 10000) {
1947 disk_device, megabytes, bytes); 1940 xbytes += 512; /* fdisk util-linux 2.28 does this */
1948 else 1941 xbytes /= 1024;
1949 printf("\nDisk %s: %lu.%lu GB, %llu bytes\n", 1942 x = 'G';
1950 disk_device, megabytes/1000, (megabytes/100)%10, bytes); 1943 }
1951 printf("%u heads, %u sectors/track, %u cylinders", 1944 printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n"
1952 g_heads, g_sectors, g_cylinders); 1945 "%u cylinders, %u heads, %u sectors/track\n"
1953 if (units_per_sector == 1) 1946 "Units: %s of %u * %u = %u bytes\n\n",
1954 printf(", total %"SECT_FMT"u sectors", 1947 disk_device, xbytes, x,
1955 total_number_of_sectors / (sector_size/512)); 1948 bytes, total_number_of_sectors,
1956 printf("\nUnits = %s of %u * %u = %u bytes\n\n", 1949 g_cylinders, g_heads, g_sectors,
1957 str_units(PLURAL), 1950 str_units(PLURAL),
1958 units_per_sector, sector_size, units_per_sector * sector_size); 1951 units_per_sector, sector_size, units_per_sector * sector_size
1952 );
1959} 1953}
1960 1954
1961/* 1955/*
@@ -2099,10 +2093,17 @@ fix_partition_table_order(void)
2099} 2093}
2100#endif 2094#endif
2101 2095
2096static const char *
2097chs_string11(unsigned cyl, unsigned head, unsigned sect)
2098{
2099 char *buf = auto_string(xzalloc(sizeof(int)*3 * 3));
2100 sprintf(buf, "%u,%u,%u", cylinder(sect,cyl), head, sector(sect));
2101 return buf;
2102}
2103
2102static void 2104static void
2103list_table(int xtra) 2105list_table(int xtra)
2104{ 2106{
2105 const struct partition *p;
2106 int i, w; 2107 int i, w;
2107 2108
2108 if (LABEL_IS_SUN) { 2109 if (LABEL_IS_SUN) {
@@ -2126,50 +2127,62 @@ list_table(int xtra)
2126 } 2127 }
2127 2128
2128 /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, 2129 /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
2129 but if the device name ends in a digit, say /dev/foo1, 2130 * but if the device name ends in a digit, say /dev/foo1,
2130 then the partition is called /dev/foo1p3. */ 2131 * then the partition is called /dev/foo1p3.
2132 */
2131 w = strlen(disk_device); 2133 w = strlen(disk_device);
2132 if (w && isdigit(disk_device[w-1])) 2134 if (w && isdigit(disk_device[w-1]))
2133 w++; 2135 w++;
2134 if (w < 5) 2136 if (w < 7)
2135 w = 5; 2137 w = 7;
2136 2138
2137 // 1 12345678901 12345678901 12345678901 12 2139 printf("%-*s Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type\n",
2138 printf("%*s Boot Start End Blocks Id System\n", 2140 w-1, "Device");
2139 w+1, "Device");
2140 2141
2141 for (i = 0; i < g_partitions; i++) { 2142 for (i = 0; i < g_partitions; i++) {
2143 const struct partition *p;
2142 const struct pte *pe = &ptes[i]; 2144 const struct pte *pe = &ptes[i];
2143 sector_t psects; 2145 char boot4[4];
2144 sector_t pblocks; 2146 char numstr6[6];
2145 unsigned podd; 2147 sector_t start_sect;
2148 sector_t end_sect;
2149 sector_t nr_sects;
2146 2150
2147 p = pe->part_table; 2151 p = pe->part_table;
2148 if (!p || is_cleared_partition(p)) 2152 if (!p || is_cleared_partition(p))
2149 continue; 2153 continue;
2150 2154
2151 psects = get_nr_sects(p); 2155 sprintf(boot4, "%02x", p->boot_ind);
2152 pblocks = psects; 2156 if ((p->boot_ind & 0x7f) == 0) {
2153 podd = 0; 2157 /* 0x80 shown as '*', 0x00 is ' ' */
2154 2158 boot4[0] = p->boot_ind ? '*' : ' ';
2155 if (sector_size < 1024) { 2159 boot4[1] = ' ';
2156 pblocks /= (1024 / sector_size);
2157 podd = psects % (1024 / sector_size);
2158 } 2160 }
2159 if (sector_size > 1024)
2160 pblocks *= (sector_size / 1024);
2161 2161
2162 printf("%s %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n", 2162 start_sect = get_partition_start_from_dev_start(pe);
2163 partname(disk_device, i+1, w+2), 2163 end_sect = start_sect;
2164 !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */ 2164 nr_sects = get_nr_sects(p);
2165 ? '*' : '?', 2165 if (nr_sects != 0)
2166 cround(get_partition_start_from_dev_start(pe)), /* start */ 2166 end_sect += nr_sects - 1;
2167 cround(get_partition_start_from_dev_start(pe) + psects /* end */ 2167
2168 - (psects ? 1 : 0)), 2168 smart_ulltoa5((ullong)nr_sects * sector_size,
2169 pblocks, podd ? '+' : ' ', /* odd flag on end */ 2169 numstr6, " KMGTPEZY")[0] = '\0';
2170 p->sys_ind, /* type id */
2171 partition_type(p->sys_ind)); /* type name */
2172 2170
2171#define SFMT SECT_FMT
2172 // Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
2173 printf("%s%s %-11s"/**/" %-11s"/**/" %10"SFMT"u %10"SFMT"u %10"SFMT"u %s %2x %s\n",
2174 partname(disk_device, i+1, w+2),
2175 boot4,
2176 chs_string11(p->cyl, p->head, p->sector),
2177 chs_string11(p->end_cyl, p->end_head, p->end_sector),
2178 start_sect,
2179 end_sect,
2180 nr_sects,
2181 numstr6,
2182 p->sys_ind,
2183 partition_type(p->sys_ind)
2184 );
2185#undef SFMT
2173 check_consistency(p, i); 2186 check_consistency(p, i);
2174 } 2187 }
2175 2188
@@ -2198,13 +2211,17 @@ x_list_table(int extend)
2198 p = (extend ? pe->ext_pointer : pe->part_table); 2211 p = (extend ? pe->ext_pointer : pe->part_table);
2199 if (p != NULL) { 2212 if (p != NULL) {
2200 printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n", 2213 printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n",
2201 i + 1, p->boot_ind, p->head, 2214 i + 1, p->boot_ind,
2215 p->head,
2202 sector(p->sector), 2216 sector(p->sector),
2203 cylinder(p->sector, p->cyl), p->end_head, 2217 cylinder(p->sector, p->cyl),
2218 p->end_head,
2204 sector(p->end_sector), 2219 sector(p->end_sector),
2205 cylinder(p->end_sector, p->end_cyl), 2220 cylinder(p->end_sector, p->end_cyl),
2206 get_start_sect(p), get_nr_sects(p), 2221 get_start_sect(p),
2207 p->sys_ind); 2222 get_nr_sects(p),
2223 p->sys_ind
2224 );
2208 if (p->sys_ind) 2225 if (p->sys_ind)
2209 check_consistency(p, i); 2226 check_consistency(p, i);
2210 } 2227 }
@@ -2261,6 +2278,7 @@ verify(void)
2261{ 2278{
2262 int i, j; 2279 int i, j;
2263 sector_t total = 1; 2280 sector_t total = 1;
2281 sector_t chs_size;
2264 sector_t first[g_partitions], last[g_partitions]; 2282 sector_t first[g_partitions], last[g_partitions];
2265 struct partition *p; 2283 struct partition *p;
2266 2284
@@ -2322,11 +2340,14 @@ verify(void)
2322 } 2340 }
2323 } 2341 }
2324 2342
2325 if (total > g_heads * g_sectors * g_cylinders) 2343 chs_size = (sector_t)g_heads * g_sectors * g_cylinders;
2326 printf("Total allocated sectors %u greater than the maximum " 2344 if (total > chs_size)
2327 "%u\n", total, g_heads * g_sectors * g_cylinders); 2345 printf("Total allocated sectors %u"
2346 " greater than CHS size %"SECT_FMT"u\n",
2347 total, chs_size
2348 );
2328 else { 2349 else {
2329 total = g_heads * g_sectors * g_cylinders - total; 2350 total = chs_size - total;
2330 if (total != 0) 2351 if (total != 0)
2331 printf("%"SECT_FMT"u unallocated sectors\n", total); 2352 printf("%"SECT_FMT"u unallocated sectors\n", total);
2332 } 2353 }
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 37514eb54..a59115dd4 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -543,8 +543,7 @@ static char *build_alias(char *alias, const char *device_name)
543 543
544/* mknod in /dev based on a path like "/sys/block/hda/hda1" 544/* mknod in /dev based on a path like "/sys/block/hda/hda1"
545 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes 545 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
546 * after NUL, but we promise to not mangle (IOW: to restore NUL if needed) 546 * after NUL, but we promise to not mangle it (IOW: to restore NUL if needed).
547 * path string.
548 * NB2: "mdev -s" may call us many times, do not leak memory/fds! 547 * NB2: "mdev -s" may call us many times, do not leak memory/fds!
549 * 548 *
550 * device_name = $DEVNAME (may be NULL) 549 * device_name = $DEVNAME (may be NULL)
@@ -810,41 +809,39 @@ static void make_device(char *device_name, char *path, int operation)
810 } /* for (;;) */ 809 } /* for (;;) */
811} 810}
812 811
813/* File callback for /sys/ traversal */ 812/* File callback for /sys/ traversal.
813 * We act only on "/sys/.../dev" (pseudo)file
814 */
814static int FAST_FUNC fileAction(const char *fileName, 815static int FAST_FUNC fileAction(const char *fileName,
815 struct stat *statbuf UNUSED_PARAM, 816 struct stat *statbuf UNUSED_PARAM,
816 void *userData, 817 void *userData,
817 int depth UNUSED_PARAM) 818 int depth UNUSED_PARAM)
818{ 819{
819 size_t len = strlen(fileName) - 4; /* can't underflow */ 820 size_t len = strlen(fileName) - 4; /* can't underflow */
820 char *scratch = userData; 821 char *path = userData; /* char array[PATH_MAX + SCRATCH_SIZE] */
821 822 char subsys[PATH_MAX];
822 /* len check is for paranoid reasons */ 823 int res;
823 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX) 824
824 return FALSE; 825 /* Is it a ".../dev" file? (len check is for paranoid reasons) */
825 826 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
826 strcpy(scratch, fileName); 827 return FALSE; /* not .../dev */
827 scratch[len] = '\0'; 828
828 make_device(/*DEVNAME:*/ NULL, scratch, OP_add); 829 strcpy(path, fileName);
829 830 path[len] = '\0';
830 return TRUE; 831
831} 832 /* Read ".../subsystem" symlink in the same directory where ".../dev" is */
832 833 strcpy(subsys, path);
833/* Directory callback for /sys/ traversal */ 834 strcpy(subsys + len, "/subsystem");
834static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, 835 res = readlink(subsys, subsys, sizeof(subsys)-1);
835 struct stat *statbuf UNUSED_PARAM, 836 if (res > 0) {
836 void *userData UNUSED_PARAM, 837 subsys[res] = '\0';
837 int depth)
838{
839 /* Extract device subsystem -- the name of the directory
840 * under /sys/class/ */
841 if (1 == depth) {
842 free(G.subsystem); 838 free(G.subsystem);
843 if (G.subsys_env) { 839 if (G.subsys_env) {
844 bb_unsetenv_and_free(G.subsys_env); 840 bb_unsetenv_and_free(G.subsys_env);
845 G.subsys_env = NULL; 841 G.subsys_env = NULL;
846 } 842 }
847 G.subsystem = strrchr(fileName, '/'); 843 /* Set G.subsystem and $SUBSYSTEM from symlink's last component */
844 G.subsystem = strrchr(subsys, '/');
848 if (G.subsystem) { 845 if (G.subsystem) {
849 G.subsystem = xstrdup(G.subsystem + 1); 846 G.subsystem = xstrdup(G.subsystem + 1);
850 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); 847 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
@@ -852,6 +849,17 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
852 } 849 }
853 } 850 }
854 851
852 make_device(/*DEVNAME:*/ NULL, path, OP_add);
853
854 return TRUE;
855}
856
857/* Directory callback for /sys/ traversal */
858static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
859 struct stat *statbuf UNUSED_PARAM,
860 void *userData UNUSED_PARAM,
861 int depth)
862{
855 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); 863 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
856} 864}
857 865
@@ -872,8 +880,9 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
872 int firmware_fd, loading_fd; 880 int firmware_fd, loading_fd;
873 881
874 /* check for /lib/firmware/$FIRMWARE */ 882 /* check for /lib/firmware/$FIRMWARE */
875 xchdir("/lib/firmware"); 883 firmware_fd = -1;
876 firmware_fd = open(firmware, O_RDONLY); /* can fail */ 884 if (chdir("/lib/firmware") == 0)
885 firmware_fd = open(firmware, O_RDONLY); /* can fail */
877 886
878 /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */ 887 /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
879 xchdir(sysfs_path); 888 xchdir(sysfs_path);
@@ -1065,25 +1074,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
1065 1074
1066 putenv((char*)"ACTION=add"); 1075 putenv((char*)"ACTION=add");
1067 1076
1068 /* ACTION_FOLLOWLINKS is needed since in newer kernels 1077 /* Create all devices from /sys/dev hierarchy */
1069 * /sys/block/loop* (for example) are symlinks to dirs, 1078 recursive_action("/sys/dev",
1070 * not real directories. 1079 ACTION_RECURSE | ACTION_FOLLOWLINKS,
1071 * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs, 1080 fileAction, dirAction, temp, 0);
1072 * but we can't enforce that on users)
1073 */
1074 if (access("/sys/class/block", F_OK) != 0) {
1075 /* Scan obsolete /sys/block only if /sys/class/block
1076 * doesn't exist. Otherwise we'll have dupes.
1077 * Also, do not complain if it doesn't exist.
1078 * Some people configure kernel to have no blockdevs.
1079 */
1080 recursive_action("/sys/block",
1081 ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
1082 fileAction, dirAction, temp, 0);
1083 }
1084 recursive_action("/sys/class",
1085 ACTION_RECURSE | ACTION_FOLLOWLINKS,
1086 fileAction, dirAction, temp, 0);
1087 } else { 1081 } else {
1088 char *fw; 1082 char *fw;
1089 char *seq; 1083 char *seq;
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 13590ceb4..eb8b7ba7b 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -1931,7 +1931,6 @@ static int singlemount(struct mntent *mp, int ignore_busy)
1931 int len; 1931 int len;
1932 char c; 1932 char c;
1933 char *hostname, *share; 1933 char *hostname, *share;
1934 char *dotted, *ip;
1935 len_and_sockaddr *lsa; 1934 len_and_sockaddr *lsa;
1936 1935
1937 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]" 1936 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
@@ -1971,13 +1970,26 @@ static int singlemount(struct mntent *mp, int ignore_busy)
1971 if (!lsa) 1970 if (!lsa)
1972 goto report_error; 1971 goto report_error;
1973 1972
1974 // Insert "ip=..." option into options 1973 // If there is no "ip=..." option yet
1975 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); 1974 if (!is_prefixed_with(filteropts, ",ip="+1)
1976 if (ENABLE_FEATURE_CLEAN_UP) free(lsa); 1975 && !strstr(filteropts, ",ip=")
1977 ip = xasprintf("ip=%s", dotted); 1976 ) {
1978 if (ENABLE_FEATURE_CLEAN_UP) free(dotted); 1977 char *dotted, *ip;
1979 parse_mount_options(ip, &filteropts); 1978 // Insert "ip=..." option into options
1980 if (ENABLE_FEATURE_CLEAN_UP) free(ip); 1979 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1980 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1981 ip = xasprintf("ip=%s", dotted);
1982 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1983// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
1984// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
1985// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
1986// musl apparently does. This results in "ip=numericIPv6%iface_name"
1987// (instead of _numeric_ iface_id) with glibc.
1988// This probably should be fixed in glibc, not here.
1989// The workaround is to manually specify correct "ip=ADDR%n" option.
1990 parse_mount_options(ip, &filteropts);
1991 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
1992 }
1981 1993
1982 mp->mnt_type = (char*)"cifs"; 1994 mp->mnt_type = (char*)"cifs";
1983 rc = mount_it_now(mp, vfsflags, filteropts); 1995 rc = mount_it_now(mp, vfsflags, filteropts);
diff --git a/util-linux/unshare.c b/util-linux/unshare.c
index fa7086add..dcc59559a 100644
--- a/util-linux/unshare.c
+++ b/util-linux/unshare.c
@@ -103,7 +103,7 @@ enum {
103 OPT_mount = 1 << 0, 103 OPT_mount = 1 << 0,
104 OPT_uts = 1 << 1, 104 OPT_uts = 1 << 1,
105 OPT_ipc = 1 << 2, 105 OPT_ipc = 1 << 2,
106 OPT_network = 1 << 3, 106 OPT_net = 1 << 3,
107 OPT_pid = 1 << 4, 107 OPT_pid = 1 << 4,
108 OPT_user = 1 << 5, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */ 108 OPT_user = 1 << 5, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
109 OPT_fork = 1 << 6, 109 OPT_fork = 1 << 6,
@@ -142,7 +142,7 @@ static const char unshare_longopts[] ALIGN1 =
142 "mount\0" Optional_argument "\xf0" 142 "mount\0" Optional_argument "\xf0"
143 "uts\0" Optional_argument "\xf1" 143 "uts\0" Optional_argument "\xf1"
144 "ipc\0" Optional_argument "\xf2" 144 "ipc\0" Optional_argument "\xf2"
145 "network\0" Optional_argument "\xf3" 145 "net\0" Optional_argument "\xf3"
146 "pid\0" Optional_argument "\xf4" 146 "pid\0" Optional_argument "\xf4"
147 "user\0" Optional_argument "\xf5" 147 "user\0" Optional_argument "\xf5"
148 "fork\0" No_argument "f" 148 "fork\0" No_argument "f"
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
index 6b97df113..b64d28ceb 100644
--- a/util-linux/volume_id/get_devname.c
+++ b/util-linux/volume_id/get_devname.c
@@ -107,7 +107,11 @@ uuidcache_check_device(const char *device,
107 int depth UNUSED_PARAM) 107 int depth UNUSED_PARAM)
108{ 108{
109 /* note: this check rejects links to devices, among other nodes */ 109 /* note: this check rejects links to devices, among other nodes */
110 if (!S_ISBLK(statbuf->st_mode)) 110 if (!S_ISBLK(statbuf->st_mode)
111#if ENABLE_FEATURE_VOLUMEID_UBIFS
112 && !(S_ISCHR(statbuf->st_mode) && strncmp(bb_basename(device), "ubi", 3) == 0)
113#endif
114 )
111 return TRUE; 115 return TRUE;
112 116
113 /* Users report that mucking with floppies (especially non-present 117 /* Users report that mucking with floppies (especially non-present
diff --git a/util-linux/volume_id/ubifs.c b/util-linux/volume_id/ubifs.c
new file mode 100644
index 000000000..13604ec35
--- /dev/null
+++ b/util-linux/volume_id/ubifs.c
@@ -0,0 +1,125 @@
1/*
2 * volume_id - reads filesystem label and uuid
3 *
4 * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8
9//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_UBIFS) += ubifs.o
10
11//config:
12//config:config FEATURE_VOLUMEID_UBIFS
13//config: bool "UBIFS filesystem"
14//config: default y
15//config: depends on VOLUMEID
16//config: help
17//config: UBIFS (Unsorted Block Image File System) is a file
18//config: system for use with raw flash memory media.
19//config:
20
21#include "volume_id_internal.h"
22
23#define UBIFS_NODE_MAGIC 0x06101831
24
25/*
26 * struct ubifs_ch - common header node.
27 * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
28 * @crc: CRC-32 checksum of the node header
29 * @sqnum: sequence number
30 * @len: full node length
31 * @node_type: node type
32 * @group_type: node group type
33 * @padding: reserved for future, zeroes
34 *
35 * Every UBIFS node starts with this common part. If the node has a key, the
36 * key always goes next.
37 */
38struct ubifs_ch {
39 uint32_t magic;
40 uint32_t crc;
41 uint64_t sqnum;
42 uint32_t len;
43 uint8_t node_type;
44 uint8_t group_type;
45 uint8_t padding[2];
46} PACKED;
47
48/*
49 * struct ubifs_sb_node - superblock node.
50 * @ch: common header
51 * @padding: reserved for future, zeroes
52 * @key_hash: type of hash function used in keys
53 * @key_fmt: format of the key
54 * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
55 * @min_io_size: minimal input/output unit size
56 * @leb_size: logical eraseblock size in bytes
57 * @leb_cnt: count of LEBs used by file-system
58 * @max_leb_cnt: maximum count of LEBs used by file-system
59 * @max_bud_bytes: maximum amount of data stored in buds
60 * @log_lebs: log size in logical eraseblocks
61 * @lpt_lebs: number of LEBs used for lprops table
62 * @orph_lebs: number of LEBs used for recording orphans
63 * @jhead_cnt: count of journal heads
64 * @fanout: tree fanout (max. number of links per indexing node)
65 * @lsave_cnt: number of LEB numbers in LPT's save table
66 * @fmt_version: UBIFS on-flash format version
67 * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
68 * @padding1: reserved for future, zeroes
69 * @rp_uid: reserve pool UID
70 * @rp_gid: reserve pool GID
71 * @rp_size: size of the reserved pool in bytes
72 * @padding2: reserved for future, zeroes
73 * @time_gran: time granularity in nanoseconds
74 * @uuid: UUID generated when the file system image was created
75 * @ro_compat_version: UBIFS R/O compatibility version
76 */
77struct ubifs_sb_node {
78 struct ubifs_ch ch;
79 uint8_t padding[2];
80 uint8_t key_hash;
81 uint8_t key_fmt;
82 uint32_t flags;
83 uint32_t min_io_size;
84 uint32_t leb_size;
85 uint32_t leb_cnt;
86 uint32_t max_leb_cnt;
87 uint64_t max_bud_bytes;
88 uint32_t log_lebs;
89 uint32_t lpt_lebs;
90 uint32_t orph_lebs;
91 uint32_t jhead_cnt;
92 uint32_t fanout;
93 uint32_t lsave_cnt;
94 uint32_t fmt_version;
95 uint16_t default_compr;
96 uint8_t padding1[2];
97 uint32_t rp_uid;
98 uint32_t rp_gid;
99 uint64_t rp_size;
100 uint32_t time_gran;
101 uint8_t uuid[16];
102 uint32_t ro_compat_version;
103/*
104 uint8_t padding2[3968];
105*/
106} PACKED;
107
108int FAST_FUNC volume_id_probe_ubifs(struct volume_id *id /*,uint64_t off*/)
109{
110#define off ((uint64_t)0)
111 struct ubifs_sb_node *sb;
112
113 dbg("UBIFS: probing at offset 0x%llx", (unsigned long long) off);
114 sb = volume_id_get_buffer(id, off, sizeof(struct ubifs_sb_node));
115 if (!sb)
116 return -1;
117
118 if (le32_to_cpu(sb->ch.magic) != UBIFS_NODE_MAGIC)
119 return -1;
120
121 IF_FEATURE_BLKID_TYPE(id->type = "ubifs";)
122 volume_id_set_uuid(id, sb->uuid, UUID_DCE);
123
124 return 0;
125}
diff --git a/util-linux/volume_id/volume_id.c b/util-linux/volume_id/volume_id.c
index 3f71e0084..5bb95994b 100644
--- a/util-linux/volume_id/volume_id.c
+++ b/util-linux/volume_id/volume_id.c
@@ -168,6 +168,9 @@ static const probe_fptr fs2[] = {
168#if ENABLE_FEATURE_VOLUMEID_OCFS2 168#if ENABLE_FEATURE_VOLUMEID_OCFS2
169 volume_id_probe_ocfs2, 169 volume_id_probe_ocfs2,
170#endif 170#endif
171#if ENABLE_FEATURE_VOLUMEID_UBIFS
172 volume_id_probe_ubifs,
173#endif
171}; 174};
172 175
173int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size) 176int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size)
diff --git a/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h
index 3061ac4d5..759a832e6 100644
--- a/util-linux/volume_id/volume_id_internal.h
+++ b/util-linux/volume_id/volume_id_internal.h
@@ -221,4 +221,6 @@ int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/);
221 221
222int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/); 222int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/);
223 223
224int FAST_FUNC volume_id_probe_ubifs(struct volume_id *id /*,uint64_t off*/);
225
224POP_SAVED_FUNCTION_VISIBILITY 226POP_SAVED_FUNCTION_VISIBILITY