aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Config.in9
-rw-r--r--Makefile7
-rw-r--r--Makefile.custom4
-rw-r--r--Makefile.flags11
-rw-r--r--NOFORK_NOEXEC.lst1
-rw-r--r--applets/usage_pod.c17
-rw-r--r--archival/Config.src11
-rw-r--r--archival/bbunzip.c6
-rw-r--r--archival/cpio.c7
-rw-r--r--archival/libarchive/data_extract_all.c8
-rw-r--r--archival/libarchive/open_transformer.c26
-rw-r--r--archival/libarchive/unsafe_prefix.c6
-rw-r--r--archival/rpm.c9
-rw-r--r--archival/tar.c2
-rw-r--r--configs/mingw32_defconfig15
-rw-r--r--configs/mingw64_defconfig15
-rw-r--r--configs/mingw64a_defconfig15
-rw-r--r--configs/mingw64u_defconfig15
-rw-r--r--console-tools/showkey.c4
-rw-r--r--coreutils/date.c2
-rw-r--r--coreutils/df.c33
-rw-r--r--coreutils/expr.c4
-rw-r--r--coreutils/ls.c251
-rw-r--r--coreutils/md5_sha1_sum.c58
-rw-r--r--coreutils/stty.c148
-rw-r--r--coreutils/tr.c7
-rw-r--r--coreutils/truncate.c6
-rw-r--r--e2fsprogs/fsck.c4
-rw-r--r--editors/diff.c2
-rw-r--r--editors/sed.c7
-rw-r--r--include/libbb.h296
-rw-r--r--include/mingw.h2
-rw-r--r--include/platform.h5
-rw-r--r--include/usage.src.h6
-rw-r--r--init/bootchartd.c2
-rw-r--r--init/init.c18
-rw-r--r--libbb/Config.src14
-rw-r--r--libbb/bitops.c128
-rw-r--r--libbb/c_escape.c20
-rw-r--r--libbb/concat_path_file.c75
-rw-r--r--libbb/const_hack.c29
-rw-r--r--libbb/dump.c65
-rw-r--r--libbb/getopt32.c13
-rw-r--r--libbb/hash_hmac.c154
-rw-r--r--libbb/hash_md5_sha.c146
-rw-r--r--libbb/hash_sha256_block.c19
-rw-r--r--libbb/hash_sha256_hwaccel_x86-32.S218
-rw-r--r--libbb/hash_sha256_hwaccel_x86-64.S218
-rw-r--r--libbb/lineedit.c114
-rw-r--r--libbb/poll_with_signals.c48
-rw-r--r--libbb/procps.c183
-rw-r--r--libbb/pw_ascii64.c91
-rw-r--r--libbb/pw_encrypt.c113
-rw-r--r--libbb/pw_encrypt_des.c88
-rw-r--r--libbb/pw_encrypt_md5.c4
-rw-r--r--libbb/pw_encrypt_sha.c5
-rw-r--r--libbb/pw_encrypt_yes.c24
-rw-r--r--libbb/read_key.c25
-rw-r--r--libbb/replace.c14
-rw-r--r--libbb/signals.c30
-rw-r--r--libbb/xfuncs.c2
-rw-r--r--libbb/yescrypt/Kbuild.src9
-rw-r--r--libbb/yescrypt/PARAMETERS196
-rw-r--r--libbb/yescrypt/README4
-rw-r--r--libbb/yescrypt/alg-sha256.c91
-rw-r--r--libbb/yescrypt/alg-yescrypt-common.c408
-rw-r--r--libbb/yescrypt/alg-yescrypt-kdf.c1212
-rw-r--r--libbb/yescrypt/alg-yescrypt.h247
-rw-r--r--libbb/yescrypt/y.c16
-rw-r--r--loginutils/Config.src11
-rw-r--r--loginutils/chpasswd.c2
-rw-r--r--loginutils/cryptpw.c37
-rw-r--r--loginutils/sulogin.c9
-rw-r--r--miscutils/crond.c12
-rw-r--r--miscutils/fbsplash.c2
-rw-r--r--miscutils/less.c2
-rw-r--r--miscutils/make.c37
-rw-r--r--miscutils/man.c2
-rw-r--r--modutils/modprobe-small.c21
-rw-r--r--modutils/modprobe.c2
-rw-r--r--modutils/modutils.c9
-rw-r--r--modutils/modutils.h1
-rw-r--r--networking/Config.src30
-rw-r--r--networking/ftpd.c39
-rw-r--r--networking/hostname.c4
-rw-r--r--networking/httpd.c2
-rw-r--r--networking/libiproute/iproute.c16
-rw-r--r--networking/ntpd.c6
-rw-r--r--networking/telnetd.c4
-rw-r--r--networking/tftp.c2
-rw-r--r--networking/tls.c710
-rw-r--r--networking/tls.h7
-rw-r--r--networking/tls_aesgcm.c5
-rw-r--r--networking/udhcp/d6_dhcpc.c193
-rw-r--r--networking/udhcp/d6_packet.c16
-rw-r--r--networking/udhcp/dhcpd.c58
-rw-r--r--procps/pmap.c141
-rw-r--r--procps/top.c355
-rw-r--r--runit/chpst.c3
-rw-r--r--scripts/kconfig/libcurses/addch.c36
-rw-r--r--scripts/kconfig/libcurses/addstr.c36
-rw-r--r--scripts/kconfig/libcurses/attr.c62
-rw-r--r--scripts/kconfig/libcurses/beep.c8
-rw-r--r--scripts/kconfig/libcurses/bkgd.c26
-rw-r--r--scripts/kconfig/libcurses/border.c66
-rw-r--r--scripts/kconfig/libcurses/clear.c20
-rw-r--r--scripts/kconfig/libcurses/color.c30
-rw-r--r--scripts/kconfig/libcurses/curses.h35
-rw-r--r--scripts/kconfig/libcurses/getch.c28
-rw-r--r--scripts/kconfig/libcurses/getyx.c32
-rw-r--r--scripts/kconfig/libcurses/inch.c20
-rw-r--r--scripts/kconfig/libcurses/initscr.c24
-rw-r--r--scripts/kconfig/libcurses/inopts.c54
-rw-r--r--scripts/kconfig/libcurses/kernel.c30
-rw-r--r--scripts/kconfig/libcurses/move.c10
-rw-r--r--scripts/kconfig/libcurses/outopts.c36
-rw-r--r--scripts/kconfig/libcurses/overlay.c10
-rw-r--r--scripts/kconfig/libcurses/pad.c18
-rw-r--r--scripts/kconfig/libcurses/pdcclip.c34
-rw-r--r--scripts/kconfig/libcurses/pdcsetsc.c9
-rw-r--r--scripts/kconfig/libcurses/printw.c16
-rw-r--r--scripts/kconfig/libcurses/refresh.c16
-rw-r--r--scripts/kconfig/libcurses/scroll.c10
-rw-r--r--scripts/kconfig/libcurses/slk.c54
-rw-r--r--scripts/kconfig/libcurses/touch.c18
-rw-r--r--scripts/kconfig/libcurses/window.c42
-rwxr-xr-xscripts/kconfig/lxdialog/check-lxdialog.sh2
-rwxr-xr-xscripts/mk_mingw64u_defconfig1
-rw-r--r--shell/Config.src7
-rw-r--r--shell/ash.c1198
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests4
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests4
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests4
-rw-r--r--shell/ash_test/ash-heredoc/herestring1.right33
-rwxr-xr-xshell/ash_test/ash-heredoc/herestring1.tests35
-rw-r--r--shell/ash_test/ash-misc/assignment2.right2
-rwxr-xr-xshell/ash_test/ash-misc/assignment2.tests2
-rw-r--r--shell/ash_test/ash-misc/func6.right3
-rwxr-xr-xshell/ash_test/ash-misc/func6.tests8
-rw-r--r--shell/ash_test/ash-misc/func7.right1
-rwxr-xr-xshell/ash_test/ash-misc/func7.tests1
-rw-r--r--shell/ash_test/ash-read/read_ifs2.right9
-rwxr-xr-xshell/ash_test/ash-read/read_ifs2.tests9
-rw-r--r--shell/ash_test/ash-read/read_t.right8
-rwxr-xr-xshell/ash_test/ash-read/read_t.tests18
-rw-r--r--shell/ash_test/ash-redir/redir_EINTR1.right2
-rwxr-xr-xshell/ash_test/ash-redir/redir_EINTR1.tests13
-rw-r--r--shell/ash_test/ash-redir/redir_EINTR2.right2
-rwxr-xr-xshell/ash_test/ash-redir/redir_EINTR2.tests14
-rw-r--r--shell/ash_test/ash-vars/var_backslash1.right35
-rwxr-xr-xshell/ash_test/ash-vars/var_backslash1.tests48
-rw-r--r--shell/ash_test/printenv.c4
-rw-r--r--shell/ash_test/recho.c2
-rw-r--r--shell/hush.c2512
-rwxr-xr-xshell/hush_leaktool.sh18
-rw-r--r--shell/hush_test/hush-bugs/tickquote1.right (renamed from shell/hush_test/hush-misc/tickquote1.right)0
-rwxr-xr-xshell/hush_test/hush-bugs/tickquote1.tests (renamed from shell/hush_test/hush-misc/tickquote1.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests2
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests2
-rw-r--r--shell/hush_test/hush-heredoc/herestring1.right33
-rwxr-xr-xshell/hush_test/hush-heredoc/herestring1.tests35
-rw-r--r--shell/hush_test/hush-misc/assignment2.right2
-rwxr-xr-xshell/hush_test/hush-misc/assignment2.tests2
-rw-r--r--shell/hush_test/hush-misc/case2.right1
-rwxr-xr-xshell/hush_test/hush-misc/case2.tests6
-rw-r--r--shell/hush_test/hush-misc/case3.right1
-rwxr-xr-xshell/hush_test/hush-misc/case3.tests7
-rw-r--r--shell/hush_test/hush-misc/case4.right2
-rwxr-xr-xshell/hush_test/hush-misc/case4.tests8
-rw-r--r--shell/hush_test/hush-misc/func6.right3
-rwxr-xr-xshell/hush_test/hush-misc/func6.tests8
-rw-r--r--shell/hush_test/hush-misc/func7.right1
-rwxr-xr-xshell/hush_test/hush-misc/func7.tests1
-rwxr-xr-xshell/hush_test/hush-misc/sig_exitcode.tests3
-rw-r--r--shell/hush_test/hush-misc/syntax_err_negate.right4
-rwxr-xr-xshell/hush_test/hush-misc/syntax_err_negate.tests5
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait6.tests3
-rw-r--r--shell/hush_test/hush-parsing/bad_cmd1.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_cmd1.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_cmd2.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_cmd2.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_cmd3.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_cmd3.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe1.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe1.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe2.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe2.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe3.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe3.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe4.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe4.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe5.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe5.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe6.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe6.tests1
-rw-r--r--shell/hush_test/hush-parsing/bad_pipe7.right1
-rwxr-xr-xshell/hush_test/hush-parsing/bad_pipe7.tests1
-rw-r--r--shell/hush_test/hush-read/read_t.right8
-rwxr-xr-xshell/hush_test/hush-read/read_t.tests18
-rw-r--r--shell/hush_test/hush-redir/redir_EINTR1.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir_EINTR1.tests13
-rw-r--r--shell/hush_test/hush-redir/redir_EINTR2.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir_EINTR2.tests14
-rw-r--r--shell/hush_test/hush-redir/redir_and_constructs1.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir_and_constructs1.tests2
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests3
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests3
-rw-r--r--shell/hush_test/hush-vars/var_backslash1.right35
-rwxr-xr-xshell/hush_test/hush-vars/var_backslash1.tests48
-rwxr-xr-xshell/hush_test/run-all7
-rw-r--r--shell/shell_common.c48
-rw-r--r--sysklogd/syslogd.c2
-rwxr-xr-xtestsuite/cpio.tests23
-rwxr-xr-xtestsuite/cryptpw.tests98
-rwxr-xr-xtestsuite/hexdump.tests101
-rwxr-xr-xtestsuite/ls.tests26
-rwxr-xr-xtestsuite/md5sum.tests7
-rwxr-xr-xtestsuite/od.tests13
-rwxr-xr-xtestsuite/sha384sum.tests3
-rwxr-xr-xtestsuite/sha3sum.tests6
-rwxr-xr-xtestsuite/tr.tests4
-rw-r--r--testsuite/wget/wget-handles-https4
-rw-r--r--util-linux/lspci.c2
-rw-r--r--util-linux/lsusb.c2
-rw-r--r--win32/dirent.c28
-rw-r--r--win32/glob.c2
-rw-r--r--win32/inet_pton.c1
-rw-r--r--win32/ioctl.c49
-rw-r--r--win32/mingw.c9
-rw-r--r--win32/paths.h1
-rw-r--r--win32/process.c3
-rw-r--r--win32/select.c7
-rw-r--r--win32/strptime.c36
-rw-r--r--win32/termios.c26
-rw-r--r--win32/termios.h29
-rw-r--r--win32/winansi.c4
255 files changed, 9233 insertions, 3230 deletions
diff --git a/Config.in b/Config.in
index f6159b30b..13eb33edc 100644
--- a/Config.in
+++ b/Config.in
@@ -566,6 +566,15 @@ config OVERRIDE_APPLETS
566 environment variable. BB_OVERRIDE_APPLETS is checked first, if it 566 environment variable. BB_OVERRIDE_APPLETS is checked first, if it
567 allows the applet and this list is non-empty it is checked too. 567 allows the applet and this list is non-empty it is checked too.
568 568
569config UNWIND_TABLES
570 bool "Include stack unwind tables in the binary"
571 default y
572 depends on PLATFORM_MINGW32
573 help
574 Binaries can include support for stack unwinding. This isn't
575 normally required in BusyBox, but Windows' Control Flow Guard
576 needs it. Disabling this reduces the size of the binaries.
577
569comment 'Build Options' 578comment 'Build Options'
570 579
571config STATIC 580config STATIC
diff --git a/Makefile b/Makefile
index ae870ecb7..52a2cff87 100644
--- a/Makefile
+++ b/Makefile
@@ -551,6 +551,7 @@ libs-y := \
551 findutils/ \ 551 findutils/ \
552 init/ \ 552 init/ \
553 libbb/ \ 553 libbb/ \
554 libbb/yescrypt/ \
554 libpwdgrp/ \ 555 libpwdgrp/ \
555 loginutils/ \ 556 loginutils/ \
556 mailutils/ \ 557 mailutils/ \
@@ -688,8 +689,12 @@ quiet_cmd_busybox__ ?= LINK $@
688 "$(core-y)" \ 689 "$(core-y)" \
689 "$(libs-y)" \ 690 "$(libs-y)" \
690 "$(LDLIBS)" \ 691 "$(LDLIBS)" \
691 "$(CONFIG_EXTRA_LDLIBS)" \ 692 $(CONFIG_EXTRA_LDLIBS) \
692 && $(srctree)/scripts/generate_BUFSIZ.sh --post include/common_bufsiz.h 693 && $(srctree)/scripts/generate_BUFSIZ.sh --post include/common_bufsiz.h
694# ^^^ note: CONFIG_xyz strings already have double quotes: their value
695# is '"LIB LIB2"', therefore $(CONFIG_EXTRA_LDLIBS) above must NOT be written
696# as "$(CONFIG_EXTRA_LDLIBS)", it would be passed as ""LIB LIB2"",
697# and LIB2 would end up in $9, not $8 (and lost or misinterpreted).
693 698
694# Generate System.map 699# Generate System.map
695quiet_cmd_sysmap = SYSMAP 700quiet_cmd_sysmap = SYSMAP
diff --git a/Makefile.custom b/Makefile.custom
index e2f6d1c2f..36170de8a 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -156,12 +156,12 @@ docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
156docs/BusyBox.txt: docs/busybox.pod 156docs/BusyBox.txt: docs/busybox.pod
157 $(disp_doc) 157 $(disp_doc)
158 $(Q)-mkdir -p docs 158 $(Q)-mkdir -p docs
159 $(Q)-pod2text $< > $@ 159 $(Q)-pod2text --quotes=none $< > $@
160 160
161docs/busybox.1: docs/busybox.pod 161docs/busybox.1: docs/busybox.pod
162 $(disp_doc) 162 $(disp_doc)
163 $(Q)-mkdir -p docs 163 $(Q)-mkdir -p docs
164 $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@ 164 $(Q)-pod2man --quotes=none --center=busybox --release="version $(KERNELVERSION)" $< > $@
165 165
166docs/BusyBox.html: docs/busybox.net/BusyBox.html 166docs/BusyBox.html: docs/busybox.net/BusyBox.html
167 $(disp_doc) 167 $(disp_doc)
diff --git a/Makefile.flags b/Makefile.flags
index f24fd9475..29fabcfb5 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -73,8 +73,13 @@ CFLAGS += $(call cc-option,-falign-jumps=1 -falign-labels=1 -falign-loops=1,)
73endif 73endif
74 74
75# Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary): 75# Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary):
76ifneq ($(CONFIG_UNWIND_TABLES),y)
76CFLAGS += $(call cc-option,-fno-unwind-tables,) 77CFLAGS += $(call cc-option,-fno-unwind-tables,)
77CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,) 78CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,)
79else
80CFLAGS += $(call cc-option,-funwind-tables,)
81CFLAGS += $(call cc-option,-fasynchronous-unwind-tables,)
82endif
78# No automatic printf->puts,putchar conversions 83# No automatic printf->puts,putchar conversions
79# (try disabling this and comparing assembly, it's instructive) 84# (try disabling this and comparing assembly, it's instructive)
80CFLAGS += $(call cc-option,-fno-builtin-printf,) 85CFLAGS += $(call cc-option,-fno-builtin-printf,)
@@ -151,11 +156,15 @@ ifeq ($(CONFIG_PLATFORM_MINGW32),y)
151CFLAGS += -Iwin32 -DHAVE_STRING_H=1 -DHAVE_CONFIG_H=0 -fno-builtin-stpcpy -fno-builtin-stpncpy -fno-ident -fno-builtin-strndup 156CFLAGS += -Iwin32 -DHAVE_STRING_H=1 -DHAVE_CONFIG_H=0 -fno-builtin-stpcpy -fno-builtin-stpncpy -fno-ident -fno-builtin-strndup
152# this seems to be necessary for setjmp/longjmp to work with clang 157# this seems to be necessary for setjmp/longjmp to work with clang
153ifeq ($(lastword $(subst -, ,$(CC))),clang) 158ifeq ($(lastword $(subst -, ,$(CC))),clang)
159ifeq ($(CONFIG_UNWIND_TABLES),y)
160CFLAGS += $(call cc-option,-fexceptions,)
161else
154CFLAGS += $(call cc-option,-fsjlj-exceptions,) 162CFLAGS += $(call cc-option,-fsjlj-exceptions,)
155endif 163endif
164endif
156 165
157EXEEXT = .exe 166EXEEXT = .exe
158LDLIBS += ws2_32 167LDLIBS += ws2_32 bcrypt secur32
159endif 168endif
160 169
161ifneq ($(CONFIG_PLATFORM_MINGW32),y) 170ifneq ($(CONFIG_PLATFORM_MINGW32),y)
diff --git a/NOFORK_NOEXEC.lst b/NOFORK_NOEXEC.lst
index 055f9fb24..a000de45b 100644
--- a/NOFORK_NOEXEC.lst
+++ b/NOFORK_NOEXEC.lst
@@ -336,6 +336,7 @@ setuidgid - noexec. spawner
336sha1sum - noexec. runner 336sha1sum - noexec. runner
337sha256sum - noexec. runner 337sha256sum - noexec. runner
338sha3sum - noexec. runner 338sha3sum - noexec. runner
339sha384sum - noexec. runner
339sha512sum - noexec. runner 340sha512sum - noexec. runner
340showkey - interactive, longterm 341showkey - interactive, longterm
341shred - runner 342shred - runner
diff --git a/applets/usage_pod.c b/applets/usage_pod.c
index 9e6d3f0ee..2c177be90 100644
--- a/applets/usage_pod.c
+++ b/applets/usage_pod.c
@@ -67,30 +67,37 @@ int main(void)
67 } 67 }
68 if (col == 0) { 68 if (col == 0) {
69 col = 6; 69 col = 6;
70 printf("\t");
71 } else { 70 } else {
72 printf(", "); 71 printf(", ");
73 } 72 }
74 printf("%s", usage_array[i].aname); 73 if (usage_array[i].usage[0] != NOUSAGE_STR[0]) {
74 /*
75 * If the applet usage string will be included in the final document
76 * optimistically link to its header (which is just the applet name).
77 */
78 printf("L<C<%1$s>|/\"%1$s\">", usage_array[i].aname);
79 } else {
80 /* Without a usage string, just output the applet name with no link. */
81 printf("C<%s>", usage_array[i].aname);
82 }
75 col += len2; 83 col += len2;
76 } 84 }
77 printf("\n\n"); 85 printf("\n\n");
78 86
79 printf("=head1 COMMAND DESCRIPTIONS\n\n"); 87 printf("=head1 COMMAND DESCRIPTIONS\n\n");
80 printf("=over 4\n\n");
81 88
82 for (i = 0; i < num_messages; i++) { 89 for (i = 0; i < num_messages; i++) {
83 if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z' 90 if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z'
84 && usage_array[i].usage[0] != NOUSAGE_STR[0] 91 && usage_array[i].usage[0] != NOUSAGE_STR[0]
85 ) { 92 ) {
86 printf("=item B<%s>\n\n", usage_array[i].aname); 93 /* This is the heading that will be linked from the command list. */
94 printf("=head2 %s\n\n", usage_array[i].aname);
87 if (usage_array[i].usage[0]) 95 if (usage_array[i].usage[0])
88 printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage); 96 printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage);
89 else 97 else
90 printf("%s\n\n", usage_array[i].aname); 98 printf("%s\n\n", usage_array[i].aname);
91 } 99 }
92 } 100 }
93 printf("=back\n\n");
94 101
95 return 0; 102 return 0;
96} 103}
diff --git a/archival/Config.src b/archival/Config.src
index 6f4f30c43..cbcd7217c 100644
--- a/archival/Config.src
+++ b/archival/Config.src
@@ -35,4 +35,15 @@ config FEATURE_LZMA_FAST
35 This option reduces decompression time by about 25% at the cost of 35 This option reduces decompression time by about 25% at the cost of
36 a 1K bigger binary. 36 a 1K bigger binary.
37 37
38config FEATURE_PATH_TRAVERSAL_PROTECTION
39 bool "Prevent extraction of filenames with /../ path component"
40 default n
41 help
42 busybox tar and unzip remove "PREFIX/../" (if it exists)
43 from extracted names.
44 This option enables this behavior for all other unpacking applets,
45 such as cpio, ar, rpm.
46 GNU cpio 2.15 has NO such sanity check.
47# try other archivers and document their behavior?
48
38endmenu 49endmenu
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index fb5deb0ce..1f1f28b61 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -71,8 +71,8 @@ int FAST_FUNC bbunpack(char **argv,
71 goto err; 71 goto err;
72 } else { 72 } else {
73 /* "clever zcat" with FILE */ 73 /* "clever zcat" with FILE */
74 /* fail_if_not_compressed because zcat refuses uncompressed input */ 74 /* die_if_not_compressed because zcat refuses uncompressed input */
75 int fd = open_zipped(filename, /*fail_if_not_compressed:*/ 1); 75 int fd = open_zipped(filename, /*die_if_not_compressed:*/ 1);
76 if (fd < 0) 76 if (fd < 0)
77 goto err_name; 77 goto err_name;
78 xmove_fd(fd, STDIN_FILENO); 78 xmove_fd(fd, STDIN_FILENO);
@@ -80,7 +80,7 @@ int FAST_FUNC bbunpack(char **argv,
80 } else 80 } else
81 if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) { 81 if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) {
82 /* "clever zcat" on stdin */ 82 /* "clever zcat" on stdin */
83 if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_compressed*/ 1)) 83 if (setup_unzip_on_fd(STDIN_FILENO, /*die_if_not_compressed*/ 1))
84 goto err; 84 goto err;
85 } 85 }
86 86
diff --git a/archival/cpio.c b/archival/cpio.c
index 167931bdb..38d826a3c 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -354,6 +354,12 @@ static NOINLINE int cpio_o(void)
354#endif 354#endif
355#endif 355#endif
356 356
357 if (sizeof(st.st_size) > 4
358 && st.st_size > (off_t)0xffffffff
359 ) {
360 bb_error_msg_and_die("error: file '%s' is larger than 4GB", name);
361 }
362
357 bytes += printf("070701" 363 bytes += printf("070701"
358 "%08X%08X%08X%08X%08X%08X%08X" 364 "%08X%08X%08X%08X%08X%08X%08X"
359 "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ 365 "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
@@ -425,6 +431,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
425#endif 431#endif
426#endif 432#endif
427 "owner\0" Required_argument "R" 433 "owner\0" Required_argument "R"
434 "file\0" Required_argument "F"
428 "verbose\0" No_argument "v" 435 "verbose\0" No_argument "v"
429 "null\0" No_argument "0" 436 "null\0" No_argument "0"
430 "quiet\0" No_argument "\xff" 437 "quiet\0" No_argument "\xff"
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 049c2c156..8a69711c1 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -65,6 +65,14 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
65 } while (--n != 0); 65 } while (--n != 0);
66 } 66 }
67#endif 67#endif
68#if ENABLE_FEATURE_PATH_TRAVERSAL_PROTECTION
69 /* Strip leading "/" and up to last "/../" path component */
70 dst_name = (char *)strip_unsafe_prefix(dst_name);
71#endif
72// ^^^ This may be a problem if some applets do need to extract absolute names.
73// (Probably will need to invent ARCHIVE_ALLOW_UNSAFE_NAME flag).
74// You might think that rpm needs it, but in my tests rpm's internal cpio
75// archive has names like "./usr/bin/FOO", not "/usr/bin/FOO".
68 76
69 if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { 77 if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
70 char *slash = strrchr(dst_name, '/'); 78 char *slash = strrchr(dst_name, '/');
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 3d202ad26..a949c4509 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -164,7 +164,7 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
164/* Used by e.g. rpm which gives us a fd without filename, 164/* Used by e.g. rpm which gives us a fd without filename,
165 * thus we can't guess the format from filename's extension. 165 * thus we can't guess the format from filename's extension.
166 */ 166 */
167static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed) 167static transformer_state_t *setup_transformer_on_fd(int fd, int die_if_not_compressed)
168{ 168{
169 transformer_state_t *xstate; 169 transformer_state_t *xstate;
170 170
@@ -211,7 +211,7 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_comp
211 } 211 }
212 212
213 /* No known magic seen */ 213 /* No known magic seen */
214 if (fail_if_not_compressed) 214 if (die_if_not_compressed)
215 bb_simple_error_msg_and_die("no gzip" 215 bb_simple_error_msg_and_die("no gzip"
216 IF_FEATURE_SEAMLESS_BZ2("/bzip2") 216 IF_FEATURE_SEAMLESS_BZ2("/bzip2")
217 IF_FEATURE_SEAMLESS_XZ("/xz") 217 IF_FEATURE_SEAMLESS_XZ("/xz")
@@ -247,13 +247,15 @@ static void fork_transformer_and_free(transformer_state_t *xstate)
247/* Used by e.g. rpm which gives us a fd without filename, 247/* Used by e.g. rpm which gives us a fd without filename,
248 * thus we can't guess the format from filename's extension. 248 * thus we can't guess the format from filename's extension.
249 */ 249 */
250int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed) 250int FAST_FUNC setup_unzip_on_fd(int fd, int die_if_not_compressed)
251{ 251{
252 transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed); 252 transformer_state_t *xstate = setup_transformer_on_fd(fd, die_if_not_compressed);
253 253
254 if (!xstate->xformer) { 254 if (!xstate->xformer) {
255 /* Not compressed */
256 int retval = xstate->signature_skipped; /* never zero */
255 free(xstate); 257 free(xstate);
256 return 1; 258 return retval;
257 } 259 }
258 260
259 fork_transformer_and_free(xstate); 261 fork_transformer_and_free(xstate);
@@ -271,7 +273,7 @@ void FAST_FUNC setup_lzma_on_fd(int fd)
271} 273}
272#endif 274#endif
273 275
274static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed) 276static transformer_state_t *open_transformer(const char *fname, int die_if_not_compressed)
275{ 277{
276 transformer_state_t *xstate; 278 transformer_state_t *xstate;
277 int fd; 279 int fd;
@@ -291,18 +293,18 @@ static transformer_state_t *open_transformer(const char *fname, int fail_if_not_
291 } 293 }
292 } 294 }
293 295
294 xstate = setup_transformer_on_fd(fd, fail_if_not_compressed); 296 xstate = setup_transformer_on_fd(fd, die_if_not_compressed);
295 297
296 return xstate; 298 return xstate;
297} 299}
298 300
299int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed) 301int FAST_FUNC open_zipped(const char *fname, int die_if_not_compressed)
300{ 302{
301 int fd; 303 int fd;
302 transformer_state_t *xstate; 304 transformer_state_t *xstate;
303 305
304 xstate = open_transformer(fname, fail_if_not_compressed); 306 xstate = open_transformer(fname, die_if_not_compressed);
305 if (!xstate) 307 if (!xstate) /* open error */
306 return -1; 308 return -1;
307 309
308 fd = xstate->src_fd; 310 fd = xstate->src_fd;
@@ -333,7 +335,7 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_
333 transformer_state_t *xstate; 335 transformer_state_t *xstate;
334 char *image; 336 char *image;
335 337
336 xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0); 338 xstate = open_transformer(fname, /*die_if_not_compressed:*/ 0);
337 if (!xstate) /* file open error */ 339 if (!xstate) /* file open error */
338 return NULL; 340 return NULL;
339 341
@@ -378,7 +380,7 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_
378 int fd; 380 int fd;
379 char *image; 381 char *image;
380 382
381 fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0); 383 fd = open_zipped(fname, /*die_if_not_compressed:*/ 0);
382 if (fd < 0) 384 if (fd < 0)
383 return NULL; 385 return NULL;
384 386
diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c
index 33e487bf9..667081195 100644
--- a/archival/libarchive/unsafe_prefix.c
+++ b/archival/libarchive/unsafe_prefix.c
@@ -14,7 +14,11 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str)
14 cp++; 14 cp++;
15 continue; 15 continue;
16 } 16 }
17 if (is_prefixed_with(cp, "/../"+1)) { 17 /* We are called lots of times.
18 * is_prefixed_with(cp, "../") is slower than open-coding it,
19 * with minimal code growth (~few bytes).
20 */
21 if (cp[0] == '.' && cp[1] == '.' && cp[2] == '/') {
18 cp += 3; 22 cp += 3;
19 continue; 23 continue;
20 } 24 }
diff --git a/archival/rpm.c b/archival/rpm.c
index d83c33137..c2f0550ff 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -330,7 +330,7 @@ static void extract_cpio(int fd, const char *source_rpm)
330 archive_handle->src_fd = fd; 330 archive_handle->src_fd = fd;
331 /*archive_handle->offset = 0; - init_handle() did it */ 331 /*archive_handle->offset = 0; - init_handle() did it */
332 332
333 setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1); 333 setup_unzip_on_fd(archive_handle->src_fd, /*die_if_not_compressed:*/ 1);
334 while (get_header_cpio(archive_handle) == EXIT_SUCCESS) 334 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
335 continue; 335 continue;
336} 336}
@@ -549,6 +549,7 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
549 // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ 549 // /* We need to know whether child (gzip/bzip/etc) exits abnormally */
550 // signal(SIGCHLD, check_errors_in_children); 550 // signal(SIGCHLD, check_errors_in_children);
551 551
552 str = NULL;
552 if (ENABLE_FEATURE_SEAMLESS_LZMA 553 if (ENABLE_FEATURE_SEAMLESS_LZMA
553 && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL 554 && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL
554 && strcmp(str, "lzma") == 0 555 && strcmp(str, "lzma") == 0
@@ -557,7 +558,11 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
557 // set up decompressor without detection 558 // set up decompressor without detection
558 setup_lzma_on_fd(rpm_fd); 559 setup_lzma_on_fd(rpm_fd);
559 } else { 560 } else {
560 setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1); 561 int signature_bytes = setup_unzip_on_fd(rpm_fd, /*die_if_not_compressed:*/ 0);
562 if (signature_bytes != 0) {
563 xlseek(rpm_fd, - signature_bytes, SEEK_CUR);
564 bb_error_msg("warning, unknown compression '%s'", str);
565 }
561 } 566 }
562 567
563 if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) 568 if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
diff --git a/archival/tar.c b/archival/tar.c
index 23ea02b5d..fd20d6ce7 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -1188,7 +1188,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1188 * on e.g. tarball with 1st file named "BZh5". 1188 * on e.g. tarball with 1st file named "BZh5".
1189 */ 1189 */
1190 ) { 1190 ) {
1191 tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0); 1191 tar_handle->src_fd = open_zipped(tar_filename, /*die_if_not_compressed:*/ 0);
1192 if (tar_handle->src_fd < 0) 1192 if (tar_handle->src_fd < 0)
1193 bb_perror_msg_and_die("can't open '%s'", tar_filename); 1193 bb_perror_msg_and_die("can't open '%s'", tar_filename);
1194 } else { 1194 } else {
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
index 6f6bdb14b..6e8f1d5e4 100644
--- a/configs/mingw32_defconfig
+++ b/configs/mingw32_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.38.0.git
4# Fri Jun 14 12:24:50 2024 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -62,6 +62,7 @@ CONFIG_TERMINAL_MODE=5
62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y 62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
63CONFIG_FEATURE_EXTRA_FILE_DATA=y 63CONFIG_FEATURE_EXTRA_FILE_DATA=y
64CONFIG_OVERRIDE_APPLETS="" 64CONFIG_OVERRIDE_APPLETS=""
65CONFIG_UNWIND_TABLES=y
65 66
66# 67#
67# Build Options 68# Build Options
@@ -120,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
120# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
121# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
122CONFIG_PASSWORD_MINLEN=6 123CONFIG_PASSWORD_MINLEN=6
124# CONFIG_FEATURE_USE_CNG_API is not set
123CONFIG_MD5_SMALL=1 125CONFIG_MD5_SMALL=1
124CONFIG_SHA1_SMALL=3 126CONFIG_SHA1_SMALL=3
125# CONFIG_SHA1_HWACCEL is not set 127# CONFIG_SHA1_HWACCEL is not set
@@ -225,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y
225CONFIG_FEATURE_UNZIP_LZMA=y 227CONFIG_FEATURE_UNZIP_LZMA=y
226CONFIG_FEATURE_UNZIP_XZ=y 228CONFIG_FEATURE_UNZIP_XZ=y
227CONFIG_FEATURE_LZMA_FAST=y 229CONFIG_FEATURE_LZMA_FAST=y
230# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set
228 231
229# 232#
230# Coreutils 233# Coreutils
@@ -272,7 +275,7 @@ CONFIG_DD=y
272CONFIG_FEATURE_DD_IBS_OBS=y 275CONFIG_FEATURE_DD_IBS_OBS=y
273CONFIG_FEATURE_DD_STATUS=y 276CONFIG_FEATURE_DD_STATUS=y
274CONFIG_DF=y 277CONFIG_DF=y
275# CONFIG_FEATURE_DF_FANCY is not set 278CONFIG_FEATURE_DF_FANCY=y
276# CONFIG_FEATURE_SKIP_ROOTFS is not set 279# CONFIG_FEATURE_SKIP_ROOTFS is not set
277CONFIG_DIRNAME=y 280CONFIG_DIRNAME=y
278CONFIG_DOS2UNIX=y 281CONFIG_DOS2UNIX=y
@@ -351,7 +354,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y
351CONFIG_STAT=y 354CONFIG_STAT=y
352CONFIG_FEATURE_STAT_FORMAT=y 355CONFIG_FEATURE_STAT_FORMAT=y
353CONFIG_FEATURE_STAT_FILESYSTEM=y 356CONFIG_FEATURE_STAT_FILESYSTEM=y
354# CONFIG_STTY is not set 357CONFIG_STTY=y
355CONFIG_SUM=y 358CONFIG_SUM=y
356CONFIG_SYNC=y 359CONFIG_SYNC=y
357# CONFIG_FEATURE_SYNC_FANCY is not set 360# CONFIG_FEATURE_SYNC_FANCY is not set
@@ -898,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
898# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
899# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
900# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904CONFIG_FEATURE_TLS_INTERNAL=y
905# CONFIG_FEATURE_TLS_SCHANNEL is not set
901# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
902# CONFIG_ARP is not set 908# CONFIG_ARP is not set
903# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
904# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
@@ -964,6 +970,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
964# CONFIG_IPNEIGH is not set 970# CONFIG_IPNEIGH is not set
965# CONFIG_FEATURE_IP_ADDRESS is not set 971# CONFIG_FEATURE_IP_ADDRESS is not set
966# CONFIG_FEATURE_IP_LINK is not set 972# CONFIG_FEATURE_IP_LINK is not set
973CONFIG_FEATURE_IP_LINK_CAN=y
967# CONFIG_FEATURE_IP_ROUTE is not set 974# CONFIG_FEATURE_IP_ROUTE is not set
968CONFIG_FEATURE_IP_ROUTE_DIR="" 975CONFIG_FEATURE_IP_ROUTE_DIR=""
969# CONFIG_FEATURE_IP_TUNNEL is not set 976# CONFIG_FEATURE_IP_TUNNEL is not set
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
index fbd2bc6e3..3a5d1248a 100644
--- a/configs/mingw64_defconfig
+++ b/configs/mingw64_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.38.0.git
4# Fri Jun 14 12:24:50 2024 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -62,6 +62,7 @@ CONFIG_TERMINAL_MODE=5
62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y 62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
63CONFIG_FEATURE_EXTRA_FILE_DATA=y 63CONFIG_FEATURE_EXTRA_FILE_DATA=y
64CONFIG_OVERRIDE_APPLETS="" 64CONFIG_OVERRIDE_APPLETS=""
65CONFIG_UNWIND_TABLES=y
65 66
66# 67#
67# Build Options 68# Build Options
@@ -120,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
120# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
121# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
122CONFIG_PASSWORD_MINLEN=6 123CONFIG_PASSWORD_MINLEN=6
124# CONFIG_FEATURE_USE_CNG_API is not set
123CONFIG_MD5_SMALL=1 125CONFIG_MD5_SMALL=1
124CONFIG_SHA1_SMALL=3 126CONFIG_SHA1_SMALL=3
125# CONFIG_SHA1_HWACCEL is not set 127# CONFIG_SHA1_HWACCEL is not set
@@ -225,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y
225CONFIG_FEATURE_UNZIP_LZMA=y 227CONFIG_FEATURE_UNZIP_LZMA=y
226CONFIG_FEATURE_UNZIP_XZ=y 228CONFIG_FEATURE_UNZIP_XZ=y
227CONFIG_FEATURE_LZMA_FAST=y 229CONFIG_FEATURE_LZMA_FAST=y
230# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set
228 231
229# 232#
230# Coreutils 233# Coreutils
@@ -272,7 +275,7 @@ CONFIG_DD=y
272CONFIG_FEATURE_DD_IBS_OBS=y 275CONFIG_FEATURE_DD_IBS_OBS=y
273CONFIG_FEATURE_DD_STATUS=y 276CONFIG_FEATURE_DD_STATUS=y
274CONFIG_DF=y 277CONFIG_DF=y
275# CONFIG_FEATURE_DF_FANCY is not set 278CONFIG_FEATURE_DF_FANCY=y
276# CONFIG_FEATURE_SKIP_ROOTFS is not set 279# CONFIG_FEATURE_SKIP_ROOTFS is not set
277CONFIG_DIRNAME=y 280CONFIG_DIRNAME=y
278CONFIG_DOS2UNIX=y 281CONFIG_DOS2UNIX=y
@@ -351,7 +354,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y
351CONFIG_STAT=y 354CONFIG_STAT=y
352CONFIG_FEATURE_STAT_FORMAT=y 355CONFIG_FEATURE_STAT_FORMAT=y
353CONFIG_FEATURE_STAT_FILESYSTEM=y 356CONFIG_FEATURE_STAT_FILESYSTEM=y
354# CONFIG_STTY is not set 357CONFIG_STTY=y
355CONFIG_SUM=y 358CONFIG_SUM=y
356CONFIG_SYNC=y 359CONFIG_SYNC=y
357# CONFIG_FEATURE_SYNC_FANCY is not set 360# CONFIG_FEATURE_SYNC_FANCY is not set
@@ -898,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
898# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
899# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
900# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904CONFIG_FEATURE_TLS_INTERNAL=y
905# CONFIG_FEATURE_TLS_SCHANNEL is not set
901# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
902# CONFIG_ARP is not set 908# CONFIG_ARP is not set
903# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
904# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
@@ -964,6 +970,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
964# CONFIG_IPNEIGH is not set 970# CONFIG_IPNEIGH is not set
965# CONFIG_FEATURE_IP_ADDRESS is not set 971# CONFIG_FEATURE_IP_ADDRESS is not set
966# CONFIG_FEATURE_IP_LINK is not set 972# CONFIG_FEATURE_IP_LINK is not set
973CONFIG_FEATURE_IP_LINK_CAN=y
967# CONFIG_FEATURE_IP_ROUTE is not set 974# CONFIG_FEATURE_IP_ROUTE is not set
968CONFIG_FEATURE_IP_ROUTE_DIR="" 975CONFIG_FEATURE_IP_ROUTE_DIR=""
969# CONFIG_FEATURE_IP_TUNNEL is not set 976# CONFIG_FEATURE_IP_TUNNEL is not set
diff --git a/configs/mingw64a_defconfig b/configs/mingw64a_defconfig
index e9a88bf62..d17737721 100644
--- a/configs/mingw64a_defconfig
+++ b/configs/mingw64a_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.38.0.git
4# Fri Jun 14 12:24:50 2024 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -62,6 +62,7 @@ CONFIG_TERMINAL_MODE=5
62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y 62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
63CONFIG_FEATURE_EXTRA_FILE_DATA=y 63CONFIG_FEATURE_EXTRA_FILE_DATA=y
64CONFIG_OVERRIDE_APPLETS="" 64CONFIG_OVERRIDE_APPLETS=""
65CONFIG_UNWIND_TABLES=y
65 66
66# 67#
67# Build Options 68# Build Options
@@ -120,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
120# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
121# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
122CONFIG_PASSWORD_MINLEN=6 123CONFIG_PASSWORD_MINLEN=6
124CONFIG_FEATURE_USE_CNG_API=y
123CONFIG_MD5_SMALL=1 125CONFIG_MD5_SMALL=1
124CONFIG_SHA1_SMALL=3 126CONFIG_SHA1_SMALL=3
125# CONFIG_SHA1_HWACCEL is not set 127# CONFIG_SHA1_HWACCEL is not set
@@ -225,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y
225CONFIG_FEATURE_UNZIP_LZMA=y 227CONFIG_FEATURE_UNZIP_LZMA=y
226CONFIG_FEATURE_UNZIP_XZ=y 228CONFIG_FEATURE_UNZIP_XZ=y
227CONFIG_FEATURE_LZMA_FAST=y 229CONFIG_FEATURE_LZMA_FAST=y
230# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set
228 231
229# 232#
230# Coreutils 233# Coreutils
@@ -272,7 +275,7 @@ CONFIG_DD=y
272CONFIG_FEATURE_DD_IBS_OBS=y 275CONFIG_FEATURE_DD_IBS_OBS=y
273CONFIG_FEATURE_DD_STATUS=y 276CONFIG_FEATURE_DD_STATUS=y
274CONFIG_DF=y 277CONFIG_DF=y
275# CONFIG_FEATURE_DF_FANCY is not set 278CONFIG_FEATURE_DF_FANCY=y
276# CONFIG_FEATURE_SKIP_ROOTFS is not set 279# CONFIG_FEATURE_SKIP_ROOTFS is not set
277CONFIG_DIRNAME=y 280CONFIG_DIRNAME=y
278CONFIG_DOS2UNIX=y 281CONFIG_DOS2UNIX=y
@@ -351,7 +354,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y
351CONFIG_STAT=y 354CONFIG_STAT=y
352CONFIG_FEATURE_STAT_FORMAT=y 355CONFIG_FEATURE_STAT_FORMAT=y
353CONFIG_FEATURE_STAT_FILESYSTEM=y 356CONFIG_FEATURE_STAT_FILESYSTEM=y
354# CONFIG_STTY is not set 357CONFIG_STTY=y
355CONFIG_SUM=y 358CONFIG_SUM=y
356CONFIG_SYNC=y 359CONFIG_SYNC=y
357# CONFIG_FEATURE_SYNC_FANCY is not set 360# CONFIG_FEATURE_SYNC_FANCY is not set
@@ -898,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
898# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
899# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
900# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904# CONFIG_FEATURE_TLS_INTERNAL is not set
905CONFIG_FEATURE_TLS_SCHANNEL=y
901# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
902# CONFIG_ARP is not set 908# CONFIG_ARP is not set
903# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
904# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
@@ -964,6 +970,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
964# CONFIG_IPNEIGH is not set 970# CONFIG_IPNEIGH is not set
965# CONFIG_FEATURE_IP_ADDRESS is not set 971# CONFIG_FEATURE_IP_ADDRESS is not set
966# CONFIG_FEATURE_IP_LINK is not set 972# CONFIG_FEATURE_IP_LINK is not set
973CONFIG_FEATURE_IP_LINK_CAN=y
967# CONFIG_FEATURE_IP_ROUTE is not set 974# CONFIG_FEATURE_IP_ROUTE is not set
968CONFIG_FEATURE_IP_ROUTE_DIR="" 975CONFIG_FEATURE_IP_ROUTE_DIR=""
969# CONFIG_FEATURE_IP_TUNNEL is not set 976# CONFIG_FEATURE_IP_TUNNEL is not set
diff --git a/configs/mingw64u_defconfig b/configs/mingw64u_defconfig
index 61753699d..adb1552f2 100644
--- a/configs/mingw64u_defconfig
+++ b/configs/mingw64u_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.38.0.git
4# Fri Jun 14 12:24:50 2024 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -62,6 +62,7 @@ CONFIG_TERMINAL_MODE=5
62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y 62CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
63CONFIG_FEATURE_EXTRA_FILE_DATA=y 63CONFIG_FEATURE_EXTRA_FILE_DATA=y
64CONFIG_OVERRIDE_APPLETS="" 64CONFIG_OVERRIDE_APPLETS=""
65CONFIG_UNWIND_TABLES=y
65 66
66# 67#
67# Build Options 68# Build Options
@@ -120,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
120# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
121# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
122CONFIG_PASSWORD_MINLEN=6 123CONFIG_PASSWORD_MINLEN=6
124CONFIG_FEATURE_USE_CNG_API=y
123CONFIG_MD5_SMALL=1 125CONFIG_MD5_SMALL=1
124CONFIG_SHA1_SMALL=3 126CONFIG_SHA1_SMALL=3
125# CONFIG_SHA1_HWACCEL is not set 127# CONFIG_SHA1_HWACCEL is not set
@@ -225,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y
225CONFIG_FEATURE_UNZIP_LZMA=y 227CONFIG_FEATURE_UNZIP_LZMA=y
226CONFIG_FEATURE_UNZIP_XZ=y 228CONFIG_FEATURE_UNZIP_XZ=y
227CONFIG_FEATURE_LZMA_FAST=y 229CONFIG_FEATURE_LZMA_FAST=y
230# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set
228 231
229# 232#
230# Coreutils 233# Coreutils
@@ -272,7 +275,7 @@ CONFIG_DD=y
272CONFIG_FEATURE_DD_IBS_OBS=y 275CONFIG_FEATURE_DD_IBS_OBS=y
273CONFIG_FEATURE_DD_STATUS=y 276CONFIG_FEATURE_DD_STATUS=y
274CONFIG_DF=y 277CONFIG_DF=y
275# CONFIG_FEATURE_DF_FANCY is not set 278CONFIG_FEATURE_DF_FANCY=y
276# CONFIG_FEATURE_SKIP_ROOTFS is not set 279# CONFIG_FEATURE_SKIP_ROOTFS is not set
277CONFIG_DIRNAME=y 280CONFIG_DIRNAME=y
278CONFIG_DOS2UNIX=y 281CONFIG_DOS2UNIX=y
@@ -351,7 +354,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y
351CONFIG_STAT=y 354CONFIG_STAT=y
352CONFIG_FEATURE_STAT_FORMAT=y 355CONFIG_FEATURE_STAT_FORMAT=y
353CONFIG_FEATURE_STAT_FILESYSTEM=y 356CONFIG_FEATURE_STAT_FILESYSTEM=y
354# CONFIG_STTY is not set 357CONFIG_STTY=y
355CONFIG_SUM=y 358CONFIG_SUM=y
356CONFIG_SYNC=y 359CONFIG_SYNC=y
357# CONFIG_FEATURE_SYNC_FANCY is not set 360# CONFIG_FEATURE_SYNC_FANCY is not set
@@ -898,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
898# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
899# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
900# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904# CONFIG_FEATURE_TLS_INTERNAL is not set
905CONFIG_FEATURE_TLS_SCHANNEL=y
901# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
902# CONFIG_ARP is not set 908# CONFIG_ARP is not set
903# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
904# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
@@ -964,6 +970,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
964# CONFIG_IPNEIGH is not set 970# CONFIG_IPNEIGH is not set
965# CONFIG_FEATURE_IP_ADDRESS is not set 971# CONFIG_FEATURE_IP_ADDRESS is not set
966# CONFIG_FEATURE_IP_LINK is not set 972# CONFIG_FEATURE_IP_LINK is not set
973CONFIG_FEATURE_IP_LINK_CAN=y
967# CONFIG_FEATURE_IP_ROUTE is not set 974# CONFIG_FEATURE_IP_ROUTE is not set
968CONFIG_FEATURE_IP_ROUTE_DIR="" 975CONFIG_FEATURE_IP_ROUTE_DIR=""
969# CONFIG_FEATURE_IP_TUNNEL is not set 976# CONFIG_FEATURE_IP_TUNNEL is not set
diff --git a/console-tools/showkey.c b/console-tools/showkey.c
index 87532a8ac..5767f2098 100644
--- a/console-tools/showkey.c
+++ b/console-tools/showkey.c
@@ -70,8 +70,8 @@ int showkey_main(int argc UNUSED_PARAM, char **argv)
70 70
71 INIT_G(); 71 INIT_G();
72 72
73 // FIXME: aks are all mutually exclusive 73 // a,k,s are all mutually exclusive
74 getopt32(argv, "aks"); 74 getopt32(argv, "^" "aks" "\0" "a--ks:k--as:s--ak");
75 75
76 // prepare for raw mode 76 // prepare for raw mode
77 xget1(&tio, &tio0); 77 xget1(&tio, &tio0);
diff --git a/coreutils/date.c b/coreutils/date.c
index 3a89b6caf..ef482af1b 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -289,7 +289,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
289 289
290 /* if setting time, set it */ 290 /* if setting time, set it */
291 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) { 291 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) {
292 bb_simple_perror_msg("can't set date"); 292 bb_simple_perror_msg_and_die("can't set date");
293 } 293 }
294 } 294 }
295 295
diff --git a/coreutils/df.c b/coreutils/df.c
index 03aa78148..01c41db38 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -64,7 +64,9 @@
64//usage: "[-Pk" 64//usage: "[-Pk"
65//usage: IF_FEATURE_HUMAN_READABLE("mh") 65//usage: IF_FEATURE_HUMAN_READABLE("mh")
66//usage: "T" 66//usage: "T"
67//usage: IF_FEATURE_DF_FANCY("ai] [-B SIZE") 67//usage: IF_FEATURE_DF_FANCY("a"
68//usage: IF_PLATFORM_POSIX("i")
69//usage: "] [-B SIZE")
68//usage: "] [-t TYPE] [FILESYSTEM]..." 70//usage: "] [-t TYPE] [FILESYSTEM]..."
69//usage:#define df_full_usage "\n\n" 71//usage:#define df_full_usage "\n\n"
70//usage: "Print filesystem usage statistics\n" 72//usage: "Print filesystem usage statistics\n"
@@ -78,7 +80,9 @@
78//usage: "\n -t TYPE Print only mounts of this type" 80//usage: "\n -t TYPE Print only mounts of this type"
79//usage: IF_FEATURE_DF_FANCY( 81//usage: IF_FEATURE_DF_FANCY(
80//usage: "\n -a Show all filesystems" 82//usage: "\n -a Show all filesystems"
83//usage: IF_PLATFORM_POSIX(
81//usage: "\n -i Inodes" 84//usage: "\n -i Inodes"
85//usage: )
82//usage: "\n -B SIZE Blocksize" 86//usage: "\n -B SIZE Blocksize"
83//usage: ) 87//usage: )
84//usage: 88//usage:
@@ -109,6 +113,12 @@ static unsigned long kscale(unsigned long b, unsigned long bs)
109} 113}
110#endif 114#endif
111 115
116#if ENABLE_PLATFORM_MINGW32
117# define ENABLE_FEATURE_DF_FANCY_POSIX 0
118#else
119# define ENABLE_FEATURE_DF_FANCY_POSIX ENABLE_FEATURE_DF_FANCY
120#endif
121
112int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 122int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
113int df_main(int argc UNUSED_PARAM, char **argv) 123int df_main(int argc UNUSED_PARAM, char **argv)
114{ 124{
@@ -124,11 +134,11 @@ int df_main(int argc UNUSED_PARAM, char **argv)
124 OPT_FSTYPE = (1 << 2), 134 OPT_FSTYPE = (1 << 2),
125 OPT_t = (1 << 3), 135 OPT_t = (1 << 3),
126 OPT_ALL = (1 << 4) * ENABLE_FEATURE_DF_FANCY, 136 OPT_ALL = (1 << 4) * ENABLE_FEATURE_DF_FANCY,
127 OPT_INODE = (1 << 5) * ENABLE_FEATURE_DF_FANCY, 137 OPT_INODE = (1 << 5) * ENABLE_FEATURE_DF_FANCY_POSIX,
128 OPT_BSIZE = (1 << 6) * ENABLE_FEATURE_DF_FANCY, 138 OPT_BSIZE = (1 << (5 + ENABLE_FEATURE_DF_FANCY_POSIX)) * ENABLE_FEATURE_DF_FANCY,
129 OPT_HUMAN = (1 << (4 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 139 OPT_HUMAN = (1 << (4 + 2*ENABLE_FEATURE_DF_FANCY + ENABLE_FEATURE_DF_FANCY_POSIX)) * ENABLE_FEATURE_HUMAN_READABLE,
130 OPT_HUMANDEC = (1 << (5 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 140 OPT_HUMANDEC = (1 << (5 + 2*ENABLE_FEATURE_DF_FANCY + ENABLE_FEATURE_DF_FANCY_POSIX)) * ENABLE_FEATURE_HUMAN_READABLE,
131 OPT_MEGA = (1 << (6 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 141 OPT_MEGA = (1 << (6 + 2*ENABLE_FEATURE_DF_FANCY + ENABLE_FEATURE_DF_FANCY_POSIX)) * ENABLE_FEATURE_HUMAN_READABLE,
132 }; 142 };
133 const char *disp_units_hdr = NULL; 143 const char *disp_units_hdr = NULL;
134 char *chp, *opt_t; 144 char *chp, *opt_t;
@@ -144,7 +154,11 @@ int df_main(int argc UNUSED_PARAM, char **argv)
144 154
145 opt = getopt32(argv, "^" 155 opt = getopt32(argv, "^"
146 "kPTt:" 156 "kPTt:"
157#if ENABLE_PLATFORM_POSIX
147 IF_FEATURE_DF_FANCY("aiB:") 158 IF_FEATURE_DF_FANCY("aiB:")
159#else
160 IF_FEATURE_DF_FANCY("aB:")
161#endif
148 IF_FEATURE_HUMAN_READABLE("hHm") 162 IF_FEATURE_HUMAN_READABLE("hHm")
149 "\0" 163 "\0"
150#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY 164#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY
@@ -155,6 +169,9 @@ int df_main(int argc UNUSED_PARAM, char **argv)
155 , &opt_t 169 , &opt_t
156 IF_FEATURE_DF_FANCY(, &chp) 170 IF_FEATURE_DF_FANCY(, &chp)
157 ); 171 );
172 /* -k overrides $POSIXLY_CORRECT: */
173 if (opt & OPT_KILO)
174 df_disp_hr = 1024;
158 if (opt & OPT_MEGA) 175 if (opt & OPT_MEGA)
159 df_disp_hr = 1024*1024; 176 df_disp_hr = 1024*1024;
160 177
@@ -185,8 +202,8 @@ int df_main(int argc UNUSED_PARAM, char **argv)
185 if (disp_units_hdr == NULL) { 202 if (disp_units_hdr == NULL) {
186#if ENABLE_FEATURE_HUMAN_READABLE 203#if ENABLE_FEATURE_HUMAN_READABLE
187 disp_units_hdr = xasprintf("%s-blocks", 204 disp_units_hdr = xasprintf("%s-blocks",
188 /* print df_disp_hr, show no fractionals, 205 /* print df_disp_hr; show no fractionals;
189 * use suffixes if OPT_POSIX is set in opt */ 206 * if -P, unit=1 (print it in full, no KMG suffixes) */
190 make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) 207 make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX))
191 ); 208 );
192#else 209#else
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 3f7e21871..c00559e4c 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -97,6 +97,10 @@ typedef long arith_t;
97 97
98/* TODO: use bb_strtol[l]? It's easier to check for errors... */ 98/* TODO: use bb_strtol[l]? It's easier to check for errors... */
99 99
100#if ENABLE_PLATFORM_MINGW32
101# define STRING BB_STRING
102#endif
103
100/* The kinds of value we can have. */ 104/* The kinds of value we can have. */
101enum { 105enum {
102 INTEGER, 106 INTEGER,
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 5d2e96a1e..0b617dd4d 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -98,6 +98,7 @@
98 98
99//usage:#define ls_trivial_usage 99//usage:#define ls_trivial_usage
100//usage: "[-1AaCxd" 100//usage: "[-1AaCxd"
101//usage: IF_PLATFORM_MINGW32("g")
101//usage: IF_FEATURE_LS_FOLLOWLINKS("LH") 102//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
102//usage: IF_FEATURE_LS_RECURSIVE("R") 103//usage: IF_FEATURE_LS_RECURSIVE("R")
103//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" 104//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
@@ -113,6 +114,7 @@
113//usage: "\n -A Like -a, but exclude . and .." 114//usage: "\n -A Like -a, but exclude . and .."
114//usage: IF_PLATFORM_MINGW32( 115//usage: IF_PLATFORM_MINGW32(
115//usage: "\n -aa,-AA Like -a,-A but omit hidden system files" 116//usage: "\n -aa,-AA Like -a,-A but omit hidden system files"
117//usage: "\n -C List by columns"
116//usage: ) 118//usage: )
117////usage: "\n -C List by columns" - don't show, this is a default anyway 119////usage: "\n -C List by columns" - don't show, this is a default anyway
118//usage: "\n -x List by lines" 120//usage: "\n -x List by lines"
@@ -129,6 +131,11 @@
129//usage: "\n -F Append indicator (one of */=@|) to names" 131//usage: "\n -F Append indicator (one of */=@|) to names"
130//usage: ) 132//usage: )
131//usage: "\n -l Long format" 133//usage: "\n -l Long format"
134//usage: IF_PLATFORM_MINGW32(
135//usage: "\n -g Long format without group column"
136//usage: )
137////usage: "\n -g Long format without group column"
138////TODO: support -G too ("suppress owner column", GNUism)
132//usage: "\n -i List inode numbers" 139//usage: "\n -i List inode numbers"
133//usage: "\n -n List numeric UIDs and GIDs instead of names" 140//usage: "\n -n List numeric UIDs and GIDs instead of names"
134//usage: "\n -s List allocated blocks" 141//usage: "\n -s List allocated blocks"
@@ -162,6 +169,8 @@
162//usage: IF_FEATURE_LS_WIDTH( 169//usage: IF_FEATURE_LS_WIDTH(
163//usage: "\n -w N Format N columns wide" 170//usage: "\n -w N Format N columns wide"
164//usage: ) 171//usage: )
172////usage: "\n -Q Double-quote names"
173////usage: "\n -q Replace unprintable chars with '?'"
165//usage: IF_FEATURE_LS_COLOR( 174//usage: IF_FEATURE_LS_COLOR(
166//usage: "\n --color[={always,never,auto}]" 175//usage: "\n --color[={always,never,auto}]"
167//usage: ) 176//usage: )
@@ -199,27 +208,47 @@ SPLIT_SUBDIR = 2,
199 208
200/* -Cadi1l Std options, busybox always supports */ 209/* -Cadi1l Std options, busybox always supports */
201/* -gnsxA Std options, busybox always supports */ 210/* -gnsxA Std options, busybox always supports */
202/* -Q GNU option, busybox always supports */ 211/* -Q GNU option, busybox always supports: */
203/* -k Std option, busybox always supports (by ignoring) */ 212/* -Q, --quote-name */
204/* It means "for -s, show sizes in kbytes" */ 213/* enclose entry names in double quotes */
205/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
206/* since otherwise -s shows kbytes anyway */
207/* -LHRctur Std options, busybox optionally supports */ 214/* -LHRctur Std options, busybox optionally supports */
208/* -Fp Std options, busybox optionally supports */ 215/* -Fp Std options, busybox optionally supports */
209/* -SXvhTw GNU options, busybox optionally supports */ 216/* -SXvhTw GNU options, busybox optionally supports */
210/* -T WIDTH Ignored (we don't use tabs on output) */ 217/* -T WIDTH Ignored (we don't use tabs on output) */
211/* -Z SELinux mandated option, busybox optionally supports */ 218/* -Z SELinux mandated option, busybox optionally supports */
219/* -q Std option, busybox always supports: */
220/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
221/* Force each instance of non-printable filename characters and */
222/* <tab> characters to be written as the <question-mark> ('?') */
223/* character. Implementations may provide this option by default */
224/* if the output is to a terminal device. */
225/* -k Std option, busybox always supports (by ignoring) */
226/* It means "for -s, show sizes in kbytes" */
227/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
228/* since otherwise -s shows kbytes anyway */
212#define ls_options \ 229#define ls_options \
213 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ 230 "Cadi1lgnsxA" /* 11 opts, total 11 */ \
214 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ 231 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
215 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ 232 IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
216 IF_SELINUX("Z") /* 1, 16 */ \ 233 IF_SELINUX("Z") /* 1, 15 */ \
217 "Q" /* 1, 17 */ \ 234 "Q" /* 1, 16 */ \
218 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ 235 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
219 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ 236 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
220 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ 237 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
221 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ 238 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
222 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 239 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
240 IF_LONG_OPTS("\xff") /* 1, 29 */ \
241 IF_LONG_OPTS("\xfe") /* 1, 30 */ \
242 IF_LONG_OPTS("\xfd") /* 1, 31 */ \
243 "qk" /* 2, 33 */
244
245#if ENABLE_LONG_OPTS
246static const char ls_longopts[] ALIGN1 =
247 "full-time\0" No_argument "\xff"
248 "group-directories-first\0" No_argument "\xfe"
249 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
250;
251#endif
223 252
224enum { 253enum {
225 OPT_C = (1 << 0), 254 OPT_C = (1 << 0),
@@ -233,29 +262,31 @@ enum {
233 OPT_s = (1 << 8), 262 OPT_s = (1 << 8),
234 OPT_x = (1 << 9), 263 OPT_x = (1 << 9),
235 OPT_A = (1 << 10), 264 OPT_A = (1 << 10),
236 //OPT_k = (1 << 11),
237 265
238 OPTBIT_F = 12, 266 OPTBIT_F = 11,
239 OPTBIT_p, /* 13 */ 267 OPTBIT_p, /* 12 */
240 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 268 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
241 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 269 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
242 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, 270 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
243 OPTBIT_c, /* 17 */ 271 OPTBIT_c, /* 16 */
244 OPTBIT_t, /* 18 */ 272 OPTBIT_t, /* 17 */
245 OPTBIT_u, /* 19 */ 273 OPTBIT_u, /* 18 */
246 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, 274 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
247 OPTBIT_X, /* 21 */ 275 OPTBIT_X, /* 20 */
248 OPTBIT_r, /* 22 */ 276 OPTBIT_r, /* 21 */
249 OPTBIT_v, /* 23 */ 277 OPTBIT_v, /* 22 */
250 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 278 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
251 OPTBIT_H, /* 25 */ 279 OPTBIT_H, /* 24 */
252 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 280 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
253 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 281 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
254 OPTBIT_w, /* 28 */ 282 OPTBIT_w, /* 27 */
255 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, 283 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
256 OPTBIT_dirs_first, 284 OPTBIT_dirs_first,
257 OPTBIT_color, /* 31 */ 285 OPTBIT_color, /* 30 */
258 /* with long opts, we use all 32 bits */ 286 OPTBIT_q = OPTBIT_color + 1, /* 31 */
287 OPTBIT_k = OPTBIT_q + 1, /* 32 */
288 /* with all options enabled, we use all 32 bits and even one extra bit! */
289 /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
259 290
260 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 291 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
261 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 292 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
@@ -277,6 +308,8 @@ enum {
277 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, 308 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
278 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, 309 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
279 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, 310 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
311 OPT_q = (1 << OPTBIT_q),
312 //-k is ignored: OPT_k = (1 << OPTBIT_k),
280}; 313};
281 314
282/* 315/*
@@ -333,6 +366,7 @@ struct globals {
333#endif 366#endif
334 smallint exit_code; 367 smallint exit_code;
335 smallint show_dirname; 368 smallint show_dirname;
369 smallint tty_out;
336#if ENABLE_FEATURE_LS_WIDTH 370#if ENABLE_FEATURE_LS_WIDTH
337 unsigned terminal_width; 371 unsigned terminal_width;
338# define G_terminal_width (G.terminal_width) 372# define G_terminal_width (G.terminal_width)
@@ -353,16 +387,21 @@ struct globals {
353 setup_common_bufsiz(); \ 387 setup_common_bufsiz(); \
354 /* we have to zero it out because of NOEXEC */ \ 388 /* we have to zero it out because of NOEXEC */ \
355 memset(&G, 0, sizeof(G)); \ 389 memset(&G, 0, sizeof(G)); \
356 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 390 IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
357 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 391 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
358} while (0) 392} while (0)
359 393
360#define ESC "\033" 394#define ESC "\033"
361 395
396static int G_isatty(void)
397{
398 if (!G.tty_out) /* not known yet? */
399 G.tty_out = isatty(STDOUT_FILENO) + 1;
400 return (G.tty_out == 2);
401}
362 402
363/*** Output code ***/ 403/*** Output code ***/
364 404
365
366/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 405/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
367 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 406 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
368 * 3/7:multiplexed char/block device) 407 * 3/7:multiplexed char/block device)
@@ -425,56 +464,93 @@ static char append_char(mode_t mode)
425} 464}
426#endif 465#endif
427 466
467/* Return the number of used columns.
468 * Note that only columnar output uses return value.
469 * -l and -1 modes don't care.
470 * coreutils 7.2 also supports:
471 * ls -b (--escape) = octal escapes (although it doesn't look like working)
472 * ls -N (--literal) = not escape at all
473 */
428static unsigned calc_name_len(const char *name) 474static unsigned calc_name_len(const char *name)
429{ 475{
430 unsigned len; 476 unsigned len;
431 uni_stat_t uni_stat; 477 uni_stat_t uni_stat;
432 478
433 // TODO: quote tab as \t, etc, if -Q 479 if (!(option_mask32 & (OPT_q|OPT_Q)))
434 name = printable_string2(&uni_stat, name); 480 return strlen(name);
435 481
436 if (!(option_mask32 & OPT_Q)) { 482 if (!(option_mask32 & OPT_Q)) {
483 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
484 printable_string2(&uni_stat, name);
437 return uni_stat.unicode_width; 485 return uni_stat.unicode_width;
438 } 486 }
439 487
440 len = 2 + uni_stat.unicode_width; 488 len = 2 + strlen(name);
441 while (*name) { 489 while (*name) {
490 unsigned char ch = (unsigned char)*name;
491 if (ch < ' ' || ch > 0x7e) {
492 ch -= 7;
493 if (ch <= 6) {
494 /* quote chars 7..13 as \a,b,t,n,v,f,r */
495 goto two;
496 }
497 /* other chars <32 or >126 as \ooo octal */
498 len += 3;
499 goto next;
500 }
442 if (*name == '"' || *name == '\\') { 501 if (*name == '"' || *name == '\\') {
502 two:
443 len++; 503 len++;
444 } 504 }
505 next:
445 name++; 506 name++;
446 } 507 }
447 return len; 508 return len;
448} 509}
449
450/* Return the number of used columns.
451 * Note that only columnar output uses return value.
452 * -l and -1 modes don't care.
453 * coreutils 7.2 also supports:
454 * ls -b (--escape) = octal escapes (although it doesn't look like working)
455 * ls -N (--literal) = not escape at all
456 */
457static unsigned print_name(const char *name) 510static unsigned print_name(const char *name)
458{ 511{
459 unsigned len; 512 unsigned len;
460 uni_stat_t uni_stat; 513 uni_stat_t uni_stat;
461 514
462 // TODO: quote tab as \t, etc, if -Q 515 if (!(option_mask32 & (OPT_q|OPT_Q))) {
463 name = printable_string2(&uni_stat, name); 516 fputs_stdout(name);
517 return strlen(name);
518 }
464 519
465 if (!(option_mask32 & OPT_Q)) { 520 if (!(option_mask32 & OPT_Q)) {
521 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
522 name = printable_string2(&uni_stat, name);
466 fputs_stdout(name); 523 fputs_stdout(name);
467 return uni_stat.unicode_width; 524 return uni_stat.unicode_width;
468 } 525 }
469 526
470 len = 2 + uni_stat.unicode_width; 527 len = 2 + strlen(name);
471 putchar('"'); 528 putchar('"');
472 while (*name) { 529 while (*name) {
473 if (*name == '"' || *name == '\\') { 530 unsigned char ch = (unsigned char)*name;
531 if (ch < ' ' || ch > 0x7e) {
532 putchar('\\');
533 ch -= 7;
534 if (ch <= 6) {
535 /* quote chars 7..13 as \a,b,t,n,v,f,r */
536 ch = c_escape_conv_str07[1 + 3 * ch];
537 goto two;
538 }
539 /* other chars <32 or >126 as \ooo octal */
540 ch = (unsigned char)*name;
541 putchar('0' + (ch>>6));
542 putchar('0' + ((ch>>3) & 7));
543 ch = '0' + (ch & 7);
544 len += 3;
545 goto put_ch;
546 }
547 if (ch == '"' || ch == '\\') {
474 putchar('\\'); 548 putchar('\\');
549 two:
475 len++; 550 len++;
476 } 551 }
477 putchar(*name); 552 put_ch:
553 putchar(ch);
478 name++; 554 name++;
479 } 555 }
480 putchar('"'); 556 putchar('"');
@@ -660,7 +736,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
660 unsigned i, ncols, nrows, row, nc; 736 unsigned i, ncols, nrows, row, nc;
661 unsigned column; 737 unsigned column;
662 unsigned nexttab; 738 unsigned nexttab;
663 unsigned column_width = 0; /* used only by coulmnal output */ 739 unsigned column_width = 0; /* used only by columnar output */
664 740
665 if (option_mask32 & (OPT_l|OPT_1)) { 741 if (option_mask32 & (OPT_l|OPT_1)) {
666 ncols = 1; 742 ncols = 1;
@@ -709,6 +785,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
709 } 785 }
710 nexttab = column + column_width; 786 nexttab = column + column_width;
711 column += display_single(dn[i]); 787 column += display_single(dn[i]);
788 } else {
789 /* if -w999999999, ncols can be very large */
790 //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
791 /* without "break", we loop millions of times here */
792 break;
712 } 793 }
713 } 794 }
714 putchar('\n'); 795 putchar('\n');
@@ -1155,25 +1236,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1155 /* need to initialize since --color has _an optional_ argument */ 1236 /* need to initialize since --color has _an optional_ argument */
1156 const char *color_opt = color_str; /* "always" */ 1237 const char *color_opt = color_str; /* "always" */
1157#endif 1238#endif
1158#if ENABLE_LONG_OPTS
1159 static const char ls_longopts[] ALIGN1 =
1160 "full-time\0" No_argument "\xff"
1161 "group-directories-first\0" No_argument "\xfe"
1162 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
1163 ;
1164#endif
1165 1239
1166 INIT_G(); 1240 INIT_G();
1167 1241
1168 init_unicode(); 1242 init_unicode();
1169 1243
1170#if ENABLE_FEATURE_LS_WIDTH
1171 /* obtain the terminal width */
1172 G_terminal_width = get_terminal_width(STDIN_FILENO);
1173 /* go one less... */
1174 G_terminal_width--;
1175#endif
1176
1177 /* process options */ 1244 /* process options */
1178 opt = getopt32long(argv, "^" 1245 opt = getopt32long(argv, "^"
1179 ls_options 1246 ls_options
@@ -1211,6 +1278,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1211 exit(0); 1278 exit(0);
1212#endif 1279#endif
1213 1280
1281 /* ftpd secret backdoor? */
1282 if (ENABLE_FTPD && applet_name[0] == 'f') {
1283 /* dirs first are much nicer */
1284 opt = option_mask32 |= OPT_dirs_first;
1285 /* don't show SEcontext */
1286 IF_SELINUX(opt = option_mask32 &= ~OPT_Z;)
1287 /* do not query stdout about size and tty-ness */
1288 IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;)
1289 G.tty_out = 1; /* not a tty */
1290 goto skip_if_ftpd;
1291 }
1292
1293#if ENABLE_FEATURE_LS_WIDTH
1294 if ((int)G_terminal_width < 0) {
1295 /* obtain the terminal width */
1296 G_terminal_width = get_terminal_width(STDIN_FILENO);
1297 /* go one less... */
1298 G_terminal_width--;
1299 }
1300 if (G_terminal_width == 0) /* -w0 */
1301 G_terminal_width = INT_MAX; /* "infinite" */
1302#endif
1303
1214#if ENABLE_SELINUX 1304#if ENABLE_SELINUX
1215 if (opt & OPT_Z) { 1305 if (opt & OPT_Z) {
1216 if (!is_selinux_enabled()) 1306 if (!is_selinux_enabled())
@@ -1229,7 +1319,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1229# endif 1319# endif
1230 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1320 /* LS_COLORS is unset, or (not empty && not "none") ? */
1231 if (!p || (p[0] && strcmp(p, "none") != 0)) { 1321 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1232 if (isatty(STDOUT_FILENO)) { 1322 if (G_isatty()) {
1233 /* check isatty() last because it's expensive (syscall) */ 1323 /* check isatty() last because it's expensive (syscall) */
1234 G_show_color = 1; 1324 G_show_color = 1;
1235 } 1325 }
@@ -1238,23 +1328,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1238 if (opt & OPT_color) { 1328 if (opt & OPT_color) {
1239 if (color_opt[0] == 'n') 1329 if (color_opt[0] == 'n')
1240 G_show_color = 0; 1330 G_show_color = 0;
1241 else switch (index_in_substrings(color_str, color_opt)) { 1331 else if (!G_show_color) {
1242 case 3: 1332 /* if() is not needed, but avoids extra isatty() if G_show_color is already set */
1243 case 4: 1333 /* Check --color=COLOR_OPT and maybe set show_color=1 */
1244 case 5: 1334 switch (index_in_substrings(color_str, color_opt)) {
1245 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { 1335 case 3: // auto
1246 case 0: 1336 case 4: // tty
1247 case 1: 1337 case 5: // if-tty
1248 case 2: 1338 if (!is_TERM_dumb() && G_isatty()) {
1249 G_show_color = 1; 1339 case 0: // always
1340 case 1: // yes
1341 case 2: // force
1342 G_show_color = 1;
1343 }
1250 } 1344 }
1251 } 1345 }
1252 } 1346 }
1253#endif 1347#endif
1348 skip_if_ftpd:
1254 1349
1255 /* sort out which command line options take precedence */ 1350 /* sort out which command line options take precedence */
1256 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) 1351 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1257 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ 1352 opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
1258 if (!(opt & OPT_l)) { /* not -l? */ 1353 if (!(opt & OPT_l)) { /* not -l? */
1259 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1354 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1260 /* when to sort by time? -t[cu] sorts by time even with -l */ 1355 /* when to sort by time? -t[cu] sorts by time even with -l */
@@ -1262,19 +1357,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1262 /* without -l, bare -c or -u enable sort too */ 1357 /* without -l, bare -c or -u enable sort too */
1263 /* (with -l, bare -c or -u just select which time to show) */ 1358 /* (with -l, bare -c or -u just select which time to show) */
1264 if (opt & (OPT_c|OPT_u)) { 1359 if (opt & (OPT_c|OPT_u)) {
1265 option_mask32 |= OPT_t; 1360 opt = option_mask32 |= OPT_t;
1266 } 1361 }
1267 } 1362 }
1268 } 1363 }
1269 1364
1270 /* choose a display format if one was not already specified by an option */ 1365 /* choose a display format if one was not already specified by an option */
1271 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) 1366 if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
1272 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); 1367 opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
1273 1368
1274 if (ENABLE_FTPD && applet_name[0] == 'f') { 1369 if (!(opt & OPT_q) && G_isatty())
1275 /* ftpd secret backdoor. dirs first are much nicer */ 1370 opt = option_mask32 |= OPT_q;
1276 option_mask32 |= OPT_dirs_first;
1277 }
1278 1371
1279#if ENABLE_FEATURE_EXTRA_FILE_DATA 1372#if ENABLE_FEATURE_EXTRA_FILE_DATA
1280 /* Enable accurate link counts for directories */ 1373 /* Enable accurate link counts for directories */
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c
index 978d328f1..4506aeb56 100644
--- a/coreutils/md5_sha1_sum.c
+++ b/coreutils/md5_sha1_sum.c
@@ -23,6 +23,12 @@
23//config: help 23//config: help
24//config: Compute and check SHA256 message digest 24//config: Compute and check SHA256 message digest
25//config: 25//config:
26//config:config SHA384SUM
27//config: bool "sha384sum (7.3 kb)"
28//config: default y
29//config: help
30//config: Compute and check SHA384 message digest
31//config:
26//config:config SHA512SUM 32//config:config SHA512SUM
27//config: bool "sha512sum (7.3 kb)" 33//config: bool "sha512sum (7.3 kb)"
28//config: default y 34//config: default y
@@ -35,13 +41,13 @@
35//config: help 41//config: help
36//config: Compute and check SHA3 message digest 42//config: Compute and check SHA3 message digest
37//config: 43//config:
38//config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" 44//config:comment "Common options for md5sum, sha1sum, sha256sum, ..., sha3sum"
39//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM 45//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM
40//config: 46//config:
41//config:config FEATURE_MD5_SHA1_SUM_CHECK 47//config:config FEATURE_MD5_SHA1_SUM_CHECK
42//config: bool "Enable -c, -s and -w options" 48//config: bool "Enable -c, -s and -w options"
43//config: default y 49//config: default y
44//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM 50//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM
45//config: help 51//config: help
46//config: Enabling the -c options allows files to be checked 52//config: Enabling the -c options allows files to be checked
47//config: against pre-calculated hash values. 53//config: against pre-calculated hash values.
@@ -51,11 +57,13 @@
51//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) 57//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
52//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) 58//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
53//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) 59//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
60//applet:IF_SHA384SUM(APPLET_NOEXEC(sha384sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha384sum))
54//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) 61//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
55 62
56//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o 63//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
57//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o 64//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
58//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o 65//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
66//kbuild:lib-$(CONFIG_SHA384SUM) += md5_sha1_sum.o
59//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o 67//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
60//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o 68//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
61 69
@@ -99,6 +107,16 @@
99//usage: "\n -w Warn about improperly formatted checksum lines" 107//usage: "\n -w Warn about improperly formatted checksum lines"
100//usage: ) 108//usage: )
101//usage: 109//usage:
110//usage:#define sha384sum_trivial_usage
111//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
112//usage:#define sha384sum_full_usage "\n\n"
113//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA384 checksums"
114//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
115//usage: "\n -c Check sums against list in FILEs"
116//usage: "\n -s Don't output anything, status code shows success"
117//usage: "\n -w Warn about improperly formatted checksum lines"
118//usage: )
119//usage:
102//usage:#define sha512sum_trivial_usage 120//usage:#define sha512sum_trivial_usage
103//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." 121//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
104//usage:#define sha512sum_full_usage "\n\n" 122//usage:#define sha512sum_full_usage "\n\n"
@@ -130,11 +148,12 @@
130 148
131enum { 149enum {
132 /* 4th letter of applet_name is... */ 150 /* 4th letter of applet_name is... */
133 HASH_MD5 = 's', /* "md5>s<um" */ 151 HASH_MD5 = 's', /* "md5>s<um" */
134 HASH_SHA1 = '1', 152 HASH_SHA1 = '1',
135 HASH_SHA256 = '2', 153 HASH_SHA256 = '2',
136 HASH_SHA3 = '3', 154 HASH_SHA3 = '3',
137 HASH_SHA512 = '5', 155 HASH_SHA512 = '5',
156 /* unfortunately, sha384sum has the same '3' as sha3 */
138}; 157};
139 158
140#define FLAG_SILENT 1 159#define FLAG_SILENT 1
@@ -158,10 +177,11 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
158#endif 177#endif
159static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width) 178static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width)
160{ 179{
161 int src_fd, hash_len, count; 180 int src_fd, count;
162 union _ctx_ { 181 union _ctx_ {
163 sha3_ctx_t sha3; 182 sha3_ctx_t sha3;
164 sha512_ctx_t sha512; 183 sha512_ctx_t sha512;
184 sha384_ctx_t sha384;
165 sha256_ctx_t sha256; 185 sha256_ctx_t sha256;
166 sha1_ctx_t sha1; 186 sha1_ctx_t sha1;
167 md5_ctx_t md5; 187 md5_ctx_t md5;
@@ -183,25 +203,31 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
183 md5_begin(&context.md5); 203 md5_begin(&context.md5);
184 update = (void*)md5_hash; 204 update = (void*)md5_hash;
185 final = (void*)md5_end; 205 final = (void*)md5_end;
186 hash_len = 16;
187 } 206 }
188 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) { 207 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
189 sha1_begin(&context.sha1); 208 sha1_begin(&context.sha1);
190 update = (void*)sha1_hash; 209 update = (void*)sha1_hash;
191 final = (void*)sha1_end; 210 final = (void*)sha1_end;
192 hash_len = 20;
193 } 211 }
194 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) { 212 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
195 sha256_begin(&context.sha256); 213 sha256_begin(&context.sha256);
196 update = (void*)sha256_hash; 214 update = (void*)sha256_hash;
197 final = (void*)sha256_end; 215 final = (void*)sha256_end;
198 hash_len = 32; 216 }
217 else if (ENABLE_SHA384SUM
218 && (ENABLE_SHA3SUM
219 ? (applet_name[4] == '8') /* check for "sha384", but do not match "sha3" */
220 : (hash_algo == '3') /* applet_name = "sha3sum" is not possible */
221 )
222 ) {
223 sha384_begin(&context.sha384);
224 update = (void*)sha384_hash;
225 final = (void*)sha384_end;
199 } 226 }
200 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) { 227 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
201 sha512_begin(&context.sha512); 228 sha512_begin(&context.sha512);
202 update = (void*)sha512_hash; 229 update = (void*)sha512_hash;
203 final = (void*)sha512_end; 230 final = (void*)sha512_end;
204 hash_len = 64;
205 } 231 }
206#if ENABLE_SHA3SUM 232#if ENABLE_SHA3SUM
207 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) { 233 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
@@ -219,9 +245,7 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
219 ) { 245 ) {
220 bb_error_msg_and_die("bad -a%u", sha3_width); 246 bb_error_msg_and_die("bad -a%u", sha3_width);
221 } 247 }
222 sha3_width /= 4; 248 context.sha3.input_block_bytes = 1600/8 - sha3_width/4;
223 context.sha3.input_block_bytes = 1600/8 - sha3_width;
224 hash_len = sha3_width/2;
225 } 249 }
226#endif 250#endif
227 else { 251 else {
@@ -236,7 +260,7 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
236 if (count < 0) 260 if (count < 0)
237 bb_perror_msg("can't read '%s'", filename); 261 bb_perror_msg("can't read '%s'", filename);
238 else /* count == 0 */ { 262 else /* count == 0 */ {
239 final(&context, in_buf); 263 unsigned hash_len = final(&context, in_buf);
240 hash_value = hash_bin_to_hex(in_buf, hash_len); 264 hash_value = hash_bin_to_hex(in_buf, hash_len);
241 } 265 }
242 } 266 }
@@ -262,14 +286,14 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
262 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ 286 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
263 /* -s and -w require -c */ 287 /* -s and -w require -c */
264#if ENABLE_SHA3SUM 288#if ENABLE_SHA3SUM
265 if (applet_name[3] == HASH_SHA3) 289 if (applet_name[3] == HASH_SHA3 && (!ENABLE_SHA384SUM || applet_name[4] != '8'))
266 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width); 290 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width);
267 else 291 else
268#endif 292#endif
269 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c"); 293 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c");
270 } else { 294 } else {
271#if ENABLE_SHA3SUM 295#if ENABLE_SHA3SUM
272 if (applet_name[3] == HASH_SHA3) 296 if (applet_name[3] == HASH_SHA3 && (!ENABLE_SHA384SUM || applet_name[4] != '8'))
273 getopt32(argv, "a:+", &sha3_width); 297 getopt32(argv, "a:+", &sha3_width);
274 else 298 else
275#endif 299#endif
diff --git a/coreutils/stty.c b/coreutils/stty.c
index c88ef07f4..92d5838c0 100644
--- a/coreutils/stty.c
+++ b/coreutils/stty.c
@@ -20,14 +20,30 @@
20//kbuild:lib-$(CONFIG_STTY) += stty.o 20//kbuild:lib-$(CONFIG_STTY) += stty.o
21 21
22//usage:#define stty_trivial_usage 22//usage:#define stty_trivial_usage
23//usage: IF_NOT_PLATFORM_MINGW32(
23//usage: "[-a|g] [-F DEVICE] [SETTING]..." 24//usage: "[-a|g] [-F DEVICE] [SETTING]..."
25//usage: )
26//usage: IF_PLATFORM_MINGW32(
27//usage: "[-a] [SETTING]..."
28//usage: )
24//usage:#define stty_full_usage "\n\n" 29//usage:#define stty_full_usage "\n\n"
30//usage: IF_NOT_PLATFORM_MINGW32(
25//usage: "Without arguments, prints baud rate, line discipline,\n" 31//usage: "Without arguments, prints baud rate, line discipline,\n"
26//usage: "and deviations from stty sane\n" 32//usage: "and deviations from stty sane\n"
27//usage: "\n -F DEVICE Open device instead of stdin" 33//usage: "\n -F DEVICE Open device instead of stdin"
34//usage: )
35//usage: IF_PLATFORM_MINGW32(
36//usage: "Without arguments, prints deviations from stty sane\n"
37//usage: )
28//usage: "\n -a Print all current settings in human-readable form" 38//usage: "\n -a Print all current settings in human-readable form"
39//usage: IF_NOT_PLATFORM_MINGW32(
29//usage: "\n -g Print in stty-readable form" 40//usage: "\n -g Print in stty-readable form"
30//usage: "\n [SETTING] See manpage" 41//usage: "\n [SETTING] See manpage"
42//usage: )
43//usage: IF_PLATFORM_MINGW32(
44//usage: "\n [SETTING] [-]echo [-]cooked [-]raw sane"
45//usage: "\n cols N rows N size"
46//usage: )
31 47
32/* If no args are given, write to stdout the baud rate and settings that 48/* If no args are given, write to stdout the baud rate and settings that
33 * have been changed from their defaults. Mode reading and changes 49 * have been changed from their defaults. Mode reading and changes
@@ -294,6 +310,7 @@ struct mode_info {
294 const tcflag_t bits; /* Bits to set for this mode */ 310 const tcflag_t bits; /* Bits to set for this mode */
295}; 311};
296 312
313#if !ENABLE_PLATFORM_MINGW32
297enum { 314enum {
298 /* Must match mode_name[] and mode_info[] order! */ 315 /* Must match mode_name[] and mode_info[] order! */
299 IDX_evenp = 0, 316 IDX_evenp = 0,
@@ -320,19 +337,30 @@ enum {
320 IDX_LCASE, 337 IDX_LCASE,
321#endif 338#endif
322}; 339};
340#else
341enum {
342 /* Must match mode_name[] and mode_info[] order! */
343 IDX_sane = 0,
344 IDX_cooked,
345 IDX_raw,
346};
347#endif
323 348
324#define MI_ENTRY(N,T,F,B,M) N "\0" 349#define MI_ENTRY(N,T,F,B,M) N "\0"
325 350
326/* Mode names given on command line */ 351/* Mode names given on command line */
327static const char mode_name[] ALIGN1 = 352static const char mode_name[] ALIGN1 =
353#if !ENABLE_PLATFORM_MINGW32
328 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) 354 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
329 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) 355 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
330 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) 356 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
331 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) 357 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
332 MI_ENTRY("ek", combination, OMIT, 0, 0 ) 358 MI_ENTRY("ek", combination, OMIT, 0, 0 )
359#endif
333 MI_ENTRY("sane", combination, OMIT, 0, 0 ) 360 MI_ENTRY("sane", combination, OMIT, 0, 0 )
334 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) 361 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
335 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) 362 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
363#if !ENABLE_PLATFORM_MINGW32
336 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) 364 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
337 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) 365 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
338 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) 366 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
@@ -454,7 +482,9 @@ static const char mode_name[] ALIGN1 =
454#if IEXTEN 482#if IEXTEN
455 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) 483 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
456#endif 484#endif
485#endif /* !ENABLE_PLATFORM_MINGW32 */
457 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) 486 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
487#if !ENABLE_PLATFORM_MINGW32
458 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) 488 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
459 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) 489 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
460 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) 490 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
@@ -482,6 +512,7 @@ static const char mode_name[] ALIGN1 =
482#ifdef EXTPROC 512#ifdef EXTPROC
483 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) 513 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 )
484#endif 514#endif
515#endif /* !ENABLE_PLATFORM_MINGW32 */
485 ; 516 ;
486 517
487#undef MI_ENTRY 518#undef MI_ENTRY
@@ -489,14 +520,17 @@ static const char mode_name[] ALIGN1 =
489 520
490static const struct mode_info mode_info[] ALIGN4 = { 521static const struct mode_info mode_info[] ALIGN4 = {
491 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ 522 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
523#if !ENABLE_PLATFORM_MINGW32
492 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) 524 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
493 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) 525 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
494 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) 526 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
495 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) 527 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
496 MI_ENTRY("ek", combination, OMIT, 0, 0 ) 528 MI_ENTRY("ek", combination, OMIT, 0, 0 )
529#endif
497 MI_ENTRY("sane", combination, OMIT, 0, 0 ) 530 MI_ENTRY("sane", combination, OMIT, 0, 0 )
498 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) 531 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
499 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) 532 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
533#if !ENABLE_PLATFORM_MINGW32
500 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) 534 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
501 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) 535 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
502 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) 536 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
@@ -618,7 +652,9 @@ static const struct mode_info mode_info[] ALIGN4 = {
618#if IEXTEN 652#if IEXTEN
619 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) 653 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
620#endif 654#endif
655#endif /* !ENABLE_PLATFORM_MINGW32 */
621 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) 656 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
657#if !ENABLE_PLATFORM_MINGW32
622 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) 658 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
623 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) 659 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
624 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) 660 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
@@ -646,6 +682,7 @@ static const struct mode_info mode_info[] ALIGN4 = {
646#ifdef EXTPROC 682#ifdef EXTPROC
647 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) 683 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 )
648#endif 684#endif
685#endif /* !ENABLE_PLATFORM_MINGW32 */
649}; 686};
650 687
651enum { 688enum {
@@ -653,6 +690,7 @@ enum {
653}; 690};
654 691
655 692
693#if !ENABLE_PLATFORM_MINGW32
656/* Control characters */ 694/* Control characters */
657struct control_info { 695struct control_info {
658 const uint8_t saneval; /* Value to set for 'stty sane' */ 696 const uint8_t saneval; /* Value to set for 'stty sane' */
@@ -786,6 +824,7 @@ static const struct control_info control_info[] ALIGN2 = {
786enum { 824enum {
787 NUM_control_info = ARRAY_SIZE(control_info) 825 NUM_control_info = ARRAY_SIZE(control_info)
788}; 826};
827#endif
789 828
790 829
791struct globals { 830struct globals {
@@ -803,6 +842,7 @@ struct globals {
803 G.current_col = 0; /* we are noexec, must clear */ \ 842 G.current_col = 0; /* we are noexec, must clear */ \
804} while (0) 843} while (0)
805 844
845#if !ENABLE_PLATFORM_MINGW32
806static void set_speed_or_die(enum speed_setting type, const char *arg, 846static void set_speed_or_die(enum speed_setting type, const char *arg,
807 struct termios *mode) 847 struct termios *mode)
808{ 848{
@@ -817,6 +857,7 @@ static void set_speed_or_die(enum speed_setting type, const char *arg,
817 cfsetospeed(mode, baud); 857 cfsetospeed(mode, baud);
818 } 858 }
819} 859}
860#endif
820 861
821static NORETURN void perror_on_device_and_die(const char *fmt) 862static NORETURN void perror_on_device_and_die(const char *fmt)
822{ 863{
@@ -918,6 +959,7 @@ static const struct mode_info *find_mode(const char *name)
918 return i >= 0 ? &mode_info[i] : NULL; 959 return i >= 0 ? &mode_info[i] : NULL;
919} 960}
920 961
962#if !ENABLE_PLATFORM_MINGW32
921static const struct control_info *find_control(const char *name) 963static const struct control_info *find_control(const char *name)
922{ 964{
923 int i = index_in_strings(control_name, name); 965 int i = index_in_strings(control_name, name);
@@ -954,7 +996,32 @@ static int find_param(const char *name)
954 i |= 0x80; 996 i |= 0x80;
955 return i; 997 return i;
956} 998}
999#else
1000enum {
1001 param_need_arg = 0x80,
1002 param_rows = 1 | 0x80,
1003 param_cols = 2 | 0x80,
1004 param_columns = 3 | 0x80,
1005 param_size = 4,
1006};
957 1007
1008static int find_param(const char *name)
1009{
1010 static const char params[] ALIGN1 =
1011 "rows\0" /* 1 */
1012 "cols\0" /* 2 */
1013 "columns\0" /* 3 */
1014 "size\0"; /* 4 */
1015 int i = index_in_strings(params, name) + 1;
1016 if (i == 0)
1017 return 0;
1018 if (i != 4)
1019 i |= 0x80;
1020 return i;
1021}
1022#endif
1023
1024#if !ENABLE_PLATFORM_MINGW32
958static int recover_mode(const char *arg, struct termios *mode) 1025static int recover_mode(const char *arg, struct termios *mode)
959{ 1026{
960 int i, n; 1027 int i, n;
@@ -1013,6 +1080,9 @@ static void display_speed(const struct termios *mode, int fancy)
1013 if (fancy) fmt_str += 9; 1080 if (fancy) fmt_str += 9;
1014 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); 1081 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1015} 1082}
1083#else
1084# define display_speed(m, f) ((void)0)
1085#endif
1016 1086
1017static void do_display(const struct termios *mode, int all) 1087static void do_display(const struct termios *mode, int all)
1018{ 1088{
@@ -1030,6 +1100,7 @@ static void do_display(const struct termios *mode, int all)
1030 newline(); 1100 newline();
1031#endif 1101#endif
1032 1102
1103#if !ENABLE_PLATFORM_MINGW32
1033 for (i = 0; i != CIDX_min; ++i) { 1104 for (i = 0; i != CIDX_min; ++i) {
1034 char ch; 1105 char ch;
1035 char buf10[10]; 1106 char buf10[10];
@@ -1059,6 +1130,7 @@ static void do_display(const struct termios *mode, int all)
1059#endif 1130#endif
1060 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); 1131 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1061 newline(); 1132 newline();
1133#endif
1062 1134
1063 for (i = 0; i < NUM_mode_info; ++i) { 1135 for (i = 0; i < NUM_mode_info; ++i) {
1064 if (mode_info[i].flags & OMIT) 1136 if (mode_info[i].flags & OMIT)
@@ -1086,6 +1158,7 @@ static void do_display(const struct termios *mode, int all)
1086 1158
1087static void sane_mode(struct termios *mode) 1159static void sane_mode(struct termios *mode)
1088{ 1160{
1161#if !ENABLE_PLATFORM_MINGW32
1089 int i; 1162 int i;
1090 1163
1091 for (i = 0; i < NUM_control_info; ++i) { 1164 for (i = 0; i < NUM_control_info; ++i) {
@@ -1110,6 +1183,11 @@ static void sane_mode(struct termios *mode)
1110 *bitsp = val & ~mode_info[i].bits; 1183 *bitsp = val & ~mode_info[i].bits;
1111 } 1184 }
1112 } 1185 }
1186#else
1187 mode->c_lflag |= ECHO;
1188 mode->w_mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT |
1189 ENABLE_PROCESSED_INPUT;
1190#endif
1113} 1191}
1114 1192
1115static void set_mode(const struct mode_info *info, int reversed, 1193static void set_mode(const struct mode_info *info, int reversed,
@@ -1129,6 +1207,7 @@ static void set_mode(const struct mode_info *info, int reversed,
1129 } 1207 }
1130 1208
1131 /* !bitsp - it's a "combination" mode */ 1209 /* !bitsp - it's a "combination" mode */
1210#if !ENABLE_PLATFORM_MINGW32
1132 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { 1211 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1133 if (reversed) 1212 if (reversed)
1134 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; 1213 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
@@ -1150,9 +1229,14 @@ static void set_mode(const struct mode_info *info, int reversed,
1150 } else if (info == &mode_info[IDX_ek]) { 1229 } else if (info == &mode_info[IDX_ek]) {
1151 mode->c_cc[VERASE] = CERASE; 1230 mode->c_cc[VERASE] = CERASE;
1152 mode->c_cc[VKILL] = CKILL; 1231 mode->c_cc[VKILL] = CKILL;
1153 } else if (info == &mode_info[IDX_sane]) { 1232 }
1233 else
1234#endif /* !ENABLE_PLATFORM_MINGW32 */
1235 if (info == &mode_info[IDX_sane]) {
1154 sane_mode(mode); 1236 sane_mode(mode);
1155 } else if (info == &mode_info[IDX_cbreak]) { 1237 }
1238#if !ENABLE_PLATFORM_MINGW32
1239 else if (info == &mode_info[IDX_cbreak]) {
1156 if (reversed) 1240 if (reversed)
1157 mode->c_lflag |= ICANON; 1241 mode->c_lflag |= ICANON;
1158 else 1242 else
@@ -1175,11 +1259,14 @@ static void set_mode(const struct mode_info *info, int reversed,
1175 mode->c_iflag &= ~ISTRIP; 1259 mode->c_iflag &= ~ISTRIP;
1176 mode->c_oflag &= ~OPOST; 1260 mode->c_oflag &= ~OPOST;
1177 } 1261 }
1178 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { 1262 }
1263#endif /* !ENABLE_PLATFORM_MINGW32 */
1264 else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1179 if ((info == &mode_info[IDX_raw] && reversed) 1265 if ((info == &mode_info[IDX_raw] && reversed)
1180 || (info == &mode_info[IDX_cooked] && !reversed) 1266 || (info == &mode_info[IDX_cooked] && !reversed)
1181 ) { 1267 ) {
1182 /* Cooked mode */ 1268 /* Cooked mode */
1269#if !ENABLE_PLATFORM_MINGW32
1183 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; 1270 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1184 mode->c_oflag |= OPOST; 1271 mode->c_oflag |= OPOST;
1185 mode->c_lflag |= ISIG | ICANON; 1272 mode->c_lflag |= ISIG | ICANON;
@@ -1189,15 +1276,23 @@ static void set_mode(const struct mode_info *info, int reversed,
1189#if VTIME == VEOL 1276#if VTIME == VEOL
1190 mode->c_cc[VEOL] = CEOL; 1277 mode->c_cc[VEOL] = CEOL;
1191#endif 1278#endif
1279#else
1280 mode->w_mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
1281#endif
1192 } else { 1282 } else {
1193 /* Raw mode */ 1283 /* Raw mode */
1284#if !ENABLE_PLATFORM_MINGW32
1194 mode->c_iflag = 0; 1285 mode->c_iflag = 0;
1195 mode->c_oflag &= ~OPOST; 1286 mode->c_oflag &= ~OPOST;
1196 mode->c_lflag &= ~(ISIG | ICANON | XCASE); 1287 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1197 mode->c_cc[VMIN] = 1; 1288 mode->c_cc[VMIN] = 1;
1198 mode->c_cc[VTIME] = 0; 1289 mode->c_cc[VTIME] = 0;
1290#else
1291 mode->w_mode &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
1292#endif
1199 } 1293 }
1200 } 1294 }
1295#if !ENABLE_PLATFORM_MINGW32
1201#if IXANY 1296#if IXANY
1202 else if (info == &mode_info[IDX_decctlq]) { 1297 else if (info == &mode_info[IDX_decctlq]) {
1203 if (reversed) 1298 if (reversed)
@@ -1244,8 +1339,10 @@ static void set_mode(const struct mode_info *info, int reversed,
1244 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; 1339 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1245 if (IXANY) mode->c_iflag &= ~IXANY; 1340 if (IXANY) mode->c_iflag &= ~IXANY;
1246 } 1341 }
1342#endif /*!ENABLE_PLATFORM_MINGW32 */
1247} 1343}
1248 1344
1345#if !ENABLE_PLATFORM_MINGW32
1249static void set_control_char_or_die(const struct control_info *info, 1346static void set_control_char_or_die(const struct control_info *info,
1250 const char *arg, struct termios *mode) 1347 const char *arg, struct termios *mode)
1251{ 1348{
@@ -1265,6 +1362,7 @@ static void set_control_char_or_die(const struct control_info *info,
1265 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); 1362 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1266 mode->c_cc[info->offset] = value; 1363 mode->c_cc[info->offset] = value;
1267} 1364}
1365#endif
1268 1366
1269#define STTY_require_set_attr (1 << 0) 1367#define STTY_require_set_attr (1 << 0)
1270#define STTY_speed_was_set (1 << 1) 1368#define STTY_speed_was_set (1 << 1)
@@ -1277,7 +1375,9 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1277{ 1375{
1278 struct termios mode; 1376 struct termios mode;
1279 void (*output_func)(const struct termios *, int); 1377 void (*output_func)(const struct termios *, int);
1378#if !ENABLE_PLATFORM_MINGW32
1280 const char *file_name = NULL; 1379 const char *file_name = NULL;
1380#endif
1281 int display_all = 0; 1381 int display_all = 0;
1282 int stty_state; 1382 int stty_state;
1283 int k; 1383 int k;
@@ -1291,7 +1391,9 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1291 k = 0; 1391 k = 0;
1292 while (argv[++k]) { 1392 while (argv[++k]) {
1293 const struct mode_info *mp; 1393 const struct mode_info *mp;
1394#if !ENABLE_PLATFORM_MINGW32
1294 const struct control_info *cp; 1395 const struct control_info *cp;
1396#endif
1295 const char *arg = argv[k]; 1397 const char *arg = argv[k];
1296 const char *argnext = argv[k+1]; 1398 const char *argnext = argv[k+1];
1297 int param; 1399 int param;
@@ -1314,6 +1416,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1314 output_func = do_display; 1416 output_func = do_display;
1315 display_all = 1; 1417 display_all = 1;
1316 break; 1418 break;
1419#if !ENABLE_PLATFORM_MINGW32
1317 case 'g': 1420 case 'g':
1318 stty_state |= STTY_recoverable_output; 1421 stty_state |= STTY_recoverable_output;
1319 output_func = display_recoverable; 1422 output_func = display_recoverable;
@@ -1334,11 +1437,14 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1334 } 1437 }
1335 } 1438 }
1336 goto end_option; 1439 goto end_option;
1440#endif
1337 default: 1441 default:
1338 goto invalid_argument; 1442 goto invalid_argument;
1339 } 1443 }
1340 } 1444 }
1445#if !ENABLE_PLATFORM_MINGW32
1341 end_option: 1446 end_option:
1447#endif
1342 continue; 1448 continue;
1343 } 1449 }
1344 1450
@@ -1348,6 +1454,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1348 continue; 1454 continue;
1349 } 1455 }
1350 1456
1457#if !ENABLE_PLATFORM_MINGW32
1351 cp = find_control(arg); 1458 cp = find_control(arg);
1352 if (cp) { 1459 if (cp) {
1353 if (!argnext) 1460 if (!argnext)
@@ -1358,6 +1465,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1358 ++k; 1465 ++k;
1359 continue; 1466 continue;
1360 } 1467 }
1468#endif
1361 1469
1362 param = find_param(arg); 1470 param = find_param(arg);
1363 if (param & param_need_arg) { 1471 if (param & param_need_arg) {
@@ -1381,7 +1489,11 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1381 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); 1489 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1382 break; 1490 break;
1383 case param_size: 1491 case param_size:
1492# if ENABLE_PLATFORM_MINGW32
1493 break;
1494# endif
1384#endif 1495#endif
1496#if !ENABLE_PLATFORM_MINGW32
1385 case param_speed: 1497 case param_speed:
1386 break; 1498 break;
1387 case param_ispeed: 1499 case param_ispeed:
@@ -1392,15 +1504,19 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1392 /* called for the side effect of xfunc death only */ 1504 /* called for the side effect of xfunc death only */
1393 set_speed_or_die(output_speed, argnext, &mode); 1505 set_speed_or_die(output_speed, argnext, &mode);
1394 break; 1506 break;
1507#endif
1395 default: 1508 default:
1509#if !ENABLE_PLATFORM_MINGW32
1396 if (recover_mode(arg, &mode) == 1) break; 1510 if (recover_mode(arg, &mode) == 1) break;
1397 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; 1511 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1512#endif
1398 invalid_argument: 1513 invalid_argument:
1399 bb_error_msg_and_die("invalid argument '%s'", arg); 1514 bb_error_msg_and_die("invalid argument '%s'", arg);
1400 } 1515 }
1401 stty_state &= ~STTY_noargs; 1516 stty_state &= ~STTY_noargs;
1402 } 1517 }
1403 1518
1519#if !ENABLE_PLATFORM_MINGW32
1404 /* Specifying both -a and -g is an error */ 1520 /* Specifying both -a and -g is an error */
1405 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == 1521 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1406 (STTY_verbose_output | STTY_recoverable_output) 1522 (STTY_verbose_output | STTY_recoverable_output)
@@ -1413,13 +1529,22 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1413 ) { 1529 ) {
1414 bb_simple_error_msg_and_die("modes may not be set when -a or -g is used"); 1530 bb_simple_error_msg_and_die("modes may not be set when -a or -g is used");
1415 } 1531 }
1532#else
1533 /* Specifying -a with non-options is an error */
1534 if ((stty_state & STTY_verbose_output) && !(stty_state & STTY_noargs)
1535 ) {
1536 bb_simple_error_msg_and_die("modes may not be set when -a is used");
1537 }
1538#endif
1416 1539
1540#if !ENABLE_PLATFORM_MINGW32
1417 /* Now it is safe to start doing things */ 1541 /* Now it is safe to start doing things */
1418 if (file_name) { 1542 if (file_name) {
1419 G.device_name = file_name; 1543 G.device_name = file_name;
1420 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); 1544 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1421 ndelay_off(STDIN_FILENO); 1545 ndelay_off(STDIN_FILENO);
1422 } 1546 }
1547#endif
1423 1548
1424 /* Initialize to all zeroes so there is no risk memcmp will report a 1549 /* Initialize to all zeroes so there is no risk memcmp will report a
1425 spurious difference in an uninitialized portion of the structure */ 1550 spurious difference in an uninitialized portion of the structure */
@@ -1437,7 +1562,9 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1437 k = 0; 1562 k = 0;
1438 while (argv[++k]) { 1563 while (argv[++k]) {
1439 const struct mode_info *mp; 1564 const struct mode_info *mp;
1565#if !ENABLE_PLATFORM_MINGW32
1440 const struct control_info *cp; 1566 const struct control_info *cp;
1567#endif
1441 const char *arg = argv[k]; 1568 const char *arg = argv[k];
1442 const char *argnext = argv[k+1]; 1569 const char *argnext = argv[k+1];
1443 int param; 1570 int param;
@@ -1459,6 +1586,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1459 continue; 1586 continue;
1460 } 1587 }
1461 1588
1589#if !ENABLE_PLATFORM_MINGW32
1462 cp = find_control(arg); 1590 cp = find_control(arg);
1463 if (cp) { 1591 if (cp) {
1464 ++k; 1592 ++k;
@@ -1466,6 +1594,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1466 stty_state |= STTY_require_set_attr; 1594 stty_state |= STTY_require_set_attr;
1467 continue; 1595 continue;
1468 } 1596 }
1597#endif
1469 1598
1470 param = find_param(arg); 1599 param = find_param(arg);
1471 if (param & param_need_arg) { 1600 if (param & param_need_arg) {
@@ -1491,6 +1620,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1491 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); 1620 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1492 break; 1621 break;
1493#endif 1622#endif
1623#if !ENABLE_PLATFORM_MINGW32
1494 case param_speed: 1624 case param_speed:
1495 display_speed(&mode, 0); 1625 display_speed(&mode, 0);
1496 break; 1626 break;
@@ -1502,7 +1632,9 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1502 set_speed_or_die(output_speed, argnext, &mode); 1632 set_speed_or_die(output_speed, argnext, &mode);
1503 stty_state |= (STTY_require_set_attr | STTY_speed_was_set); 1633 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1504 break; 1634 break;
1635#endif
1505 default: 1636 default:
1637#if !ENABLE_PLATFORM_MINGW32
1506 if (recover_mode(arg, &mode) == 1) 1638 if (recover_mode(arg, &mode) == 1)
1507 stty_state |= STTY_require_set_attr; 1639 stty_state |= STTY_require_set_attr;
1508 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ 1640 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
@@ -1510,15 +1642,24 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1510 stty_state |= (STTY_require_set_attr | STTY_speed_was_set); 1642 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1511 } /* else - impossible (caught in the first pass): 1643 } /* else - impossible (caught in the first pass):
1512 bb_error_msg_and_die("invalid argument '%s'", arg); */ 1644 bb_error_msg_and_die("invalid argument '%s'", arg); */
1645#endif
1513 } 1646 }
1514 } 1647 }
1515 1648
1516 if (stty_state & STTY_require_set_attr) { 1649 if (stty_state & STTY_require_set_attr) {
1650#if !ENABLE_PLATFORM_MINGW32
1517 struct termios new_mode; 1651 struct termios new_mode;
1652#else
1653 if (mode.c_lflag & ECHO)
1654 mode.w_mode |= ENABLE_ECHO_INPUT;
1655 else
1656 mode.w_mode &= ~ENABLE_ECHO_INPUT;
1657#endif
1518 1658
1519 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) 1659 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1520 perror_on_device_and_die("%s"); 1660 perror_on_device_and_die("%s");
1521 1661
1662#if !ENABLE_PLATFORM_MINGW32
1522 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if 1663 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1523 it performs *any* of the requested operations. This means it 1664 it performs *any* of the requested operations. This means it
1524 can report 'success' when it has actually failed to perform 1665 can report 'success' when it has actually failed to perform
@@ -1554,6 +1695,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
1554#endif 1695#endif
1555 perror_on_device_and_die("%s: cannot perform all requested operations"); 1696 perror_on_device_and_die("%s: cannot perform all requested operations");
1556 } 1697 }
1698#endif
1557 } 1699 }
1558 1700
1559 return EXIT_SUCCESS; 1701 return EXIT_SUCCESS;
diff --git a/coreutils/tr.c b/coreutils/tr.c
index 8d779d8ea..f51c4caea 100644
--- a/coreutils/tr.c
+++ b/coreutils/tr.c
@@ -117,6 +117,13 @@ static unsigned expand(char *arg, char **buffer_p)
117 arg++; 117 arg++;
118 z = arg; 118 z = arg;
119 ac = bb_process_escape_sequence(&z); 119 ac = bb_process_escape_sequence(&z);
120#if ENABLE_PLATFORM_MINGW32
121 if (ac == '\\' && *z == '-') {
122 /* An escaped dash isn't a range, don't fall through */
123 buffer[pos++] = *z;
124 continue;
125 }
126#endif
120 arg = (char *)z; 127 arg = (char *)z;
121 arg--; 128 arg--;
122 *arg = ac; 129 *arg = ac;
diff --git a/coreutils/truncate.c b/coreutils/truncate.c
index 8826e6b4c..87a47bb09 100644
--- a/coreutils/truncate.c
+++ b/coreutils/truncate.c
@@ -73,6 +73,12 @@ int truncate_main(int argc UNUSED_PARAM, char **argv)
73 * do not report error, exitcode is also 0. 73 * do not report error, exitcode is also 0.
74 */ 74 */
75 } else { 75 } else {
76#if ENABLE_PLATFORM_MINGW32
77 struct stat st;
78
79 if (fstat(fd, &st) == 0 && size > st.st_size)
80 make_sparse(fd, st.st_size, size);
81#endif
76 if (ftruncate(fd, size) == -1) { 82 if (ftruncate(fd, size) == -1) {
77 bb_perror_msg("%s: truncate", *argv); 83 bb_perror_msg("%s: truncate", *argv);
78 ret = EXIT_FAILURE; 84 ret = EXIT_FAILURE;
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c
index fd4ea737c..f7e93497d 100644
--- a/e2fsprogs/fsck.c
+++ b/e2fsprogs/fsck.c
@@ -423,13 +423,11 @@ static int wait_one(int flags)
423 /* if (G.noexecute) { already returned -1; } */ 423 /* if (G.noexecute) { already returned -1; } */
424 424
425 while (1) { 425 while (1) {
426 pid = waitpid(-1, &status, flags); 426 pid = safe_waitpid(-1, &status, flags);
427 kill_all_if_got_signal(); 427 kill_all_if_got_signal();
428 if (pid == 0) /* flags == WNOHANG and no children exited */ 428 if (pid == 0) /* flags == WNOHANG and no children exited */
429 return -1; 429 return -1;
430 if (pid < 0) { 430 if (pid < 0) {
431 if (errno == EINTR)
432 continue;
433 if (errno == ECHILD) { /* paranoia */ 431 if (errno == ECHILD) { /* paranoia */
434 bb_simple_error_msg("wait: no more children"); 432 bb_simple_error_msg("wait: no more children");
435 return -1; 433 return -1;
diff --git a/editors/diff.c b/editors/diff.c
index b324feaa5..8911859cd 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -773,7 +773,7 @@ static int diffreg(char *file[2])
773 fd = fd_tmp; 773 fd = fd_tmp;
774 xlseek(fd, 0, SEEK_SET); 774 xlseek(fd, 0, SEEK_SET);
775 } 775 }
776 fp[i] = fdopen(fd, "r"); 776 fp[i] = xfdopen_for_read(fd);
777 } 777 }
778 778
779 setup_common_bufsiz(); 779 setup_common_bufsiz();
diff --git a/editors/sed.c b/editors/sed.c
index 107e664a0..204417108 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -257,7 +257,12 @@ static FILE *sed_xfopen_w(const char *fname)
257 257
258static void cleanup_outname(void) 258static void cleanup_outname(void)
259{ 259{
260 if (G.outname) unlink(G.outname); 260 if (G.outname) {
261#if ENABLE_PLATFORM_MINGW32
262 fclose(G.nonstdout);
263#endif
264 unlink(G.outname);
265 }
261} 266}
262 267
263/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */ 268/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
diff --git a/include/libbb.h b/include/libbb.h
index bc1453e12..03ca9f057 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -281,6 +281,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
281# endif 281# endif
282#endif 282#endif
283 283
284#if ENABLE_FEATURE_TLS_SCHANNEL || ENABLE_FEATURE_USE_CNG_API
285# define SECURITY_WIN32
286# include <windows.h>
287# include <security.h>
288#endif
289
290#if ENABLE_FEATURE_USE_CNG_API
291# include <bcrypt.h>
292
293// these work on Windows >= 10
294# define BCRYPT_HMAC_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x000000a1)
295# define BCRYPT_HMAC_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x000000b1)
296# define sha1_begin_hmac BCRYPT_HMAC_SHA1_ALG_HANDLE
297# define sha256_begin_hmac BCRYPT_HMAC_SHA256_ALG_HANDLE
298#else
299# define sha1_begin_hmac sha1_begin
300# define sha256_begin_hmac sha256_begin
301# define hmac_uninit(...) ((void)0)
302#endif
303
284/* Tested to work correctly with all int types (IIRC :]) */ 304/* Tested to work correctly with all int types (IIRC :]) */
285#define MAXINT(T) (T)( \ 305#define MAXINT(T) (T)( \
286 ((T)-1) > 0 \ 306 ((T)-1) > 0 \
@@ -699,6 +719,8 @@ int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC;
699int sigprocmask_allsigs(int how) FAST_FUNC; 719int sigprocmask_allsigs(int how) FAST_FUNC;
700/* Return old set in the same set: */ 720/* Return old set in the same set: */
701int sigprocmask2(int how, sigset_t *set) FAST_FUNC; 721int sigprocmask2(int how, sigset_t *set) FAST_FUNC;
722/* SIG_BLOCK all signals, return old set: */
723int sigblockall(sigset_t *set) FAST_FUNC;
702#else 724#else
703#define bb_signals(s, f) 725#define bb_signals(s, f)
704#define kill_myself_with_sig(s) 726#define kill_myself_with_sig(s)
@@ -899,7 +921,36 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC;
899// Also mount.c and inetd.c are using gethostbyname(), 921// Also mount.c and inetd.c are using gethostbyname(),
900// + inet_common.c has additional IPv4-only stuff 922// + inet_common.c has additional IPv4-only stuff
901 923
924#if defined CONFIG_FEATURE_TLS_SCHANNEL
925enum schannel_connection_state {
926 BB_SCHANNEL_OPEN = 0,
927 BB_SCHANNEL_CLOSED = 1,
928 BB_SCHANNEL_CLOSED_AND_FREED = 2
929};
930
931typedef struct tls_state {
932 int ofd;
933 int ifd;
934
935 // handles
936 CredHandle cred_handle;
937 CtxtHandle ctx_handle;
938
939 // buffers
940 char in_buffer[16384 + 256]; // input buffer (to read from server), length is maximum TLS packet size
941 unsigned long in_buffer_offset;
942
943 char *out_buffer; // output buffer (for decrypted data, offset from in_buffer)
944 unsigned long out_buffer_length;
945 unsigned long out_buffer_extra;
902 946
947 // data
948 char *hostname;
949 SecPkgContext_StreamSizes stream_sizes;
950 bool initialized;
951 enum schannel_connection_state connection_state;
952} tls_state_t;
953#else
903struct tls_aes { 954struct tls_aes {
904 uint32_t key[60]; 955 uint32_t key[60];
905 unsigned rounds; 956 unsigned rounds;
@@ -956,12 +1007,14 @@ typedef struct tls_state {
956 struct tls_aes aes_decrypt; 1007 struct tls_aes aes_decrypt;
957 uint8_t H[16]; //used by AES_GCM 1008 uint8_t H[16]; //used by AES_GCM
958} tls_state_t; 1009} tls_state_t;
1010#endif
959 1011
960static inline tls_state_t *new_tls_state(void) 1012static inline tls_state_t *new_tls_state(void)
961{ 1013{
962 tls_state_t *tls = xzalloc(sizeof(*tls)); 1014 tls_state_t *tls = xzalloc(sizeof(*tls));
963 return tls; 1015 return tls;
964} 1016}
1017
965void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC; 1018void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC;
966#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) 1019#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0)
967void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; 1020void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC;
@@ -1071,13 +1124,13 @@ unsigned bb_clk_tck(void) FAST_FUNC;
1071 1124
1072#if SEAMLESS_COMPRESSION 1125#if SEAMLESS_COMPRESSION
1073/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ 1126/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */
1074int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC; 1127int setup_unzip_on_fd(int fd, int die_if_not_compressed) FAST_FUNC;
1075/* Autodetects .gz etc */ 1128/* Autodetects .gz etc */
1076extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC; 1129extern int open_zipped(const char *fname, int die_if_not_compressed) FAST_FUNC;
1077extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; 1130extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
1078#else 1131#else
1079# define setup_unzip_on_fd(...) (0) 1132# define setup_unzip_on_fd(...) (0)
1080# define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY); 1133# define open_zipped(fname, die_if_not_compressed) open((fname), O_RDONLY);
1081# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p)) 1134# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p))
1082#endif 1135#endif
1083/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */ 1136/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */
@@ -1173,6 +1226,32 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
1173/* Reverse */ 1226/* Reverse */
1174char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; 1227char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
1175 1228
1229/* Returns strlen as a bonus */
1230//size_t replace_char(char *s, char what, char with) FAST_FUNC;
1231static inline size_t replace_char(char *str, char from, char to)
1232{
1233 char *p = str;
1234 while (*p) {
1235 if (*p == from)
1236 *p = to;
1237 p++;
1238 }
1239 return p - str;
1240}
1241
1242extern const char c_escape_conv_str00[];
1243#define c_escape_conv_str07 (c_escape_conv_str00+3)
1244
1245void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count);
1246void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count);
1247void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask);
1248void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2);
1249#if BB_UNALIGNED_MEMACCESS_OK
1250# define xorbuf16(buf,mask) xorbuf16_aligned_long(buf,mask)
1251#else
1252void FAST_FUNC xorbuf16(void* buf, const void* mask);
1253#endif
1254
1176/* Generate a UUID */ 1255/* Generate a UUID */
1177void generate_uuid(uint8_t *buf) FAST_FUNC; 1256void generate_uuid(uint8_t *buf) FAST_FUNC;
1178 1257
@@ -1887,18 +1966,25 @@ extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_F
1887extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; 1966extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
1888/* 1967/*
1889 * rnd is additional random input. New one is returned. 1968 * rnd is additional random input. New one is returned.
1890 * Useful if you call crypt_make_salt many times in a row: 1969 * Useful if you call crypt_make_rand64encoded many times in a row:
1891 * rnd = crypt_make_salt(buf1, 4, 0); 1970 * rnd = crypt_make_rand64encoded(buf1, 4, 0);
1892 * rnd = crypt_make_salt(buf2, 4, rnd); 1971 * rnd = crypt_make_rand64encoded(buf2, 4, rnd);
1893 * rnd = crypt_make_salt(buf3, 4, rnd); 1972 * rnd = crypt_make_rand64encoded(buf3, 4, rnd);
1894 * (otherwise we risk having same salt generated) 1973 * (otherwise we risk having same salt generated)
1895 */ 1974 */
1896extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; 1975extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC;
1897/* "$N$" + sha_salt_16_bytes + NUL */ 1976/* Size of char salt[] to hold randomly-generated salt string
1898#define MAX_PW_SALT_LEN (3 + 16 + 1) 1977 * sha256/512:
1978 * "$5$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1979 * "$6$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1980 * #define MAX_PW_SALT_LEN (3 + sizeof("rounds=999999999$")-1 + 16 + 1)
1981 * yescrypt:
1982 * "$y$" <up to 8 params of up to 6 chars each> "$" <up to 86 chars salt><NUL>
1983 * (86 chars are ascii64-encoded 64 binary bytes)
1984 */
1985#define MAX_PW_SALT_LEN (3 + 8*6 + 1 + 86 + 1)
1899extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; 1986extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
1900 1987
1901
1902/* Returns number of lines changed, or -1 on error */ 1988/* Returns number of lines changed, or -1 on error */
1903#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) 1989#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
1904#define update_passwd(filename, username, data, member) \ 1990#define update_passwd(filename, username, data, member) \
@@ -2041,6 +2127,10 @@ int64_t windows_read_key(int fd, char *buffer, int timeout) FAST_FUNC;
2041int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC; 2127int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC;
2042void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; 2128void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC;
2043 2129
2130int check_got_signal_and_poll(struct pollfd pfd[1], int timeout) FAST_FUNC;
2131#if ENABLE_PLATFORM_MINGW32
2132# define check_got_signal_and_poll(p, t) poll(p, 1, t)
2133#endif
2044 2134
2045#if ENABLE_FEATURE_EDITING 2135#if ENABLE_FEATURE_EDITING
2046/* It's NOT just ENABLEd or disabled. It's a number: */ 2136/* It's NOT just ENABLEd or disabled. It's a number: */
@@ -2087,7 +2177,7 @@ typedef struct line_input_t {
2087# if MAX_HISTORY 2177# if MAX_HISTORY
2088 int cnt_history; 2178 int cnt_history;
2089 int cur_history; 2179 int cur_history;
2090 int max_history; /* must never be <= 0 */ 2180 int max_history; /* must never be < 0 */
2091# if ENABLE_FEATURE_EDITING_SAVEHISTORY 2181# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2092 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: 2182 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT:
2093 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are 2183 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are
@@ -2153,33 +2243,6 @@ enum { COMM_LEN = 16 };
2153# endif 2243# endif
2154#endif 2244#endif
2155 2245
2156struct smaprec {
2157 unsigned long mapped_rw;
2158 unsigned long mapped_ro;
2159 unsigned long shared_clean;
2160 unsigned long shared_dirty;
2161 unsigned long private_clean;
2162 unsigned long private_dirty;
2163 unsigned long stack;
2164 unsigned long smap_pss, smap_swap;
2165 unsigned long smap_size;
2166 // For mixed 32/64 userspace, 32-bit pmap still needs
2167 // 64-bit field here to correctly show 64-bit processes:
2168 unsigned long long smap_start;
2169 // (strictly speaking, other fields need to be wider too,
2170 // but they are in kbytes, not bytes, and they hold sizes,
2171 // not start addresses, sizes tend to be less than 4 terabytes)
2172 char smap_mode[5];
2173 char *smap_name;
2174};
2175
2176#if !ENABLE_PMAP
2177#define procps_read_smaps(pid, total, cb, data) \
2178 procps_read_smaps(pid, total)
2179#endif
2180int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
2181 void (*cb)(struct smaprec *, void *), void *data);
2182
2183typedef struct procps_status_t { 2246typedef struct procps_status_t {
2184#if !ENABLE_PLATFORM_MINGW32 2247#if !ENABLE_PLATFORM_MINGW32
2185 DIR *dir; 2248 DIR *dir;
@@ -2215,7 +2278,13 @@ typedef struct procps_status_t {
2215#endif 2278#endif
2216 unsigned tty_major,tty_minor; 2279 unsigned tty_major,tty_minor;
2217#if ENABLE_FEATURE_TOPMEM 2280#if ENABLE_FEATURE_TOPMEM
2218 struct smaprec smaps; 2281 unsigned long mapped_rw;
2282 unsigned long mapped_ro;
2283 unsigned long shared_clean;
2284 unsigned long shared_dirty;
2285 unsigned long private_clean;
2286 unsigned long private_dirty;
2287 unsigned long stack;
2219#endif 2288#endif
2220 char state[4]; 2289 char state[4];
2221 /* basename of executable in exec(2), read from /proc/N/stat 2290 /* basename of executable in exec(2), read from /proc/N/stat
@@ -2264,11 +2333,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC;
2264procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; 2333procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
2265/* Format cmdline (up to col chars) into char buf[size] */ 2334/* Format cmdline (up to col chars) into char buf[size] */
2266/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ 2335/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */
2267void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; 2336int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC;
2268pid_t *find_pid_by_name(const char* procName) FAST_FUNC; 2337pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
2269pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; 2338pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
2270int starts_with_cpu(const char *str) FAST_FUNC; 2339int starts_with_cpu(const char *str) FAST_FUNC;
2271unsigned get_cpu_count(void) FAST_FUNC; 2340unsigned get_cpu_count(void) FAST_FUNC;
2341/* Some internals reused by pmap: */
2342unsigned long FAST_FUNC fast_strtoul_10(char **endptr);
2343unsigned long long FAST_FUNC fast_strtoull_16(char **endptr);
2344char* FAST_FUNC skip_fields(char *str, int count);
2272 2345
2273 2346
2274/* Use strict=1 if you process input from untrusted source: 2347/* Use strict=1 if you process input from untrusted source:
@@ -2294,6 +2367,56 @@ char *decode_base64(char *dst, const char **pp_src) FAST_FUNC;
2294char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; 2367char *decode_base32(char *dst, const char **pp_src) FAST_FUNC;
2295void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; 2368void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC;
2296 2369
2370int FAST_FUNC i2a64(int i);
2371int FAST_FUNC a2i64(char c);
2372char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n);
2373
2374enum {
2375 /* how many bytes XYZ_end() fills */
2376 MD5_OUTSIZE = 16,
2377 SHA1_OUTSIZE = 20,
2378 SHA256_OUTSIZE = 32,
2379 SHA384_OUTSIZE = 48,
2380 SHA512_OUTSIZE = 64,
2381 //SHA3-224_OUTSIZE = 28,
2382 /* size of input block */
2383 SHA2_INSIZE = 64,
2384};
2385
2386#if defined CONFIG_FEATURE_USE_CNG_API
2387struct bcrypt_hash_ctx_t {
2388 void *handle;
2389 void *hash_obj;
2390 unsigned int output_size;
2391};
2392typedef struct bcrypt_hash_ctx_t md5_ctx_t;
2393typedef struct bcrypt_hash_ctx_t sha1_ctx_t;
2394typedef struct bcrypt_hash_ctx_t sha256_ctx_t;
2395typedef struct bcrypt_hash_ctx_t sha384_ctx_t;
2396typedef struct bcrypt_hash_ctx_t sha512_ctx_t;
2397typedef struct sha3_ctx_t {
2398 uint64_t state[25];
2399 unsigned bytes_queued;
2400 unsigned input_block_bytes;
2401} sha3_ctx_t;
2402void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2403void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2404void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2405void sha384_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2406void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2407void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2408unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC;
2409# define md5_hash generic_hash
2410# define sha1_hash generic_hash
2411# define sha256_hash generic_hash
2412# define sha384_hash generic_hash
2413# define sha512_hash generic_hash
2414# define md5_end generic_end
2415# define sha1_end generic_end
2416# define sha256_end generic_end
2417# define sha384_end generic_end
2418# define sha512_end generic_end
2419#else
2297typedef struct md5_ctx_t { 2420typedef struct md5_ctx_t {
2298 uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ 2421 uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */
2299 void (*process_block)(struct md5_ctx_t*) FAST_FUNC; 2422 void (*process_block)(struct md5_ctx_t*) FAST_FUNC;
@@ -2307,6 +2430,7 @@ typedef struct sha512_ctx_t {
2307 uint64_t hash[8]; 2430 uint64_t hash[8];
2308 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ 2431 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
2309} sha512_ctx_t; 2432} sha512_ctx_t;
2433typedef struct sha512_ctx_t sha384_ctx_t;
2310typedef struct sha3_ctx_t { 2434typedef struct sha3_ctx_t {
2311 uint64_t state[25]; 2435 uint64_t state[25];
2312 unsigned bytes_queued; 2436 unsigned bytes_queued;
@@ -2324,20 +2448,69 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
2324void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; 2448void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
2325void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2449void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2326unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; 2450unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
2451void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC;
2452#define sha384_hash sha512_hash
2453unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC;
2454#endif
2327void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; 2455void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
2328void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2456void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2329unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; 2457unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC;
2458void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]);
2330/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ 2459/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */
2460#if defined CONFIG_FEATURE_USE_CNG_API
2461typedef struct bcrypt_hash_ctx_t md5sha_ctx_t;
2462#define md5sha_hash generic_hash
2463#define sha_end generic_end
2464#else
2331typedef struct md5_ctx_t md5sha_ctx_t; 2465typedef struct md5_ctx_t md5sha_ctx_t;
2332#define md5sha_hash md5_hash 2466#define md5sha_hash md5_hash
2333#define sha_end sha1_end 2467#define sha_end sha1_end
2334enum { 2468#endif
2335 MD5_OUTSIZE = 16, 2469
2336 SHA1_OUTSIZE = 20, 2470/* RFC 2104 HMAC (hash-based message authentication code) */
2337 SHA256_OUTSIZE = 32, 2471#if !ENABLE_FEATURE_USE_CNG_API
2338 SHA512_OUTSIZE = 64, 2472typedef struct hmac_ctx {
2339 SHA3_OUTSIZE = 28, 2473 md5sha_ctx_t hashed_key_xor_ipad;
2340}; 2474 md5sha_ctx_t hashed_key_xor_opad;
2475} hmac_ctx_t;
2476#else
2477typedef struct bcrypt_hash_ctx_t hmac_ctx_t;
2478#endif
2479#define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1)
2480typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
2481#if !ENABLE_FEATURE_USE_CNG_API
2482#if HMAC_ONLY_SHA256
2483#define hmac_begin(ctx,key,key_size,begin) \
2484 hmac_begin(ctx,key,key_size)
2485#endif
2486void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin);
2487static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len)
2488{
2489 md5sha_hash(&ctx->hashed_key_xor_ipad, in, len);
2490}
2491#else
2492# if HMAC_ONLY_SHA256
2493# define hmac_begin(pre,key,key_size,begin) \
2494 _hmac_begin(pre, key, key_size, sha256_begin_hmac)
2495# else
2496# define hmac_begin _hmac_begin
2497# endif
2498void _hmac_begin(hmac_ctx_t *pre, uint8_t *key, unsigned key_size,
2499 BCRYPT_ALG_HANDLE alg_handle);
2500void hmac_uninit(hmac_ctx_t *pre);
2501#endif
2502unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out);
2503#if HMAC_ONLY_SHA256
2504#define hmac_block(key,key_size,begin,in,sz,out) \
2505 hmac_block(key,key_size,in,sz,out)
2506#endif
2507unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size,
2508 md5sha_begin_func *begin,
2509 const void *in, unsigned sz,
2510 uint8_t *out);
2511/* HMAC helpers for TLS: */
2512void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va);
2513unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...);
2341 2514
2342extern uint32_t *global_crc32_table; 2515extern uint32_t *global_crc32_table;
2343uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; 2516uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
@@ -2473,31 +2646,10 @@ extern struct globals *BB_GLOBAL_CONST ptr_to_globals;
2473#define barrier() asm volatile ("":::"memory") 2646#define barrier() asm volatile ("":::"memory")
2474 2647
2475#if defined(__clang_major__) && __clang_major__ >= 9 2648#if defined(__clang_major__) && __clang_major__ >= 9
2476/* Clang/llvm drops assignment to "constant" storage. Silently. 2649/* {ASSIGN,XZALLOC}_CONST_PTR() are out-of-line functions
2477 * Needs serious convincing to not eliminate the store. 2650 * to prevent clang from reading pointer before it is assigned.
2478 */
2479static ALWAYS_INLINE void* not_const_pp(const void *p)
2480{
2481 void *pp;
2482 asm volatile (
2483 "# forget that p points to const"
2484 : /*outputs*/ "=r" (pp)
2485 : /*inputs*/ "0" (p)
2486 );
2487 return pp;
2488}
2489# if !ENABLE_PLATFORM_MINGW32
2490# define ASSIGN_CONST_PTR(pptr, v) do { \
2491 *(void**)not_const_pp(pptr) = (void*)(v); \
2492 barrier(); \
2493} while (0)
2494#else
2495/* On Windows it seems necessary for this to be a function too. */
2496void ASSIGN_CONST_PTR(const void *pptr, const void *ptr) FAST_FUNC;
2497#endif
2498/* XZALLOC_CONST_PTR() is an out-of-line function to prevent
2499 * clang from reading pointer before it is assigned.
2500 */ 2651 */
2652void ASSIGN_CONST_PTR(const void *pptr, void *v) FAST_FUNC;
2501void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC; 2653void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC;
2502#else 2654#else
2503# define ASSIGN_CONST_PTR(pptr, v) do { \ 2655# define ASSIGN_CONST_PTR(pptr, v) do { \
diff --git a/include/mingw.h b/include/mingw.h
index 3ee1cc46f..276e40659 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -259,6 +259,7 @@ int ffs(int i);
259 */ 259 */
260 260
261#define TIOCGWINSZ 0x5413 261#define TIOCGWINSZ 0x5413
262#define TIOCSWINSZ 0x5414
262 263
263int ioctl(int fd, int code, ...); 264int ioctl(int fd, int code, ...);
264 265
@@ -670,3 +671,4 @@ enum {
670int elevation_state(void); 671int elevation_state(void);
671void set_interp(int i) FAST_FUNC; 672void set_interp(int i) FAST_FUNC;
672int mingw_shell_execute(SHELLEXECUTEINFO *info); 673int mingw_shell_execute(SHELLEXECUTEINFO *info);
674void mingw_die_if_error(NTSTATUS status, const char *function_name);
diff --git a/include/platform.h b/include/platform.h
index 5795a0cf3..0db8bf345 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -208,7 +208,7 @@
208#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN 208#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
209# define BB_BIG_ENDIAN 0 209# define BB_BIG_ENDIAN 0
210# define BB_LITTLE_ENDIAN 1 210# define BB_LITTLE_ENDIAN 1
211#elif defined(__386__) 211#elif defined(__i386__)
212# define BB_BIG_ENDIAN 0 212# define BB_BIG_ENDIAN 0
213# define BB_LITTLE_ENDIAN 1 213# define BB_LITTLE_ENDIAN 1
214#else 214#else
@@ -230,6 +230,8 @@
230# define SWAP_LE64(x) bb_bswap_64(x) 230# define SWAP_LE64(x) bb_bswap_64(x)
231# define IF_BIG_ENDIAN(...) __VA_ARGS__ 231# define IF_BIG_ENDIAN(...) __VA_ARGS__
232# define IF_LITTLE_ENDIAN(...) 232# define IF_LITTLE_ENDIAN(...)
233/* How do bytes a,b,c,d (sequential in memory) look if fetched into uint32_t? */
234# define PACK32_BYTES(a,b,c,d) (uint32_t)((d)+((c)<<8)+((b)<<16)+((a)<<24))
233#else 235#else
234# define SWAP_BE16(x) bswap_16(x) 236# define SWAP_BE16(x) bswap_16(x)
235# define SWAP_BE32(x) bswap_32(x) 237# define SWAP_BE32(x) bswap_32(x)
@@ -239,6 +241,7 @@
239# define SWAP_LE64(x) (x) 241# define SWAP_LE64(x) (x)
240# define IF_BIG_ENDIAN(...) 242# define IF_BIG_ENDIAN(...)
241# define IF_LITTLE_ENDIAN(...) __VA_ARGS__ 243# define IF_LITTLE_ENDIAN(...) __VA_ARGS__
244# define PACK32_BYTES(a,b,c,d) (uint32_t)((a)+((b)<<8)+((c)<<16)+((d)<<24))
242#endif 245#endif
243 246
244 247
diff --git a/include/usage.src.h b/include/usage.src.h
index 5d2038834..0881337f8 100644
--- a/include/usage.src.h
+++ b/include/usage.src.h
@@ -17,11 +17,11 @@
17#define scripted_trivial_usage NOUSAGE_STR 17#define scripted_trivial_usage NOUSAGE_STR
18#define scripted_full_usage "" 18#define scripted_full_usage ""
19 19
20#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA 20#if !ENABLE_USE_BB_CRYPT
21# define CRYPT_METHODS_HELP_STR "des,md5,sha256/512" \ 21# define CRYPT_METHODS_HELP_STR "des,md5,sha256/512,yescrypt" \
22 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" 22 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")"
23#else 23#else
24# define CRYPT_METHODS_HELP_STR "des,md5" \ 24# define CRYPT_METHODS_HELP_STR "des,md5"IF_USE_BB_CRYPT_SHA(",sha256/512")IF_USE_BB_CRYPT_YES(",yescrypt") \
25 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" 25 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")"
26#endif 26#endif
27 27
diff --git a/init/bootchartd.c b/init/bootchartd.c
index 0929890a3..a5447c6ad 100644
--- a/init/bootchartd.c
+++ b/init/bootchartd.c
@@ -133,7 +133,7 @@ static void dump_file(FILE *fp, const char *filename)
133static int dump_procs(FILE *fp, int look_for_login_process) 133static int dump_procs(FILE *fp, int look_for_login_process)
134{ 134{
135 struct dirent *entry; 135 struct dirent *entry;
136 DIR *dir = opendir("/proc"); 136 DIR *dir = xopendir("/proc");
137 int found_login_process = 0; 137 int found_login_process = 0;
138 138
139 fputs(G.jiffy_line, fp); 139 fputs(G.jiffy_line, fp);
diff --git a/init/init.c b/init/init.c
index 2ee1e4cde..294be9952 100644
--- a/init/init.c
+++ b/init/init.c
@@ -1198,17 +1198,29 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1198 /* Wait for any child process(es) to exit */ 1198 /* Wait for any child process(es) to exit */
1199 while (1) { 1199 while (1) {
1200 pid_t wpid; 1200 pid_t wpid;
1201 int status;
1201 struct init_action *a; 1202 struct init_action *a;
1202 1203
1203 wpid = waitpid(-1, NULL, WNOHANG); 1204 wpid = wait_any_nohang(&status);
1204 if (wpid <= 0) 1205 if (wpid <= 0)
1205 break; 1206 break;
1206 1207
1207 a = mark_terminated(wpid); 1208 a = mark_terminated(wpid);
1208 if (a) { 1209 if (a) {
1209 message(L_LOG, "process '%s' (pid %u) exited. " 1210 const char *s = "killed, signal";
1211 int ex = WTERMSIG(status);
1212 /* "if (!WIFSIGNALED(status))" generates more code:
1213 * on linux, WIFEXITED(status) is "WTERMSIG(status) == 0"
1214 * and WTERMSIG(status) is known, so compiler optimizes.
1215 */
1216 if (WIFEXITED(status)) {
1217 s = "exited, exitcode";
1218 ex = WEXITSTATUS(status);
1219 }
1220 message(L_LOG, "process '%s' (pid %u) %s:%d. "
1210 "Scheduling for restart.", 1221 "Scheduling for restart.",
1211 a->command, (unsigned)wpid); 1222 a->command, (unsigned)wpid,
1223 s, ex);
1212 } 1224 }
1213 } 1225 }
1214 1226
diff --git a/libbb/Config.src b/libbb/Config.src
index 61b4601d6..eff327c2a 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -37,6 +37,14 @@ config PASSWORD_MINLEN
37 help 37 help
38 Minimum allowable password length. 38 Minimum allowable password length.
39 39
40config FEATURE_USE_CNG_API
41 bool "Use the Windows CNG API for checksums (Windows 10+ only)"
42 default n
43 depends on PLATFORM_MINGW32
44 help
45 Use the in-built Windows CNG API for checksums.
46 This reduces code size, but is only supported on Windows 10+.
47
40config MD5_SMALL 48config MD5_SMALL
41 int "MD5: Trade bytes for speed (0:fast, 3:slow)" 49 int "MD5: Trade bytes for speed (0:fast, 3:slow)"
42 default 1 # all "fast or small" options default to small 50 default 1 # all "fast or small" options default to small
@@ -67,6 +75,7 @@ config SHA1_SMALL
67config SHA1_HWACCEL 75config SHA1_HWACCEL
68 bool "SHA1: Use hardware accelerated instructions if possible" 76 bool "SHA1: Use hardware accelerated instructions if possible"
69 default y 77 default y
78 depends on !FEATURE_USE_CNG_API
70 help 79 help
71 On x86, this adds ~590 bytes of code. Throughput 80 On x86, this adds ~590 bytes of code. Throughput
72 is about twice as fast as fully-unrolled generic code. 81 is about twice as fast as fully-unrolled generic code.
@@ -74,6 +83,7 @@ config SHA1_HWACCEL
74config SHA256_HWACCEL 83config SHA256_HWACCEL
75 bool "SHA256: Use hardware accelerated instructions if possible" 84 bool "SHA256: Use hardware accelerated instructions if possible"
76 default y 85 default y
86 depends on !FEATURE_USE_CNG_API
77 help 87 help
78 On x86, this adds ~1k bytes of code. 88 On x86, this adds ~1k bytes of code.
79 89
@@ -182,8 +192,8 @@ config FEATURE_EDITING_VI
182config FEATURE_EDITING_HISTORY 192config FEATURE_EDITING_HISTORY
183 int "History size" 193 int "History size"
184 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member 194 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member
185 range 0 9999 195 range 0 2000
186 default 255 196 default 200
187 depends on FEATURE_EDITING 197 depends on FEATURE_EDITING
188 help 198 help
189 Specify command history size (0 - disable). 199 Specify command history size (0 - disable).
diff --git a/libbb/bitops.c b/libbb/bitops.c
new file mode 100644
index 000000000..467e1a2d9
--- /dev/null
+++ b/libbb/bitops.c
@@ -0,0 +1,128 @@
1/*
2 * Utility routines.
3 *
4 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8//kbuild:lib-y += bitops.o
9
10#include "libbb.h"
11
12void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count)
13{
14 uint8_t *d = dst;
15 const uint8_t *s1 = src1;
16 const uint8_t *s2 = src2;
17#if BB_UNALIGNED_MEMACCESS_OK
18 while (count >= sizeof(long)) {
19 *(long*)d = *(long*)s1 ^ *(long*)s2;
20 count -= sizeof(long);
21 d += sizeof(long);
22 s1 += sizeof(long);
23 s2 += sizeof(long);
24 }
25#endif
26 while (count--)
27 *d++ = *s1++ ^ *s2++;
28}
29
30void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count)
31{
32 xorbuf_3(dst, dst, src, count);
33}
34
35void FAST_FUNC xorbuf16_aligned_long(void *dst, const void *src)
36{
37#if defined(__SSE__) /* any x86_64 has it */
38 asm volatile(
39"\n movups (%0),%%xmm0"
40"\n movups (%1),%%xmm1" // can't just xorps(%1),%%xmm0:
41"\n xorps %%xmm1,%%xmm0" // SSE requires 16-byte alignment
42"\n movups %%xmm0,(%0)"
43"\n"
44 : "=r" (dst), "=r" (src)
45 : "0" (dst), "1" (src)
46 : "xmm0", "xmm1", "memory"
47 );
48#else
49 unsigned long *d = dst;
50 const unsigned long *s = src;
51 d[0] ^= s[0];
52# if LONG_MAX <= 0x7fffffffffffffff
53 d[1] ^= s[1];
54# if LONG_MAX == 0x7fffffff
55 d[2] ^= s[2];
56 d[3] ^= s[3];
57# endif
58# endif
59#endif
60}
61// The above can be inlined in libbb.h, in a way where compiler
62// is even free to use better addressing modes than (%reg), and
63// to keep the result in a register
64// (to not store it to memory after each XOR):
65//#if defined(__SSE__)
66//#include <xmmintrin.h>
67//^^^ or just: typedef float __m128_u attribute((__vector_size__(16),__may_alias__,__aligned__(1)));
68//static ALWAYS_INLINE void xorbuf16_aligned_long(void *dst, const void *src)
69//{
70// __m128_u xmm0, xmm1;
71// asm volatile(
72//"\n xorps %1,%0"
73// : "=x" (xmm0), "=x" (xmm1)
74// : "0" (*(__m128_u*)dst), "1" (*(__m128_u*)src)
75// );
76// *(__m128_u*)dst = xmm0; // this store may be optimized out!
77//}
78//#endif
79// but I don't trust gcc optimizer enough to not generate some monstrosity.
80// See GMULT() function in TLS code as an example.
81
82void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2)
83{
84#if defined(__SSE__) /* any x86_64 has it */
85 asm volatile(
86"\n movups 0*16(%1),%%xmm0"
87"\n movups 0*16(%2),%%xmm1" // can't just xorps(%2),%%xmm0:
88"\n xorps %%xmm1,%%xmm0" // SSE requires 16-byte alignment, we have only 8-byte
89"\n movups %%xmm0,0*16(%0)"
90"\n movups 1*16(%1),%%xmm0"
91"\n movups 1*16(%2),%%xmm1"
92"\n xorps %%xmm1,%%xmm0"
93"\n movups %%xmm0,1*16(%0)"
94"\n movups 2*16(%1),%%xmm0"
95"\n movups 2*16(%2),%%xmm1"
96"\n xorps %%xmm1,%%xmm0"
97"\n movups %%xmm0,2*16(%0)"
98"\n movups 3*16(%1),%%xmm0"
99"\n movups 3*16(%2),%%xmm1"
100"\n xorps %%xmm1,%%xmm0"
101"\n movups %%xmm0,3*16(%0)"
102"\n"
103 : "=r" (dst), "=r" (src1), "=r" (src2)
104 : "0" (dst), "1" (src1), "2" (src2)
105 : "xmm0", "xmm1", "memory"
106 );
107#else
108 long *d = dst;
109 const long *s1 = src1;
110 const long *s2 = src2;
111 unsigned count = 64 / sizeof(long);
112 do {
113 *d++ = *s1++ ^ *s2++;
114 } while (--count != 0);
115#endif
116}
117
118#if !BB_UNALIGNED_MEMACCESS_OK
119void FAST_FUNC xorbuf16(void *dst, const void *src)
120{
121#define p_aligned(a) (((uintptr_t)(a) & (sizeof(long)-1)) == 0)
122 if (p_aligned(src) && p_aligned(dst)) {
123 xorbuf16_aligned_long(dst, src);
124 return;
125 }
126 xorbuf_3(dst, dst, src, 16);
127}
128#endif
diff --git a/libbb/c_escape.c b/libbb/c_escape.c
new file mode 100644
index 000000000..6c109f2e0
--- /dev/null
+++ b/libbb/c_escape.c
@@ -0,0 +1,20 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7//kbuild:lib-y += c_escape.o
8
9#include "libbb.h"
10
11const char c_escape_conv_str00[] ALIGN1 =
12 "\\""0""\0" // [0]:00
13 "\\""a""\0" // [1]:07
14 "\\""b""\0" // [2]:08
15 "\\""t""\0" // [3]:09
16 "\\""n""\0" // [4]:0a
17 "\\""v""\0" // [5]:0b
18 "\\""f""\0" // [6]:0c
19 "\\""r" // [7]:0d
20 ;
diff --git a/libbb/concat_path_file.c b/libbb/concat_path_file.c
index 3afb0e3a4..96fcd4a1d 100644
--- a/libbb/concat_path_file.c
+++ b/libbb/concat_path_file.c
@@ -17,6 +17,7 @@
17 17
18char* FAST_FUNC concat_path_file(const char *path, const char *filename) 18char* FAST_FUNC concat_path_file(const char *path, const char *filename)
19{ 19{
20#if 0
20 char *lc; 21 char *lc;
21 22
22 if (!path) 23 if (!path)
@@ -31,4 +32,78 @@ char* FAST_FUNC concat_path_file(const char *path, const char *filename)
31 filename++; 32 filename++;
32#endif 33#endif
33 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); 34 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
35#else
36/* ^^^^^^^^^^^ timing of xasprintf-based code above:
37 * real 7.074s
38 * user 0.156s <<<
39 * sys 6.394s
40 * "rm -rf" of a Linux kernel tree from tmpfs (run time still dominated by in-kernel work, though)
41 * real 6.989s
42 * user 0.055s <<< 3 times less CPU used
43 * sys 6.450s
44 * vvvvvvvvvvv timing of open-coded malloc+memcpy code below (+59 bytes):
45 */
46 char *buf, *p;
47 size_t n1, n2, n3;
48
49 while (*filename == '/')
50 filename++;
51
52 if (!path || !path[0])
53 return xstrdup(filename);
54
55 n1 = strlen(path);
56 n2 = (path[n1 - 1] != '/'); /* 1: "path has no trailing slash" */
57 n3 = strlen(filename) + 1;
58
59 buf = xmalloc(n1 + n2 + n3);
60 p = mempcpy(buf, path, n1);
61 if (n2)
62 *p++ = '/';
63 memcpy(p, filename, n3);
64 return buf;
65#endif
34} 66}
67
68/* If second component comes from struct dirent,
69 * it's possible to eliminate one strlen() by using name length
70 * provided by kernel in struct dirent. See below.
71 * However, the win seems to be insignificant.
72 */
73
74#if 0
75
76/* Extract d_namlen from struct dirent */
77static size_t get_d_namlen(const struct dirent *de)
78{
79#if defined(_DIRENT_HAVE_D_NAMLEN)
80 return de->d_namlen;
81#elif defined(_DIRENT_HAVE_D_RECLEN)
82 const size_t prefix_sz = offsetof(struct dirent, d_name);
83 return de->d_reclen - prefix_sz;
84#else
85 return strlen(de->d_name);
86#endif
87}
88
89char* FAST_FUNC concat_path_dirent(const char *path, const struct dirent *de)
90{
91 char *buf, *p;
92 size_t n1, n2, n3;
93
94 if (!path || !path[0])
95 return xstrdup(de->d_name);
96
97 n1 = strlen(path);
98 n2 = (path[n1 - 1] != '/');
99 n3 = get_d_namlen(de) + 1;
100
101 buf = xmalloc(n1 + n2 + n3);
102 p = mempcpy(buf, path, n1);
103 if (n2)
104 *p++ = '/';
105 memcpy(p, de->d_name, n3);
106 return buf;
107}
108
109#endif
diff --git a/libbb/const_hack.c b/libbb/const_hack.c
index 75163fede..1d175481b 100644
--- a/libbb/const_hack.c
+++ b/libbb/const_hack.c
@@ -9,18 +9,27 @@
9#include "libbb.h" 9#include "libbb.h"
10 10
11#if defined(__clang_major__) && __clang_major__ >= 9 11#if defined(__clang_major__) && __clang_major__ >= 9
12void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size) 12/* Clang/llvm drops assignment to "constant" storage. Silently.
13 * Needs serious convincing to not eliminate the store.
14 */
15static ALWAYS_INLINE void* not_const_pp(const void *p)
13{ 16{
14 ASSIGN_CONST_PTR(pptr, xzalloc(size)); 17 void *pp;
18 asm volatile (
19 "# forget that p points to const"
20 : /*outputs*/ "=r" (pp)
21 : /*inputs*/ "0" (p)
22 );
23 return pp;
15} 24}
16 25void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, void *v)
17# if ENABLE_PLATFORM_MINGW32 26{
18void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, const void *v) 27 *(void**)not_const_pp(pptr) = v;
28 barrier();
29}
30void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size)
19{ 31{
20 do { 32 *(void**)not_const_pp(pptr) = xzalloc(size);
21 *(void**)not_const_pp(pptr) = (void*)(v); 33 barrier();
22 barrier();
23 } while (0);
24} 34}
25# endif
26#endif 35#endif
diff --git a/libbb/dump.c b/libbb/dump.c
index aa57eca8c..3dc53d55f 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -514,37 +514,52 @@ static void bpad(PR *pr)
514 continue; 514 continue;
515} 515}
516 516
517static const char conv_str[] ALIGN1 =
518 "\0" "\\""0""\0"
519 "\007""\\""a""\0"
520 "\b" "\\""b""\0"
521 "\f" "\\""f""\0"
522 "\n" "\\""n""\0"
523 "\r" "\\""r""\0"
524 "\t" "\\""t""\0"
525 "\v" "\\""v""\0"
526 ;
527
528static void conv_c(PR *pr, unsigned char *p) 517static void conv_c(PR *pr, unsigned char *p)
529{ 518{
530 const char *str = conv_str; 519 const char *str;
531 520 unsigned char ch;
532 do { 521
533 if (*p == *str) { 522 ch = *p;
534 ++str; 523 if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) {
535 goto strpr; /* map e.g. '\n' to "\\n" */ 524 /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */
536 } 525 str = c_escape_conv_str00 + 3 * ch;
537 str += 4; 526 goto strpr;
538 } while (*str); 527 }
539 528
540 if (isprint_asciionly(*p)) { 529 if (isprint_asciionly(*p)) {
541 *pr->cchar = 'c'; 530 *pr->cchar = 'c';
542 printf(pr->fmt, *p); 531 printf(pr->fmt, *p);
543 } else { 532 } else {
533#if defined(__i386__) || defined(__x86_64__)
534 /* Abuse partial register operations */
535 uint32_t buf;
536 unsigned n = *p;
537 asm ( //00000000 00000000 00000000 aabbbccc
538"\n shll $10,%%eax" //00000000 000000aa bbbccc00 00000000
539"\n shrw $5,%%ax" //00000000 000000aa 00000bbb ccc00000
540"\n shrb $5,%%al" //00000000 000000aa 00000bbb 00000ccc
541"\n shll $8,%%eax" //000000aa 00000bbb 00000ccc 00000000
542"\n bswapl %%eax" //00000000 00000ccc 00000bbb 000000aa
543"\n addl $0x303030,%%eax"
544"\n" : "=a" (n)
545 : "0" (n)
546 );
547 buf = n;
548 str = (void*)&buf;
549#elif 1
544 char buf[4]; 550 char buf[4];
545 /* gcc-8.0.1 needs lots of casts to shut up */ 551 /* gcc-8.0.1 needs lots of casts to shut up */
546 sprintf(buf, "%03o", (unsigned)(uint8_t)*p); 552 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
547 str = buf; 553 str = buf;
554#else // use faster version? +20 bytes of code relative to sprintf() method
555 char buf[4];
556 buf[3] = '\0';
557 ch = *p;
558 buf[2] = '0' + (ch & 7); ch >>= 3;
559 buf[1] = '0' + (ch & 7); ch >>= 3;
560 buf[0] = '0' + ch;
561 str = buf;
562#endif
548 strpr: 563 strpr:
549 *pr->cchar = 's'; 564 *pr->cchar = 's';
550 printf(pr->fmt, str); 565 printf(pr->fmt, str);
@@ -703,15 +718,21 @@ static NOINLINE void display(priv_dumper_t* dumper)
703 conv_u(pr, bp); 718 conv_u(pr, bp);
704 break; 719 break;
705 case F_UINT: { 720 case F_UINT: {
721 union {
722 uint16_t uval16;
723 uint32_t uval32;
724 } u;
706 unsigned value = (unsigned char)*bp; 725 unsigned value = (unsigned char)*bp;
707 switch (pr->bcnt) { 726 switch (pr->bcnt) {
708 case 1: 727 case 1:
709 break; 728 break;
710 case 2: 729 case 2:
711 move_from_unaligned16(value, bp); 730 move_from_unaligned16(u.uval16, bp);
731 value = u.uval16;
712 break; 732 break;
713 case 4: 733 case 4:
714 move_from_unaligned32(value, bp); 734 move_from_unaligned32(u.uval32, bp);
735 value = u.uval32;
715 break; 736 break;
716 /* case 8: no users yet */ 737 /* case 8: no users yet */
717 } 738 }
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 76d29d5eb..9247588d9 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -530,6 +530,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
530 * "fake" short options, like this one: 530 * "fake" short options, like this one:
531 * wget $'-\203' "Test: test" http://kernel.org/ 531 * wget $'-\203' "Test: test" http://kernel.org/
532 * (supposed to act as --header, but doesn't) */ 532 * (supposed to act as --header, but doesn't) */
533 next_opt:
533#if ENABLE_LONG_OPTS 534#if ENABLE_LONG_OPTS
534 while ((c = getopt_long(argc, argv, applet_opts, 535 while ((c = getopt_long(argc, argv, applet_opts,
535 long_options, NULL)) != -1) { 536 long_options, NULL)) != -1) {
@@ -544,8 +545,16 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
544 * but we construct long opts so that flag 545 * but we construct long opts so that flag
545 * is always NULL (see above) */ 546 * is always NULL (see above) */
546 if (on_off->opt_char == '\0' /* && c != '\0' */) { 547 if (on_off->opt_char == '\0' /* && c != '\0' */) {
547 /* c is probably '?' - "bad option" */ 548 /* We reached the end of complementary[] and did not find -c */
548 goto error; 549 if (c == '?') /* getopt says: "bad option, or option has no required argument" */
550 goto error;
551 /* if there were options beyond 32 bits (example: ls),
552 * they got no complementary[] slot, and no result bit.
553 * IOW: they must be "accept but ignore" options.
554 * For them, we end up here.
555 */
556 //bb_error_msg("ignored option '%c', skipping", c);
557 goto next_opt;
549 } 558 }
550 } 559 }
551 if (flags & on_off->incongruously) 560 if (flags & on_off->incongruously)
diff --git a/libbb/hash_hmac.c b/libbb/hash_hmac.c
new file mode 100644
index 000000000..b3138029f
--- /dev/null
+++ b/libbb/hash_hmac.c
@@ -0,0 +1,154 @@
1/*
2 * Copyright (C) 2025 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6//kbuild:lib-$(CONFIG_TLS) += hash_hmac.o
7//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += hash_hmac.o
8
9#include "libbb.h"
10
11// RFC 2104:
12// HMAC(key, text) based on a hash H (say, sha256) is:
13// ipad = [0x36 x INSIZE]
14// opad = [0x5c x INSIZE]
15// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
16//
17// H(key XOR opad) and H(key XOR ipad) can be precomputed
18// if we often need HMAC hmac with the same key.
19//
20// text is often given in disjoint pieces.
21#if !ENABLE_FEATURE_USE_CNG_API
22void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
23{
24#if HMAC_ONLY_SHA256
25#define begin sha256_begin
26#endif
27 uint8_t key_xor_ipad[SHA2_INSIZE];
28 uint8_t key_xor_opad[SHA2_INSIZE];
29 unsigned i;
30
31 // "The authentication key can be of any length up to INSIZE, the
32 // block length of the hash function. Applications that use keys longer
33 // than INSIZE bytes will first hash the key using H and then use the
34 // resultant OUTSIZE byte string as the actual key to HMAC."
35 if (key_size > SHA2_INSIZE) {
36 uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
37 /* use ctx->hashed_key_xor_ipad as scratch ctx */
38 begin(&ctx->hashed_key_xor_ipad);
39 md5sha_hash(&ctx->hashed_key_xor_ipad, key, key_size);
40 key_size = sha_end(&ctx->hashed_key_xor_ipad, tempkey);
41 key = tempkey;
42 }
43
44 for (i = 0; i < key_size; i++) {
45 key_xor_ipad[i] = key[i] ^ 0x36;
46 key_xor_opad[i] = key[i] ^ 0x5c;
47 }
48 for (; i < SHA2_INSIZE; i++) {
49 key_xor_ipad[i] = 0x36;
50 key_xor_opad[i] = 0x5c;
51 }
52
53 begin(&ctx->hashed_key_xor_ipad);
54 begin(&ctx->hashed_key_xor_opad);
55 md5sha_hash(&ctx->hashed_key_xor_ipad, key_xor_ipad, SHA2_INSIZE);
56 md5sha_hash(&ctx->hashed_key_xor_opad, key_xor_opad, SHA2_INSIZE);
57}
58#undef begin
59
60unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
61{
62 unsigned len = sha_end(&ctx->hashed_key_xor_ipad, out);
63 /* out = H((key XOR opad) + out) */
64 md5sha_hash(&ctx->hashed_key_xor_opad, out, len);
65 return sha_end(&ctx->hashed_key_xor_opad, out);
66}
67
68unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, md5sha_begin_func *begin, const void *in, unsigned sz, uint8_t *out)
69{
70 hmac_ctx_t ctx;
71 hmac_begin(&ctx, key, key_size, begin);
72 hmac_hash(&ctx, in, sz);
73 return hmac_end(&ctx, out);
74}
75
76/* TLS helpers */
77
78void FAST_FUNC hmac_hash_v(
79 hmac_ctx_t *ctx,
80 va_list va)
81{
82 uint8_t *in;
83
84 /* ctx->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
85 /* ctx->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
86
87 /* calculate out = H((key XOR ipad) + text) */
88 while ((in = va_arg(va, uint8_t*)) != NULL) {
89 unsigned size = va_arg(va, unsigned);
90 md5sha_hash(&ctx->hashed_key_xor_ipad, in, size);
91 }
92}
93#else
94void _hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size,
95 BCRYPT_ALG_HANDLE alg_handle) {
96 DWORD hash_object_length = 0;
97 ULONG _unused;
98 NTSTATUS status;
99
100 status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH,
101 (PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0);
102 mingw_die_if_error(status, "BCryptGetProperty");
103 status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH,
104 (PUCHAR)&ctx->output_size, sizeof(DWORD), &_unused, 0);
105 mingw_die_if_error(status, "BCryptGetProperty");
106
107 ctx->hash_obj = xmalloc(hash_object_length);
108
109 status = BCryptCreateHash(alg_handle, &ctx->handle, ctx->hash_obj,
110 hash_object_length, key, key_size, BCRYPT_HASH_REUSABLE_FLAG);
111 mingw_die_if_error(status, "BCryptCreateHash");
112}
113
114unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
115{
116 NTSTATUS status;
117
118 status = BCryptFinishHash(ctx->handle, out, ctx->output_size, 0);
119 mingw_die_if_error(status, "BCryptFinishHash");
120
121 return ctx->output_size;
122}
123
124void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va)
125{
126 uint8_t *in;
127
128 while ((in = va_arg(va, uint8_t*)) != NULL) {
129 unsigned size = va_arg(va, unsigned);
130 BCryptHashData(ctx->handle, in, size, 0);
131 }
132}
133
134void hmac_uninit(hmac_ctx_t *ctx) {
135 BCryptDestroyHash(ctx->handle);
136 free(ctx->hash_obj);
137}
138#endif
139
140/* Using HMAC state, make a copy of it (IOW: without affecting this state!)
141 * hash in the list of (ptr,size) blocks, and finish the HMAC to out[] buffer.
142 * This function is useful for TLS PRF.
143 */
144unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...)
145{
146 hmac_ctx_t tmpctx = *ctx; /* struct copy */
147 va_list va;
148
149 va_start(va, out);
150 hmac_hash_v(&tmpctx, va);
151 va_end(va);
152
153 return hmac_end(&tmpctx, out);
154}
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 75a61c32c..fd56d831b 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -11,7 +11,93 @@
11#define STR1(s) #s 11#define STR1(s) #s
12#define STR(s) STR1(s) 12#define STR(s) STR1(s)
13 13
14#define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_USE_BB_CRYPT_SHA) 14#define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_SHA384SUM || ENABLE_USE_BB_CRYPT_SHA)
15
16#if ENABLE_FEATURE_USE_CNG_API
17# include <windows.h>
18# include <bcrypt.h>
19
20// these work on Windows >= 10
21# define BCRYPT_MD5_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000021)
22# define BCRYPT_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000031)
23# define BCRYPT_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000041)
24# define BCRYPT_SHA384_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000051)
25# define BCRYPT_SHA512_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000061)
26
27/* Initialize structure containing state of computation.
28 * (RFC 1321, 3.3: Step 3)
29 */
30
31static void generic_init(struct bcrypt_hash_ctx_t *ctx, BCRYPT_ALG_HANDLE alg_handle) {
32 DWORD hash_object_length = 0;
33 ULONG _unused;
34 NTSTATUS status;
35
36 status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0);
37 mingw_die_if_error(status, "BCryptGetProperty");
38 status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, (PUCHAR)&ctx->output_size, sizeof(DWORD), &_unused, 0);
39 mingw_die_if_error(status, "BCryptGetProperty");
40
41
42 ctx->hash_obj = xmalloc(hash_object_length);
43
44 status = BCryptCreateHash(alg_handle, &ctx->handle, ctx->hash_obj, hash_object_length, NULL, 0, 0);
45 mingw_die_if_error(status, "BCryptCreateHash");
46}
47
48void FAST_FUNC md5_begin(md5_ctx_t *ctx)
49{
50 generic_init(ctx, BCRYPT_MD5_ALG_HANDLE);
51}
52
53void FAST_FUNC sha1_begin(sha1_ctx_t *ctx)
54{
55 generic_init(ctx, BCRYPT_SHA1_ALG_HANDLE);
56}
57
58/* Initialize structure containing state of computation.
59 (FIPS 180-2:5.3.2) */
60void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
61{
62 generic_init(ctx, BCRYPT_SHA256_ALG_HANDLE);
63}
64
65#if ENABLE_SHA384SUM
66/* Initialize structure containing state of computation.
67 (FIPS 180-2:5.3.3) */
68void FAST_FUNC sha384_begin(sha384_ctx_t *ctx)
69{
70 generic_init(ctx, BCRYPT_SHA384_ALG_HANDLE);
71}
72#endif /* ENABLE_SHA384SUM */
73
74#if NEED_SHA512
75/* Initialize structure containing state of computation.
76 (FIPS 180-2:5.3.4) */
77void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
78{
79 generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE);
80}
81#endif /* NEED_SHA512 */
82
83void FAST_FUNC generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len)
84{
85 /*
86 for perf, no error checking here
87 */
88 /*NTSTATUS status = */ BCryptHashData(ctx->handle, (const PUCHAR)buffer, len, 0);
89 // mingw_die_if_error(status, "BCryptHashData");
90}
91
92unsigned FAST_FUNC generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf)
93{
94 NTSTATUS status = BCryptFinishHash(ctx->handle, resbuf, ctx->output_size, 0);
95 mingw_die_if_error(status, "BCryptFinishHash");
96 BCryptDestroyHash(ctx->handle);
97 free(ctx->hash_obj);
98 return ctx->output_size;
99}
100#endif /* !ENABLE_FEATURE_USE_CNG_API */
15 101
16#if ENABLE_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL 102#if ENABLE_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL
17# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 103# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
@@ -80,6 +166,7 @@ static ALWAYS_INLINE uint64_t rotl64(uint64_t x, unsigned n)
80 return (x << n) | (x >> (64 - n)); 166 return (x << n) | (x >> (64 - n));
81} 167}
82 168
169#if !ENABLE_FEATURE_USE_CNG_API
83/* Process the remaining bytes in the buffer */ 170/* Process the remaining bytes in the buffer */
84static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed) 171static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed)
85{ 172{
@@ -1032,7 +1119,7 @@ static const sha_K_int sha_K[] ALIGN8 = {
1032 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL), 1119 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL),
1033 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL), 1120 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL),
1034 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL), 1121 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL),
1035#if NEED_SHA512 /* [64]+ are used for sha512 only */ 1122#if NEED_SHA512 /* [64]+ are used for sha384 and sha512 only */
1036 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL), 1123 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL),
1037 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL), 1124 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL),
1038 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL), 1125 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL),
@@ -1229,11 +1316,20 @@ static const uint32_t init512_lo[] ALIGN4 = {
1229 0x137e2179, 1316 0x137e2179,
1230}; 1317};
1231#endif /* NEED_SHA512 */ 1318#endif /* NEED_SHA512 */
1232 1319#if ENABLE_SHA384SUM
1233// Note: SHA-384 is identical to SHA-512, except that initial hash values are 1320static const uint64_t init384[] ALIGN8 = {
1234// 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 1321 0,
1235// 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, 1322 0,
1236// and the output is constructed by omitting last two 64-bit words of it. 1323 0xcbbb9d5dc1059ed8,
1324 0x629a292a367cd507,
1325 0x9159015a3070dd17,
1326 0x152fecd8f70e5939,
1327 0x67332667ffc00b31,
1328 0x8eb44a8768581511,
1329 0xdb0c2e0d64f98fa7,
1330 0x47b5481dbefa4fa4,
1331};
1332#endif
1237 1333
1238/* Initialize structure containing state of computation. 1334/* Initialize structure containing state of computation.
1239 (FIPS 180-2:5.3.2) */ 1335 (FIPS 180-2:5.3.2) */
@@ -1255,9 +1351,19 @@ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
1255#endif 1351#endif
1256} 1352}
1257 1353
1258#if NEED_SHA512 1354#if ENABLE_SHA384SUM
1259/* Initialize structure containing state of computation. 1355/* Initialize structure containing state of computation.
1260 (FIPS 180-2:5.3.3) */ 1356 (FIPS 180-2:5.3.3) */
1357void FAST_FUNC sha384_begin(sha512_ctx_t *ctx)
1358{
1359 memcpy(&ctx->total64, init384, sizeof(init384));
1360 /*ctx->total64[0] = ctx->total64[1] = 0; - already done */
1361}
1362#endif
1363
1364#if NEED_SHA512
1365/* Initialize structure containing state of computation.
1366 (FIPS 180-2:5.3.4) */
1261void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) 1367void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
1262{ 1368{
1263 int i; 1369 int i;
@@ -1332,7 +1438,7 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf)
1332} 1438}
1333 1439
1334#if NEED_SHA512 1440#if NEED_SHA512
1335unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) 1441static unsigned FAST_FUNC sha512384_end(sha512_ctx_t *ctx, void *resbuf, unsigned outsize)
1336{ 1442{
1337 unsigned bufpos = ctx->total64[0] & 127; 1443 unsigned bufpos = ctx->total64[0] & 127;
1338 1444
@@ -1363,11 +1469,22 @@ unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
1363 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i) 1469 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i)
1364 ctx->hash[i] = SWAP_BE64(ctx->hash[i]); 1470 ctx->hash[i] = SWAP_BE64(ctx->hash[i]);
1365 } 1471 }
1366 memcpy(resbuf, ctx->hash, sizeof(ctx->hash)); 1472 memcpy(resbuf, ctx->hash, outsize);
1367 return sizeof(ctx->hash); 1473 return outsize;
1474}
1475unsigned FAST_FUNC sha512_end(sha384_ctx_t *ctx, void *resbuf)
1476{
1477 return sha512384_end(ctx, resbuf, SHA512_OUTSIZE);
1368} 1478}
1369#endif /* NEED_SHA512 */ 1479#endif /* NEED_SHA512 */
1370 1480
1481#if ENABLE_SHA384SUM
1482unsigned FAST_FUNC sha384_end(sha384_ctx_t *ctx, void *resbuf)
1483{
1484 return sha512384_end(ctx, resbuf, SHA384_OUTSIZE);
1485}
1486#endif
1487#endif /* !ENABLE_FEATURE_USE_CNG_API */
1371 1488
1372/* 1489/*
1373 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, 1490 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
@@ -1904,6 +2021,8 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1904 2021
1905unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) 2022unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1906{ 2023{
2024 unsigned hash_len;
2025
1907 /* Padding */ 2026 /* Padding */
1908 uint8_t *buf = (uint8_t*)ctx->state; 2027 uint8_t *buf = (uint8_t*)ctx->state;
1909 /* 2028 /*
@@ -1926,6 +2045,7 @@ unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1926 sha3_process_block72(ctx->state); 2045 sha3_process_block72(ctx->state);
1927 2046
1928 /* Output */ 2047 /* Output */
1929 memcpy(resbuf, ctx->state, 64); 2048 hash_len = (1600/8 - ctx->input_block_bytes) / 2;
1930 return 64; 2049 memcpy(resbuf, ctx->state, hash_len);
2050 return hash_len;
1931} 2051}
diff --git a/libbb/hash_sha256_block.c b/libbb/hash_sha256_block.c
new file mode 100644
index 000000000..3c4366321
--- /dev/null
+++ b/libbb/hash_sha256_block.c
@@ -0,0 +1,19 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2025 Denys Vlasenko
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9//kbuild:lib-y += hash_sha256_block.o
10#include "libbb.h"
11
12void FAST_FUNC
13sha256_block(const void *in, size_t len, uint8_t hash[32])
14{
15 sha256_ctx_t ctx;
16 sha256_begin(&ctx);
17 sha256_hash(&ctx, in, len);
18 sha256_end(&ctx, hash);
19}
diff --git a/libbb/hash_sha256_hwaccel_x86-32.S b/libbb/hash_sha256_hwaccel_x86-32.S
index a0e4a571a..8d84055e8 100644
--- a/libbb/hash_sha256_hwaccel_x86-32.S
+++ b/libbb/hash_sha256_hwaccel_x86-32.S
@@ -34,21 +34,21 @@
34#define MSG %xmm0 34#define MSG %xmm0
35#define STATE0 %xmm1 35#define STATE0 %xmm1
36#define STATE1 %xmm2 36#define STATE1 %xmm2
37#define MSGTMP0 %xmm3 37#define MSG0 %xmm3
38#define MSGTMP1 %xmm4 38#define MSG1 %xmm4
39#define MSGTMP2 %xmm5 39#define MSG2 %xmm5
40#define MSGTMP3 %xmm6 40#define MSG3 %xmm6
41 41
42#define XMMTMP %xmm7 42#define XMMTMP %xmm7
43 43
44#define SHUF(a,b,c,d) $(a+(b<<2)+(c<<4)+(d<<6)) 44#define SHUF(a,b,c,d) $((a)+((b)<<2)+((c)<<4)+((d)<<6))
45 45
46 .balign 8 # allow decoders to fetch at least 2 first insns 46 .balign 8 # allow decoders to fetch at least 2 first insns
47sha256_process_block64_shaNI: 47sha256_process_block64_shaNI:
48 48
49 movu128 76+0*16(%eax), XMMTMP /* ABCD (little-endian dword order) */ 49 movu128 76+0*16(%eax), XMMTMP /* ABCD (shown least-significant-dword-first) */
50 movu128 76+1*16(%eax), STATE1 /* EFGH */ 50 movu128 76+1*16(%eax), STATE1 /* EFGH */
51/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 51/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
52 mova128 STATE1, STATE0 52 mova128 STATE1, STATE0
53 /* --- -------------- ABCD -- EFGH */ 53 /* --- -------------- ABCD -- EFGH */
54 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */ 54 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */
@@ -58,190 +58,208 @@ sha256_process_block64_shaNI:
58 mova128 PSHUFFLE_BSWAP32_FLIP_MASK, XMMTMP 58 mova128 PSHUFFLE_BSWAP32_FLIP_MASK, XMMTMP
59 movl $K256+8*16, SHA256CONSTANTS 59 movl $K256+8*16, SHA256CONSTANTS
60 60
61// sha256rnds2 instruction uses only lower 64 bits of MSG.
62// The code below needs to move upper 64 bits to lower 64 bits
63// for the second sha256rnds2 invocation
64// (what remains in upper bits does not matter).
65// There are several ways to do it:
66// movhlps MSG, MSG // abcd -> cdcd (3 bytes of code)
67// shuf128_32 SHUF(2,3,n,n), MSG, MSG // abcd -> cdXX (4 bytes)
68// punpckhqdq MSG, MSG // abcd -> cdcd (4 bytes)
69// unpckhpd MSG, MSG // abcd -> cdcd (4 bytes)
70// psrldq $8, MSG // abcd -> cd00 (5 bytes)
71// palignr $8, MSG, MSG // abcd -> cdab (6 bytes, SSSE3 insn)
72#define MOVE_UPPER64_DOWN(reg) movhlps reg, reg
73//#define MOVE_UPPER64_DOWN(reg) shuf128_32 SHUF(2,3,0,0), reg, reg
74//#define MOVE_UPPER64_DOWN(reg) punpckhqdq reg, reg
75//#define MOVE_UPPER64_DOWN(reg) unpckhpd reg, reg
76//#define MOVE_UPPER64_DOWN(reg) psrldq $8, reg
77//#define MOVE_UPPER64_DOWN(reg) palignr $8, reg, reg
78
61 /* Rounds 0-3 */ 79 /* Rounds 0-3 */
62 movu128 0*16(DATA_PTR), MSG 80 movu128 0*16(DATA_PTR), MSG
63 pshufb XMMTMP, MSG 81 pshufb XMMTMP, MSG
64 mova128 MSG, MSGTMP0 82 mova128 MSG, MSG0
65 paddd 0*16-8*16(SHA256CONSTANTS), MSG 83 paddd 0*16-8*16(SHA256CONSTANTS), MSG
66 sha256rnds2 MSG, STATE0, STATE1 84 sha256rnds2 MSG, STATE0, STATE1
67 shuf128_32 $0x0E, MSG, MSG 85 MOVE_UPPER64_DOWN(MSG)
68 sha256rnds2 MSG, STATE1, STATE0 86 sha256rnds2 MSG, STATE1, STATE0
69 87
70 /* Rounds 4-7 */ 88 /* Rounds 4-7 */
71 movu128 1*16(DATA_PTR), MSG 89 movu128 1*16(DATA_PTR), MSG
72 pshufb XMMTMP, MSG 90 pshufb XMMTMP, MSG
73 mova128 MSG, MSGTMP1 91 mova128 MSG, MSG1
74 paddd 1*16-8*16(SHA256CONSTANTS), MSG 92 paddd 1*16-8*16(SHA256CONSTANTS), MSG
75 sha256rnds2 MSG, STATE0, STATE1 93 sha256rnds2 MSG, STATE0, STATE1
76 shuf128_32 $0x0E, MSG, MSG 94 MOVE_UPPER64_DOWN(MSG)
77 sha256rnds2 MSG, STATE1, STATE0 95 sha256rnds2 MSG, STATE1, STATE0
78 sha256msg1 MSGTMP1, MSGTMP0 96 sha256msg1 MSG1, MSG0
79 97
80 /* Rounds 8-11 */ 98 /* Rounds 8-11 */
81 movu128 2*16(DATA_PTR), MSG 99 movu128 2*16(DATA_PTR), MSG
82 pshufb XMMTMP, MSG 100 pshufb XMMTMP, MSG
83 mova128 MSG, MSGTMP2 101 mova128 MSG, MSG2
84 paddd 2*16-8*16(SHA256CONSTANTS), MSG 102 paddd 2*16-8*16(SHA256CONSTANTS), MSG
85 sha256rnds2 MSG, STATE0, STATE1 103 sha256rnds2 MSG, STATE0, STATE1
86 shuf128_32 $0x0E, MSG, MSG 104 MOVE_UPPER64_DOWN(MSG)
87 sha256rnds2 MSG, STATE1, STATE0 105 sha256rnds2 MSG, STATE1, STATE0
88 sha256msg1 MSGTMP2, MSGTMP1 106 sha256msg1 MSG2, MSG1
89 107
90 /* Rounds 12-15 */ 108 /* Rounds 12-15 */
91 movu128 3*16(DATA_PTR), MSG 109 movu128 3*16(DATA_PTR), MSG
92 pshufb XMMTMP, MSG 110 pshufb XMMTMP, MSG
93/* ...to here */ 111/* ...to here */
94 mova128 MSG, MSGTMP3 112 mova128 MSG, MSG3
95 paddd 3*16-8*16(SHA256CONSTANTS), MSG 113 paddd 3*16-8*16(SHA256CONSTANTS), MSG
96 sha256rnds2 MSG, STATE0, STATE1 114 sha256rnds2 MSG, STATE0, STATE1
97 mova128 MSGTMP3, XMMTMP 115 mova128 MSG3, XMMTMP
98 palignr $4, MSGTMP2, XMMTMP 116 palignr $4, MSG2, XMMTMP
99 paddd XMMTMP, MSGTMP0 117 paddd XMMTMP, MSG0
100 sha256msg2 MSGTMP3, MSGTMP0 118 sha256msg2 MSG3, MSG0
101 shuf128_32 $0x0E, MSG, MSG 119 MOVE_UPPER64_DOWN(MSG)
102 sha256rnds2 MSG, STATE1, STATE0 120 sha256rnds2 MSG, STATE1, STATE0
103 sha256msg1 MSGTMP3, MSGTMP2 121 sha256msg1 MSG3, MSG2
104 122
105 /* Rounds 16-19 */ 123 /* Rounds 16-19 */
106 mova128 MSGTMP0, MSG 124 mova128 MSG0, MSG
107 paddd 4*16-8*16(SHA256CONSTANTS), MSG 125 paddd 4*16-8*16(SHA256CONSTANTS), MSG
108 sha256rnds2 MSG, STATE0, STATE1 126 sha256rnds2 MSG, STATE0, STATE1
109 mova128 MSGTMP0, XMMTMP 127 mova128 MSG0, XMMTMP
110 palignr $4, MSGTMP3, XMMTMP 128 palignr $4, MSG3, XMMTMP
111 paddd XMMTMP, MSGTMP1 129 paddd XMMTMP, MSG1
112 sha256msg2 MSGTMP0, MSGTMP1 130 sha256msg2 MSG0, MSG1
113 shuf128_32 $0x0E, MSG, MSG 131 MOVE_UPPER64_DOWN(MSG)
114 sha256rnds2 MSG, STATE1, STATE0 132 sha256rnds2 MSG, STATE1, STATE0
115 sha256msg1 MSGTMP0, MSGTMP3 133 sha256msg1 MSG0, MSG3
116 134
117 /* Rounds 20-23 */ 135 /* Rounds 20-23 */
118 mova128 MSGTMP1, MSG 136 mova128 MSG1, MSG
119 paddd 5*16-8*16(SHA256CONSTANTS), MSG 137 paddd 5*16-8*16(SHA256CONSTANTS), MSG
120 sha256rnds2 MSG, STATE0, STATE1 138 sha256rnds2 MSG, STATE0, STATE1
121 mova128 MSGTMP1, XMMTMP 139 mova128 MSG1, XMMTMP
122 palignr $4, MSGTMP0, XMMTMP 140 palignr $4, MSG0, XMMTMP
123 paddd XMMTMP, MSGTMP2 141 paddd XMMTMP, MSG2
124 sha256msg2 MSGTMP1, MSGTMP2 142 sha256msg2 MSG1, MSG2
125 shuf128_32 $0x0E, MSG, MSG 143 MOVE_UPPER64_DOWN(MSG)
126 sha256rnds2 MSG, STATE1, STATE0 144 sha256rnds2 MSG, STATE1, STATE0
127 sha256msg1 MSGTMP1, MSGTMP0 145 sha256msg1 MSG1, MSG0
128 146
129 /* Rounds 24-27 */ 147 /* Rounds 24-27 */
130 mova128 MSGTMP2, MSG 148 mova128 MSG2, MSG
131 paddd 6*16-8*16(SHA256CONSTANTS), MSG 149 paddd 6*16-8*16(SHA256CONSTANTS), MSG
132 sha256rnds2 MSG, STATE0, STATE1 150 sha256rnds2 MSG, STATE0, STATE1
133 mova128 MSGTMP2, XMMTMP 151 mova128 MSG2, XMMTMP
134 palignr $4, MSGTMP1, XMMTMP 152 palignr $4, MSG1, XMMTMP
135 paddd XMMTMP, MSGTMP3 153 paddd XMMTMP, MSG3
136 sha256msg2 MSGTMP2, MSGTMP3 154 sha256msg2 MSG2, MSG3
137 shuf128_32 $0x0E, MSG, MSG 155 MOVE_UPPER64_DOWN(MSG)
138 sha256rnds2 MSG, STATE1, STATE0 156 sha256rnds2 MSG, STATE1, STATE0
139 sha256msg1 MSGTMP2, MSGTMP1 157 sha256msg1 MSG2, MSG1
140 158
141 /* Rounds 28-31 */ 159 /* Rounds 28-31 */
142 mova128 MSGTMP3, MSG 160 mova128 MSG3, MSG
143 paddd 7*16-8*16(SHA256CONSTANTS), MSG 161 paddd 7*16-8*16(SHA256CONSTANTS), MSG
144 sha256rnds2 MSG, STATE0, STATE1 162 sha256rnds2 MSG, STATE0, STATE1
145 mova128 MSGTMP3, XMMTMP 163 mova128 MSG3, XMMTMP
146 palignr $4, MSGTMP2, XMMTMP 164 palignr $4, MSG2, XMMTMP
147 paddd XMMTMP, MSGTMP0 165 paddd XMMTMP, MSG0
148 sha256msg2 MSGTMP3, MSGTMP0 166 sha256msg2 MSG3, MSG0
149 shuf128_32 $0x0E, MSG, MSG 167 MOVE_UPPER64_DOWN(MSG)
150 sha256rnds2 MSG, STATE1, STATE0 168 sha256rnds2 MSG, STATE1, STATE0
151 sha256msg1 MSGTMP3, MSGTMP2 169 sha256msg1 MSG3, MSG2
152 170
153 /* Rounds 32-35 */ 171 /* Rounds 32-35 */
154 mova128 MSGTMP0, MSG 172 mova128 MSG0, MSG
155 paddd 8*16-8*16(SHA256CONSTANTS), MSG 173 paddd 8*16-8*16(SHA256CONSTANTS), MSG
156 sha256rnds2 MSG, STATE0, STATE1 174 sha256rnds2 MSG, STATE0, STATE1
157 mova128 MSGTMP0, XMMTMP 175 mova128 MSG0, XMMTMP
158 palignr $4, MSGTMP3, XMMTMP 176 palignr $4, MSG3, XMMTMP
159 paddd XMMTMP, MSGTMP1 177 paddd XMMTMP, MSG1
160 sha256msg2 MSGTMP0, MSGTMP1 178 sha256msg2 MSG0, MSG1
161 shuf128_32 $0x0E, MSG, MSG 179 MOVE_UPPER64_DOWN(MSG)
162 sha256rnds2 MSG, STATE1, STATE0 180 sha256rnds2 MSG, STATE1, STATE0
163 sha256msg1 MSGTMP0, MSGTMP3 181 sha256msg1 MSG0, MSG3
164 182
165 /* Rounds 36-39 */ 183 /* Rounds 36-39 */
166 mova128 MSGTMP1, MSG 184 mova128 MSG1, MSG
167 paddd 9*16-8*16(SHA256CONSTANTS), MSG 185 paddd 9*16-8*16(SHA256CONSTANTS), MSG
168 sha256rnds2 MSG, STATE0, STATE1 186 sha256rnds2 MSG, STATE0, STATE1
169 mova128 MSGTMP1, XMMTMP 187 mova128 MSG1, XMMTMP
170 palignr $4, MSGTMP0, XMMTMP 188 palignr $4, MSG0, XMMTMP
171 paddd XMMTMP, MSGTMP2 189 paddd XMMTMP, MSG2
172 sha256msg2 MSGTMP1, MSGTMP2 190 sha256msg2 MSG1, MSG2
173 shuf128_32 $0x0E, MSG, MSG 191 MOVE_UPPER64_DOWN(MSG)
174 sha256rnds2 MSG, STATE1, STATE0 192 sha256rnds2 MSG, STATE1, STATE0
175 sha256msg1 MSGTMP1, MSGTMP0 193 sha256msg1 MSG1, MSG0
176 194
177 /* Rounds 40-43 */ 195 /* Rounds 40-43 */
178 mova128 MSGTMP2, MSG 196 mova128 MSG2, MSG
179 paddd 10*16-8*16(SHA256CONSTANTS), MSG 197 paddd 10*16-8*16(SHA256CONSTANTS), MSG
180 sha256rnds2 MSG, STATE0, STATE1 198 sha256rnds2 MSG, STATE0, STATE1
181 mova128 MSGTMP2, XMMTMP 199 mova128 MSG2, XMMTMP
182 palignr $4, MSGTMP1, XMMTMP 200 palignr $4, MSG1, XMMTMP
183 paddd XMMTMP, MSGTMP3 201 paddd XMMTMP, MSG3
184 sha256msg2 MSGTMP2, MSGTMP3 202 sha256msg2 MSG2, MSG3
185 shuf128_32 $0x0E, MSG, MSG 203 MOVE_UPPER64_DOWN(MSG)
186 sha256rnds2 MSG, STATE1, STATE0 204 sha256rnds2 MSG, STATE1, STATE0
187 sha256msg1 MSGTMP2, MSGTMP1 205 sha256msg1 MSG2, MSG1
188 206
189 /* Rounds 44-47 */ 207 /* Rounds 44-47 */
190 mova128 MSGTMP3, MSG 208 mova128 MSG3, MSG
191 paddd 11*16-8*16(SHA256CONSTANTS), MSG 209 paddd 11*16-8*16(SHA256CONSTANTS), MSG
192 sha256rnds2 MSG, STATE0, STATE1 210 sha256rnds2 MSG, STATE0, STATE1
193 mova128 MSGTMP3, XMMTMP 211 mova128 MSG3, XMMTMP
194 palignr $4, MSGTMP2, XMMTMP 212 palignr $4, MSG2, XMMTMP
195 paddd XMMTMP, MSGTMP0 213 paddd XMMTMP, MSG0
196 sha256msg2 MSGTMP3, MSGTMP0 214 sha256msg2 MSG3, MSG0
197 shuf128_32 $0x0E, MSG, MSG 215 MOVE_UPPER64_DOWN(MSG)
198 sha256rnds2 MSG, STATE1, STATE0 216 sha256rnds2 MSG, STATE1, STATE0
199 sha256msg1 MSGTMP3, MSGTMP2 217 sha256msg1 MSG3, MSG2
200 218
201 /* Rounds 48-51 */ 219 /* Rounds 48-51 */
202 mova128 MSGTMP0, MSG 220 mova128 MSG0, MSG
203 paddd 12*16-8*16(SHA256CONSTANTS), MSG 221 paddd 12*16-8*16(SHA256CONSTANTS), MSG
204 sha256rnds2 MSG, STATE0, STATE1 222 sha256rnds2 MSG, STATE0, STATE1
205 mova128 MSGTMP0, XMMTMP 223 mova128 MSG0, XMMTMP
206 palignr $4, MSGTMP3, XMMTMP 224 palignr $4, MSG3, XMMTMP
207 paddd XMMTMP, MSGTMP1 225 paddd XMMTMP, MSG1
208 sha256msg2 MSGTMP0, MSGTMP1 226 sha256msg2 MSG0, MSG1
209 shuf128_32 $0x0E, MSG, MSG 227 MOVE_UPPER64_DOWN(MSG)
210 sha256rnds2 MSG, STATE1, STATE0 228 sha256rnds2 MSG, STATE1, STATE0
211 sha256msg1 MSGTMP0, MSGTMP3 229 sha256msg1 MSG0, MSG3
212 230
213 /* Rounds 52-55 */ 231 /* Rounds 52-55 */
214 mova128 MSGTMP1, MSG 232 mova128 MSG1, MSG
215 paddd 13*16-8*16(SHA256CONSTANTS), MSG 233 paddd 13*16-8*16(SHA256CONSTANTS), MSG
216 sha256rnds2 MSG, STATE0, STATE1 234 sha256rnds2 MSG, STATE0, STATE1
217 mova128 MSGTMP1, XMMTMP 235 mova128 MSG1, XMMTMP
218 palignr $4, MSGTMP0, XMMTMP 236 palignr $4, MSG0, XMMTMP
219 paddd XMMTMP, MSGTMP2 237 paddd XMMTMP, MSG2
220 sha256msg2 MSGTMP1, MSGTMP2 238 sha256msg2 MSG1, MSG2
221 shuf128_32 $0x0E, MSG, MSG 239 MOVE_UPPER64_DOWN(MSG)
222 sha256rnds2 MSG, STATE1, STATE0 240 sha256rnds2 MSG, STATE1, STATE0
223 241
224 /* Rounds 56-59 */ 242 /* Rounds 56-59 */
225 mova128 MSGTMP2, MSG 243 mova128 MSG2, MSG
226 paddd 14*16-8*16(SHA256CONSTANTS), MSG 244 paddd 14*16-8*16(SHA256CONSTANTS), MSG
227 sha256rnds2 MSG, STATE0, STATE1 245 sha256rnds2 MSG, STATE0, STATE1
228 mova128 MSGTMP2, XMMTMP 246 mova128 MSG2, XMMTMP
229 palignr $4, MSGTMP1, XMMTMP 247 palignr $4, MSG1, XMMTMP
230 paddd XMMTMP, MSGTMP3 248 paddd XMMTMP, MSG3
231 sha256msg2 MSGTMP2, MSGTMP3 249 sha256msg2 MSG2, MSG3
232 shuf128_32 $0x0E, MSG, MSG 250 MOVE_UPPER64_DOWN(MSG)
233 sha256rnds2 MSG, STATE1, STATE0 251 sha256rnds2 MSG, STATE1, STATE0
234 252
235 /* Rounds 60-63 */ 253 /* Rounds 60-63 */
236 mova128 MSGTMP3, MSG 254 mova128 MSG3, MSG
237 paddd 15*16-8*16(SHA256CONSTANTS), MSG 255 paddd 15*16-8*16(SHA256CONSTANTS), MSG
238 sha256rnds2 MSG, STATE0, STATE1 256 sha256rnds2 MSG, STATE0, STATE1
239 shuf128_32 $0x0E, MSG, MSG 257 MOVE_UPPER64_DOWN(MSG)
240 sha256rnds2 MSG, STATE1, STATE0 258 sha256rnds2 MSG, STATE1, STATE0
241 259
242 /* Write hash values back in the correct order */ 260 /* Write hash values back in the correct order */
243 mova128 STATE0, XMMTMP 261 mova128 STATE0, XMMTMP
244/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 262/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
245 /* --- -------------- HGDC -- FEBA */ 263 /* --- -------------- HGDC -- FEBA */
246 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */ 264 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */
247 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */ 265 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */
diff --git a/libbb/hash_sha256_hwaccel_x86-64.S b/libbb/hash_sha256_hwaccel_x86-64.S
index 172c2eae2..ee3abbd1f 100644
--- a/libbb/hash_sha256_hwaccel_x86-64.S
+++ b/libbb/hash_sha256_hwaccel_x86-64.S
@@ -34,24 +34,24 @@
34#define MSG %xmm0 34#define MSG %xmm0
35#define STATE0 %xmm1 35#define STATE0 %xmm1
36#define STATE1 %xmm2 36#define STATE1 %xmm2
37#define MSGTMP0 %xmm3 37#define MSG0 %xmm3
38#define MSGTMP1 %xmm4 38#define MSG1 %xmm4
39#define MSGTMP2 %xmm5 39#define MSG2 %xmm5
40#define MSGTMP3 %xmm6 40#define MSG3 %xmm6
41 41
42#define XMMTMP %xmm7 42#define XMMTMP %xmm7
43 43
44#define SAVE0 %xmm8 44#define SAVE0 %xmm8
45#define SAVE1 %xmm9 45#define SAVE1 %xmm9
46 46
47#define SHUF(a,b,c,d) $(a+(b<<2)+(c<<4)+(d<<6)) 47#define SHUF(a,b,c,d) $((a)+((b)<<2)+((c)<<4)+((d)<<6))
48 48
49 .balign 8 # allow decoders to fetch at least 2 first insns 49 .balign 8 # allow decoders to fetch at least 2 first insns
50sha256_process_block64_shaNI: 50sha256_process_block64_shaNI:
51 51
52 movu128 80+0*16(%rdi), XMMTMP /* ABCD (little-endian dword order) */ 52 movu128 80+0*16(%rdi), XMMTMP /* ABCD (shown least-significant-dword-first) */
53 movu128 80+1*16(%rdi), STATE1 /* EFGH */ 53 movu128 80+1*16(%rdi), STATE1 /* EFGH */
54/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 54/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
55 mova128 STATE1, STATE0 55 mova128 STATE1, STATE0
56 /* --- -------------- ABCD -- EFGH */ 56 /* --- -------------- ABCD -- EFGH */
57 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */ 57 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */
@@ -65,185 +65,203 @@ sha256_process_block64_shaNI:
65 mova128 STATE0, SAVE0 65 mova128 STATE0, SAVE0
66 mova128 STATE1, SAVE1 66 mova128 STATE1, SAVE1
67 67
68// sha256rnds2 instruction uses only lower 64 bits of MSG.
69// The code below needs to move upper 64 bits to lower 64 bits
70// for the second sha256rnds2 invocation
71// (what remains in upper bits does not matter).
72// There are several ways to do it:
73// movhlps MSG, MSG // abcd -> cdcd (3 bytes of code)
74// shuf128_32 SHUF(2,3,n,n), MSG, MSG // abcd -> cdXX (4 bytes)
75// punpckhqdq MSG, MSG // abcd -> cdcd (4 bytes)
76// unpckhpd MSG, MSG // abcd -> cdcd (4 bytes)
77// psrldq $8, MSG // abcd -> cd00 (5 bytes)
78// palignr $8, MSG, MSG // abcd -> cdab (6 bytes, SSSE3 insn)
79#define MOVE_UPPER64_DOWN(reg) movhlps reg, reg
80//#define MOVE_UPPER64_DOWN(reg) shuf128_32 SHUF(2,3,0,0), reg, reg
81//#define MOVE_UPPER64_DOWN(reg) punpckhqdq reg, reg
82//#define MOVE_UPPER64_DOWN(reg) unpckhpd reg, reg
83//#define MOVE_UPPER64_DOWN(reg) psrldq $8, reg
84//#define MOVE_UPPER64_DOWN(reg) palignr $8, reg, reg
85
68 /* Rounds 0-3 */ 86 /* Rounds 0-3 */
69 movu128 0*16(DATA_PTR), MSG 87 movu128 0*16(DATA_PTR), MSG
70 pshufb XMMTMP, MSG 88 pshufb XMMTMP, MSG
71 mova128 MSG, MSGTMP0 89 mova128 MSG, MSG0
72 paddd 0*16-8*16(SHA256CONSTANTS), MSG 90 paddd 0*16-8*16(SHA256CONSTANTS), MSG
73 sha256rnds2 MSG, STATE0, STATE1 91 sha256rnds2 MSG, STATE0, STATE1
74 shuf128_32 $0x0E, MSG, MSG 92 MOVE_UPPER64_DOWN(MSG)
75 sha256rnds2 MSG, STATE1, STATE0 93 sha256rnds2 MSG, STATE1, STATE0
76 94
77 /* Rounds 4-7 */ 95 /* Rounds 4-7 */
78 movu128 1*16(DATA_PTR), MSG 96 movu128 1*16(DATA_PTR), MSG
79 pshufb XMMTMP, MSG 97 pshufb XMMTMP, MSG
80 mova128 MSG, MSGTMP1 98 mova128 MSG, MSG1
81 paddd 1*16-8*16(SHA256CONSTANTS), MSG 99 paddd 1*16-8*16(SHA256CONSTANTS), MSG
82 sha256rnds2 MSG, STATE0, STATE1 100 sha256rnds2 MSG, STATE0, STATE1
83 shuf128_32 $0x0E, MSG, MSG 101 MOVE_UPPER64_DOWN(MSG)
84 sha256rnds2 MSG, STATE1, STATE0 102 sha256rnds2 MSG, STATE1, STATE0
85 sha256msg1 MSGTMP1, MSGTMP0 103 sha256msg1 MSG1, MSG0
86 104
87 /* Rounds 8-11 */ 105 /* Rounds 8-11 */
88 movu128 2*16(DATA_PTR), MSG 106 movu128 2*16(DATA_PTR), MSG
89 pshufb XMMTMP, MSG 107 pshufb XMMTMP, MSG
90 mova128 MSG, MSGTMP2 108 mova128 MSG, MSG2
91 paddd 2*16-8*16(SHA256CONSTANTS), MSG 109 paddd 2*16-8*16(SHA256CONSTANTS), MSG
92 sha256rnds2 MSG, STATE0, STATE1 110 sha256rnds2 MSG, STATE0, STATE1
93 shuf128_32 $0x0E, MSG, MSG 111 MOVE_UPPER64_DOWN(MSG)
94 sha256rnds2 MSG, STATE1, STATE0 112 sha256rnds2 MSG, STATE1, STATE0
95 sha256msg1 MSGTMP2, MSGTMP1 113 sha256msg1 MSG2, MSG1
96 114
97 /* Rounds 12-15 */ 115 /* Rounds 12-15 */
98 movu128 3*16(DATA_PTR), MSG 116 movu128 3*16(DATA_PTR), MSG
99 pshufb XMMTMP, MSG 117 pshufb XMMTMP, MSG
100/* ...to here */ 118/* ...to here */
101 mova128 MSG, MSGTMP3 119 mova128 MSG, MSG3
102 paddd 3*16-8*16(SHA256CONSTANTS), MSG 120 paddd 3*16-8*16(SHA256CONSTANTS), MSG
103 sha256rnds2 MSG, STATE0, STATE1 121 sha256rnds2 MSG, STATE0, STATE1
104 mova128 MSGTMP3, XMMTMP 122 mova128 MSG3, XMMTMP
105 palignr $4, MSGTMP2, XMMTMP 123 palignr $4, MSG2, XMMTMP
106 paddd XMMTMP, MSGTMP0 124 paddd XMMTMP, MSG0
107 sha256msg2 MSGTMP3, MSGTMP0 125 sha256msg2 MSG3, MSG0
108 shuf128_32 $0x0E, MSG, MSG 126 MOVE_UPPER64_DOWN(MSG)
109 sha256rnds2 MSG, STATE1, STATE0 127 sha256rnds2 MSG, STATE1, STATE0
110 sha256msg1 MSGTMP3, MSGTMP2 128 sha256msg1 MSG3, MSG2
111 129
112 /* Rounds 16-19 */ 130 /* Rounds 16-19 */
113 mova128 MSGTMP0, MSG 131 mova128 MSG0, MSG
114 paddd 4*16-8*16(SHA256CONSTANTS), MSG 132 paddd 4*16-8*16(SHA256CONSTANTS), MSG
115 sha256rnds2 MSG, STATE0, STATE1 133 sha256rnds2 MSG, STATE0, STATE1
116 mova128 MSGTMP0, XMMTMP 134 mova128 MSG0, XMMTMP
117 palignr $4, MSGTMP3, XMMTMP 135 palignr $4, MSG3, XMMTMP
118 paddd XMMTMP, MSGTMP1 136 paddd XMMTMP, MSG1
119 sha256msg2 MSGTMP0, MSGTMP1 137 sha256msg2 MSG0, MSG1
120 shuf128_32 $0x0E, MSG, MSG 138 MOVE_UPPER64_DOWN(MSG)
121 sha256rnds2 MSG, STATE1, STATE0 139 sha256rnds2 MSG, STATE1, STATE0
122 sha256msg1 MSGTMP0, MSGTMP3 140 sha256msg1 MSG0, MSG3
123 141
124 /* Rounds 20-23 */ 142 /* Rounds 20-23 */
125 mova128 MSGTMP1, MSG 143 mova128 MSG1, MSG
126 paddd 5*16-8*16(SHA256CONSTANTS), MSG 144 paddd 5*16-8*16(SHA256CONSTANTS), MSG
127 sha256rnds2 MSG, STATE0, STATE1 145 sha256rnds2 MSG, STATE0, STATE1
128 mova128 MSGTMP1, XMMTMP 146 mova128 MSG1, XMMTMP
129 palignr $4, MSGTMP0, XMMTMP 147 palignr $4, MSG0, XMMTMP
130 paddd XMMTMP, MSGTMP2 148 paddd XMMTMP, MSG2
131 sha256msg2 MSGTMP1, MSGTMP2 149 sha256msg2 MSG1, MSG2
132 shuf128_32 $0x0E, MSG, MSG 150 MOVE_UPPER64_DOWN(MSG)
133 sha256rnds2 MSG, STATE1, STATE0 151 sha256rnds2 MSG, STATE1, STATE0
134 sha256msg1 MSGTMP1, MSGTMP0 152 sha256msg1 MSG1, MSG0
135 153
136 /* Rounds 24-27 */ 154 /* Rounds 24-27 */
137 mova128 MSGTMP2, MSG 155 mova128 MSG2, MSG
138 paddd 6*16-8*16(SHA256CONSTANTS), MSG 156 paddd 6*16-8*16(SHA256CONSTANTS), MSG
139 sha256rnds2 MSG, STATE0, STATE1 157 sha256rnds2 MSG, STATE0, STATE1
140 mova128 MSGTMP2, XMMTMP 158 mova128 MSG2, XMMTMP
141 palignr $4, MSGTMP1, XMMTMP 159 palignr $4, MSG1, XMMTMP
142 paddd XMMTMP, MSGTMP3 160 paddd XMMTMP, MSG3
143 sha256msg2 MSGTMP2, MSGTMP3 161 sha256msg2 MSG2, MSG3
144 shuf128_32 $0x0E, MSG, MSG 162 MOVE_UPPER64_DOWN(MSG)
145 sha256rnds2 MSG, STATE1, STATE0 163 sha256rnds2 MSG, STATE1, STATE0
146 sha256msg1 MSGTMP2, MSGTMP1 164 sha256msg1 MSG2, MSG1
147 165
148 /* Rounds 28-31 */ 166 /* Rounds 28-31 */
149 mova128 MSGTMP3, MSG 167 mova128 MSG3, MSG
150 paddd 7*16-8*16(SHA256CONSTANTS), MSG 168 paddd 7*16-8*16(SHA256CONSTANTS), MSG
151 sha256rnds2 MSG, STATE0, STATE1 169 sha256rnds2 MSG, STATE0, STATE1
152 mova128 MSGTMP3, XMMTMP 170 mova128 MSG3, XMMTMP
153 palignr $4, MSGTMP2, XMMTMP 171 palignr $4, MSG2, XMMTMP
154 paddd XMMTMP, MSGTMP0 172 paddd XMMTMP, MSG0
155 sha256msg2 MSGTMP3, MSGTMP0 173 sha256msg2 MSG3, MSG0
156 shuf128_32 $0x0E, MSG, MSG 174 MOVE_UPPER64_DOWN(MSG)
157 sha256rnds2 MSG, STATE1, STATE0 175 sha256rnds2 MSG, STATE1, STATE0
158 sha256msg1 MSGTMP3, MSGTMP2 176 sha256msg1 MSG3, MSG2
159 177
160 /* Rounds 32-35 */ 178 /* Rounds 32-35 */
161 mova128 MSGTMP0, MSG 179 mova128 MSG0, MSG
162 paddd 8*16-8*16(SHA256CONSTANTS), MSG 180 paddd 8*16-8*16(SHA256CONSTANTS), MSG
163 sha256rnds2 MSG, STATE0, STATE1 181 sha256rnds2 MSG, STATE0, STATE1
164 mova128 MSGTMP0, XMMTMP 182 mova128 MSG0, XMMTMP
165 palignr $4, MSGTMP3, XMMTMP 183 palignr $4, MSG3, XMMTMP
166 paddd XMMTMP, MSGTMP1 184 paddd XMMTMP, MSG1
167 sha256msg2 MSGTMP0, MSGTMP1 185 sha256msg2 MSG0, MSG1
168 shuf128_32 $0x0E, MSG, MSG 186 MOVE_UPPER64_DOWN(MSG)
169 sha256rnds2 MSG, STATE1, STATE0 187 sha256rnds2 MSG, STATE1, STATE0
170 sha256msg1 MSGTMP0, MSGTMP3 188 sha256msg1 MSG0, MSG3
171 189
172 /* Rounds 36-39 */ 190 /* Rounds 36-39 */
173 mova128 MSGTMP1, MSG 191 mova128 MSG1, MSG
174 paddd 9*16-8*16(SHA256CONSTANTS), MSG 192 paddd 9*16-8*16(SHA256CONSTANTS), MSG
175 sha256rnds2 MSG, STATE0, STATE1 193 sha256rnds2 MSG, STATE0, STATE1
176 mova128 MSGTMP1, XMMTMP 194 mova128 MSG1, XMMTMP
177 palignr $4, MSGTMP0, XMMTMP 195 palignr $4, MSG0, XMMTMP
178 paddd XMMTMP, MSGTMP2 196 paddd XMMTMP, MSG2
179 sha256msg2 MSGTMP1, MSGTMP2 197 sha256msg2 MSG1, MSG2
180 shuf128_32 $0x0E, MSG, MSG 198 MOVE_UPPER64_DOWN(MSG)
181 sha256rnds2 MSG, STATE1, STATE0 199 sha256rnds2 MSG, STATE1, STATE0
182 sha256msg1 MSGTMP1, MSGTMP0 200 sha256msg1 MSG1, MSG0
183 201
184 /* Rounds 40-43 */ 202 /* Rounds 40-43 */
185 mova128 MSGTMP2, MSG 203 mova128 MSG2, MSG
186 paddd 10*16-8*16(SHA256CONSTANTS), MSG 204 paddd 10*16-8*16(SHA256CONSTANTS), MSG
187 sha256rnds2 MSG, STATE0, STATE1 205 sha256rnds2 MSG, STATE0, STATE1
188 mova128 MSGTMP2, XMMTMP 206 mova128 MSG2, XMMTMP
189 palignr $4, MSGTMP1, XMMTMP 207 palignr $4, MSG1, XMMTMP
190 paddd XMMTMP, MSGTMP3 208 paddd XMMTMP, MSG3
191 sha256msg2 MSGTMP2, MSGTMP3 209 sha256msg2 MSG2, MSG3
192 shuf128_32 $0x0E, MSG, MSG 210 MOVE_UPPER64_DOWN(MSG)
193 sha256rnds2 MSG, STATE1, STATE0 211 sha256rnds2 MSG, STATE1, STATE0
194 sha256msg1 MSGTMP2, MSGTMP1 212 sha256msg1 MSG2, MSG1
195 213
196 /* Rounds 44-47 */ 214 /* Rounds 44-47 */
197 mova128 MSGTMP3, MSG 215 mova128 MSG3, MSG
198 paddd 11*16-8*16(SHA256CONSTANTS), MSG 216 paddd 11*16-8*16(SHA256CONSTANTS), MSG
199 sha256rnds2 MSG, STATE0, STATE1 217 sha256rnds2 MSG, STATE0, STATE1
200 mova128 MSGTMP3, XMMTMP 218 mova128 MSG3, XMMTMP
201 palignr $4, MSGTMP2, XMMTMP 219 palignr $4, MSG2, XMMTMP
202 paddd XMMTMP, MSGTMP0 220 paddd XMMTMP, MSG0
203 sha256msg2 MSGTMP3, MSGTMP0 221 sha256msg2 MSG3, MSG0
204 shuf128_32 $0x0E, MSG, MSG 222 MOVE_UPPER64_DOWN(MSG)
205 sha256rnds2 MSG, STATE1, STATE0 223 sha256rnds2 MSG, STATE1, STATE0
206 sha256msg1 MSGTMP3, MSGTMP2 224 sha256msg1 MSG3, MSG2
207 225
208 /* Rounds 48-51 */ 226 /* Rounds 48-51 */
209 mova128 MSGTMP0, MSG 227 mova128 MSG0, MSG
210 paddd 12*16-8*16(SHA256CONSTANTS), MSG 228 paddd 12*16-8*16(SHA256CONSTANTS), MSG
211 sha256rnds2 MSG, STATE0, STATE1 229 sha256rnds2 MSG, STATE0, STATE1
212 mova128 MSGTMP0, XMMTMP 230 mova128 MSG0, XMMTMP
213 palignr $4, MSGTMP3, XMMTMP 231 palignr $4, MSG3, XMMTMP
214 paddd XMMTMP, MSGTMP1 232 paddd XMMTMP, MSG1
215 sha256msg2 MSGTMP0, MSGTMP1 233 sha256msg2 MSG0, MSG1
216 shuf128_32 $0x0E, MSG, MSG 234 MOVE_UPPER64_DOWN(MSG)
217 sha256rnds2 MSG, STATE1, STATE0 235 sha256rnds2 MSG, STATE1, STATE0
218 sha256msg1 MSGTMP0, MSGTMP3 236 sha256msg1 MSG0, MSG3
219 237
220 /* Rounds 52-55 */ 238 /* Rounds 52-55 */
221 mova128 MSGTMP1, MSG 239 mova128 MSG1, MSG
222 paddd 13*16-8*16(SHA256CONSTANTS), MSG 240 paddd 13*16-8*16(SHA256CONSTANTS), MSG
223 sha256rnds2 MSG, STATE0, STATE1 241 sha256rnds2 MSG, STATE0, STATE1
224 mova128 MSGTMP1, XMMTMP 242 mova128 MSG1, XMMTMP
225 palignr $4, MSGTMP0, XMMTMP 243 palignr $4, MSG0, XMMTMP
226 paddd XMMTMP, MSGTMP2 244 paddd XMMTMP, MSG2
227 sha256msg2 MSGTMP1, MSGTMP2 245 sha256msg2 MSG1, MSG2
228 shuf128_32 $0x0E, MSG, MSG 246 MOVE_UPPER64_DOWN(MSG)
229 sha256rnds2 MSG, STATE1, STATE0 247 sha256rnds2 MSG, STATE1, STATE0
230 248
231 /* Rounds 56-59 */ 249 /* Rounds 56-59 */
232 mova128 MSGTMP2, MSG 250 mova128 MSG2, MSG
233 paddd 14*16-8*16(SHA256CONSTANTS), MSG 251 paddd 14*16-8*16(SHA256CONSTANTS), MSG
234 sha256rnds2 MSG, STATE0, STATE1 252 sha256rnds2 MSG, STATE0, STATE1
235 mova128 MSGTMP2, XMMTMP 253 mova128 MSG2, XMMTMP
236 palignr $4, MSGTMP1, XMMTMP 254 palignr $4, MSG1, XMMTMP
237 paddd XMMTMP, MSGTMP3 255 paddd XMMTMP, MSG3
238 sha256msg2 MSGTMP2, MSGTMP3 256 sha256msg2 MSG2, MSG3
239 shuf128_32 $0x0E, MSG, MSG 257 MOVE_UPPER64_DOWN(MSG)
240 sha256rnds2 MSG, STATE1, STATE0 258 sha256rnds2 MSG, STATE1, STATE0
241 259
242 /* Rounds 60-63 */ 260 /* Rounds 60-63 */
243 mova128 MSGTMP3, MSG 261 mova128 MSG3, MSG
244 paddd 15*16-8*16(SHA256CONSTANTS), MSG 262 paddd 15*16-8*16(SHA256CONSTANTS), MSG
245 sha256rnds2 MSG, STATE0, STATE1 263 sha256rnds2 MSG, STATE0, STATE1
246 shuf128_32 $0x0E, MSG, MSG 264 MOVE_UPPER64_DOWN(MSG)
247 sha256rnds2 MSG, STATE1, STATE0 265 sha256rnds2 MSG, STATE1, STATE0
248 266
249 /* Add current hash values with previously saved */ 267 /* Add current hash values with previously saved */
@@ -252,7 +270,7 @@ sha256_process_block64_shaNI:
252 270
253 /* Write hash values back in the correct order */ 271 /* Write hash values back in the correct order */
254 mova128 STATE0, XMMTMP 272 mova128 STATE0, XMMTMP
255/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 273/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
256 /* --- -------------- HGDC -- FEBA */ 274 /* --- -------------- HGDC -- FEBA */
257 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */ 275 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */
258 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */ 276 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 8e2b37853..e0c0a17dc 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -457,7 +457,7 @@ static void put_cur_glyph_and_inc_cursor(void)
457 * have automargin (IOW: it is moving cursor to next line 457 * have automargin (IOW: it is moving cursor to next line
458 * by itself (which is wrong for VT-10x terminals)), 458 * by itself (which is wrong for VT-10x terminals)),
459 * this will break things: there will be one extra empty line */ 459 * this will break things: there will be one extra empty line */
460 puts("\r"); /* + implicit '\n' */ 460 fputs("\r\n", stderr);
461#else 461#else
462 /* VT-10x terminals don't wrap cursor to next line when last char 462 /* VT-10x terminals don't wrap cursor to next line when last char
463 * on the line is printed - cursor stays "over" this char. 463 * on the line is printed - cursor stays "over" this char.
@@ -1302,9 +1302,10 @@ static void showfiles(void)
1302 ); 1302 );
1303 } 1303 }
1304 if (ENABLE_UNICODE_SUPPORT) 1304 if (ENABLE_UNICODE_SUPPORT)
1305 puts(printable_string(matches[n])); 1305 fputs(printable_string(matches[n]), stderr);
1306 else 1306 else
1307 puts(matches[n]); 1307 fputs(matches[n], stderr);
1308 bb_putchar_stderr('\n');
1308 } 1309 }
1309} 1310}
1310 1311
@@ -1595,8 +1596,8 @@ unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
1595# endif 1596# endif
1596 if (hp) { 1597 if (hp) {
1597 size = atoi(hp); 1598 size = atoi(hp);
1598 if (size <= 0) 1599 if (size < 0)
1599 return 1; 1600 return 0;
1600 if (size > MAX_HISTORY) 1601 if (size > MAX_HISTORY)
1601 return MAX_HISTORY; 1602 return MAX_HISTORY;
1602 } 1603 }
@@ -1690,18 +1691,21 @@ static void load_history(line_input_t *st_parm)
1690 /* NB: do not trash old history if file can't be opened */ 1691 /* NB: do not trash old history if file can't be opened */
1691 1692
1692 fp = fopen_for_read(st_parm->hist_file); 1693 fp = fopen_for_read(st_parm->hist_file);
1693 if (fp) { 1694 if (!fp)
1694 /* clean up old history */ 1695 return;
1695 for (idx = st_parm->cnt_history; idx > 0;) { 1696
1696 idx--; 1697 /* clean up old history */
1697 free(st_parm->history[idx]); 1698 for (idx = st_parm->cnt_history; idx > 0;) {
1698 st_parm->history[idx] = NULL; 1699 idx--;
1699 } 1700 free(st_parm->history[idx]);
1701 st_parm->history[idx] = NULL;
1702 }
1700 1703
1701 /* fill temp_h[], retaining only last MAX_HISTORY lines */ 1704 /* fill temp_h[], retaining only last max_history lines */
1702 memset(temp_h, 0, sizeof(temp_h)); 1705 memset(temp_h, 0, sizeof(temp_h));
1703 idx = 0; 1706 idx = 0;
1704 st_parm->cnt_history_in_file = 0; 1707 st_parm->cnt_history_in_file = 0;
1708 if (st_parm->max_history != 0) {
1705 while ((line = xmalloc_fgetline(fp)) != NULL) { 1709 while ((line = xmalloc_fgetline(fp)) != NULL) {
1706 if (line[0] == '\0') { 1710 if (line[0] == '\0') {
1707 free(line); 1711 free(line);
@@ -1714,34 +1718,34 @@ static void load_history(line_input_t *st_parm)
1714 if (idx == st_parm->max_history) 1718 if (idx == st_parm->max_history)
1715 idx = 0; 1719 idx = 0;
1716 } 1720 }
1717 fclose(fp); 1721 }
1718 1722 fclose(fp);
1719 /* find first non-NULL temp_h[], if any */
1720 if (st_parm->cnt_history_in_file) {
1721 while (temp_h[idx] == NULL) {
1722 idx++;
1723 if (idx == st_parm->max_history)
1724 idx = 0;
1725 }
1726 }
1727 1723
1728 /* copy temp_h[] to st_parm->history[] */ 1724 /* find first non-NULL temp_h[], if any */
1729 for (i = 0; i < st_parm->max_history;) { 1725 if (st_parm->cnt_history_in_file != 0) {
1730 line = temp_h[idx]; 1726 while (temp_h[idx] == NULL) {
1731 if (!line)
1732 break;
1733 idx++; 1727 idx++;
1734 if (idx == st_parm->max_history) 1728 if (idx == st_parm->max_history)
1735 idx = 0; 1729 idx = 0;
1736 line_len = strlen(line);
1737 if (line_len >= MAX_LINELEN)
1738 line[MAX_LINELEN-1] = '\0';
1739 st_parm->history[i++] = line;
1740 } 1730 }
1741 st_parm->cnt_history = i;
1742 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1743 st_parm->cnt_history_in_file = i;
1744 } 1731 }
1732
1733 /* copy temp_h[] to st_parm->history[] */
1734 for (i = 0; i < st_parm->max_history;) {
1735 line = temp_h[idx];
1736 if (!line)
1737 break;
1738 idx++;
1739 if (idx == st_parm->max_history)
1740 idx = 0;
1741 line_len = strlen(line);
1742 if (line_len >= MAX_LINELEN)
1743 line[MAX_LINELEN-1] = '\0';
1744 st_parm->history[i++] = line;
1745 }
1746 st_parm->cnt_history = i;
1747 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1748 st_parm->cnt_history_in_file = i;
1745} 1749}
1746 1750
1747# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 1751# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
@@ -1749,17 +1753,27 @@ void FAST_FUNC save_history(line_input_t *st)
1749{ 1753{
1750 FILE *fp; 1754 FILE *fp;
1751 1755
1752 if (!st || !st->hist_file) 1756 /* bash compat: HISTFILE="" disables history saving */
1757 if (!st || !st->hist_file || !st->hist_file[0])
1753 return; 1758 return;
1754 if (st->cnt_history <= st->cnt_history_in_file) 1759 if (st->cnt_history <= st->cnt_history_in_file)
1755 return; 1760 return; /* no new entries were added */
1761 /* note: if st->max_history is 0, we do not abort: we truncate the history to 0 lines */
1756 1762
1757 fp = fopen(st->hist_file, "a"); 1763 fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a"));
1758 if (fp) { 1764 if (fp) {
1759 int i, fd; 1765 int i, fd;
1760 char *new_name; 1766 char *new_name;
1761 line_input_t *st_temp; 1767 line_input_t *st_temp;
1762 1768
1769 /* max_history==0 needs special-casing in general code,
1770 * just handle it in a simpler way: */
1771 if (st->max_history == 0) {
1772 /* fopen("w") already truncated it */
1773 fclose(fp);
1774 return;
1775 }
1776
1763 for (i = st->cnt_history_in_file; i < st->cnt_history; i++) 1777 for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1764 fprintf(fp, "%s\n", st->history[i]); 1778 fprintf(fp, "%s\n", st->history[i]);
1765 fclose(fp); 1779 fclose(fp);
@@ -1769,6 +1783,8 @@ void FAST_FUNC save_history(line_input_t *st)
1769 st_temp = new_line_input_t(st->flags); 1783 st_temp = new_line_input_t(st->flags);
1770 st_temp->hist_file = st->hist_file; 1784 st_temp->hist_file = st->hist_file;
1771 st_temp->max_history = st->max_history; 1785 st_temp->max_history = st->max_history;
1786 /* load no more than max_history last lines */
1787 /* (in unlikely case that file disappeared, st_temp gets empty history) */
1772 load_history(st_temp); 1788 load_history(st_temp);
1773 1789
1774 /* write out temp file and replace hist_file atomically */ 1790 /* write out temp file and replace hist_file atomically */
@@ -1792,13 +1808,13 @@ static void save_history(char *str)
1792 int fd; 1808 int fd;
1793 int len, len2; 1809 int len, len2;
1794 1810
1795 if (!state->hist_file) 1811 /* bash compat: HISTFILE="" disables history saving */
1812 if (!state->hist_file || !state->hist_file[0])
1796 return; 1813 return;
1797 1814
1798 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); 1815 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1799 if (fd < 0) 1816 if (fd < 0)
1800 return; 1817 return;
1801 xlseek(fd, 0, SEEK_END); /* paranoia */
1802 len = strlen(str); 1818 len = strlen(str);
1803 str[len] = '\n'; /* we (try to) do atomic write */ 1819 str[len] = '\n'; /* we (try to) do atomic write */
1804 len2 = full_write(fd, str, len + 1); 1820 len2 = full_write(fd, str, len + 1);
@@ -1853,13 +1869,10 @@ static void remember_in_history(char *str)
1853 if (str[0] == '\0') 1869 if (str[0] == '\0')
1854 return; 1870 return;
1855 i = state->cnt_history; 1871 i = state->cnt_history;
1856 /* Don't save dupes */ 1872 /* Don't save dups */
1857 if (i && strcmp(state->history[i-1], str) == 0) 1873 if (i != 0 && strcmp(state->history[i-1], str) == 0)
1858 return; 1874 return;
1859 1875
1860 free(state->history[state->max_history]); /* redundant, paranoia */
1861 state->history[state->max_history] = NULL; /* redundant, paranoia */
1862
1863 /* If history[] is full, remove the oldest command */ 1876 /* If history[] is full, remove the oldest command */
1864 /* we need to keep history[state->max_history] empty, hence >=, not > */ 1877 /* we need to keep history[state->max_history] empty, hence >=, not > */
1865 if (i >= state->max_history) { 1878 if (i >= state->max_history) {
@@ -1872,7 +1885,7 @@ static void remember_in_history(char *str)
1872 state->cnt_history_in_file--; 1885 state->cnt_history_in_file--;
1873# endif 1886# endif
1874 } 1887 }
1875 /* i <= state->max_history-1 */ 1888 /* i < state->max_history */
1876 state->history[i++] = xstrdup(str); 1889 state->history[i++] = xstrdup(str);
1877 /* i <= state->max_history */ 1890 /* i <= state->max_history */
1878 state->cur_history = i; 1891 state->cur_history = i;
@@ -2260,7 +2273,7 @@ static void parse_and_put_prompt(const char *prmt_ptr)
2260 if (c == 'w') 2273 if (c == 'w')
2261 break; 2274 break;
2262 cp = strrchr(pbuf, '/'); 2275 cp = strrchr(pbuf, '/');
2263 if (cp) 2276 if (cp IF_PLATFORM_MINGW32(&& cp[1]))
2264 pbuf = (char*)cp + 1; 2277 pbuf = (char*)cp + 1;
2265 break; 2278 break;
2266// bb_process_escape_sequence does this now: 2279// bb_process_escape_sequence does this now:
@@ -2388,7 +2401,6 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
2388 errno = EINTR; 2401 errno = EINTR;
2389 return -1; 2402 return -1;
2390 } 2403 }
2391//FIXME: still races here with signals, but small window to poll() inside read_key
2392 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) 2404 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;)
2393 /* errno = 0; - read_key does this itself */ 2405 /* errno = 0; - read_key does this itself */
2394 ic = read_key(STDIN_FILENO, read_key_buffer, timeout); 2406 ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
diff --git a/libbb/poll_with_signals.c b/libbb/poll_with_signals.c
new file mode 100644
index 000000000..d3c005418
--- /dev/null
+++ b/libbb/poll_with_signals.c
@@ -0,0 +1,48 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2025 Denys Vlasenko <vda.linux@googlemail.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9//kbuild:lib-$(CONFIG_PLATFORM_POSIX) += poll_with_signals.o
10
11#include "libbb.h"
12
13/* Shells, for example, need their line input and "read" builtin
14 * to be interruptible, and the naive handling of it a-la:
15 * if (bb_got_signal) {
16 * errno = EINTR;
17 * return -1;
18 * }
19 * poll(pfd, 1, -1); // signal here would set EINTR
20 * is racy.
21 * This is a bit heavy-handed, but safe wrt races:
22 */
23int FAST_FUNC check_got_signal_and_poll(struct pollfd pfd[1], int timeout)
24{
25 int n;
26 struct timespec tv;
27 sigset_t orig_mask;
28
29 if (bb_got_signal) /* optimization */
30 goto eintr;
31
32 if (timeout >= 0) {
33 tv.tv_sec = timeout / 1000;
34 tv.tv_nsec = (timeout % 1000) * 1000000;
35 }
36 /* test bb_got_signal, then poll(), atomically wrt signals */
37 sigfillset(&orig_mask);
38 sigprocmask2(SIG_BLOCK, &orig_mask);
39 if (bb_got_signal) {
40 sigprocmask2(SIG_SETMASK, &orig_mask);
41 eintr:
42 errno = EINTR; /* inform the caller that we got a signal */
43 return -1;
44 }
45 n = ppoll(pfd, 1, timeout >= 0 ? &tv : NULL, &orig_mask);
46 sigprocmask2(SIG_SETMASK, &orig_mask);
47 return n;
48}
diff --git a/libbb/procps.c b/libbb/procps.c
index 8c9cac125..dfea8af6b 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -110,7 +110,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp)
110} 110}
111 111
112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
113static unsigned long long fast_strtoull_16(char **endptr) 113unsigned long long FAST_FUNC fast_strtoull_16(char **endptr)
114{ 114{
115 unsigned char c; 115 unsigned char c;
116 char *str = *endptr; 116 char *str = *endptr;
@@ -131,7 +131,7 @@ static unsigned long long fast_strtoull_16(char **endptr)
131 131
132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
133/* We cut a lot of corners here for speed */ 133/* We cut a lot of corners here for speed */
134static unsigned long fast_strtoul_10(char **endptr) 134unsigned long FAST_FUNC fast_strtoul_10(char **endptr)
135{ 135{
136 unsigned char c; 136 unsigned char c;
137 char *str = *endptr; 137 char *str = *endptr;
@@ -144,6 +144,24 @@ static unsigned long fast_strtoul_10(char **endptr)
144 *endptr = str + 1; /* We skip trailing space! */ 144 *endptr = str + 1; /* We skip trailing space! */
145 return n; 145 return n;
146} 146}
147# if LONG_MAX < LLONG_MAX
148/* For VSZ, which can be very large */
149static unsigned long long fast_strtoull_10(char **endptr)
150{
151 unsigned char c;
152 char *str = *endptr;
153 unsigned long long n = *str - '0';
154
155 /* Need to stop on both ' ' and '\n' */
156 while ((c = *++str) > ' ')
157 n = n*10 + (c - '0');
158
159 *endptr = str + 1; /* We skip trailing space! */
160 return n;
161}
162# else
163# define fast_strtoull_10(endptr) fast_strtoul_10(endptr)
164# endif
147 165
148# if ENABLE_FEATURE_FAST_TOP 166# if ENABLE_FEATURE_FAST_TOP
149static long fast_strtol_10(char **endptr) 167static long fast_strtol_10(char **endptr)
@@ -156,7 +174,7 @@ static long fast_strtol_10(char **endptr)
156} 174}
157# endif 175# endif
158 176
159static char *skip_fields(char *str, int count) 177char* FAST_FUNC skip_fields(char *str, int count)
160{ 178{
161 do { 179 do {
162 while (*str++ != ' ') 180 while (*str++ != ' ')
@@ -167,35 +185,25 @@ static char *skip_fields(char *str, int count)
167} 185}
168#endif 186#endif
169 187
170#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 188#if ENABLE_FEATURE_TOPMEM
171static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) 189static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp)
172{ 190{
173 char *tp = is_prefixed_with(buf, prefix); 191 // There is A LOT of /proc/PID/smaps data on a big system.
174 if (tp) { 192 // Optimize this for speed, makes "top -m" faster.
175 tp = skip_whitespace(tp); 193//TODO large speedup:
176 } 194//read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster)
177 return tp; 195//and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW)
178}
179 196
180int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
181 void (*cb)(struct smaprec *, void *), void *data)
182{
183 FILE *file; 197 FILE *file;
184 struct smaprec currec;
185 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; 198 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
186 char buf[PROCPS_BUFSIZE]; 199 char buf[PROCPS_BUFSIZE] ALIGN4;
187#if !ENABLE_PMAP
188 void (*cb)(struct smaprec *, void *) = NULL;
189 void *data = NULL;
190#endif
191 200
192 sprintf(filename, "/proc/%u/smaps", (int)pid); 201 sprintf(filename, "/proc/%u/smaps", (int)pid);
193 202
194 file = fopen_for_read(filename); 203 file = fopen_for_read(filename);
195 if (!file) 204 if (!file)
196 return 1; 205 return;
197 206
198 memset(&currec, 0, sizeof(currec));
199 while (fgets(buf, PROCPS_BUFSIZE, file)) { 207 while (fgets(buf, PROCPS_BUFSIZE, file)) {
200 // Each mapping datum has this form: 208 // Each mapping datum has this form:
201 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 209 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
@@ -203,80 +211,60 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
203 // Rss: nnn kB 211 // Rss: nnn kB
204 // ..... 212 // .....
205 213
206 char *tp, *p; 214 char *tp;
207 215#define bytes4 *(uint32_t*)buf
216#define Priv PACK32_BYTES('P','r','i','v')
217#define Shar PACK32_BYTES('S','h','a','r')
208#define SCAN(S, X) \ 218#define SCAN(S, X) \
209 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ 219if (memcmp(buf+4, S, sizeof(S)-1) == 0) { \
210 total->X += currec.X = fast_strtoul_10(&tp); \ 220 tp = skip_whitespace(buf+4 + sizeof(S)-1); \
211 continue; \ 221 sp->X += fast_strtoul_10(&tp); \
222 continue; \
223}
224 if (bytes4 == Priv) {
225 SCAN("ate_Dirty:", private_dirty)
226 SCAN("ate_Clean:", private_clean)
212 } 227 }
213 if (cb) { 228 if (bytes4 == Shar) {
214 SCAN("Pss:" , smap_pss ); 229 SCAN("ed_Dirty:" , shared_dirty )
215 SCAN("Swap:" , smap_swap ); 230 SCAN("ed_Clean:" , shared_clean )
216 } 231 }
217 SCAN("Private_Dirty:", private_dirty); 232#undef bytes4
218 SCAN("Private_Clean:", private_clean); 233#undef Priv
219 SCAN("Shared_Dirty:" , shared_dirty ); 234#undef Shar
220 SCAN("Shared_Clean:" , shared_clean );
221#undef SCAN 235#undef SCAN
222 tp = strchr(buf, '-'); 236 tp = strchr(buf, '-');
223 if (tp) { 237 if (tp) {
224 // We reached next mapping - the line of this form: 238 // We reached next mapping - the line of this form:
225 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 239 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
226 240
227 if (cb) { 241 char *rwx;
228 /* If we have a previous record, there's nothing more 242 unsigned long long sz;
229 * for it, call the callback and clear currec
230 */
231 if (currec.smap_size)
232 cb(&currec, data);
233 free(currec.smap_name);
234 }
235 memset(&currec, 0, sizeof(currec));
236 243
237 *tp = ' '; 244 *tp = ' ';
238 tp = buf; 245 tp = buf;
239 currec.smap_start = fast_strtoull_16(&tp); 246 sz = fast_strtoull_16(&tp); // start
240 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; 247 sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start
241 248 // tp -> "rw-s" string
242 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); 249 rwx = tp;
243
244 // skipping "rw-s FILEOFS M:m INODE " 250 // skipping "rw-s FILEOFS M:m INODE "
245 tp = skip_whitespace(skip_fields(tp, 4)); 251 tp = skip_whitespace(skip_fields(tp, 4));
246 // filter out /dev/something (something != zero) 252 // if not a device memory mapped...
247 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { 253 if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something"
248 if (currec.smap_mode[1] == 'w') { 254 || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device)
249 currec.mapped_rw = currec.smap_size; 255 ) {
250 total->mapped_rw += currec.smap_size; 256 if (rwx[1] == 'w')
251 } else if (currec.smap_mode[1] == '-') { 257 sp->mapped_rw += sz;
252 currec.mapped_ro = currec.smap_size; 258 else if (rwx[0] == 'r' || rwx[2] == 'x')
253 total->mapped_ro += currec.smap_size; 259 sp->mapped_ro += sz;
254 } 260 // else: seen "---p" mappings (mmap guard gaps?),
261 // do NOT account these as VSZ, they aren't really
255 } 262 }
256
257 if (strcmp(tp, "[stack]\n") == 0) 263 if (strcmp(tp, "[stack]\n") == 0)
258 total->stack += currec.smap_size; 264 sp->stack += sz;
259 if (cb) {
260 p = skip_non_whitespace(tp);
261 if (p == tp) {
262 currec.smap_name = xstrdup(" [ anon ]");
263 } else {
264 *p = '\0';
265 currec.smap_name = xstrdup(tp);
266 }
267 }
268 total->smap_size += currec.smap_size;
269 } 265 }
270 } 266 }
271 fclose(file); 267 fclose(file);
272
273 if (cb) {
274 if (currec.smap_size)
275 cb(&currec, data);
276 free(currec.smap_name);
277 }
278
279 return 0;
280} 268}
281#endif 269#endif
282 270
@@ -371,7 +359,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
371 char *cp, *comm1; 359 char *cp, *comm1;
372 int tty; 360 int tty;
373#if !ENABLE_FEATURE_FAST_TOP 361#if !ENABLE_FEATURE_FAST_TOP
374 unsigned long vsz, rss; 362 unsigned long long vsz;
363 unsigned long rss;
375#endif 364#endif
376 /* see proc(5) for some details on this */ 365 /* see proc(5) for some details on this */
377 strcpy(filename_tail, "stat"); 366 strcpy(filename_tail, "stat");
@@ -397,7 +386,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
397 "%ld " /* nice */ 386 "%ld " /* nice */
398 "%*s %*s " /* timeout, it_real_value */ 387 "%*s %*s " /* timeout, it_real_value */
399 "%lu " /* start_time */ 388 "%lu " /* start_time */
400 "%lu " /* vsize */ 389 "%llu " /* vsize - can be very large */
401 "%lu " /* rss */ 390 "%lu " /* rss */
402# if ENABLE_FEATURE_TOP_SMP_PROCESS 391# if ENABLE_FEATURE_TOP_SMP_PROCESS
403 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 392 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -450,7 +439,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
450 cp = skip_fields(cp, 2); /* timeout, it_real_value */ 439 cp = skip_fields(cp, 2); /* timeout, it_real_value */
451 sp->start_time = fast_strtoul_10(&cp); 440 sp->start_time = fast_strtoul_10(&cp);
452 /* vsz is in bytes and we want kb */ 441 /* vsz is in bytes and we want kb */
453 sp->vsz = fast_strtoul_10(&cp) >> 10; 442 sp->vsz = fast_strtoull_10(&cp) >> 10;
454 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 443 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
455 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; 444 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
456# if ENABLE_FEATURE_TOP_SMP_PROCESS 445# if ENABLE_FEATURE_TOP_SMP_PROCESS
@@ -484,7 +473,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
484 473
485#if ENABLE_FEATURE_TOPMEM 474#if ENABLE_FEATURE_TOPMEM
486 if (flags & PSSCAN_SMAPS) 475 if (flags & PSSCAN_SMAPS)
487 procps_read_smaps(pid, &sp->smaps, NULL, NULL); 476 procps_read_smaps(pid, sp);
488#endif /* TOPMEM */ 477#endif /* TOPMEM */
489#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 478#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
490 if (flags & PSSCAN_RUIDGID) { 479 if (flags & PSSCAN_RUIDGID) {
@@ -567,36 +556,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
567 return sp; 556 return sp;
568} 557}
569 558
570void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 559int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
571{ 560{
572 int sz; 561 int sz;
573 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; 562 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
574 563
575 sprintf(filename, "/proc/%u/cmdline", pid); 564 sprintf(filename, "/proc/%u/cmdline", pid);
576 sz = open_read_close(filename, buf, col - 1); 565 sz = open_read_close(filename, buf, col - 1);
566 if (sz < 0)
567 return sz;
577 if (sz > 0) { 568 if (sz > 0) {
578 const char *base; 569 const char *program_basename;
579 int comm_len; 570 int comm_len;
580 571
581 buf[sz] = '\0'; 572 buf[sz] = '\0';
582 while (--sz >= 0 && buf[sz] == '\0') 573 while (--sz >= 0 && buf[sz] == '\0')
583 continue; 574 continue;
584 /* Prevent basename("process foo/bar") = "bar" */ 575
585 strchrnul(buf, ' ')[0] = '\0'; 576 /* Find "program" in "[-][/PATH/TO/]program" */
586 base = bb_basename(buf); /* before we replace argv0's NUL with space */ 577 strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */
578 program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf);
579 /* ^^^ note: must do it *before* replacing argv0's NUL with space */
580
581 /* Prevent stuff like this:
582 * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c
583 * messing up top and ps output (or worse).
584 * This also replaces NULs with spaces, converting
585 * list of NUL-strings into one string.
586 */
587 while (sz >= 0) { 587 while (sz >= 0) {
588 if ((unsigned char)(buf[sz]) < ' ') 588 if ((unsigned char)(buf[sz]) < ' ')
589 buf[sz] = ' '; 589 buf[sz] = (buf[sz] ? /*ctrl*/'?' : /*NUL*/' ');
590 sz--; 590 sz--;
591 } 591 }
592 if (base[0] == '-') /* "-sh" (login shell)? */
593 base++;
594 592
595 /* If comm differs from argv0, prepend "{comm} ". 593 /* If comm differs from argv0, prepend "{comm} ".
596 * It allows to see thread names set by prctl(PR_SET_NAME). 594 * It allows to see thread names set by prctl(PR_SET_NAME).
597 */ 595 */
598 if (!comm) 596 if (!comm)
599 return; 597 return 0;
600 comm_len = strlen(comm); 598 comm_len = strlen(comm);
601 /* Why compare up to comm_len, not COMM_LEN-1? 599 /* Why compare up to comm_len, not COMM_LEN-1?
602 * Well, some processes rewrite argv, and use _spaces_ there 600 * Well, some processes rewrite argv, and use _spaces_ there
@@ -604,19 +602,20 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
604 * I prefer to still treat argv0 "process foo bar" 602 * I prefer to still treat argv0 "process foo bar"
605 * as 'equal' to comm "process". 603 * as 'equal' to comm "process".
606 */ 604 */
607 if (strncmp(base, comm, comm_len) != 0) { 605 if (strncmp(program_basename, comm, comm_len) != 0) {
608 comm_len += 3; 606 comm_len += 3;
609 if (col > comm_len) 607 if (col > comm_len)
610 memmove(buf + comm_len, buf, col - comm_len); 608 memmove(buf + comm_len, buf, col - comm_len);
611 snprintf(buf, col, "{%s}", comm); 609 snprintf(buf, col, "{%s}", comm);
612 if (col <= comm_len) 610 if (col <= comm_len)
613 return; 611 return 0;
614 buf[comm_len - 1] = ' '; 612 buf[comm_len - 1] = ' ';
615 buf[col - 1] = '\0'; 613 buf[col - 1] = '\0';
616 } 614 }
617 } else { 615 } else {
618 snprintf(buf, col, "[%s]", comm ? comm : "?"); 616 snprintf(buf, col, "[%s]", comm ? comm : "?");
619 } 617 }
618 return 0;
620} 619}
621 620
622#endif /* ENABLE_PLATFORM_MINGW32 */ 621#endif /* ENABLE_PLATFORM_MINGW32 */
diff --git a/libbb/pw_ascii64.c b/libbb/pw_ascii64.c
new file mode 100644
index 000000000..3993932ca
--- /dev/null
+++ b/libbb/pw_ascii64.c
@@ -0,0 +1,91 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10/* Returns >=64 for invalid chars */
11int FAST_FUNC a2i64(char c)
12{
13 unsigned char ch = c;
14 if (ch >= 'a')
15 /* "a..z" to 38..63 */
16 /* anything after "z": positive int >= 64 */
17 return (ch - 'a' + 38);
18
19 if (ch > 'Z')
20 /* after "Z" but before "a": positive byte >= 64 */
21 return ch;
22
23 if (ch >= 'A')
24 /* "A..Z" to 12..37 */
25 return (ch - 'A' + 12);
26
27 if (ch > '9')
28 return 64;
29
30 /* "./0123456789" to 0,1,2..11 */
31 /* anything before "." becomes positive byte >= 64 */
32 return (unsigned char)(ch - '.');
33}
34
35/* 0..63 ->
36 * "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
37 */
38int FAST_FUNC i2a64(int i)
39{
40 i &= 0x3f;
41
42 i += '.';
43 /* the above maps 0..11 to "./0123456789":
44 * ACSII codes of "./" are ('0'-2) and ('0'-1) */
45
46 if (i > '9')
47 i += ('A' - '9' - 1);
48 if (i > 'Z')
49 i += ('a' - 'Z' - 1);
50 return i;
51}
52
53char* FAST_FUNC
54num2str64_lsb_first(char *s, unsigned v, int n)
55{
56 while (--n >= 0) {
57 *s++ = i2a64(v);
58 v >>= 6;
59 }
60 return s;
61}
62
63static void
64num2str64_4chars_msb_first(char *s, unsigned v)
65{
66 *s++ = i2a64(v >> 18); /* bits 23..18 */
67 *s++ = i2a64(v >> 12); /* bits 17..12 */
68 *s++ = i2a64(v >> 6); /* bits 11..6 */
69 *s = i2a64(v); /* bits 5..0 */
70}
71
72int FAST_FUNC crypt_make_rand64encoded(char *p, int cnt /*, int x */)
73{
74 /* was: x += ... */
75 unsigned x = getpid() + monotonic_us();
76 do {
77 /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
78 * (low-order bit is not "random", etc...),
79 * but for our purposes it is good enough */
80 x = x*1664525 + 1013904223;
81 /* BTW, Park and Miller's "minimal standard generator" is
82 * x = x*16807 % ((2^31)-1)
83 * It has no problem with visibly alternating lowest bit
84 * but is also weak in cryptographic sense + needs div,
85 * which needs more code (and slower) on many CPUs */
86 *p++ = i2a64(x >> 16);
87 *p++ = i2a64(x >> 22);
88 } while (--cnt);
89 *p = '\0';
90 return x;
91}
diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c
index 3463fd95b..93653de9f 100644
--- a/libbb/pw_encrypt.c
+++ b/libbb/pw_encrypt.c
@@ -13,48 +13,11 @@
13#endif 13#endif
14#include "libbb.h" 14#include "libbb.h"
15 15
16/* static const uint8_t ascii64[] ALIGN1 = 16#include "pw_ascii64.c"
17 * "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
18 */
19
20static int i64c(int i)
21{
22 i &= 0x3f;
23 if (i == 0)
24 return '.';
25 if (i == 1)
26 return '/';
27 if (i < 12)
28 return ('0' - 2 + i);
29 if (i < 38)
30 return ('A' - 12 + i);
31 return ('a' - 38 + i);
32}
33
34int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
35{
36 /* was: x += ... */
37 unsigned x = getpid() + monotonic_us();
38 do {
39 /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
40 * (low-order bit is not "random", etc...),
41 * but for our purposes it is good enough */
42 x = x*1664525 + 1013904223;
43 /* BTW, Park and Miller's "minimal standard generator" is
44 * x = x*16807 % ((2^31)-1)
45 * It has no problem with visibly alternating lowest bit
46 * but is also weak in cryptographic sense + needs div,
47 * which needs more code (and slower) on many CPUs */
48 *p++ = i64c(x >> 16);
49 *p++ = i64c(x >> 22);
50 } while (--cnt);
51 *p = '\0';
52 return x;
53}
54 17
55char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo) 18char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
56{ 19{
57 int len = 2/2; 20 int len = 2 / 2;
58 char *salt_ptr = salt; 21 char *salt_ptr = salt;
59 22
60 /* Standard chpasswd uses uppercase algos ("MD5", not "md5"). 23 /* Standard chpasswd uses uppercase algos ("MD5", not "md5").
@@ -67,28 +30,61 @@ char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
67 *salt_ptr++ = '$'; 30 *salt_ptr++ = '$';
68#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA 31#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
69 if ((algo[0]|0x20) == 's') { /* sha */ 32 if ((algo[0]|0x20) == 's') { /* sha */
70 salt[1] = '5' + (strcasecmp(algo, "sha512") == 0); 33 salt[1] = '5' + (strncasecmp(algo, "sha512", 6) == 0);
71 len = 16/2; 34 len = 16 / 2;
35 }
36#endif
37#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_YES
38 if ((algo[0]|0x20) == 'y') { /* yescrypt */
39 int rnd;
40 salt[1] = 'y';
41// The "j9T$" below is the default "yescrypt parameters" encoded by yescrypt_encode_params_r():
42//shadow-4.17.4/src/passwd.c
43// salt = crypt_make_salt(NULL, NULL);
44//shadow-4.17.4/lib/salt.c
45//const char *crypt_make_salt(const char *meth, void *arg)
46// if (streq(method, "YESCRYPT")) {
47// MAGNUM(result, 'y');
48// salt_len = YESCRYPT_SALT_SIZE; // 24
49// rounds = YESCRYPT_get_salt_cost(arg); // always Y_COST_DEFAULT == 5 for NULL arg
50// YESCRYPT_salt_cost_to_buf(result, rounds); // always "j9T$"
51// char *retval = crypt_gensalt(result, rounds, NULL, 0);
52//libxcrypt-4.4.38/lib/crypt-yescrypt.c
53//void gensalt_yescrypt_rn (unsigned long count,
54// const uint8_t *rbytes, size_t nrbytes,
55// uint8_t *output, size_t o_size)
56// yescrypt_params_t params = {
57// .flags = YESCRYPT_DEFAULTS,
58// .p = 1,
59// };
60// if (count < 3) ... else
61// params.r = 32; // N in 4KiB
62// params.N = 1ULL << (count + 7); // 3 -> 1024, 4 -> 2048, ... 11 -> 262144
63// yescrypt_encode_params_r(&params, rbytes, nrbytes, outbuf, o_size) // always "$y$j9T$<random>"
64 len = 22 / 2;
65 salt_ptr = stpcpy(salt_ptr, "j9T$");
66 /* append 2*len random chars */
67 rnd = crypt_make_rand64encoded(salt_ptr, len);
68 /* fix up last char: it must be in 0..3 range (encoded as one of "./01").
69 * IOW: salt_ptr[20..21] encode 16th random byte, must not be > 0xff.
70 * Without this, we can generate salts which are rejected
71 * by implementations with more strict salt length check.
72 */
73 salt_ptr[21] = i2a64(rnd & 3);
74 /* For "mkpasswd -m yescrypt PASS j9T$<salt>" use case,
75 * "j9T$" is considered part of salt,
76 * need to return pointer to 'j'. Without -4,
77 * we'd end up using "j9T$j9T$<salt>" as salt.
78 */
79 return salt_ptr - 4;
72 } 80 }
73#endif 81#endif
74 } 82 }
75 crypt_make_salt(salt_ptr, len); 83 crypt_make_rand64encoded(salt_ptr, len); /* appends 2*len random chars */
76 return salt_ptr; 84 return salt_ptr;
77} 85}
78 86
79#if ENABLE_USE_BB_CRYPT 87#if ENABLE_USE_BB_CRYPT
80
81static char*
82to64(char *s, unsigned v, int n)
83{
84 while (--n >= 0) {
85 /* *s++ = ascii64[v & 0x3f]; */
86 *s++ = i64c(v);
87 v >>= 6;
88 }
89 return s;
90}
91
92/* 88/*
93 * DES and MD5 crypt implementations are taken from uclibc. 89 * DES and MD5 crypt implementations are taken from uclibc.
94 * They were modified to not use static buffers. 90 * They were modified to not use static buffers.
@@ -99,6 +95,9 @@ to64(char *s, unsigned v, int n)
99#if ENABLE_USE_BB_CRYPT_SHA 95#if ENABLE_USE_BB_CRYPT_SHA
100#include "pw_encrypt_sha.c" 96#include "pw_encrypt_sha.c"
101#endif 97#endif
98#if ENABLE_USE_BB_CRYPT_YES
99#include "pw_encrypt_yes.c"
100#endif
102 101
103/* Other advanced crypt ids (TODO?): */ 102/* Other advanced crypt ids (TODO?): */
104/* $2$ or $2a$: Blowfish */ 103/* $2$ or $2a$: Blowfish */
@@ -109,7 +108,7 @@ static struct des_ctx *des_ctx;
109/* my_crypt returns malloc'ed data */ 108/* my_crypt returns malloc'ed data */
110static char *my_crypt(const char *key, const char *salt) 109static char *my_crypt(const char *key, const char *salt)
111{ 110{
112 /* MD5 or SHA? */ 111 /* "$x$...." string? */
113 if (salt[0] == '$' && salt[1] && salt[2] == '$') { 112 if (salt[0] == '$' && salt[1] && salt[2] == '$') {
114 if (salt[1] == '1') 113 if (salt[1] == '1')
115 return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt); 114 return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt);
@@ -117,6 +116,10 @@ static char *my_crypt(const char *key, const char *salt)
117 if (salt[1] == '5' || salt[1] == '6') 116 if (salt[1] == '5' || salt[1] == '6')
118 return sha_crypt((char*)key, (char*)salt); 117 return sha_crypt((char*)key, (char*)salt);
119#endif 118#endif
119#if ENABLE_USE_BB_CRYPT_YES
120 if (salt[1] == 'y')
121 return yes_crypt(key, salt);
122#endif
120 } 123 }
121 124
122 if (!des_cctx) 125 if (!des_cctx)
diff --git a/libbb/pw_encrypt_des.c b/libbb/pw_encrypt_des.c
index fe8237cfe..ca8aa9bcc 100644
--- a/libbb/pw_encrypt_des.c
+++ b/libbb/pw_encrypt_des.c
@@ -186,39 +186,9 @@ static const uint8_t pbox[32] ALIGN1 = {
186 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 186 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
187}; 187};
188 188
189static const uint32_t bits32[32] ALIGN4 = {
190 0x80000000, 0x40000000, 0x20000000, 0x10000000,
191 0x08000000, 0x04000000, 0x02000000, 0x01000000,
192 0x00800000, 0x00400000, 0x00200000, 0x00100000,
193 0x00080000, 0x00040000, 0x00020000, 0x00010000,
194 0x00008000, 0x00004000, 0x00002000, 0x00001000,
195 0x00000800, 0x00000400, 0x00000200, 0x00000100,
196 0x00000080, 0x00000040, 0x00000020, 0x00000010,
197 0x00000008, 0x00000004, 0x00000002, 0x00000001
198};
199
200static const uint8_t bits8[8] ALIGN1 = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; 189static const uint8_t bits8[8] ALIGN1 = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
201 190
202 191
203static int
204ascii_to_bin(char ch)
205{
206 if (ch > 'z')
207 return 0;
208 if (ch >= 'a')
209 return (ch - 'a' + 38);
210 if (ch > 'Z')
211 return 0;
212 if (ch >= 'A')
213 return (ch - 'A' + 12);
214 if (ch > '9')
215 return 0;
216 if (ch >= '.')
217 return (ch - '.');
218 return 0;
219}
220
221
222/* Static stuff that stays resident and doesn't change after 192/* Static stuff that stays resident and doesn't change after
223 * being initialized, and therefore doesn't need to be made 193 * being initialized, and therefore doesn't need to be made
224 * reentrant. */ 194 * reentrant. */
@@ -354,11 +324,18 @@ des_init(struct des_ctx *ctx, const struct const_des_ctx *cctx)
354 int i, j, b, k, inbit, obit; 324 int i, j, b, k, inbit, obit;
355 uint32_t p; 325 uint32_t p;
356 const uint32_t *bits28, *bits24; 326 const uint32_t *bits28, *bits24;
327 uint32_t bits32[32];
357 328
358 if (!ctx) 329 if (!ctx)
359 ctx = xmalloc(sizeof(*ctx)); 330 ctx = xmalloc(sizeof(*ctx));
360 const_ctx = cctx; 331 const_ctx = cctx;
361 332
333 p = 0x80000000U;
334 for (i = 0; p; i++) {
335 bits32[i] = p;
336 p >>= 1;
337 }
338
362#if USE_REPETITIVE_SPEEDUP 339#if USE_REPETITIVE_SPEEDUP
363 old_rawkey0 = old_rawkey1 = 0; 340 old_rawkey0 = old_rawkey1 = 0;
364 old_salt = 0; 341 old_salt = 0;
@@ -694,21 +671,6 @@ do_des(struct des_ctx *ctx, /*uint32_t l_in, uint32_t r_in,*/ uint32_t *l_out, u
694 671
695#define DES_OUT_BUFSIZE 21 672#define DES_OUT_BUFSIZE 21
696 673
697static void
698to64_msb_first(char *s, unsigned v)
699{
700#if 0
701 *s++ = ascii64[(v >> 18) & 0x3f]; /* bits 23..18 */
702 *s++ = ascii64[(v >> 12) & 0x3f]; /* bits 17..12 */
703 *s++ = ascii64[(v >> 6) & 0x3f]; /* bits 11..6 */
704 *s = ascii64[v & 0x3f]; /* bits 5..0 */
705#endif
706 *s++ = i64c(v >> 18); /* bits 23..18 */
707 *s++ = i64c(v >> 12); /* bits 17..12 */
708 *s++ = i64c(v >> 6); /* bits 11..6 */
709 *s = i64c(v); /* bits 5..0 */
710}
711
712static char * 674static char *
713NOINLINE 675NOINLINE
714des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE], 676des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE],
@@ -740,44 +702,28 @@ des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE],
740 */ 702 */
741 output[0] = salt_str[0]; 703 output[0] = salt_str[0];
742 output[1] = salt_str[1]; 704 output[1] = salt_str[1];
743 salt = (ascii_to_bin(salt_str[1]) << 6) 705
744 | ascii_to_bin(salt_str[0]); 706 salt = a2i64(salt_str[0]);
707 if (salt >= 64)
708 return NULL; /* bad salt char */
709 salt |= (a2i64(salt_str[1]) << 6);
710 if (salt >= (64 << 6))
711 return NULL; /* bad salt char */
745 setup_salt(ctx, salt); /* set ctx->saltbits for do_des() */ 712 setup_salt(ctx, salt); /* set ctx->saltbits for do_des() */
746 713
747 /* Do it. */ 714 /* Do it. */
748 do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */); 715 do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */);
749 716
750 /* Now encode the result. */ 717 /* Now encode the result. */
751#if 0
752{
753 uint32_t l = (r0 >> 8);
754 q = (uint8_t *)output + 2;
755 *q++ = ascii64[(l >> 18) & 0x3f]; /* bits 31..26 of r0 */
756 *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 25..20 of r0 */
757 *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 19..14 of r0 */
758 *q++ = ascii64[l & 0x3f]; /* bits 13..8 of r0 */
759 l = ((r0 << 16) | (r1 >> 16));
760 *q++ = ascii64[(l >> 18) & 0x3f]; /* bits 7..2 of r0 */
761 *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 1..2 of r0 and 31..28 of r1 */
762 *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 27..22 of r1 */
763 *q++ = ascii64[l & 0x3f]; /* bits 21..16 of r1 */
764 l = r1 << 2;
765 *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 15..10 of r1 */
766 *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 9..4 of r1 */
767 *q++ = ascii64[l & 0x3f]; /* bits 3..0 of r1 + 00 */
768 *q = 0;
769}
770#else
771 /* Each call takes low-order 24 bits and stores 4 chars */ 718 /* Each call takes low-order 24 bits and stores 4 chars */
772 /* bits 31..8 of r0 */ 719 /* bits 31..8 of r0 */
773 to64_msb_first(output + 2, (r0 >> 8)); 720 num2str64_4chars_msb_first(output + 2, (r0 >> 8));
774 /* bits 7..0 of r0 and 31..16 of r1 */ 721 /* bits 7..0 of r0 and 31..16 of r1 */
775 to64_msb_first(output + 6, (r0 << 16) | (r1 >> 16)); 722 num2str64_4chars_msb_first(output + 6, (r0 << 16) | (r1 >> 16));
776 /* bits 15..0 of r1 and two zero bits (plus extra zero byte) */ 723 /* bits 15..0 of r1 and two zero bits (plus extra zero byte) */
777 to64_msb_first(output + 10, (r1 << 8)); 724 num2str64_4chars_msb_first(output + 10, (r1 << 8));
778 /* extra zero byte is encoded as '.', fixing it */ 725 /* extra zero byte is encoded as '.', fixing it */
779 output[13] = '\0'; 726 output[13] = '\0';
780#endif
781 727
782 return output; 728 return output;
783} 729}
diff --git a/libbb/pw_encrypt_md5.c b/libbb/pw_encrypt_md5.c
index 1e52ecaea..92d039f96 100644
--- a/libbb/pw_encrypt_md5.c
+++ b/libbb/pw_encrypt_md5.c
@@ -149,9 +149,9 @@ md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned
149 final[16] = final[5]; 149 final[16] = final[5];
150 for (i = 0; i < 5; i++) { 150 for (i = 0; i < 5; i++) {
151 unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12]; 151 unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12];
152 p = to64(p, l, 4); 152 p = num2str64_lsb_first(p, l, 4);
153 } 153 }
154 p = to64(p, final[11], 2); 154 p = num2str64_lsb_first(p, final[11], 2);
155 *p = '\0'; 155 *p = '\0';
156 156
157 /* Don't leave anything around in vm they could use. */ 157 /* Don't leave anything around in vm they could use. */
diff --git a/libbb/pw_encrypt_sha.c b/libbb/pw_encrypt_sha.c
index 5457d7ab6..695a5c07f 100644
--- a/libbb/pw_encrypt_sha.c
+++ b/libbb/pw_encrypt_sha.c
@@ -84,8 +84,7 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
84 as a scratch space later. */ 84 as a scratch space later. */
85 salt_data = xstrndup(salt_data, salt_len); 85 salt_data = xstrndup(salt_data, salt_len);
86 /* add "salt$" to result */ 86 /* add "salt$" to result */
87 strcpy(resptr, salt_data); 87 resptr = stpcpy(resptr, salt_data);
88 resptr += salt_len;
89 *resptr++ = '$'; 88 *resptr++ = '$';
90 /* key data doesn't need much processing */ 89 /* key data doesn't need much processing */
91 key_len = strlen(key_data); 90 key_len = strlen(key_data);
@@ -198,7 +197,7 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
198#define b64_from_24bit(B2, B1, B0, N) \ 197#define b64_from_24bit(B2, B1, B0, N) \
199do { \ 198do { \
200 unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \ 199 unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \
201 resptr = to64(resptr, w, N); \ 200 resptr = num2str64_lsb_first(resptr, w, N); \
202} while (0) 201} while (0)
203 if (_32or64 == 32) { /* sha256 */ 202 if (_32or64 == 32) { /* sha256 */
204 unsigned i = 0; 203 unsigned i = 0;
diff --git a/libbb/pw_encrypt_yes.c b/libbb/pw_encrypt_yes.c
new file mode 100644
index 000000000..50bd06418
--- /dev/null
+++ b/libbb/pw_encrypt_yes.c
@@ -0,0 +1,24 @@
1/*
2 * Utility routines.
3 *
4 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8#include "yescrypt/alg-yescrypt.h"
9
10static char *
11yes_crypt(const char *passwd, const char *salt_data)
12{
13 /* prefix, '$', hash, NUL */
14 char buf[YESCRYPT_PREFIX_LEN + 1 + YESCRYPT_HASH_LEN + 1];
15 char *retval;
16
17 retval = yescrypt_r(
18 (const uint8_t *)passwd, strlen(passwd),
19 (const uint8_t *)salt_data,
20 buf, sizeof(buf));
21 /* The returned value is either buf[], or NULL on error */
22
23 return xstrdup(retval);
24}
diff --git a/libbb/read_key.c b/libbb/read_key.c
index 54886cc9c..2414105ee 100644
--- a/libbb/read_key.c
+++ b/libbb/read_key.c
@@ -11,7 +11,7 @@
11 11
12int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) 12int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
13{ 13{
14 struct pollfd pfd; 14 struct pollfd pfd[1];
15 const char *seq; 15 const char *seq;
16 int n; 16 int n;
17 17
@@ -117,8 +117,8 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
117 return windows_read_key(fd, buffer, timeout); 117 return windows_read_key(fd, buffer, timeout);
118#endif 118#endif
119 119
120 pfd.fd = fd; 120 pfd->fd = fd;
121 pfd.events = POLLIN; 121 pfd->events = POLLIN;
122 122
123 buffer++; /* saved chars counter is in buffer[-1] now */ 123 buffer++; /* saved chars counter is in buffer[-1] now */
124 124
@@ -126,12 +126,16 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
126 errno = 0; 126 errno = 0;
127 n = (unsigned char)buffer[-1]; 127 n = (unsigned char)buffer[-1];
128 if (n == 0) { 128 if (n == 0) {
129 /* If no data, wait for input. 129 /* No data. Wait for input. */
130 * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful 130
131 * if fd can be in non-blocking mode. 131 /* timeout == -2 means "do not poll". Else: */
132 */
133 if (timeout >= -1) { 132 if (timeout >= -1) {
134 n = poll(&pfd, 1, timeout); 133 /* We must poll even if timeout == -1:
134 * we want to be interrupted if signal arrives,
135 * regardless of SA_RESTART-ness of that signal!
136 */
137 /* test bb_got_signal, then poll(), atomically wrt signals */
138 n = check_got_signal_and_poll(pfd, timeout);
135 if (n < 0 && errno == EINTR) 139 if (n < 0 && errno == EINTR)
136 return n; 140 return n;
137 if (n == 0) { 141 if (n == 0) {
@@ -140,6 +144,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
140 return -1; 144 return -1;
141 } 145 }
142 } 146 }
147
143 /* It is tempting to read more than one byte here, 148 /* It is tempting to read more than one byte here,
144 * but it breaks pasting. Example: at shell prompt, 149 * but it breaks pasting. Example: at shell prompt,
145 * user presses "c","a","t" and then pastes "\nline\n". 150 * user presses "c","a","t" and then pastes "\nline\n".
@@ -178,7 +183,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
178 * so if we block for long it's not really an escape sequence. 183 * so if we block for long it's not really an escape sequence.
179 * Timeout is needed to reconnect escape sequences 184 * Timeout is needed to reconnect escape sequences
180 * split up by transmission over a serial console. */ 185 * split up by transmission over a serial console. */
181 if (safe_poll(&pfd, 1, 50) == 0) { 186 if (safe_poll(pfd, 1, 50) == 0) {
182 /* No more data! 187 /* No more data!
183 * Array is sorted from shortest to longest, 188 * Array is sorted from shortest to longest,
184 * we can't match anything later in array - 189 * we can't match anything later in array -
@@ -227,7 +232,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
227 * n = bytes read. Try to read more until we time out. 232 * n = bytes read. Try to read more until we time out.
228 */ 233 */
229 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ 234 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */
230 if (safe_poll(&pfd, 1, 50) == 0) { 235 if (safe_poll(pfd, 1, 50) == 0) {
231 /* No more data! */ 236 /* No more data! */
232 break; 237 break;
233 } 238 }
diff --git a/libbb/replace.c b/libbb/replace.c
index 6183d3e6f..bc26b04cc 100644
--- a/libbb/replace.c
+++ b/libbb/replace.c
@@ -46,3 +46,17 @@ char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char
46 //dbg_msg("subst9:'%s'", buf); 46 //dbg_msg("subst9:'%s'", buf);
47 return buf; 47 return buf;
48} 48}
49
50#if 0 /* inlined in libbb.h */
51/* Returns strlen as a bonus */
52size_t FAST_FUNC replace_char(char *str, char from, char to)
53{
54 char *p = str;
55 while (*p) {
56 if (*p == from)
57 *p = to;
58 p++;
59 }
60 return p - str;
61}
62#endif
diff --git a/libbb/signals.c b/libbb/signals.c
index c09a562ed..a1ac2005b 100644
--- a/libbb/signals.c
+++ b/libbb/signals.c
@@ -25,13 +25,6 @@ int FAST_FUNC sigaction_set(int signum, const struct sigaction *act)
25 return sigaction(signum, act, NULL); 25 return sigaction(signum, act, NULL);
26} 26}
27 27
28int FAST_FUNC sigprocmask_allsigs(int how)
29{
30 sigset_t set;
31 sigfillset(&set);
32 return sigprocmask(how, &set, NULL);
33}
34
35int FAST_FUNC sigprocmask2(int how, sigset_t *set) 28int FAST_FUNC sigprocmask2(int how, sigset_t *set)
36{ 29{
37 // Grr... gcc 8.1.1: 30 // Grr... gcc 8.1.1:
@@ -41,7 +34,26 @@ int FAST_FUNC sigprocmask2(int how, sigset_t *set)
41 oset = set; 34 oset = set;
42 return sigprocmask(how, set, oset); 35 return sigprocmask(how, set, oset);
43} 36}
37
38int FAST_FUNC sigprocmask_allsigs(int how)
39{
40 sigset_t set;
41 sigfillset(&set);
42 return sigprocmask2(how, &set);
43}
44
45int FAST_FUNC sigblockall(sigset_t *set)
46{
47#if 0 /* nope. set can be NULL */
48 sigfillset(set);
49 return sigprocmask2(SIG_SETMASK, set);
50#else
51 sigset_t mask;
52 sigfillset(&mask);
53 return sigprocmask(SIG_SETMASK, &mask, set);
44#endif 54#endif
55}
56#endif /* !ENABLE_PLATFORM_MINGW32 */
45 57
46void FAST_FUNC bb_signals(int sigs, void (*f)(int)) 58void FAST_FUNC bb_signals(int sigs, void (*f)(int))
47{ 59{
@@ -84,7 +96,7 @@ void FAST_FUNC sig_block(int sig)
84 sigset_t ss; 96 sigset_t ss;
85 sigemptyset(&ss); 97 sigemptyset(&ss);
86 sigaddset(&ss, sig); 98 sigaddset(&ss, sig);
87 sigprocmask(SIG_BLOCK, &ss, NULL); 99 sigprocmask2(SIG_BLOCK, &ss);
88} 100}
89 101
90void FAST_FUNC sig_unblock(int sig) 102void FAST_FUNC sig_unblock(int sig)
@@ -92,7 +104,7 @@ void FAST_FUNC sig_unblock(int sig)
92 sigset_t ss; 104 sigset_t ss;
93 sigemptyset(&ss); 105 sigemptyset(&ss);
94 sigaddset(&ss, sig); 106 sigaddset(&ss, sig);
95 sigprocmask(SIG_UNBLOCK, &ss, NULL); 107 sigprocmask2(SIG_UNBLOCK, &ss);
96} 108}
97 109
98void FAST_FUNC wait_for_any_sig(void) 110void FAST_FUNC wait_for_any_sig(void)
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 7df1a4cd3..5609858d1 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -333,7 +333,7 @@ int FAST_FUNC get_termios_and_make_raw(int fd, struct termios *newterm, struct t
333 *newterm = *oldterm; 333 *newterm = *oldterm;
334 334
335#if ENABLE_PLATFORM_MINGW32 335#if ENABLE_PLATFORM_MINGW32
336 newterm->imode &= 336 newterm->w_mode &=
337 ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); 337 ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
338#else 338#else
339 /* Turn off buffered input (ICANON) 339 /* Turn off buffered input (ICANON)
diff --git a/libbb/yescrypt/Kbuild.src b/libbb/yescrypt/Kbuild.src
new file mode 100644
index 000000000..a61211a29
--- /dev/null
+++ b/libbb/yescrypt/Kbuild.src
@@ -0,0 +1,9 @@
1# Makefile for busybox
2#
3# Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
4#
5# Licensed under GPLv2, see file LICENSE in this source tree.
6
7lib-y:=
8
9INSERT
diff --git a/libbb/yescrypt/PARAMETERS b/libbb/yescrypt/PARAMETERS
new file mode 100644
index 000000000..d9f5d24e6
--- /dev/null
+++ b/libbb/yescrypt/PARAMETERS
@@ -0,0 +1,196 @@
1 Optimal yescrypt configuration.
2
3yescrypt is very flexible, but configuring it optimally is complicated.
4Here are some guidelines to simplify near-optimal configuration. We
5start by listing the parameters and their typical values, and then give
6currently recommended parameter sets by use case.
7
8
9 Parameters and their typical values.
10
11Set flags (yescrypt flavor) to YESCRYPT_DEFAULTS to use the currently
12recommended flavor. (Other flags values exist for compatibility and for
13specialized cases where you think you know what you're doing.)
14
15Set N (block count) based on target memory usage and running time, as
16well as on the value of r (block size in 128 byte units). N must be a
17power of two.
18
19Set r (block size) to 8 (so that N is in KiB, which is convenient) or to
20another small value (if more optimal or for fine-tuning of the total
21size and/or running time). Reasonable values for r are from 8 to 96.
22
23Set p (parallelism) to 1 meaning no thread-level parallelism within one
24computation of yescrypt. (Use of thread-level parallelism within
25yescrypt makes sense for ROM initialization and for key derivation at
26high memory usage, but usually not for password hashing where
27parallelism is available through concurrent authentication attempts.
28Don't use p > 1 unnecessarily.)
29
30Set t (time) to 0 to use the optimal running time for a given memory
31usage. This will allow you to maximize the memory usage (the value of
32N*r) while staying within your running time constraints. (Non-zero t
33makes sense in specialized cases where you can't afford higher memory
34usage but can afford more time.)
35
36Set g (upgrades) to 0 because there have been no hash upgrades yet.
37
38Set NROM (block count of ROM) to 0 unless you use a ROM (see below).
39NROM must be a power of two.
40
41
42 Password hashing for user authentication, no ROM.
43
44Small and fast (memory usage 2 MiB, performance like bcrypt cost 2^5 -
45latency 2-3 ms and throughput 10,000+ per second on a 16-core server):
46
47flags = YESCRYPT_DEFAULTS, N = 2048, r = 8, p = 1, t = 0, g = 0, NROM = 0
48
49Large and slow (memory usage 16 MiB, performance like bcrypt cost 2^8 -
50latency 10-30 ms and throughput 1000+ per second on a 16-core server):
51
52flags = YESCRYPT_DEFAULTS, N = 4096, r = 32, p = 1, t = 0, g = 0, NROM = 0
53
54Of course, even heavier and slower settings are possible, if affordable.
55Simply double the value of N as many times as needed. Since N must be a
56power of two, you may use r (in the range of 8 to 32) or/and t (in the
57range of 0 to 2) for fine-tuning the running time, but first bring N to
58the maximum you can afford. If this feels too complicated, just use one
59of the two parameter sets given above (preferably the second) as-is.
60
61
62 Password hashing for user authentication, with ROM.
63
64It's similar to the above, except that you need to adjust r, set NROM,
65and initialize the ROM.
66
67First decide on a ROM size, such as making it a large portion of your
68dedicated authentication servers' RAM sizes. Since NROM (block count)
69must be a power of two, you might need to choose r (block size) based on
70how your desired ROM size corresponds to a power of two. Also tuning
71for performance on current hardware, you'll likely end up with r in the
72range from slightly below 16 to 32. For example, to use 15/16 of a
73server's 256 GiB RAM as ROM (thus, making it 240 GiB), you could use
74r=15 or r=30. To use 23/24 of a server's 384 GiB RAM as ROM (thus,
75making it 368 GiB), you'd use r=23. Then set NROM to your desired ROM
76size in KiB divided by 128*r. Note that these examples might (or might
77not) be too extreme, leaving little memory for the rest of the system.
78You could as well opt for 7/8 with r=14 or 11/12 with r=11 or r=22.
79
80Note that higher r may make placing of ROM in e.g. NVMe flash memory
81instead of in RAM more reasonable (or less unreasonable) than it would
82have been with a lower r. If this is a concern as it relates to
83possible attacks and you do not intend to ever do it defensively, you
84might want to keep r lower (e.g., prefer r=15 over r=30 in the example
85above, even if 30 performs slightly faster).
86
87Your adjustments to r, if you deviate from powers of two, will also
88result in weirder memory usage per hash. Like 1.75 MiB at r=14 instead
89of 2 MiB at r=8 that you would have used without a ROM. That's OK.
90
91For ROM initialization, which you do with yescrypt_init_shared(), use
92the same r and NROM that you'd later use for password hashing, choose p
93based on your servers' physical and/or logical CPU count (maybe
94considering eventual upgrades as you won't be able to change this later,
95but without going unnecessarily high - e.g., p=28, p=56, or p=112 make
96sense on servers that currently have 28 physical / 56 logical CPUs), and
97set the rest of the parameters to:
98
99flags = YESCRYPT_DEFAULTS, N = 0, t = 0, g = 0
100
101N is set to 0 because it isn't relevant during ROM initialization (you
102can use different values of N for hashing passwords with the same ROM).
103
104To keep the ROM in e.g. SysV shared memory and reuse it across your
105authentication service restarts, you'd need to allocate the memory and
106set the flags to "YESCRYPT_DEFAULTS | YESCRYPT_SHARED_PREALLOCATED".
107
108For actual password hashing, you'd use your chosen values for N, r,
109NROM, and set the rest of the parameters to:
110
111flags = YESCRYPT_DEFAULTS, p = 1, t = 0, g = 0
112
113Note that although you'd use a large p for ROM initialization, you
114should use p=1 for actual password hashing like you would without a ROM.
115
116Do not forget to pass the ROM into the actual password hashing (and keep
117r and NROM set accordingly).
118
119Since N must be a power of two and r is dependent on ROM size, you may
120use t (in the range of 0 to 2) for fine-tuning the running time, but
121first bring N to the maximum you can afford.
122
123If this feels too complicated, or even if it doesn't, please consider
124engaging Openwall for your yescrypt deployment. We'd be happy to help.
125
126
127 Password-based key derivation.
128
129(Or rather passphrase-based.)
130
131Use settings similar to those for password hashing without a ROM, but
132adjusted for higher memory usage and running time, and optionally with
133thread-level parallelism.
134
135Small and fast (memory usage 128 MiB, running time under 100 ms on a
136fast desktop):
137
138flags = YESCRYPT_DEFAULTS, N = 32768, r = 32, p = 1, t = 0, g = 0, NROM = 0
139
140Large and fast (memory usage 1 GiB, running time under 200 ms on a fast
141quad-core desktop not including memory allocation overhead, under 250 ms
142with the overhead included), but requires build with OpenMP support (or
143otherwise will run as slow as yet be weaker than its p=1 alternative):
144
145flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 4, t = 0, g = 0, NROM = 0
146
147Large and slower (memory usage 1 GiB, running time under 300 ms on a
148fast quad-core desktop not including memory allocation overhead, under
149350 ms with the overhead included), also requires build with OpenMP
150support (or otherwise will run slower than the p=1 alternative below):
151
152flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 4, t = 2, g = 0, NROM = 0
153
154Large and slow (memory usage 1 GiB, running time under 600 ms on a fast
155desktop not including memory allocation overhead, under 650 ms with the
156overhead included):
157
158flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 1, t = 0, g = 0, NROM = 0
159
160Just like with password hashing, even heavier and slower settings are
161possible, if affordable, and you achieve them by adjusting N, r, t in
162the same way and in the same preferred ranges (please see the section on
163password hashing without a ROM, above). Unlike with password hashing,
164it makes some sense to go above t=2 if you expect that your users might
165not be able to afford more memory but can afford more time. However,
166increasing the memory usage provides better protection, and we don't
167recommend forcing your users to wait for more than 1 second as they
168could as well type more characters in that time. If this feels too
169complicated, just use one of the above parameter sets as-is.
170
171
172 Amortization of memory allocation overhead.
173
174It takes a significant fraction of yescrypt's total running time to
175allocate memory from the operating system, especially considering that
176the kernel zeroizes the memory before handing it over to your program.
177
178Unless you naturally need to compute yescrypt just once per process, you
179may achieve greater efficiency by fully using advanced yescrypt APIs
180that let you preserve and reuse the memory allocation across yescrypt
181invocations. This is done by reusing the structure pointed to by the
182"yescrypt_local_t *local" argument of yescrypt_r() or yescrypt_kdf()
183without calling yescrypt_free_local() inbetween the repeated invocations
184of yescrypt.
185
186
187 YESCRYPT_DEFAULTS macro.
188
189Please note that the value of the YESCRYPT_DEFAULTS macro might change
190later, so if you use the macro like it's recommended here then for
191results reproducible across versions you might need to store its value
192somewhere along with the hashes or the encrypted data.
193
194If you use yescrypt's standard hash string encoding, then yescrypt
195already encodes and decodes this value for you, so you don't need to
196worry about this.
diff --git a/libbb/yescrypt/README b/libbb/yescrypt/README
new file mode 100644
index 000000000..c1011c56a
--- /dev/null
+++ b/libbb/yescrypt/README
@@ -0,0 +1,4 @@
1The yescrypt code in this directory is adapted from libxcrypt-4.4.38
2with minimal edits, hopefully making it easier to track
3backports by resetting the tree to the commit which created this file,
4then comparing changes in upstream libxcrypt to the tree.
diff --git a/libbb/yescrypt/alg-sha256.c b/libbb/yescrypt/alg-sha256.c
new file mode 100644
index 000000000..dc748c968
--- /dev/null
+++ b/libbb/yescrypt/alg-sha256.c
@@ -0,0 +1,91 @@
1/*-
2 * Copyright 2005-2016 Colin Percival
3 * Copyright 2016-2018,2021 Alexander Peslyak
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/**
29 * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
30 * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
31 * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
32 */
33static void
34PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen,
35 const uint8_t *salt, size_t saltlen,
36 uint64_t c, uint8_t *buf, size_t dkLen)
37{
38 hmac_ctx_t Phctx, PShctx;
39 uint32_t i;
40
41 /* Compute HMAC state after processing P. */
42 hmac_begin(&Phctx, passwd, passwdlen, sha256_begin);
43
44 /* Compute HMAC state after processing P and S. */
45 PShctx = Phctx;
46 hmac_hash(&PShctx, salt, saltlen);
47
48 /* Iterate through the blocks. */
49 for (i = 0; dkLen != 0; ) {
50 long U[32 / sizeof(long)];
51 long T[32 / sizeof(long)];
52// Do not make these ^^ uint64_t[]. Keep them long[].
53// Even though the XORing loop below is optimized out,
54// gcc is not smart enough to realize that 64-bit alignment of the stack
55// is no longer useful, and generates ~50 more bytes of code on i386...
56 uint32_t ivec;
57 size_t clen;
58 int k;
59
60 /* Generate INT(i). */
61 i++;
62 ivec = SWAP_BE32(i);
63
64 /* Compute U_1 = PRF(P, S || INT(i)). */
65 hmac_peek_hash(&PShctx, (void*)T, &ivec, 4, NULL);
66//TODO: the above is a vararg function, might incur some ABI pain
67//does libbb need a non-vararg version with just one (buf,len)?
68
69 if (c > 1) {
70//in yescrypt, c is always 1, so this if() branch is optimized out
71 uint64_t j;
72 /* T_i = U_1 ... */
73 memcpy(U, T, 32);
74 for (j = 2; j <= c; j++) {
75 /* Compute U_j. */
76 hmac_peek_hash(&Phctx, (void*)U, U, 32, NULL);
77 /* ... xor U_j ... */
78 for (k = 0; k < 32 / sizeof(long); k++)
79 T[k] ^= U[k];
80 //TODO: xorbuf32_aligned_long(T, U);
81 }
82 }
83
84 /* Copy as many bytes as necessary into buf. */
85 clen = dkLen;
86 if (clen > 32)
87 clen = 32;
88 buf = mempcpy(buf, T, clen);
89 dkLen -= clen;
90 }
91}
diff --git a/libbb/yescrypt/alg-yescrypt-common.c b/libbb/yescrypt/alg-yescrypt-common.c
new file mode 100644
index 000000000..c51823787
--- /dev/null
+++ b/libbb/yescrypt/alg-yescrypt-common.c
@@ -0,0 +1,408 @@
1/*-
2 * Copyright 2013-2018 Alexander Peslyak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted.
7 *
8 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
9 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
10 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
11 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
12 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
17 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
18 * SUCH DAMAGE.
19 */
20
21#if RESTRICTED_PARAMS
22
23#define decode64_uint32(dst, src, min) \
24({ \
25 uint32_t d32 = a2i64(*(src)); \
26 if (d32 > 47) \
27 goto fail; \
28 *(dst) = d32 + (min); \
29 ++src; \
30})
31#define test_decode64_uint32() ((void)0)
32#define FULL_PARAMS(...)
33
34#else
35
36#define FULL_PARAMS(...) __VA_ARGS__
37
38/* Not inlining:
39 * de/encode64 functions are only used to read
40 * yescrypt_params_t field, and convert salt to binary -
41 * both of these are negligible compared to main hashing operation
42 */
43static NOINLINE const uint8_t *decode64_uint32(
44 uint32_t *dst,
45 const uint8_t *src, uint32_t val)
46{
47 uint32_t start = 0, end = 47, bits = 0;
48 uint32_t c;
49
50 if (!src) /* previous decode failed already? */
51 goto fail;
52
53 c = a2i64(*src++);
54 if (c > 63)
55 goto fail;
56
57// The encoding of number N:
58// start = 0 end = 47
59// If N < 48, it is encoded verbatim, else
60// N -= 48
61// start = end+1 = 48
62// end += (64-end)/2 = 55
63// If N < (end+1-start)<<6 = 8<<6, it is encoded as 48+(N>>6)|low6bits (that is, 48...55|<6bit>), else
64// N -= 8<<6
65// start = end+1 = 56
66// end += (64-end)/2 = 59
67// If N < (end+1-start)<<2*6 = 4<<12, it is encoded as 56+(N>>2*6)|low12bits (that is, 56...59|<6bit>|<6bit>), else
68// ...same for 60..61|<6bit>|<6bit>|<6bit>
69// .......same for 62|<6bit>|<6bit>|<6bit>|<6bit>
70// .......same for 63|<6bit>|<6bit>|<6bit>|<6bit>|<6bit>
71 dbg_dec64("c:%d val:0x%08x", (int)c, (unsigned)val);
72 while (c > end) {
73 dbg_dec64("c:%d > end:%d", (int)c, (int)end);
74 val += (end + 1 - start) << bits;
75 dbg_dec64("val+=0x%08x", (int)((end + 1 - start) << bits));
76 dbg_dec64(" val:0x%08x", (unsigned)val);
77 start = end + 1;
78 end += (64 - end) / 2;
79 bits += 6;
80 dbg_dec64("start=%d", (int)start);
81 dbg_dec64("end=%d", (int)end);
82 dbg_dec64("bits=%d", (int)bits);
83 }
84
85 val += (c - start) << bits;
86 dbg_dec64("final val+=0x%08x", (int)((c - start) << bits));
87 dbg_dec64(" val:0x%08x", (unsigned)val);
88
89 while (bits != 0) {
90 c = a2i64(*src++);
91 if (c > 63)
92 goto fail;
93 bits -= 6;
94 val += c << bits;
95 dbg_dec64("low bits val+=0x%08x", (int)(c << bits));
96 dbg_dec64(" val:0x%08x", (unsigned)val);
97 }
98 ret:
99 *dst = val;
100 return src;
101 fail:
102 val = 0;
103 src = NULL;
104 goto ret;
105}
106
107#if TEST_DECODE64
108static void test_decode64_uint32(void)
109{
110 const uint8_t *src, *end;
111 uint32_t u32;
112 int a = 48;
113 int b = 8<<6; // 0x0200
114 int c = 4<<12; // 0x04000
115 int d = 2<<18; // 0x080000
116 int e = 1<<24; // 0x1000000
117
118 src = (void*)"wzzz";
119 end = decode64_uint32(&u32, src, 0);
120 if (u32 != 0x0003ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
121 if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
122 src = (void*)"xzzz";
123 end = decode64_uint32(&u32, src, 0);
124 if (u32 != 0x0007ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
125 if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
126 // Note how the last representable "x---" encoding, 0x7ffff, is exactly d-1!
127 // And if we now increment it, we get:
128 src = (void*)"y....";
129 end = decode64_uint32(&u32, src, 0);
130 if (u32 != 0x00000000+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
131 if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
132 src = (void*)"yzzzz";
133 end = decode64_uint32(&u32, src, 0);
134 if (u32 != 0x00ffffff+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
135 if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
136
137 src = (void*)"zzzzzz";
138 end = decode64_uint32(&u32, src, 0);
139 if (u32 != 0x3fffffff+e+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
140 if (end != src + 6) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
141
142 bb_error_msg("test_decode64_uint32() OK");
143}
144#else
145# define test_decode64_uint32() ((void)0)
146#endif
147
148#endif /* !RESTRICTED_PARAMS */
149
150#if 1
151static const uint8_t *decode64(
152 uint8_t *dst, size_t *dstlen,
153 const uint8_t *src)
154{
155 unsigned dstpos = 0;
156
157 dbg_dec64("src:'%s'", src);
158 for (;;) {
159 uint32_t c, value = 0;
160 int bits = 0;
161 while (*src != '\0' && *src != '$') {
162 c = a2i64(*src);
163 if (c > 63) { /* bad ascii64 char, stop decoding at it */
164 break;
165 }
166 src++;
167 value |= c << bits;
168 bits += 6;
169 if (bits == 24) /* got 4 chars */
170 goto store;
171 }
172 /* we read entire src, or met a non-ascii64 char (such as "$") */
173 if (bits == 0)
174 break;
175 /* else: we got last, partial bit block - store it */
176 store:
177 dbg_dec64(" storing bits:%d dstpos:%u v:%08x", bits, dstpos, (int)SWAP_BE32(value)); //BE to see lsb first
178 for (;;) {
179 if ((*src == '\0' || *src == '$')
180 && value == 0 && bits < 8
181 ) {
182 /* Example: mkpasswd PWD '$y$j9T$123':
183 * the "123" is bits:18 value:03,51,00
184 * is considered to be 2 bytes, not 3!
185 *
186 * '$y$j9T$zzz' in upstream fails outright (3rd byte isn't zero).
187 * IOW: for upstream, validity of salt depends on VALUE,
188 * not just size of salt. Which is a bug.
189 * The '$y$j9T$zzz.' salt is the same
190 * (it adds 6 zero msbits) but upstream works with it,
191 * thus '$y$j9T$zzz' should work too and give the same result.
192 */
193 goto end;
194 }
195 if (dstpos >= *dstlen) {
196 dbg_dec64(" ERR: bits:%d dstpos:%u dst[] is too small", bits, dstpos);
197 goto fail;
198 }
199 *dst++ = value;
200 dstpos++;
201 value >>= 8;
202 bits -= 8;
203 if (bits <= 0) /* can get negative, if we e.g. had 6 bits */
204 break;
205 }
206 if (*src == '\0' || *src == '$')
207 break;
208 }
209 end:
210 *dstlen = dstpos;
211 dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
212 return src;
213 fail:
214 /* *dstlen = 0; - not needed, caller detects error by seeing NULL */
215 return NULL;
216}
217#else
218/* Buggy (and larger) original code */
219static const uint8_t *decode64(
220 uint8_t *dst, size_t *dstlen,
221 const uint8_t *src, size_t srclen)
222{
223 size_t dstpos = 0;
224
225 while (dstpos <= *dstlen && srclen) {
226 uint32_t value = 0, bits = 0;
227 while (srclen--) {
228 uint32_t c = a2i64(*src);
229 if (c > 63) {
230 srclen = 0;
231 break;
232 }
233 src++;
234 value |= c << bits;
235 bits += 6;
236 if (bits >= 24)
237 break;
238 }
239 if (!bits)
240 break;
241 if (bits < 12) /* must have at least one full byte */
242 goto fail;
243 dbg_dec64(" storing bits:%d v:%08x", (int)bits, (int)SWAP_BE32(value)); //BE to see lsb first
244 while (dstpos++ < *dstlen) {
245 *dst++ = value;
246 value >>= 8;
247 bits -= 8;
248 if (bits < 8) { /* 2 or 4 */
249 if (value) /* must be 0 */
250 goto fail;
251 bits = 0;
252 break;
253 }
254 }
255 if (bits)
256 goto fail;
257 }
258
259 if (!srclen && dstpos <= *dstlen) {
260 *dstlen = dstpos;
261 dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
262 return src;
263 }
264 fail:
265 /* *dstlen = 0; - not needed, caller detects error by seeing NULL */
266 return NULL;
267}
268#endif
269
270static char *encode64(
271 char *dst, size_t dstlen,
272 const uint8_t *src, size_t srclen)
273{
274 while (srclen) {
275 uint32_t value = 0, b = 0;
276 do {
277 value |= (uint32_t)(*src++ << b);
278 b += 8;
279 srclen--;
280 } while (srclen && b < 24);
281
282 b >>= 3; /* number of bits to number of bytes */
283 b++; /* 1, 2 or 3 bytes will become 2, 3 or 4 ascii64 chars */
284 dstlen -= b;
285 if ((ssize_t)dstlen <= 0)
286 return NULL;
287 dst = num2str64_lsb_first(dst, value, b);
288 }
289 *dst = '\0';
290 return dst;
291}
292
293char *yescrypt_r(
294 const uint8_t *passwd, size_t passwdlen,
295 const uint8_t *setting,
296 char *buf, size_t buflen)
297{
298 struct {
299 yescrypt_ctx_t yctx[1];
300 unsigned char hashbin32[32];
301 } u;
302#define yctx u.yctx
303#define hashbin32 u.hashbin32
304 char *dst;
305 const uint8_t *src, *saltend;
306 size_t need, prefixlen;
307 uint32_t u32;
308
309 test_decode64_uint32();
310
311 memset(yctx, 0, sizeof(yctx));
312 FULL_PARAMS(yctx->param.p = 1;)
313
314 /* we assume setting starts with "$y$" (caller must ensure this) */
315 src = setting + 3;
316
317 src = decode64_uint32(&yctx->param.flags, src, 0);
318 /* "j9T" returns: 0x2f */
319 //if (!src)
320 // goto fail;
321
322 if (yctx->param.flags < YESCRYPT_RW) {
323 dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags);
324 goto fail; // bbox: we don't support scrypt - only yescrypt
325 } else if (yctx->param.flags <= YESCRYPT_RW + (YESCRYPT_RW_FLAVOR_MASK >> 2)) {
326 /* "j9T" sets flags to 0xb6 */
327 yctx->param.flags = YESCRYPT_RW + ((yctx->param.flags - YESCRYPT_RW) << 2);
328 dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags);
329 dbg(" YESCRYPT_RW:%u", !!(yctx->param.flags & YESCRYPT_RW));
330 dbg((yctx->param.flags & YESCRYPT_RW_FLAVOR_MASK) ==
331 (YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
332 ? " YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K"
333 : " flags are not standard"
334 );
335 } else {
336 goto fail;
337 }
338
339 src = decode64_uint32(&u32, src, 1);
340 if (/*!src ||*/ u32 > 63)
341 goto fail;
342 yctx->param.N = (uint64_t)1 << u32;
343 /* "j9T" sets to 4096 (1<<12) */
344 dbg("yctx->param.N=%llu (1<<%u)", (unsigned long long)yctx->param.N, (unsigned)u32);
345
346 src = decode64_uint32(&yctx->param.r, src, 1);
347 /* "j9T" sets to 32 */
348 dbg("yctx->param.r=%u", yctx->param.r);
349
350 if (!src)
351 goto fail;
352 if (*src != '$') {
353#if RESTRICTED_PARAMS
354 goto fail;
355#else
356 src = decode64_uint32(&u32, src, 1);
357 dbg("yescrypt has extended params:0x%x", (unsigned)u32);
358 if (u32 & 1)
359 src = decode64_uint32(&yctx->param.p, src, 2);
360 if (u32 & 2)
361 src = decode64_uint32(&yctx->param.t, src, 1);
362 if (u32 & 4)
363 src = decode64_uint32(&yctx->param.g, src, 1);
364 if (u32 & 8) {
365 src = decode64_uint32(&u32, src, 1);
366 if (/*!src ||*/ u32 > 63)
367 goto fail;
368 yctx->param.NROM = (uint64_t)1 << u32;
369 }
370 if (!src)
371 goto fail;
372 if (*src != '$')
373 goto fail;
374#endif
375 }
376
377 yctx->saltlen = sizeof(yctx->salt);
378 src++; /* now points to salt */
379 saltend = decode64(yctx->salt, &yctx->saltlen, src);
380 if (!saltend || (*saltend != '\0' && *saltend != '$'))
381 goto fail; /* salt[] is too small, or bad char during decode */
382 dbg_dec64("salt is %d ascii64 chars -> %d bytes (in binary)", (int)(saltend - src), (int)yctx->saltlen);
383
384 prefixlen = saltend - setting;
385 need = prefixlen + 1 + YESCRYPT_HASH_LEN + 1;
386 if (need > buflen /*overflow is quite unlikely: || need < prefixlen*/)
387 goto fail;
388
389 if (yescrypt_kdf32(yctx, passwd, passwdlen, hashbin32)) {
390 dbg("error in yescrypt_kdf32");
391 goto fail;
392 }
393
394 dst = mempcpy(buf, setting, prefixlen);
395 *dst++ = '$';
396 dst = encode64(dst, buflen - (dst - buf), hashbin32, sizeof(hashbin32));
397 if (!dst)
398 goto fail;
399 ret:
400 free_region(yctx->local);
401 explicit_bzero(&u, sizeof(u));
402 return buf;
403 fail:
404 buf = NULL;
405 goto ret;
406#undef yctx
407#undef hashbin32
408}
diff --git a/libbb/yescrypt/alg-yescrypt-kdf.c b/libbb/yescrypt/alg-yescrypt-kdf.c
new file mode 100644
index 000000000..a9a1bd591
--- /dev/null
+++ b/libbb/yescrypt/alg-yescrypt-kdf.c
@@ -0,0 +1,1212 @@
1/*-
2 * Copyright 2009 Colin Percival
3 * Copyright 2012-2018 Alexander Peslyak
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * This file was originally written by Colin Percival as part of the Tarsnap
28 * online backup system.
29 */
30
31#if __STDC_VERSION__ >= 199901L
32/* Have restrict */
33#elif defined(__GNUC__)
34#define restrict __restrict
35#else
36#define restrict
37#endif
38
39#ifdef __GNUC__
40#define unlikely(exp) __builtin_expect(exp, 0)
41#else
42#define unlikely(exp) (exp)
43#endif
44
45typedef union {
46 uint32_t w[16];
47 uint64_t d[8];
48} salsa20_blk_t;
49
50static void salsa20_simd_shuffle(
51 const salsa20_blk_t *Bin,
52 salsa20_blk_t *Bout)
53{
54#define COMBINE(out, in1, in2) \
55do { \
56 Bout->d[out] = Bin->w[in1 * 2] | ((uint64_t)Bin->w[in2 * 2 + 1] << 32); \
57} while (0)
58 COMBINE(0, 0, 2);
59 COMBINE(1, 5, 7);
60 COMBINE(2, 2, 4);
61 COMBINE(3, 7, 1);
62 COMBINE(4, 4, 6);
63 COMBINE(5, 1, 3);
64 COMBINE(6, 6, 0);
65 COMBINE(7, 3, 5);
66#undef COMBINE
67}
68
69static void salsa20_simd_unshuffle(
70 const salsa20_blk_t *Bin,
71 salsa20_blk_t *Bout)
72{
73#define UNCOMBINE(out, in1, in2) \
74do { \
75 Bout->w[out * 2] = Bin->d[in1]; \
76 Bout->w[out * 2 + 1] = Bin->d[in2] >> 32; \
77} while (0)
78 UNCOMBINE(0, 0, 6);
79 UNCOMBINE(1, 5, 3);
80 UNCOMBINE(2, 2, 0);
81 UNCOMBINE(3, 7, 5);
82 UNCOMBINE(4, 4, 2);
83 UNCOMBINE(5, 1, 7);
84 UNCOMBINE(6, 6, 4);
85 UNCOMBINE(7, 3, 1);
86#undef UNCOMBINE
87}
88
89#define DECL_X \
90 salsa20_blk_t X
91#define DECL_Y \
92 salsa20_blk_t Y
93
94#if KDF_UNROLL_COPY
95#define COPY(out, in) \
96do { \
97 (out).d[0] = (in).d[0]; \
98 (out).d[1] = (in).d[1]; \
99 (out).d[2] = (in).d[2]; \
100 (out).d[3] = (in).d[3]; \
101 (out).d[4] = (in).d[4]; \
102 (out).d[5] = (in).d[5]; \
103 (out).d[6] = (in).d[6]; \
104 (out).d[7] = (in).d[7]; \
105} while (0)
106#else
107#define COPY(out, in) \
108do { \
109 memcpy((out).d, (in).d, sizeof((in).d)); \
110} while (0)
111#endif
112
113#define READ_X(in) COPY(X, in)
114#define WRITE_X(out) COPY(out, X)
115
116/**
117 * salsa20(B):
118 * Apply the Salsa20 core to the provided block.
119 */
120static void salsa20(salsa20_blk_t *restrict B,
121 salsa20_blk_t *restrict Bout,
122 uint32_t doublerounds)
123{
124 salsa20_blk_t X;
125#define x X.w
126
127 salsa20_simd_unshuffle(B, &X);
128
129 do {
130#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
131 /* Operate on columns */
132#if KDF_UNROLL_SALSA20
133 x[ 4] ^= R(x[ 0]+x[12], 7); // x[j] ^= R(x[k]+x[l], CONST)
134 x[ 8] ^= R(x[ 4]+x[ 0], 9);
135 x[12] ^= R(x[ 8]+x[ 4],13);
136 x[ 0] ^= R(x[12]+x[ 8],18);
137
138 x[ 9] ^= R(x[ 5]+x[ 1], 7);
139 x[13] ^= R(x[ 9]+x[ 5], 9);
140 x[ 1] ^= R(x[13]+x[ 9],13);
141 x[ 5] ^= R(x[ 1]+x[13],18);
142
143 x[14] ^= R(x[10]+x[ 6], 7);
144 x[ 2] ^= R(x[14]+x[10], 9);
145 x[ 6] ^= R(x[ 2]+x[14],13);
146 x[10] ^= R(x[ 6]+x[ 2],18);
147
148 x[ 3] ^= R(x[15]+x[11], 7);
149 x[ 7] ^= R(x[ 3]+x[15], 9);
150 x[11] ^= R(x[ 7]+x[ 3],13);
151 x[15] ^= R(x[11]+x[ 7],18);
152#else
153 {
154 unsigned j, k, l;
155 j = 4; k = 0; l = 12;
156 for (;;) {
157 uint32_t t;
158 x[j] ^= ({ t = x[k] + x[l]; R(t, 7); }); l = k; k = j; j = (j+4) & 0xf;
159 x[j] ^= ({ t = x[k] + x[l]; R(t, 9); }); l = k; k = j; j = (j+4) & 0xf;
160 x[j] ^= ({ t = x[k] + x[l]; R(t,13); }); l = k; k = j; j = (j+4) & 0xf;
161 x[j] ^= ({ t = x[k] + x[l]; R(t,18); });
162 if (j == 15) break;
163 l = j + 1; k = j + 5; j = (j+9) & 0xf;
164 }
165 }
166#endif
167 /* Operate on rows */
168#if KDF_UNROLL_SALSA20
169// i=0 n=0
170 x[ 1] ^= R(x[ 0]+x[ 3], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
171 x[ 2] ^= R(x[ 1]+x[ 0], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
172 x[ 3] ^= R(x[ 2]+x[ 1],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
173 x[ 0] ^= R(x[ 3]+x[ 2],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
174// i=4 n=1 ^^^j^^^ ^^^k^^^ ^^^l^^^
175 x[ 6] ^= R(x[ 5]+x[ 4], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
176 x[ 7] ^= R(x[ 6]+x[ 5], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
177 x[ 4] ^= R(x[ 7]+x[ 6],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
178 x[ 5] ^= R(x[ 4]+x[ 7],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
179// i=8 n=2
180 x[11] ^= R(x[10]+x[ 9], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
181 x[ 8] ^= R(x[11]+x[10], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
182 x[ 9] ^= R(x[ 8]+x[11],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
183 x[10] ^= R(x[ 9]+x[ 8],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
184// i=12 n=3
185 x[12] ^= R(x[15]+x[14], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
186 x[13] ^= R(x[12]+x[15], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
187 x[14] ^= R(x[13]+x[12],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
188 x[15] ^= R(x[14]+x[13],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
189#else
190 {
191 unsigned j, k, l;
192 uint32_t *xrow;
193 j = 1; k = 0; l = 3;
194 xrow = &x[0];
195 for (;;) {
196 uint32_t t;
197 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t, 7); }); l = k; k = j; j = (j+1) & 3;
198 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t, 9); }); l = k; k = j; j = (j+1) & 3;
199 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t,13); }); l = k; k = j; j = (j+1) & 3;
200 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t,18); });
201 if (j == 3) break;
202 l = j; k = j + 1; j = (j+2) & 3;
203 xrow += 4;
204 }
205 }
206#endif
207
208#undef R
209 } while (--doublerounds);
210#undef x
211
212 {
213 uint32_t i;
214 salsa20_simd_shuffle(&X, Bout);
215 for (i = 0; i < 16; i++) {
216 // bbox: note: was unrolled x4
217 B->w[i] = Bout->w[i] += B->w[i];
218 }
219 }
220#if 0
221 /* Too expensive */
222 explicit_bzero(&X, sizeof(X));
223#endif
224}
225
226/**
227 * Apply the Salsa20/2 core to the block provided in X.
228 */
229#define SALSA20_2(out) \
230 salsa20(&X, &out, 1)
231
232#if 0
233#define XOR(out, in1, in2) \
234do { \
235 (out).d[0] = (in1).d[0] ^ (in2).d[0]; \
236 (out).d[1] = (in1).d[1] ^ (in2).d[1]; \
237 (out).d[2] = (in1).d[2] ^ (in2).d[2]; \
238 (out).d[3] = (in1).d[3] ^ (in2).d[3]; \
239 (out).d[4] = (in1).d[4] ^ (in2).d[4]; \
240 (out).d[5] = (in1).d[5] ^ (in2).d[5]; \
241 (out).d[6] = (in1).d[6] ^ (in2).d[6]; \
242 (out).d[7] = (in1).d[7] ^ (in2).d[7]; \
243} while (0)
244#else
245#define XOR(out, in1, in2) \
246do { \
247 xorbuf64_3_aligned64(&(out).d, &(in1).d, &(in2).d); \
248} while (0)
249#endif
250
251#define XOR_X(in) XOR(X, X, in)
252#define XOR_X_2(in1, in2) XOR(X, in1, in2)
253#define XOR_X_WRITE_XOR_Y_2(out, in) \
254do { \
255 XOR(Y, out, in); \
256 COPY(out, Y); \
257 XOR(X, X, Y); \
258} while (0)
259
260/**
261 * Apply the Salsa20/8 core to the block provided in X ^ in.
262 */
263#define SALSA20_8_XOR_MEM(in, out) \
264do { \
265 XOR_X(in); \
266 salsa20(&X, &out, 4); \
267} while (0)
268
269#define INTEGERIFY ((uint32_t)X.d[0])
270
271/**
272 * blockmix_salsa8(Bin, Bout, r):
273 * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r
274 * bytes in length; the output Bout must also be the same size.
275 */
276static void blockmix_salsa8(
277 const salsa20_blk_t *restrict Bin,
278 salsa20_blk_t *restrict Bout,
279 size_t r)
280{
281 size_t i;
282 DECL_X;
283
284 READ_X(Bin[r * 2 - 1]);
285 for (i = 0; i < r; i++) {
286 SALSA20_8_XOR_MEM(Bin[i * 2], Bout[i]);
287 SALSA20_8_XOR_MEM(Bin[i * 2 + 1], Bout[r + i]);
288 }
289}
290
291static uint32_t blockmix_salsa8_xor(
292 const salsa20_blk_t *restrict Bin1,
293 const salsa20_blk_t *restrict Bin2,
294 salsa20_blk_t *restrict Bout,
295 size_t r)
296{
297 size_t i;
298 DECL_X;
299
300 XOR_X_2(Bin1[r * 2 - 1], Bin2[r * 2 - 1]);
301 for (i = 0; i < r; i++) {
302 XOR_X(Bin1[i * 2]);
303 SALSA20_8_XOR_MEM(Bin2[i * 2], Bout[i]);
304 XOR_X(Bin1[i * 2 + 1]);
305 SALSA20_8_XOR_MEM(Bin2[i * 2 + 1], Bout[r + i]);
306 }
307
308 return INTEGERIFY;
309}
310
311/* This is tunable */
312#define Swidth 8
313
314/* Not tunable in this implementation, hard-coded in a few places */
315#define PWXsimple 2
316#define PWXgather 4
317
318/* Derived values. Not tunable except via Swidth above. */
319#define PWXbytes (PWXgather * PWXsimple * 8)
320#define Sbytes (3 * (1 << Swidth) * PWXsimple * 8)
321#define Smask (((1 << Swidth) - 1) * PWXsimple * 8)
322#define Smask2 (((uint64_t)Smask << 32) | Smask)
323
324#define DECL_SMASK2REG do {} while (0)
325#define FORCE_REGALLOC_3 do {} while (0)
326#define MAYBE_MEMORY_BARRIER do {} while (0)
327
328#define PWXFORM_SIMD(x0, x1) \
329do { \
330 uint64_t x = x0 & Smask2; \
331 uint64_t *p0 = (uint64_t *)(S0 + (uint32_t)x); \
332 uint64_t *p1 = (uint64_t *)(S1 + (x >> 32)); \
333 x0 = ((x0 >> 32) * (uint32_t)x0 + p0[0]) ^ p1[0]; \
334 x1 = ((x1 >> 32) * (uint32_t)x1 + p0[1]) ^ p1[1]; \
335} while (0)
336
337#if KDF_UNROLL_PWXFORM_ROUND
338#define PWXFORM_ROUND \
339do { \
340 PWXFORM_SIMD(X.d[0], X.d[1]); \
341 PWXFORM_SIMD(X.d[2], X.d[3]); \
342 PWXFORM_SIMD(X.d[4], X.d[5]); \
343 PWXFORM_SIMD(X.d[6], X.d[7]); \
344} while (0)
345#else
346#define PWXFORM_ROUND \
347do { \
348 for (int pwxi=0; pwxi<8; pwxi+=2) \
349 PWXFORM_SIMD(X.d[pwxi], X.d[pwxi + 1]); \
350} while (0)
351#endif
352
353/*
354 * This offset helps address the 256-byte write block via the single-byte
355 * displacements encodable in x86(-64) instructions. It is needed because the
356 * displacements are signed. Without it, we'd get 4-byte displacements for
357 * half of the writes. Setting it to 0x80 instead of 0x7c would avoid needing
358 * a displacement for one of the writes, but then the LEA instruction would
359 * need a 4-byte displacement.
360 */
361#define PWXFORM_WRITE_OFFSET 0x7c
362
363#define PWXFORM_WRITE \
364do { \
365 WRITE_X(*(salsa20_blk_t *)(Sw - PWXFORM_WRITE_OFFSET)); \
366 Sw += 64; \
367} while (0)
368
369#if KDF_UNROLL_PWXFORM
370#define PWXFORM \
371do { \
372 uint8_t *Sw = S2 + w + PWXFORM_WRITE_OFFSET; \
373 FORCE_REGALLOC_3; \
374 MAYBE_MEMORY_BARRIER; \
375 PWXFORM_ROUND; \
376 PWXFORM_ROUND; PWXFORM_WRITE; \
377 PWXFORM_ROUND; PWXFORM_WRITE; \
378 PWXFORM_ROUND; PWXFORM_WRITE; \
379 PWXFORM_ROUND; PWXFORM_WRITE; \
380 PWXFORM_ROUND; \
381 w = (w + 64 * 4) & Smask2; \
382 { \
383 uint8_t *Stmp = S2; \
384 S2 = S1; \
385 S1 = S0; \
386 S0 = Stmp; \
387 } \
388} while (0)
389#else
390#define PWXFORM \
391do { \
392 uint8_t *Sw = S2 + w + PWXFORM_WRITE_OFFSET; \
393 FORCE_REGALLOC_3; \
394 MAYBE_MEMORY_BARRIER; \
395 PWXFORM_ROUND; \
396 for (int pwxj=0; pwxj<4; pwxj++) {\
397 PWXFORM_ROUND; PWXFORM_WRITE; \
398 } \
399 PWXFORM_ROUND; \
400 w = (w + 64 * 4) & Smask2; \
401 { \
402 uint8_t *Stmp = S2; \
403 S2 = S1; \
404 S1 = S0; \
405 S0 = Stmp; \
406 } \
407} while (0)
408#endif
409
410typedef struct {
411 uint8_t *S0, *S1, *S2;
412 size_t w;
413} pwxform_ctx_t;
414
415#define Salloc (Sbytes + ((sizeof(pwxform_ctx_t) + 63) & ~63U))
416
417/**
418 * blockmix_pwxform(Bin, Bout, r, S):
419 * Compute Bout = BlockMix_pwxform{salsa20/2, r, S}(Bin). The input Bin must
420 * be 128r bytes in length; the output Bout must also be the same size.
421 */
422static void blockmix(
423 const salsa20_blk_t *restrict Bin,
424 salsa20_blk_t *restrict Bout,
425 size_t r,
426 pwxform_ctx_t *restrict ctx)
427{
428 uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
429 size_t w = ctx->w;
430 size_t i;
431 DECL_X;
432
433 /* Convert count of 128-byte blocks to max index of 64-byte block */
434 r = r * 2 - 1;
435
436 READ_X(Bin[r]);
437
438 DECL_SMASK2REG;
439
440 i = 0;
441 for (;;) {
442 XOR_X(Bin[i]);
443 PWXFORM;
444 if (unlikely(i >= r))
445 break;
446 WRITE_X(Bout[i]);
447 i++;
448 }
449
450 ctx->S0 = S0;
451 ctx->S1 = S1;
452 ctx->S2 = S2;
453 ctx->w = w;
454
455 SALSA20_2(Bout[i]);
456}
457
458static uint32_t blockmix_xor(
459 const salsa20_blk_t *Bin1,
460 const salsa20_blk_t *restrict Bin2,
461 salsa20_blk_t *Bout,
462 size_t r,
463 pwxform_ctx_t *restrict ctx)
464{
465 uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
466 size_t w = ctx->w;
467 size_t i;
468 DECL_X;
469
470 /* Convert count of 128-byte blocks to max index of 64-byte block */
471 r = r * 2 - 1;
472
473 XOR_X_2(Bin1[r], Bin2[r]);
474
475 DECL_SMASK2REG;
476
477 i = 0;
478 r--;
479 for (;;) {
480 XOR_X(Bin1[i]);
481 XOR_X(Bin2[i]);
482 PWXFORM;
483 if (unlikely(i > r))
484 break;
485 WRITE_X(Bout[i]);
486 i++;
487 }
488
489 ctx->S0 = S0;
490 ctx->S1 = S1;
491 ctx->S2 = S2;
492 ctx->w = w;
493
494 SALSA20_2(Bout[i]);
495
496 return INTEGERIFY;
497}
498
499static uint32_t blockmix_xor_save(
500 salsa20_blk_t *restrict Bin1out,
501 salsa20_blk_t *restrict Bin2,
502 size_t r,
503 pwxform_ctx_t *restrict ctx)
504{
505 uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
506 size_t w = ctx->w;
507 size_t i;
508 DECL_X;
509 DECL_Y;
510
511 /* Convert count of 128-byte blocks to max index of 64-byte block */
512 r = r * 2 - 1;
513
514 XOR_X_2(Bin1out[r], Bin2[r]);
515
516 DECL_SMASK2REG;
517
518 i = 0;
519 r--;
520 for (;;) {
521 XOR_X_WRITE_XOR_Y_2(Bin2[i], Bin1out[i]);
522 PWXFORM;
523 if (unlikely(i > r))
524 break;
525 WRITE_X(Bin1out[i]);
526 i++;
527 }
528
529 ctx->S0 = S0;
530 ctx->S1 = S1;
531 ctx->S2 = S2;
532 ctx->w = w;
533
534 SALSA20_2(Bin1out[i]);
535
536 return INTEGERIFY;
537}
538
539/**
540 * integerify(B, r):
541 * Return the result of parsing B_{2r-1} as a little-endian integer.
542 */
543static inline uint32_t integerify(const salsa20_blk_t *B, size_t r)
544{
545/*
546 * Our 64-bit words are in host byte order, which is why we don't just read
547 * w[0] here (would be wrong on big-endian). Also, our 32-bit words are
548 * SIMD-shuffled (so the next 32 bits would be part of d[6]), but currently
549 * this does not matter as we only care about the least significant 32 bits.
550 */
551 return (uint32_t)B[2 * r - 1].d[0];
552}
553
554/**
555 * smix1(B, r, N, flags, V, NROM, VROM, XY, ctx):
556 * Compute first loop of B = SMix_r(B, N). The input B must be 128r bytes in
557 * length; the temporary storage V must be 128rN bytes in length; the temporary
558 * storage XY must be 128r+64 bytes in length. N must be even and at least 4.
559 * The array V must be aligned to a multiple of 64 bytes, and arrays B and XY
560 * to a multiple of at least 16 bytes.
561 */
562#if DISABLE_NROM_CODE
563#define smix1(B,r,N,flags,V,NROM,VROM,XY,ctx) \
564 smix1(B,r,N,flags,V,XY,ctx)
565#endif
566static void smix1(uint8_t *B, size_t r, uint32_t N,
567 uint32_t flags,
568 salsa20_blk_t *V,
569 uint32_t NROM, const salsa20_blk_t *VROM,
570 salsa20_blk_t *XY,
571 pwxform_ctx_t *ctx)
572{
573#if DISABLE_NROM_CODE
574 uint32_t NROM = 0;
575 const salsa20_blk_t *VROM = NULL;
576#endif
577 size_t s = 2 * r;
578 salsa20_blk_t *X = V, *Y = &V[s];
579 uint32_t i, j;
580
581 for (i = 0; i < 2 * r; i++) {
582 const salsa20_blk_t *src = (salsa20_blk_t *)&B[i * 64];
583 salsa20_blk_t *tmp = Y;
584 salsa20_blk_t *dst = &X[i];
585 size_t k;
586 for (k = 0; k < 16; k++)
587 tmp->w[k] = SWAP_LE32(src->w[k]);
588 salsa20_simd_shuffle(tmp, dst);
589 }
590
591 if (VROM) {
592 uint32_t n;
593 const salsa20_blk_t *V_j;
594
595 V_j = &VROM[(NROM - 1) * s];
596 j = blockmix_xor(X, V_j, Y, r, ctx) & (NROM - 1);
597 V_j = &VROM[j * s];
598 X = Y + s;
599 j = blockmix_xor(Y, V_j, X, r, ctx);
600
601 for (n = 2; n < N; n <<= 1) {
602 uint32_t m = (n < N / 2) ? n : (N - 1 - n);
603 for (i = 1; i < m; i += 2) {
604 j &= n - 1;
605 j += i - 1;
606 V_j = &V[j * s];
607 Y = X + s;
608 j = blockmix_xor(X, V_j, Y, r, ctx) & (NROM - 1);
609 V_j = &VROM[j * s];
610 X = Y + s;
611 j = blockmix_xor(Y, V_j, X, r, ctx);
612 }
613 }
614 n >>= 1;
615
616 j &= n - 1;
617 j += N - 2 - n;
618 V_j = &V[j * s];
619 Y = X + s;
620 j = blockmix_xor(X, V_j, Y, r, ctx) & (NROM - 1);
621 V_j = &VROM[j * s];
622 blockmix_xor(Y, V_j, XY, r, ctx);
623 } else if (flags & YESCRYPT_RW) {
624//can't use flags___YESCRYPT_RW, smix1() may be called with flags = 0
625 uint32_t n;
626 salsa20_blk_t *V_j;
627
628 blockmix(X, Y, r, ctx);
629 X = Y + s;
630 blockmix(Y, X, r, ctx);
631 j = integerify(X, r);
632
633 for (n = 2; n < N; n <<= 1) {
634 uint32_t m = (n < N / 2) ? n : (N - 1 - n);
635 for (i = 1; i < m; i += 2) {
636 Y = X + s;
637 j &= n - 1;
638 j += i - 1;
639 V_j = &V[j * s];
640 j = blockmix_xor(X, V_j, Y, r, ctx);
641 j &= n - 1;
642 j += i;
643 V_j = &V[j * s];
644 X = Y + s;
645 j = blockmix_xor(Y, V_j, X, r, ctx);
646 }
647 }
648 n >>= 1;
649
650 j &= n - 1;
651 j += N - 2 - n;
652 V_j = &V[j * s];
653 Y = X + s;
654 j = blockmix_xor(X, V_j, Y, r, ctx);
655 j &= n - 1;
656 j += N - 1 - n;
657 V_j = &V[j * s];
658 blockmix_xor(Y, V_j, XY, r, ctx);
659 } else {
660 N -= 2;
661 do {
662 blockmix_salsa8(X, Y, r);
663 X = Y + s;
664 blockmix_salsa8(Y, X, r);
665 Y = X + s;
666 } while ((N -= 2));
667
668 blockmix_salsa8(X, Y, r);
669 blockmix_salsa8(Y, XY, r);
670 }
671
672 for (i = 0; i < 2 * r; i++) {
673 const salsa20_blk_t *src = &XY[i];
674 salsa20_blk_t *tmp = &XY[s];
675 salsa20_blk_t *dst = (salsa20_blk_t *)&B[i * 64];
676 size_t k;
677 for (k = 0; k < 16; k++)
678 tmp->w[k] = SWAP_LE32(src->w[k]);
679 salsa20_simd_unshuffle(tmp, dst);
680 }
681}
682
683/**
684 * smix2(B, r, N, Nloop, flags, V, NROM, VROM, XY, ctx):
685 * Compute second loop of B = SMix_r(B, N). The input B must be 128r bytes in
686 * length; the temporary storage V must be 128rN bytes in length; the temporary
687 * storage XY must be 256r bytes in length. N must be a power of 2 and at
688 * least 2. Nloop must be even. The array V must be aligned to a multiple of
689 * 64 bytes, and arrays B and XY to a multiple of at least 16 bytes.
690 */
691#if DISABLE_NROM_CODE
692#define smix2(B,r,N,Nloop,flags,V,NROM,VROM,XY,ctx) \
693 smix2(B,r,N,Nloop,flags,V,XY,ctx)
694#endif
695static void smix2(uint8_t *B, size_t r, uint32_t N, uint64_t Nloop,
696 uint32_t flags,
697 salsa20_blk_t *V,
698 uint32_t NROM, const salsa20_blk_t *VROM,
699 salsa20_blk_t *XY,
700 pwxform_ctx_t *ctx)
701{
702#if DISABLE_NROM_CODE
703 uint32_t NROM = 0;
704 const salsa20_blk_t *VROM = NULL;
705#endif
706 size_t s = 2 * r;
707 salsa20_blk_t *X = XY, *Y = &XY[s];
708 uint32_t i, j;
709
710 if (Nloop == 0)
711 return;
712
713 for (i = 0; i < 2 * r; i++) {
714 const salsa20_blk_t *src = (salsa20_blk_t *)&B[i * 64];
715 salsa20_blk_t *tmp = Y;
716 salsa20_blk_t *dst = &X[i];
717 size_t k;
718 for (k = 0; k < 16; k++)
719 tmp->w[k] = SWAP_LE32(src->w[k]);
720 salsa20_simd_shuffle(tmp, dst);
721 }
722
723 j = integerify(X, r) & (N - 1);
724
725/*
726 * Normally, VROM implies YESCRYPT_RW, but we check for these separately
727 * because our SMix resets YESCRYPT_RW for the smix2() calls operating on the
728 * entire V when p > 1.
729 */
730//and this is why bbox can't use flags___YESCRYPT_RW in this function
731 if (VROM && (flags & YESCRYPT_RW)) {
732 do {
733 salsa20_blk_t *V_j = &V[j * s];
734 const salsa20_blk_t *VROM_j;
735 j = blockmix_xor_save(X, V_j, r, ctx) & (NROM - 1);
736 VROM_j = &VROM[j * s];
737 j = blockmix_xor(X, VROM_j, X, r, ctx) & (N - 1);
738 } while (Nloop -= 2);
739 } else if (VROM) {
740 do {
741 const salsa20_blk_t *V_j = &V[j * s];
742 j = blockmix_xor(X, V_j, X, r, ctx) & (NROM - 1);
743 V_j = &VROM[j * s];
744 j = blockmix_xor(X, V_j, X, r, ctx) & (N - 1);
745 } while (Nloop -= 2);
746 } else if (flags & YESCRYPT_RW) {
747 do {
748 salsa20_blk_t *V_j = &V[j * s];
749 j = blockmix_xor_save(X, V_j, r, ctx) & (N - 1);
750 V_j = &V[j * s];
751 j = blockmix_xor_save(X, V_j, r, ctx) & (N - 1);
752 } while (Nloop -= 2);
753 } else if (ctx) {
754 do {
755 const salsa20_blk_t *V_j = &V[j * s];
756 j = blockmix_xor(X, V_j, X, r, ctx) & (N - 1);
757 V_j = &V[j * s];
758 j = blockmix_xor(X, V_j, X, r, ctx) & (N - 1);
759 } while (Nloop -= 2);
760 } else {
761 do {
762 const salsa20_blk_t *V_j = &V[j * s];
763 j = blockmix_salsa8_xor(X, V_j, Y, r) & (N - 1);
764 V_j = &V[j * s];
765 j = blockmix_salsa8_xor(Y, V_j, X, r) & (N - 1);
766 } while (Nloop -= 2);
767 }
768
769 for (i = 0; i < 2 * r; i++) {
770 const salsa20_blk_t *src = &X[i];
771 salsa20_blk_t *tmp = Y;
772 salsa20_blk_t *dst = (salsa20_blk_t *)&B[i * 64];
773 size_t k;
774 for (k = 0; k < 16; k++)
775 tmp->w[k] = SWAP_LE32(src->w[k]);
776 salsa20_simd_unshuffle(tmp, dst);
777 }
778}
779
780/**
781 * p2floor(x):
782 * Largest power of 2 not greater than argument.
783 */
784static uint64_t p2floor(uint64_t x)
785{
786 uint64_t y;
787 while ((y = x & (x - 1)))
788 x = y;
789 return x;
790}
791
792/**
793 * smix(B, r, N, p, t, flags, V, NROM, VROM, XY, S, passwd):
794 * Compute B = SMix_r(B, N). The input B must be 128rp bytes in length; the
795 * temporary storage V must be 128rN bytes in length; the temporary storage
796 * XY must be 256r or 256rp bytes in length (the larger size is required with
797 * OpenMP-enabled builds). N must be a power of 2 and at least 4. The array V
798 * must be aligned to a multiple of 64 bytes, and arrays B and XY to a multiple
799 * of at least 16 bytes (aligning them to 64 bytes as well saves cache lines
800 * and helps avoid false sharing in OpenMP-enabled builds when p > 1, but it
801 * might also result in cache bank conflicts).
802 */
803#if DISABLE_NROM_CODE
804#define smix(B,r,N,p,t,flags,V,NROM,VROM,XY,S,passwd) \
805 smix(B,r,N,p,t,flags,V,XY,S,passwd)
806#endif
807static void smix(uint8_t *B, size_t r, uint32_t N, uint32_t p, uint32_t t,
808 uint32_t flags,
809 salsa20_blk_t *V,
810 uint32_t NROM, const salsa20_blk_t *VROM,
811 salsa20_blk_t *XY,
812 uint8_t *S, uint8_t *passwd)
813{
814 size_t s = 2 * r;
815 uint32_t Nchunk;
816 uint64_t Nloop_all, Nloop_rw;
817 uint32_t i;
818
819 Nchunk = N / p;
820 Nloop_all = Nchunk;
821 if (flags___YESCRYPT_RW) {
822 if (t <= 1) {
823 if (t)
824 Nloop_all *= 2; /* 2/3 */
825 Nloop_all = (Nloop_all + 2) / 3; /* 1/3, round up */
826 } else {
827 Nloop_all *= t - 1;
828 }
829 } else if (t) {
830 if (t == 1)
831 Nloop_all += (Nloop_all + 1) / 2; /* 1.5, round up */
832 Nloop_all *= t;
833 }
834
835 Nloop_rw = 0;
836 if (flags___YESCRYPT_RW)
837 Nloop_rw = Nloop_all / p;
838
839 Nchunk &= ~(uint32_t)1; /* round down to even */
840 Nloop_all++; Nloop_all &= ~(uint64_t)1; /* round up to even */
841 Nloop_rw++; Nloop_rw &= ~(uint64_t)1; /* round up to even */
842
843 for (i = 0; i < p; i++) {
844 uint32_t Vchunk = i * Nchunk;
845 uint32_t Np = (i < p - 1) ? Nchunk : (N - Vchunk);
846 uint8_t *Bp = &B[128 * r * i];
847 salsa20_blk_t *Vp = &V[Vchunk * s];
848 salsa20_blk_t *XYp = XY;
849 pwxform_ctx_t *ctx_i = NULL;
850 if (flags___YESCRYPT_RW) {
851 uint8_t *Si = S + i * Salloc;
852 smix1(Bp, 1, Sbytes / 128, 0 /* no flags */,
853 (salsa20_blk_t *)Si, 0, NULL, XYp, NULL);
854 ctx_i = (pwxform_ctx_t *)(Si + Sbytes);
855 ctx_i->S2 = Si;
856 ctx_i->S1 = Si + Sbytes / 3;
857 ctx_i->S0 = Si + Sbytes / 3 * 2;
858 ctx_i->w = 0;
859 if (i == 0)
860 hmac_block(
861 /* key,len: */ Bp + (128 * r - 64), 64,
862 /* hash fn: */ sha256_begin,
863 /* in,len: */ passwd, 32,
864 /* outbuf: */ passwd
865 );
866 }
867 smix1(Bp, r, Np, flags, Vp, NROM, VROM, XYp, ctx_i);
868 smix2(Bp, r, p2floor(Np), Nloop_rw, flags, Vp,
869 NROM, VROM, XYp, ctx_i);
870 }
871
872 if (Nloop_all > Nloop_rw) {
873 for (i = 0; i < p; i++) {
874 uint8_t *Bp = &B[128 * r * i];
875 salsa20_blk_t *XYp = XY;
876 pwxform_ctx_t *ctx_i = NULL;
877 if (flags___YESCRYPT_RW) {
878 uint8_t *Si = S + i * Salloc;
879 ctx_i = (pwxform_ctx_t *)(Si + Sbytes);
880 }
881 smix2(Bp, r, N, Nloop_all - Nloop_rw,
882 flags & (uint32_t)~YESCRYPT_RW,
883 V, NROM, VROM, XYp, ctx_i);
884 }
885 }
886}
887
888/* Allocator code */
889
890static void alloc_region(yescrypt_region_t *region, size_t size)
891{
892 uint8_t *base;
893 int flags =
894# ifdef MAP_NOCORE /* huh? */
895 MAP_NOCORE |
896# endif
897 MAP_ANON | MAP_PRIVATE;
898
899 base = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, -1, 0);
900 if (base == MAP_FAILED)
901 bb_die_memory_exhausted();
902
903#if defined(MADV_HUGEPAGE)
904 /* Reduces mkpasswd qweRTY123@-+ '$y$jHT$123'
905 * (which allocates 4 Gbytes)
906 * run time from 10.543s to 5.635s
907 * Seen on linux-5.18.0.
908 */
909 madvise(base, size, MADV_HUGEPAGE);
910#endif
911 //region->base = base;
912 //region->base_size = size;
913 region->aligned = base;
914 region->aligned_size = size;
915}
916
917static void free_region(yescrypt_region_t *region)
918{
919 if (region->aligned)
920 munmap(region->aligned, region->aligned_size);
921 //region->base = NULL;
922 //region->base_size = 0;
923 region->aligned = NULL;
924 region->aligned_size = 0;
925}
926/**
927 * yescrypt_kdf_body(shared, local, passwd, passwdlen, salt, saltlen,
928 * flags, N, r, p, t, NROM, buf, buflen):
929 * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
930 * p, buflen), or a revision of scrypt as requested by flags and shared, and
931 * write the result into buf.
932 *
933 * shared and flags may request special modes as described in yescrypt.h.
934 *
935 * local is the thread-local data structure, allowing to preserve and reuse a
936 * memory allocation across calls, thereby reducing its overhead.
937 *
938 * t controls computation time while not affecting peak memory usage.
939 *
940 * Return 0 on success; or -1 on error.
941 *
942 * This optimized implementation currently limits N to the range from 4 to
943 * 2^31, but other implementations might not.
944 */
945static int yescrypt_kdf32_body(
946 yescrypt_ctx_t *yctx,
947 const uint8_t *passwd, size_t passwdlen,
948 uint32_t flags, uint64_t N, uint32_t t,
949 uint8_t *buf32)
950{
951#if !DISABLE_NROM_CODE
952 const salsa20_blk_t *VROM;
953#endif
954 size_t B_size, V_size, XY_size, need;
955 uint8_t *B, *S;
956 salsa20_blk_t *V, *XY;
957 struct {
958 uint8_t sha256[32];
959 uint8_t dk[32];
960 } u;
961#define sha256 u.sha256
962#define dk u.dk
963 uint8_t *dkp = buf32;
964 uint32_t r, p;
965
966 /* Sanity-check parameters */
967 switch (flags___YESCRYPT_MODE_MASK) {
968 case 0: /* classic scrypt - can't have anything non-standard */
969 if (flags || t || YCTX_param_NROM)
970 goto out_EINVAL;
971 break;
972 case YESCRYPT_WORM:
973 if (flags != YESCRYPT_WORM || YCTX_param_NROM)
974 goto out_EINVAL;
975 break;
976 case YESCRYPT_RW:
977 if (flags != (flags & YESCRYPT_KNOWN_FLAGS))
978 goto out_EINVAL;
979#if PWXsimple == 2 && PWXgather == 4 && Sbytes == 12288
980 if ((flags & YESCRYPT_RW_FLAVOR_MASK) ==
981 (YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 |
982 YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K))
983 break;
984#else
985#error "Unsupported pwxform settings"
986#endif
987 /* FALLTHRU */
988 default:
989 goto out_EINVAL;
990 }
991
992 r = YCTX_param_r;
993 p = YCTX_param_p;
994 if ((uint64_t)r * (uint64_t)p >= 1 << 30) {
995 dbg("r * n >= 2^30");
996 goto out_EINVAL;
997 }
998 if (N > UINT32_MAX) {
999 dbg("N > 0x%lx", (long)UINT32_MAX);
1000 goto out_EINVAL;
1001 }
1002 if (N <= 3
1003 || r < 1
1004 || p < 1
1005 ) {
1006 dbg("bad N, r or p");
1007 goto out_EINVAL;
1008 }
1009 if (r > SIZE_MAX / 256 / p
1010 || N > SIZE_MAX / 128 / r
1011 ) {
1012 /* 32-bit testcase: mkpasswd qweRTY123@-+ '$y$jHT$123'
1013 * (works on 64-bit, needs buffer > 4Gbytes)
1014 */
1015 dbg("r > SIZE_MAX / 256 / p? %c", "NY"[r > SIZE_MAX / 256 / p]);
1016 dbg("N > SIZE_MAX / 128 / r? %c", "NY"[N > SIZE_MAX / 128 / r]);
1017 goto out_EINVAL;
1018 }
1019 if (flags___YESCRYPT_RW) {
1020 /* p cannot be greater than SIZE_MAX/Salloc on 64-bit systems,
1021 but it can on 32-bit systems. */
1022#pragma GCC diagnostic push
1023#pragma GCC diagnostic ignored "-Wtype-limits"
1024 if (N / p <= 3 || p > SIZE_MAX / Salloc) {
1025 dbg("bad p:%ld", (long)p);
1026 goto out_EINVAL;
1027 }
1028#pragma GCC diagnostic pop
1029 }
1030
1031#if !DISABLE_NROM_CODE
1032 VROM = NULL;
1033 if (YCTX_param_NROM)
1034 goto out_EINVAL;
1035#endif
1036
1037 /* Allocate memory */
1038 V = NULL;
1039 V_size = (size_t)128 * r * N;
1040 need = V_size;
1041 B_size = (size_t)128 * r * p;
1042 need += B_size;
1043 if (need < B_size) {
1044 dbg("integer overflow at += B_size(%lu)", (long)B_size);
1045 goto out_EINVAL;
1046 }
1047 XY_size = (size_t)256 * r;
1048 need += XY_size;
1049 if (need < XY_size) {
1050 dbg("integer overflow at += XY_size(%lu)", (long)XY_size);
1051 goto out_EINVAL;
1052 }
1053 if (flags___YESCRYPT_RW) {
1054 size_t S_size = (size_t)Salloc * p;
1055 need += S_size;
1056 if (need < S_size) {
1057 dbg("integer overflow at += S_size(%lu)", (long)S_size);
1058 goto out_EINVAL;
1059 }
1060 }
1061 if (yctx->local->aligned_size < need) {
1062 free_region(yctx->local);
1063 alloc_region(yctx->local, need);
1064 dbg("allocated local:%lu 0x%lx", (long)need, (long)need);
1065 /* standard "j9T" params allocate 16Mbytes here */
1066 }
1067 if (flags & YESCRYPT_ALLOC_ONLY)
1068 return -3; /* expected "failure" */
1069 B = (uint8_t *)yctx->local->aligned;
1070 V = (salsa20_blk_t *)((uint8_t *)B + B_size);
1071 XY = (salsa20_blk_t *)((uint8_t *)V + V_size);
1072 S = NULL;
1073 if (flags___YESCRYPT_RW)
1074 S = (uint8_t *)XY + XY_size;
1075
1076 if (flags) {
1077 hmac_block(
1078 /* key,len: */ (const void*)"yescrypt-prehash", (flags & YESCRYPT_PREHASH) ? 16 : 8,
1079 /* hash fn: */ sha256_begin,
1080 /* in,len: */ passwd, passwdlen,
1081 /* outbuf: */ sha256
1082 );
1083 passwd = sha256;
1084 passwdlen = sizeof(sha256);
1085 }
1086
1087 PBKDF2_SHA256(passwd, passwdlen, yctx->salt, yctx->saltlen, 1, B, B_size);
1088
1089 if (flags)
1090 memcpy(sha256, B, sizeof(sha256));
1091
1092 if (p == 1 || (flags___YESCRYPT_RW)) {
1093 smix(B, r, N, p, t, flags, V, YCTX_param_NROM, VROM, XY, S, sha256);
1094 } else {
1095 uint32_t i;
1096 for (i = 0; i < p; i++) {
1097 smix(&B[(size_t)128 * r * i], r, N, 1, t, flags, V,
1098 YCTX_param_NROM, VROM, XY, NULL, NULL);
1099 }
1100 }
1101
1102 dkp = buf32;
1103 if (flags && /*buflen:*/32 < sizeof(dk)) {
1104 PBKDF2_SHA256(passwd, passwdlen, B, B_size, 1, dk, sizeof(dk));
1105 dkp = dk;
1106 }
1107
1108 PBKDF2_SHA256(passwd, passwdlen, B, B_size, 1, buf32, /*buflen:*/32);
1109
1110 /*
1111 * Except when computing classic scrypt, allow all computation so far
1112 * to be performed on the client. The final steps below match those of
1113 * SCRAM (RFC 5802), so that an extension of SCRAM (with the steps so
1114 * far in place of SCRAM's use of PBKDF2 and with SHA-256 in place of
1115 * SCRAM's use of SHA-1) would be usable with yescrypt hashes.
1116 */
1117 if (flags && !(flags & YESCRYPT_PREHASH)) {
1118 /* Compute ClientKey */
1119 hmac_block(
1120 /* key,len: */ dkp, sizeof(dk),
1121 /* hash fn: */ sha256_begin,
1122 /* in,len: */ "Client Key", 10,
1123 /* outbuf: */ sha256
1124 );
1125 /* Compute StoredKey */
1126 {
1127 size_t clen = /*buflen:*/32;
1128 if (clen > sizeof(dk))
1129 clen = sizeof(dk);
1130 if (sizeof(dk) != 32) { /* not true, optimize it out */
1131 sha256_block(sha256, sizeof(sha256), dk);
1132 memcpy(buf32, dk, clen);
1133 } else {
1134 sha256_block(sha256, sizeof(sha256), buf32);
1135 }
1136 }
1137 }
1138
1139 explicit_bzero(&u, sizeof(u));
1140
1141 /* Success! */
1142 return 0;
1143
1144 out_EINVAL:
1145 //bbox does not need this: errno = EINVAL;
1146 return -1;
1147#undef sha256
1148#undef dk
1149}
1150
1151/**
1152 * yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen, params,
1153 * buf, buflen):
1154 * Compute scrypt or its revision as requested by the parameters. The inputs
1155 * to this function are the same as those for yescrypt_kdf_body() above, with
1156 * the addition of g, which controls hash upgrades (0 for no upgrades so far).
1157 */
1158static
1159int yescrypt_kdf32(
1160 yescrypt_ctx_t *yctx,
1161 const uint8_t *passwd, size_t passwdlen,
1162 uint8_t *buf32)
1163{
1164 uint32_t flags = YCTX_param_flags;
1165 uint64_t N = YCTX_param_N;
1166 uint32_t r = YCTX_param_r;
1167 uint32_t p = YCTX_param_p;
1168 uint32_t t = YCTX_param_t;
1169 uint32_t g = YCTX_param_g;
1170 uint8_t dk32[32];
1171 int retval;
1172
1173 /* Support for hash upgrades has been temporarily removed */
1174 if (g) {
1175 //bbox does not need this: errno = EINVAL;
1176 return -1;
1177 }
1178
1179 if ((flags___YESCRYPT_RW)
1180 && p >= 1
1181 && N / p >= 0x100
1182 && N / p * r >= 0x20000
1183 ) {
1184 if (yescrypt_kdf32_body(yctx,
1185 passwd, passwdlen,
1186 flags | YESCRYPT_ALLOC_ONLY, N, t,
1187 buf32) != -3
1188 ) {
1189 dbg("yescrypt_kdf32_body: not -3");
1190 return -1;
1191 }
1192 retval = yescrypt_kdf32_body(yctx,
1193 passwd, passwdlen,
1194 flags | YESCRYPT_PREHASH, N >> 6, 0,
1195 dk32);
1196 if (retval) {
1197 dbg("yescrypt_kdf32_body(PREHASH):%d", retval);
1198 return retval;
1199 }
1200 passwd = dk32;
1201 passwdlen = sizeof(dk32);
1202 }
1203
1204 retval = yescrypt_kdf32_body(yctx,
1205 passwd, passwdlen,
1206 flags, N, t, buf32);
1207
1208 explicit_bzero(dk32, sizeof(dk32));
1209
1210 dbg("yescrypt_kdf32_body:%d", retval);
1211 return retval;
1212}
diff --git a/libbb/yescrypt/alg-yescrypt.h b/libbb/yescrypt/alg-yescrypt.h
new file mode 100644
index 000000000..b69843f5d
--- /dev/null
+++ b/libbb/yescrypt/alg-yescrypt.h
@@ -0,0 +1,247 @@
1/*-
2 * Copyright 2009 Colin Percival
3 * Copyright 2013-2018 Alexander Peslyak
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * This file was originally written by Colin Percival as part of the Tarsnap
28 * online backup system.
29 */
30
31// busybox debug and size-reduction configuration
32
33#ifdef YESCRYPT_INTERNAL
34# if 1
35# define dbg(...) ((void)0)
36# else
37# define dbg(...) bb_error_msg(__VA_ARGS__)
38# endif
39# if 1
40# define dbg_dec64(...) ((void)0)
41# else
42# define dbg_dec64(...) bb_error_msg(__VA_ARGS__)
43# endif
44# define TEST_DECODE64 0
45#endif
46
47// Only accept one-char parameters in salt, and only first three?
48// Almost any reasonable yescrypt hashes in /etc/shadow should
49// only ever use "jXY" parameters which set N and r.
50// Fancy multi-byte-encoded wide integers are not needed for that.
51#define RESTRICTED_PARAMS 1
52// Note: if you enable the above, please also enable
53// YCTX_param_p, YCTX_param_t, YCTX_param_g, YCTX_param_NROM
54// optimizations, and DISABLE_NROM_CODE.
55
56#define DISABLE_NROM_CODE 1
57
58// How much we save by forcing "standard" value by commenting the next line:
59// 160 bytes
60//#define YCTX_param_flags yctx->param.flags
61// 260 bytes
62//#define flags___YESCRYPT_RW (flags & YESCRYPT_RW)
63// 140 bytes
64//#define flags___YESCRYPT_MODE_MASK (flags & YESCRYPT_MODE_MASK)
65// ^^^^ forcing the above since the code already requires (checks for) this
66// 50 bytes
67#define YCTX_param_N yctx->param.N
68// -100 bytes (negative!!!)
69#define YCTX_param_r yctx->param.r
70// 400 bytes
71//#define YCTX_param_p yctx->param.p
72// 130 bytes
73//#define YCTX_param_t yctx->param.t
74// 2 bytes
75//#define YCTX_param_g yctx->param.g
76// 1 bytes
77// ^^^^ this looks wrong, compiler should be able to constant-propagate the fact that NROM code is dead
78//#define YCTX_param_NROM yctx->param.NROM
79
80#ifndef YCTX_param_flags
81#define YCTX_param_flags (YESCRYPT_RW | YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
82#endif
83#ifndef flags___YESCRYPT_RW
84#define flags___YESCRYPT_RW ((void)flags, YESCRYPT_RW)
85#endif
86#ifndef flags___YESCRYPT_MODE_MASK
87#define flags___YESCRYPT_MODE_MASK ((void)flags, YESCRYPT_RW)
88#endif
89// standard ("j9T") values:
90#ifndef YCTX_param_N
91#define YCTX_param_N 4096
92#endif
93#ifndef YCTX_param_r
94#define YCTX_param_r 32
95#endif
96#ifndef YCTX_param_p
97#define YCTX_param_p 1
98#endif
99#ifndef YCTX_param_t
100#define YCTX_param_t 0
101#endif
102#ifndef YCTX_param_g
103#define YCTX_param_g 0
104#endif
105#ifndef YCTX_param_NROM
106#define YCTX_param_NROM 0
107#endif
108
109// "Faster/smaller code" knobs:
110// -941 bytes:
111#define KDF_UNROLL_COPY 0
112// -5324 bytes if 0:
113#define KDF_UNROLL_PWXFORM_ROUND 0
114// -4864 bytes if 0:
115#define KDF_UNROLL_PWXFORM 0
116// if both this ^^^^^^^^^^ and PWXFORM_ROUND set to 0: -7666 bytes
117// -464 bytes:
118#define KDF_UNROLL_SALSA20 0
119
120/**
121 * Type and possible values for the flags argument of yescrypt_kdf(),
122 * yescrypt_encode_params_r(), yescrypt_encode_params(). Most of these may be
123 * OR'ed together, except that YESCRYPT_WORM stands on its own.
124 * Please refer to the description of yescrypt_kdf() below for the meaning of
125 * these flags.
126 */
127/* yescrypt flags:
128 * bits pos: 7654321076543210
129 * ss r w
130 * sbox gg y
131 */
132/* Public */
133#define YESCRYPT_WORM 1
134#define YESCRYPT_RW 0x002
135#define YESCRYPT_ROUNDS_3 0x000 //r=0
136#define YESCRYPT_ROUNDS_6 0x004 //r=1
137#define YESCRYPT_GATHER_1 0x000 //gg=00
138#define YESCRYPT_GATHER_2 0x008 //gg=01
139#define YESCRYPT_GATHER_4 0x010 //gg=10
140#define YESCRYPT_GATHER_8 0x018 //gg=11
141#define YESCRYPT_SIMPLE_1 0x000 //ss=00
142#define YESCRYPT_SIMPLE_2 0x020 //ss=01
143#define YESCRYPT_SIMPLE_4 0x040 //ss=10
144#define YESCRYPT_SIMPLE_8 0x060 //ss=11
145#define YESCRYPT_SBOX_6K 0x000 //sbox=0000
146#define YESCRYPT_SBOX_12K 0x080 //sbox=0001
147#define YESCRYPT_SBOX_24K 0x100 //sbox=0010
148#define YESCRYPT_SBOX_48K 0x180 //sbox=0011
149#define YESCRYPT_SBOX_96K 0x200 //sbox=0100
150#define YESCRYPT_SBOX_192K 0x280 //sbox=0101
151#define YESCRYPT_SBOX_384K 0x300 //sbox=0110
152#define YESCRYPT_SBOX_768K 0x380 //sbox=0111
153
154#ifdef YESCRYPT_INTERNAL
155/* Private */
156#define YESCRYPT_MODE_MASK 0x003
157#define YESCRYPT_RW_FLAVOR_MASK 0x3fc
158#define YESCRYPT_ALLOC_ONLY 0x08000000
159#define YESCRYPT_PREHASH 0x10000000
160#endif
161
162#define YESCRYPT_RW_DEFAULTS \
163 (YESCRYPT_RW | \
164 YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | \
165 YESCRYPT_SBOX_12K)
166
167#define YESCRYPT_DEFAULTS YESCRYPT_RW_DEFAULTS
168
169#ifdef YESCRYPT_INTERNAL
170#define YESCRYPT_KNOWN_FLAGS \
171 (YESCRYPT_MODE_MASK | YESCRYPT_RW_FLAVOR_MASK | \
172 YESCRYPT_ALLOC_ONLY | YESCRYPT_PREHASH)
173#endif
174
175/* How many chars base-64 encoded bytes require? */
176#define YESCRYPT_BYTES2CHARS(bytes) ((((bytes) * 8) + 5) / 6)
177/* The /etc/passwd-style hash is "<prefix>$<hash><NUL>" */
178/*
179 * "$y$", up to 8 params of up to 6 chars each, '$', salt
180 * Alternatively, but that's smaller:
181 * "$7$", 3 params encoded as 1+5+5 chars, salt
182 */
183#define YESCRYPT_PREFIX_LEN (3 + 8 * 6 + 1 + YESCRYPT_BYTES2CHARS(32))
184
185#define YESCRYPT_HASH_SIZE 32
186#define YESCRYPT_HASH_LEN YESCRYPT_BYTES2CHARS(YESCRYPT_HASH_SIZE)
187
188/**
189 * Internal type used by the memory allocator. Please do not use it directly.
190 * Use yescrypt_shared_t and yescrypt_local_t as appropriate instead, since
191 * they might differ from each other in a future version.
192 */
193typedef struct {
194// void *base;
195 void *aligned;
196// size_t base_size;
197 size_t aligned_size;
198} yescrypt_region_t;
199
200/**
201 * yescrypt parameters combined into one struct. N, r, p are the same as in
202 * classic scrypt, except that the meaning of p changes when YESCRYPT_RW is
203 * set. flags, t, g, NROM are special to yescrypt.
204 */
205typedef struct {
206 uint32_t flags;
207 uint32_t r;
208 uint64_t N;
209#if !RESTRICTED_PARAMS
210 uint32_t p, t, g;
211 uint64_t NROM;
212#endif
213} yescrypt_params_t;
214
215typedef struct {
216 yescrypt_params_t param;
217
218 /* salt in binary form */
219 /* stored here to cut down on the amount of function paramaters */
220 unsigned char salt[64];
221 size_t saltlen;
222
223 /* used by the memory allocator */
224 //yescrypt_region_t shared[1];
225 yescrypt_region_t local[1];
226} yescrypt_ctx_t;
227
228/**
229 * yescrypt_r(shared, local, passwd, passwdlen, setting, key, buf, buflen):
230 * Compute and encode an scrypt or enhanced scrypt hash of passwd given the
231 * parameters and salt value encoded in setting. If shared is not NULL, a ROM
232 * is used and YESCRYPT_RW is required. Otherwise, whether to compute classic
233 * scrypt, YESCRYPT_WORM (a slight deviation from classic scrypt), or
234 * YESCRYPT_RW (time-memory tradeoff discouraging modification) is determined
235 * by the setting string. shared (if not NULL) and local must be initialized
236 * as described above for yescrypt_kdf(). buf must be large enough (as
237 * indicated by buflen) to hold the encoded hash string.
238 *
239 * Return the encoded hash string on success; or NULL on error.
240 *
241 * MT-safe as long as local and buf are local to the thread.
242 */
243extern char *yescrypt_r(
244 const uint8_t *passwd, size_t passwdlen,
245 const uint8_t *setting,
246 char *buf, size_t buflen
247);
diff --git a/libbb/yescrypt/y.c b/libbb/yescrypt/y.c
new file mode 100644
index 000000000..d5ab8903f
--- /dev/null
+++ b/libbb/yescrypt/y.c
@@ -0,0 +1,16 @@
1/*
2 * The compilation unit for yescrypt-related code.
3 *
4 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += y.o
9
10#include "libbb.h"
11
12#define YESCRYPT_INTERNAL
13#include "alg-yescrypt.h"
14#include "alg-sha256.c"
15#include "alg-yescrypt-kdf.c"
16#include "alg-yescrypt-common.c"
diff --git a/loginutils/Config.src b/loginutils/Config.src
index cbb09646b..a7812bd32 100644
--- a/loginutils/Config.src
+++ b/loginutils/Config.src
@@ -91,6 +91,17 @@ config USE_BB_CRYPT_SHA
91 With this option off, login will fail password check for any 91 With this option off, login will fail password check for any
92 user which has password encrypted with these algorithms. 92 user which has password encrypted with these algorithms.
93 93
94config USE_BB_CRYPT_YES
95 bool "Enable yescrypt functions"
96 default y
97 depends on USE_BB_CRYPT
98 help
99 Enable this if you have passwords starting with "$y$" or
100 in your /etc/passwd or /etc/shadow files. These passwords
101 are hashed using yescrypt algorithms.
102 With this option off, login will fail password check for any
103 user which has password encrypted with these algorithms.
104
94INSERT 105INSERT
95 106
96endmenu 107endmenu
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c
index 65530b614..353f19961 100644
--- a/loginutils/chpasswd.c
+++ b/loginutils/chpasswd.c
@@ -17,7 +17,7 @@
17//config: default "des" 17//config: default "des"
18//config: depends on PASSWD || CRYPTPW || CHPASSWD 18//config: depends on PASSWD || CRYPTPW || CHPASSWD
19//config: help 19//config: help
20//config: Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512". 20//config: Possible choices: "d[es]", "m[d5]", "s[ha256]", "sha512", "yescrypt"
21 21
22//applet:IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP)) 22//applet:IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP))
23 23
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c
index 1c338540f..666deff0b 100644
--- a/loginutils/cryptpw.c
+++ b/loginutils/cryptpw.c
@@ -84,8 +84,7 @@ to cryptpw. -a option (alias for -m) came from cryptpw.
84int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 84int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
85int cryptpw_main(int argc UNUSED_PARAM, char **argv) 85int cryptpw_main(int argc UNUSED_PARAM, char **argv)
86{ 86{
87 /* Supports: cryptpw -m sha256 PASS 'rounds=999999999$SALT' */ 87 char salt[MAX_PW_SALT_LEN];
88 char salt[MAX_PW_SALT_LEN + sizeof("rounds=999999999$")];
89 char *salt_ptr; 88 char *salt_ptr;
90 char *password; 89 char *password;
91 const char *opt_m, *opt_S; 90 const char *opt_m, *opt_S;
@@ -100,7 +99,7 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
100 ; 99 ;
101#endif 100#endif
102 fd = STDIN_FILENO; 101 fd = STDIN_FILENO;
103 opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; 102 opt_m = NULL;
104 opt_S = NULL; 103 opt_S = NULL;
105 /* at most two non-option arguments; -P NUM */ 104 /* at most two non-option arguments; -P NUM */
106 getopt32long(argv, "^" "sP:+S:m:a:" "\0" "?2", 105 getopt32long(argv, "^" "sP:+S:m:a:" "\0" "?2",
@@ -114,10 +113,34 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
114 if (argv[0] && !opt_S) 113 if (argv[0] && !opt_S)
115 opt_S = argv[1]; 114 opt_S = argv[1];
116 115
117 salt_ptr = crypt_make_pw_salt(salt, opt_m); 116 if (opt_S && !opt_S[0]) {
118 if (opt_S) 117 /* mkpasswd 5.6.2 compat: SALT of ""
119 /* put user's data after the "$N$" prefix */ 118 * is treated as not specified
120 safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1)); 119 * (both forms: -S "" and argv[1] of "")
120 */
121 opt_S = NULL;
122 }
123
124 if (opt_m) {
125 /* "cryptpw -m ALGO PASSWORD [SALT]" */
126 /* generate "$x$" algo prefix + random salt */
127 salt_ptr = crypt_make_pw_salt(salt, opt_m);
128 if (opt_S) {
129 /* "cryptpw -m ALGO PASSWORD SALT" */
130 /* put SALT data after the "$x$" prefix */
131 safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1));
132 }
133 } else {
134 if (!opt_S) {
135 /* "cryptpw PASSWORD" */
136 /* generate random salt with default algo */
137 crypt_make_pw_salt(salt, CONFIG_FEATURE_DEFAULT_PASSWD_ALGO);
138 } else {
139 /* "cryptpw PASSWORD '$x$SALT'" */
140 /* use given salt; algo will be detected by pw_encrypt() */
141 safe_strncpy(salt, opt_S, sizeof(salt));
142 }
143 }
121 144
122 xmove_fd(fd, STDIN_FILENO); 145 xmove_fd(fd, STDIN_FILENO);
123 146
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c
index 9c927ed79..984889915 100644
--- a/loginutils/sulogin.c
+++ b/loginutils/sulogin.c
@@ -79,7 +79,7 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
79 break; 79 break;
80 } 80 }
81 pause_after_failed_login(); 81 pause_after_failed_login();
82 bb_simple_info_msg("Login incorrect"); 82 bb_simple_error_msg("Login incorrect");
83 } 83 }
84 84
85 /* util-linux 2.36.1 compat: no message */ 85 /* util-linux 2.36.1 compat: no message */
@@ -119,9 +119,12 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
119 } 119 }
120 120
121 /* 121 /*
122 * Note: login does this (should we do it too?): 122 * Note: login does this. util-linux's sulogin does NOT.
123 * But it's rather unpleasant to have non-functioning ^C in a shell,
124 * and surprisingly, there is no easy way to remove SIG_IGN from ^C
125 * in the shell. So, we are doing it:
123 */ 126 */
124 /*signal(SIGINT, SIG_DFL);*/ 127 signal(SIGINT, SIG_DFL);
125 128
126 /* Exec shell with no additional parameters. Never returns. */ 129 /* Exec shell with no additional parameters. Never returns. */
127 exec_shell(shell, /* -p? then shell is login:*/(opts & 1), NULL); 130 exec_shell(shell, /* -p? then shell is login:*/(opts & 1), NULL);
diff --git a/miscutils/crond.c b/miscutils/crond.c
index b3762d327..6a384fdfb 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -177,7 +177,7 @@ static void crondlog(unsigned level, const char *msg, va_list va)
177{ 177{
178 if (level >= G.log_level) { 178 if (level >= G.log_level) {
179 /* 179 /*
180 * We are called only for info meesages. 180 * We are called only for info messages.
181 * Warnings/errors use plain bb_[p]error_msg's, which 181 * Warnings/errors use plain bb_[p]error_msg's, which
182 * need not touch syslog_level 182 * need not touch syslog_level
183 * (they are ok with LOG_ERR default). 183 * (they are ok with LOG_ERR default).
@@ -989,7 +989,7 @@ static int check_completions(void)
989 if (line->cl_pid <= 0) 989 if (line->cl_pid <= 0)
990 continue; 990 continue;
991 991
992 r = waitpid(line->cl_pid, NULL, WNOHANG); 992 r = safe_waitpid(line->cl_pid, NULL, WNOHANG);
993 if (r < 0 || r == line->cl_pid) { 993 if (r < 0 || r == line->cl_pid) {
994 process_finished_job(file->cf_username, line); 994 process_finished_job(file->cf_username, line);
995 if (line->cl_pid == 0) { 995 if (line->cl_pid == 0) {
@@ -1001,6 +1001,14 @@ static int check_completions(void)
1001 /* else: r == 0: "process is still running" */ 1001 /* else: r == 0: "process is still running" */
1002 file->cf_has_running = 1; 1002 file->cf_has_running = 1;
1003 } 1003 }
1004
1005 /* Reap any other children we don't actively track.
1006 * Reportedly, some people run crond as init process!
1007 * Thus, we need to reap orphans, like init does.
1008 */
1009 while (wait_any_nohang(NULL) > 0)
1010 continue;
1011
1004//FIXME: if !file->cf_has_running && file->deleted: delete it! 1012//FIXME: if !file->cf_has_running && file->deleted: delete it!
1005//otherwise deleted entries will stay forever, right? 1013//otherwise deleted entries will stay forever, right?
1006 num_still_running += file->cf_has_running; 1014 num_still_running += file->cf_has_running;
diff --git a/miscutils/fbsplash.c b/miscutils/fbsplash.c
index 2934d8eb7..912a501a3 100644
--- a/miscutils/fbsplash.c
+++ b/miscutils/fbsplash.c
@@ -382,7 +382,7 @@ static void fb_drawimage(void)
382 if (LONE_DASH(G.image_filename)) { 382 if (LONE_DASH(G.image_filename)) {
383 theme_file = stdin; 383 theme_file = stdin;
384 } else { 384 } else {
385 int fd = open_zipped(G.image_filename, /*fail_if_not_compressed:*/ 0); 385 int fd = open_zipped(G.image_filename, /*die_if_not_compressed:*/ 0);
386 if (fd < 0) 386 if (fd < 0)
387 bb_simple_perror_msg_and_die(G.image_filename); 387 bb_simple_perror_msg_and_die(G.image_filename);
388 theme_file = xfdopen_for_read(fd); 388 theme_file = xfdopen_for_read(fd);
diff --git a/miscutils/less.c b/miscutils/less.c
index 467c76e2a..5a8fc5a74 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -1178,7 +1178,7 @@ static int64_t getch_nowait(void)
1178 1178
1179 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key() 1179 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key()
1180 * would not block even if there is no input available */ 1180 * would not block even if there is no input available */
1181 key64 = safe_read_key(kbd_fd, kbd_input, /*timeout off:*/ -2); 1181 key64 = safe_read_key(kbd_fd, kbd_input, /*do not poll:*/ -2);
1182 if ((int)key64 == -1) { 1182 if ((int)key64 == -1) {
1183 if (errno == EAGAIN) { 1183 if (errno == EAGAIN) {
1184 /* No keyboard input available. Since poll() did return, 1184 /* No keyboard input available. Since poll() did return,
diff --git a/miscutils/make.c b/miscutils/make.c
index a165274aa..8026917b3 100644
--- a/miscutils/make.c
+++ b/miscutils/make.c
@@ -276,6 +276,7 @@ struct globals {
276 time_t ar_mtime; 276 time_t ar_mtime;
277 int lineno; // Physical line number in file 277 int lineno; // Physical line number in file
278 int dispno; // Line number for display purposes 278 int dispno; // Line number for display purposes
279 struct cmd *curr_cmd;
279 const char *rulepos; 280 const char *rulepos;
280 int rule_idx; 281 int rule_idx;
281#define IF_MAX 10 282#define IF_MAX 10
@@ -307,6 +308,7 @@ struct globals {
307#define ar_mtime (G.ar_mtime) 308#define ar_mtime (G.ar_mtime)
308#define lineno (G.lineno) 309#define lineno (G.lineno)
309#define dispno (G.dispno) 310#define dispno (G.dispno)
311#define curr_cmd (G.curr_cmd)
310#define rulepos (G.rulepos) 312#define rulepos (G.rulepos)
311#define rule_idx (G.rule_idx) 313#define rule_idx (G.rule_idx)
312#define clevel (G.clevel) 314#define clevel (G.clevel)
@@ -338,9 +340,20 @@ static struct name *dyndep(struct name *np, struct rule *infrule,
338static void 340static void
339vwarning(FILE *stream, const char *msg, va_list list) 341vwarning(FILE *stream, const char *msg, va_list list)
340{ 342{
343 const char *m = NULL;
344 int d = 0;
345
346 if (curr_cmd) {
347 m = curr_cmd->c_makefile;
348 d = curr_cmd->c_dispno;
349 } else if (makefile) {
350 m = makefile;
351 d = dispno;
352 }
353
341 fprintf(stream, "%s: ", applet_name); 354 fprintf(stream, "%s: ", applet_name);
342 if (makefile) 355 if (m)
343 fprintf(stream, "(%s:%d): ", makefile, dispno); 356 fprintf(stream, "(%s:%d): ", m, d);
344 vfprintf(stream, msg, list); 357 vfprintf(stream, msg, list);
345 fputc('\n', stream); 358 fputc('\n', stream);
346} 359}
@@ -726,6 +739,7 @@ addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag)
726{ 739{
727 struct rule *rp; 740 struct rule *rp;
728 struct rule **rpp; 741 struct rule **rpp;
742 struct cmd *old_cp;
729 743
730 // Can't mix single-colon and double-colon rules 744 // Can't mix single-colon and double-colon rules
731 if (!posix && (np->n_flag & N_TARGET)) { 745 if (!posix && (np->n_flag & N_TARGET)) {
@@ -742,7 +756,7 @@ addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag)
742 return; 756 return;
743 } 757 }
744 758
745 if (cp && !(np->n_flag & N_DOUBLE) && getcmd(np)) { 759 if (cp && !(np->n_flag & N_DOUBLE) && (old_cp = getcmd(np))) {
746 // Handle the inference rule redefinition case 760 // Handle the inference rule redefinition case
747 // .DEFAULT rule can also be redefined (as an extension). 761 // .DEFAULT rule can also be redefined (as an extension).
748 if ((np->n_flag & N_INFERENCE) 762 if ((np->n_flag & N_INFERENCE)
@@ -751,7 +765,17 @@ addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag)
751 freerules(np->n_rule); 765 freerules(np->n_rule);
752 np->n_rule = NULL; 766 np->n_rule = NULL;
753 } else { 767 } else {
754 error("commands defined twice for target %s", np->n_name); 768 // We're adding commands to a single colon rule which
769 // already has some. Clear the old ones first.
770 warning("overriding rule for target %s", np->n_name);
771 curr_cmd = old_cp;
772 warning("previous rule for target %s", np->n_name);
773 curr_cmd = NULL;
774
775 for (rp = np->n_rule; rp; rp = rp->r_next) {
776 freecmds(rp->r_cmd);
777 rp->r_cmd = NULL;
778 }
755 } 779 }
756 } 780 }
757 781
@@ -2602,8 +2626,7 @@ docmds(struct name *np, struct cmd *cp)
2602 uint32_t ssilent, signore, sdomake; 2626 uint32_t ssilent, signore, sdomake;
2603 2627
2604 // Location of command in makefile (for use in error messages) 2628 // Location of command in makefile (for use in error messages)
2605 makefile = cp->c_makefile; 2629 curr_cmd = cp;
2606 dispno = cp->c_dispno;
2607 opts &= ~OPT_make; // We want to know if $(MAKE) is expanded 2630 opts &= ~OPT_make; // We want to know if $(MAKE) is expanded
2608 q = command = expand_macros(cp->c_cmd, FALSE); 2631 q = command = expand_macros(cp->c_cmd, FALSE);
2609 ssilent = silent || (np->n_flag & N_SILENT) || dotouch; 2632 ssilent = silent || (np->n_flag & N_SILENT) || dotouch;
@@ -2697,7 +2720,7 @@ docmds(struct name *np, struct cmd *cp)
2697 estat = MAKE_DIDSOMETHING; 2720 estat = MAKE_DIDSOMETHING;
2698 } 2721 }
2699 2722
2700 makefile = NULL; 2723 curr_cmd = NULL;
2701 return estat; 2724 return estat;
2702} 2725}
2703 2726
diff --git a/miscutils/man.c b/miscutils/man.c
index 3954455b4..38c1b9aa3 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -143,7 +143,7 @@ static int run_pipe(char *man_filename, int man, int level)
143 143
144 ordinary_manpage: 144 ordinary_manpage:
145 close(STDIN_FILENO); 145 close(STDIN_FILENO);
146 open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */ 146 open_zipped(man_filename, /*die_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */
147 if (man) { 147 if (man) {
148 int w = get_terminal_width(-1); 148 int w = get_terminal_width(-1);
149 if (w > 10) 149 if (w > 10)
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 77e42e3fb..31a215a29 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -186,15 +186,6 @@ static char* find_keyword(char *ptr, size_t len, const char *word)
186 return NULL; 186 return NULL;
187} 187}
188 188
189static void replace(char *s, char what, char with)
190{
191 while (*s) {
192 if (what == *s)
193 *s = with;
194 ++s;
195 }
196}
197
198static char *filename2modname(const char *filename, char *modname) 189static char *filename2modname(const char *filename, char *modname)
199{ 190{
200 int i; 191 int i;
@@ -230,7 +221,7 @@ static char* str_2_list(const char *str)
230 dst[len] = '\0'; 221 dst[len] = '\0';
231 memcpy(dst, str, len); 222 memcpy(dst, str, len);
232//TODO: protect against 2+ spaces: "word word" 223//TODO: protect against 2+ spaces: "word word"
233 replace(dst, ' ', '\0'); 224 replace_char(dst, ' ', '\0');
234 return dst; 225 return dst;
235} 226}
236 227
@@ -369,14 +360,14 @@ static int parse_module(module_info *info, const char *pathname)
369 } 360 }
370 bksp(); /* remove last ' ' */ 361 bksp(); /* remove last ' ' */
371 info->aliases = copy_stringbuf(); 362 info->aliases = copy_stringbuf();
372 replace(info->aliases, '-', '_'); 363 replace_char(info->aliases, '-', '_');
373 364
374 /* "dependency1 depandency2" */ 365 /* "dependency1 depandency2" */
375 reset_stringbuf(); 366 reset_stringbuf();
376 ptr = find_keyword(module_image, len, "depends="); 367 ptr = find_keyword(module_image, len, "depends=");
377 if (ptr && *ptr) { 368 if (ptr && *ptr) {
378 replace(ptr, ',', ' '); 369 replace_char(ptr, ',', ' ');
379 replace(ptr, '-', '_'); 370 replace_char(ptr, '-', '_');
380 dbg2_error_msg("dep:'%s'", ptr); 371 dbg2_error_msg("dep:'%s'", ptr);
381 append(ptr); 372 append(ptr);
382 } 373 }
@@ -707,7 +698,7 @@ static int process_module(char *name, const char *cmdline_options)
707 698
708 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); 699 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
709 700
710 replace(name, '-', '_'); 701 replace_char(name, '-', '_');
711 702
712 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); 703 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
713 704
@@ -735,7 +726,7 @@ static int process_module(char *name, const char *cmdline_options)
735 char *opt_filename = xasprintf("/etc/modules/%s", name); 726 char *opt_filename = xasprintf("/etc/modules/%s", name);
736 options = xmalloc_open_read_close(opt_filename, NULL); 727 options = xmalloc_open_read_close(opt_filename, NULL);
737 if (options) 728 if (options)
738 replace(options, '\n', ' '); 729 replace_char(options, '\n', ' ');
739#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 730#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
740 if (cmdline_options) { 731 if (cmdline_options) {
741 /* NB: cmdline_options always have one leading ' ' 732 /* NB: cmdline_options always have one leading ' '
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index 543f53e99..f890abe53 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -579,7 +579,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
579 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); 579 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
580 580
581 for (i = 0; argv[i]; i++) 581 for (i = 0; argv[i]; i++)
582 replace(argv[i], '-', '_'); 582 replace_char(argv[i], '-', '_');
583 583
584 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { 584 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
585 colon = last_char_is(tokens[0], ':'); 585 colon = last_char_is(tokens[0], ':');
diff --git a/modutils/modutils.c b/modutils/modutils.c
index cbff20961..862f71f57 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -69,15 +69,6 @@ void FAST_FUNC moddb_free(module_db *db)
69 } 69 }
70} 70}
71 71
72void FAST_FUNC replace(char *s, char what, char with)
73{
74 while (*s) {
75 if (what == *s)
76 *s = with;
77 ++s;
78 }
79}
80
81int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) 72int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
82{ 73{
83 char *tok; 74 char *tok;
diff --git a/modutils/modutils.h b/modutils/modutils.h
index 4a702e97c..9b05116d1 100644
--- a/modutils/modutils.h
+++ b/modutils/modutils.h
@@ -47,7 +47,6 @@ module_entry *moddb_get(module_db *db, const char *s) FAST_FUNC;
47module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC; 47module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC;
48void moddb_free(module_db *db) FAST_FUNC; 48void moddb_free(module_db *db) FAST_FUNC;
49 49
50void replace(char *s, char what, char with) FAST_FUNC;
51int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; 50int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC;
52char *filename2modname(const char *filename, char *modname) FAST_FUNC; 51char *filename2modname(const char *filename, char *modname) FAST_FUNC;
53#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 52#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
diff --git a/networking/Config.src b/networking/Config.src
index 0942645c3..aa0806a18 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -72,9 +72,28 @@ config FEATURE_HWIB
72 help 72 help
73 Support for printing infiniband addresses in network applets. 73 Support for printing infiniband addresses in network applets.
74 74
75choice
76 prompt "TLS implementation"
77 default FEATURE_TLS_INTERNAL
78
79config FEATURE_TLS_INTERNAL
80 bool "Internal"
81 depends on TLS
82 help
83 Use the BusyBox default internal TLS implementation.
84
85config FEATURE_TLS_SCHANNEL
86 bool "Schannel SSP"
87 depends on TLS && PLATFORM_MINGW32
88 help
89 Use the Schannel SSP to provide TLS support.
90 Reduces code size and enables certificate checking.
91
92endchoice
93
75config FEATURE_TLS_SHA1 94config FEATURE_TLS_SHA1
76 bool "In TLS code, support ciphers which use deprecated SHA1" 95 bool "In TLS code, support ciphers which use deprecated SHA1"
77 depends on TLS 96 depends on FEATURE_TLS_INTERNAL
78 default n 97 default n
79 help 98 help
80 Selecting this option increases interoperability with very old 99 Selecting this option increases interoperability with very old
@@ -83,6 +102,15 @@ config FEATURE_TLS_SHA1
83 Most TLS servers support SHA256 today (2018), since SHA1 is 102 Most TLS servers support SHA256 today (2018), since SHA1 is
84 considered possibly insecure (although not yet definitely broken). 103 considered possibly insecure (although not yet definitely broken).
85 104
105config FEATURE_TLS_SCHANNEL_1_3
106 bool "Enable TLS 1.3 support for Schannel"
107 depends on FEATURE_TLS_SCHANNEL
108 default n
109 help
110 Enable TLS 1.3 support for Schannel.
111 This only works on Windows 11/Server 2022
112 and up.
113
86INSERT 114INSERT
87 115
88source networking/udhcp/Config.in 116source networking/udhcp/Config.in
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 0d6a289c7..c3125410e 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -190,54 +190,39 @@ struct globals {
190} while (0) 190} while (0)
191 191
192 192
193/* escape_text("pfx:", str, (0xff << 8) + '\r')
194 * Duplicate 0xff, append \r ^^^^^^^^^^^^^^^^^^
195 */
193static char * 196static char *
194escape_text(const char *prepend, const char *str, unsigned escapee) 197escape_text(const char *prepend, const char *str, unsigned escapee)
195{ 198{
196 unsigned retlen, remainlen, chunklen; 199 char *ret, *p;
197 char *ret, *found;
198 char append; 200 char append;
199 201
200 append = (char)escapee; 202 append = (char)escapee;
201 escapee >>= 8; 203 escapee >>= 8;
202 204
203 remainlen = strlen(str); 205 ret = xmalloc(strlen(prepend) + strlen(str) * 2 + 1 + 1);
204 retlen = strlen(prepend); 206 p = stpcpy(ret, prepend);
205 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
206 strcpy(ret, prepend);
207 207
208 for (;;) { 208 for (;;) {
209 found = strchrnul(str, escapee); 209 char *found = strchrnul(str, escapee);
210 chunklen = found - str + 1;
211 210
212 /* Copy chunk up to and including escapee (or NUL) to ret */ 211 /* Copy up to and including escapee (or NUL) */
213 memcpy(ret + retlen, str, chunklen); 212 p = mempcpy(p, str, found - str + 1);
214 retlen += chunklen;
215 213
216 if (*found == '\0') { 214 if (*found == '\0') {
217 /* It wasn't escapee, it was NUL! */ 215 /* It wasn't escapee, it was NUL! */
218 ret[retlen - 1] = append; /* replace NUL */
219 ret[retlen] = '\0'; /* add NUL */
220 break; 216 break;
221 } 217 }
222 ret[retlen++] = escapee; /* duplicate escapee */
223 str = found + 1; 218 str = found + 1;
219 *p++ = escapee; /* duplicate escapee */
224 } 220 }
221 p[-1] = append; /* replace NUL */
222 *p = '\0'; /* add NUL */
225 return ret; 223 return ret;
226} 224}
227 225
228/* Returns strlen as a bonus */
229static unsigned
230replace_char(char *str, char from, char to)
231{
232 char *p = str;
233 while (*p) {
234 if (*p == from)
235 *p = to;
236 p++;
237 }
238 return p - str;
239}
240
241static void 226static void
242verbose_log(const char *str) 227verbose_log(const char *str)
243{ 228{
diff --git a/networking/hostname.c b/networking/hostname.c
index 36cb70866..101b89e77 100644
--- a/networking/hostname.c
+++ b/networking/hostname.c
@@ -25,8 +25,8 @@
25//applet:IF_DNSDOMAINNAME(APPLET_NOEXEC(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname)) 25//applet:IF_DNSDOMAINNAME(APPLET_NOEXEC(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname))
26//applet:IF_HOSTNAME( APPLET_NOEXEC(hostname, hostname, BB_DIR_BIN, BB_SUID_DROP, hostname )) 26//applet:IF_HOSTNAME( APPLET_NOEXEC(hostname, hostname, BB_DIR_BIN, BB_SUID_DROP, hostname ))
27 27
28//kbuild: lib-$(CONFIG_HOSTNAME) += hostname.o 28//kbuild:lib-$(CONFIG_HOSTNAME) += hostname.o
29//kbuild: lib-$(CONFIG_DNSDOMAINNAME) += hostname.o 29//kbuild:lib-$(CONFIG_DNSDOMAINNAME) += hostname.o
30 30
31//usage:#define hostname_trivial_usage 31//usage:#define hostname_trivial_usage
32//usage: "[-sidf] [HOSTNAME | -F FILE]" 32//usage: "[-sidf] [HOSTNAME | -F FILE]"
diff --git a/networking/httpd.c b/networking/httpd.c
index 1dae602ee..50595104c 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -3074,7 +3074,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
3074 salt[0] = '$'; 3074 salt[0] = '$';
3075 salt[1] = '1'; 3075 salt[1] = '1';
3076 salt[2] = '$'; 3076 salt[2] = '$';
3077 crypt_make_salt(salt + 3, 4); 3077 crypt_make_rand64encoded(salt + 3, 8 / 2); /* 8 chars */
3078 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0)); 3078 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
3079 return 0; 3079 return 0;
3080 } 3080 }
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index cd77f642f..a30f070eb 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -302,24 +302,22 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
302 printf("notify "); 302 printf("notify ");
303 } 303 }
304 304
305 if (r->rtm_family == AF_INET6) { 305 if (r->rtm_family == AF_INET || r->rtm_family == AF_INET6) {
306 struct rta_cacheinfo *ci = NULL; 306 if (r->rtm_family == AF_INET) {
307 if (tb[RTA_CACHEINFO]) {
308 ci = RTA_DATA(tb[RTA_CACHEINFO]);
309 }
310 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
311 if (r->rtm_flags & RTM_F_CLONED) { 307 if (r->rtm_flags & RTM_F_CLONED) {
312 printf("%c cache ", _SL_); 308 printf("%c cache ", _SL_);
309 /* upstream: print_cache_flags() prints more here */
313 } 310 }
311 }
312 if (tb[RTA_CACHEINFO]) {
313 struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]);
314 if (ci->rta_expires) { 314 if (ci->rta_expires) {
315 printf(" expires %dsec", ci->rta_expires / get_hz()); 315 printf(" expires %dsec", ci->rta_expires / get_hz());
316 } 316 }
317 if (ci->rta_error != 0) { 317 if (ci->rta_error != 0) {
318 printf(" error %d", ci->rta_error); 318 printf(" error %d", ci->rta_error);
319 } 319 }
320 } else if (ci) { 320 /* upstream: print_rta_cacheinfo() prints more here */
321 if (ci->rta_error != 0)
322 printf(" error %d", ci->rta_error);
323 } 321 }
324 } 322 }
325 if (tb[RTA_IIF] && G_filter.iif == 0) { 323 if (tb[RTA_IIF] && G_filter.iif == 0) {
diff --git a/networking/ntpd.c b/networking/ntpd.c
index dcbdb8e60..dd0a9c91f 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -205,7 +205,7 @@
205#define MINDISP 0.01 /* minimum dispersion (sec) */ 205#define MINDISP 0.01 /* minimum dispersion (sec) */
206#define MAXDISP 16 /* maximum dispersion (sec) */ 206#define MAXDISP 16 /* maximum dispersion (sec) */
207#define MAXSTRAT 16 /* maximum stratum (infinity metric) */ 207#define MAXSTRAT 16 /* maximum stratum (infinity metric) */
208#define MAXDIST 1 /* distance threshold (sec) */ 208#define MAXDIST 3 /* distance threshold (sec): do not use peers who are farther away */
209#define MIN_SELECTED 1 /* minimum intersection survivors */ 209#define MIN_SELECTED 1 /* minimum intersection survivors */
210#define MIN_CLUSTERED 3 /* minimum cluster survivors */ 210#define MIN_CLUSTERED 3 /* minimum cluster survivors */
211 211
@@ -1057,7 +1057,7 @@ step_time(double offset)
1057 } 1057 }
1058 tval = tvn.tv_sec; 1058 tval = tvn.tv_sec;
1059 strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval); 1059 strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
1060 bb_info_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset); 1060 bb_error_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset);
1061 //maybe? G.FREQHOLD_cnt = 0; 1061 //maybe? G.FREQHOLD_cnt = 0;
1062 1062
1063 /* Correct various fields which contain time-relative values: */ 1063 /* Correct various fields which contain time-relative values: */
@@ -1976,7 +1976,7 @@ recv_and_process_peer_pkt(peer_t *p)
1976 1976
1977 p->reachable_bits |= 1; 1977 p->reachable_bits |= 1;
1978 if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) { 1978 if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) {
1979 bb_info_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x", 1979 bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
1980 p->p_dotted, 1980 p->p_dotted,
1981 offset, 1981 offset,
1982 p->p_raw_delay, 1982 p->p_raw_delay,
diff --git a/networking/telnetd.c b/networking/telnetd.c
index bfeea1400..a5a783047 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -104,8 +104,8 @@
104//usage:#define telnetd_full_usage "\n\n" 104//usage:#define telnetd_full_usage "\n\n"
105//usage: "Handle incoming telnet connections" 105//usage: "Handle incoming telnet connections"
106//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n" 106//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
107//usage: "\n -l LOGIN Exec LOGIN on connect" 107//usage: "\n -l LOGIN Exec LOGIN on connect (default /bin/login)"
108//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" 108//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue.net"
109//usage: "\n -K Close connection as soon as login exits" 109//usage: "\n -K Close connection as soon as login exits"
110//usage: "\n (normally wait until all programs close slave pty)" 110//usage: "\n (normally wait until all programs close slave pty)"
111//usage: IF_FEATURE_TELNETD_STANDALONE( 111//usage: IF_FEATURE_TELNETD_STANDALONE(
diff --git a/networking/tftp.c b/networking/tftp.c
index f5b4367ca..b698a9288 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -250,7 +250,7 @@ static int tftp_blksize_check(const char *blksize_str, int maxsize)
250 return -1; 250 return -1;
251 } 251 }
252# if ENABLE_TFTP_DEBUG 252# if ENABLE_TFTP_DEBUG
253 bb_info_msg("using blksize %u", blksize); 253 bb_error_msg("using blksize %u", blksize);
254# endif 254# endif
255 return blksize; 255 return blksize;
256} 256}
diff --git a/networking/tls.c b/networking/tls.c
index 9f1dd67ec..c099d056f 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -10,18 +10,19 @@
10//Config.src also defines FEATURE_TLS_SHA1 option 10//Config.src also defines FEATURE_TLS_SHA1 option
11 11
12//kbuild:lib-$(CONFIG_TLS) += tls.o 12//kbuild:lib-$(CONFIG_TLS) += tls.o
13//kbuild:lib-$(CONFIG_TLS) += tls_pstm.o 13//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm.o
14//kbuild:lib-$(CONFIG_TLS) += tls_pstm_montgomery_reduce.o 14//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm_montgomery_reduce.o
15//kbuild:lib-$(CONFIG_TLS) += tls_pstm_mul_comba.o 15//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm_mul_comba.o
16//kbuild:lib-$(CONFIG_TLS) += tls_pstm_sqr_comba.o 16//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm_sqr_comba.o
17//kbuild:lib-$(CONFIG_TLS) += tls_aes.o 17//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_aes.o
18//kbuild:lib-$(CONFIG_TLS) += tls_aesgcm.o 18//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_aesgcm.o
19//kbuild:lib-$(CONFIG_TLS) += tls_rsa.o 19//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_rsa.o
20//kbuild:lib-$(CONFIG_TLS) += tls_fe.o 20//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_fe.o
21//kbuild:lib-$(CONFIG_TLS) += tls_sp_c32.o 21//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_sp_c32.o
22 22
23#include "tls.h" 23#include "tls.h"
24 24
25#if !ENABLE_FEATURE_TLS_SCHANNEL
25// Usually enabled. You can disable some of them to force only 26// Usually enabled. You can disable some of them to force only
26// specific ciphers to be advertized to server. 27// specific ciphers to be advertized to server.
27// (this would not exclude code to handle disabled ciphers, no code size win) 28// (this would not exclude code to handle disabled ciphers, no code size win)
@@ -188,8 +189,6 @@
188#define TLS_MAX_OUTBUF (1 << 14) 189#define TLS_MAX_OUTBUF (1 << 14)
189 190
190enum { 191enum {
191 SHA_INSIZE = 64,
192
193 AES128_KEYSIZE = 16, 192 AES128_KEYSIZE = 16,
194 AES256_KEYSIZE = 32, 193 AES256_KEYSIZE = 32,
195 194
@@ -335,34 +334,6 @@ void FAST_FUNC tls_get_random(void *buf, unsigned len)
335 xfunc_die(); 334 xfunc_die();
336} 335}
337 336
338static void xorbuf3(void *dst, const void *src1, const void *src2, unsigned count)
339{
340 uint8_t *d = dst;
341 const uint8_t *s1 = src1;
342 const uint8_t* s2 = src2;
343 while (count--)
344 *d++ = *s1++ ^ *s2++;
345}
346
347void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count)
348{
349 xorbuf3(dst, dst, src, count);
350}
351
352void FAST_FUNC xorbuf_aligned_AES_BLOCK_SIZE(void *dst, const void *src)
353{
354 unsigned long *d = dst;
355 const unsigned long *s = src;
356 d[0] ^= s[0];
357#if ULONG_MAX <= 0xffffffffffffffff
358 d[1] ^= s[1];
359 #if ULONG_MAX == 0xffffffff
360 d[2] ^= s[2];
361 d[3] ^= s[3];
362 #endif
363#endif
364}
365
366#if !TLS_DEBUG_HASH 337#if !TLS_DEBUG_HASH
367# define hash_handshake(tls, fmt, buffer, len) \ 338# define hash_handshake(tls, fmt, buffer, len) \
368 hash_handshake(tls, buffer, len) 339 hash_handshake(tls, buffer, len)
@@ -393,128 +364,6 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer
393# define TLS_MAC_SIZE(tls) (tls)->MAC_size 364# define TLS_MAC_SIZE(tls) (tls)->MAC_size
394#endif 365#endif
395 366
396// RFC 2104:
397// HMAC(key, text) based on a hash H (say, sha256) is:
398// ipad = [0x36 x INSIZE]
399// opad = [0x5c x INSIZE]
400// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
401//
402// H(key XOR opad) and H(key XOR ipad) can be precomputed
403// if we often need HMAC hmac with the same key.
404//
405// text is often given in disjoint pieces.
406typedef struct hmac_precomputed {
407 md5sha_ctx_t hashed_key_xor_ipad;
408 md5sha_ctx_t hashed_key_xor_opad;
409} hmac_precomputed_t;
410
411typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
412#if !ENABLE_FEATURE_TLS_SHA1
413#define hmac_begin(pre,key,key_size,begin) \
414 hmac_begin(pre,key,key_size)
415#define begin sha256_begin
416#endif
417static void hmac_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
418{
419 uint8_t key_xor_ipad[SHA_INSIZE];
420 uint8_t key_xor_opad[SHA_INSIZE];
421// uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
422 unsigned i;
423
424 // "The authentication key can be of any length up to INSIZE, the
425 // block length of the hash function. Applications that use keys longer
426 // than INSIZE bytes will first hash the key using H and then use the
427 // resultant OUTSIZE byte string as the actual key to HMAC."
428 if (key_size > SHA_INSIZE) {
429 bb_simple_error_msg_and_die("HMAC key>64"); //does not happen (yet?)
430// md5sha_ctx_t ctx;
431// begin(&ctx);
432// md5sha_hash(&ctx, key, key_size);
433// key_size = sha_end(&ctx, tempkey);
434// //key = tempkey; - right? RIGHT? why does it work without this?
435// // because SHA_INSIZE is 64, but hmac() is always called with
436// // key_size = tls->MAC_size = SHA1/256_OUTSIZE (20 or 32),
437// // and prf_hmac_sha256() -> hmac_sha256() key sizes are:
438// // - RSA_PREMASTER_SIZE is 48
439// // - CURVE25519_KEYSIZE is 32
440// // - master_secret[] is 48
441 }
442
443 for (i = 0; i < key_size; i++) {
444 key_xor_ipad[i] = key[i] ^ 0x36;
445 key_xor_opad[i] = key[i] ^ 0x5c;
446 }
447 for (; i < SHA_INSIZE; i++) {
448 key_xor_ipad[i] = 0x36;
449 key_xor_opad[i] = 0x5c;
450 }
451
452 begin(&pre->hashed_key_xor_ipad);
453 begin(&pre->hashed_key_xor_opad);
454 md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
455 md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
456}
457#undef begin
458
459static unsigned hmac_sha_precomputed_v(
460 hmac_precomputed_t *pre,
461 uint8_t *out,
462 va_list va)
463{
464 uint8_t *text;
465 unsigned len;
466
467 /* pre->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
468 /* pre->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
469
470 /* calculate out = H((key XOR ipad) + text) */
471 while ((text = va_arg(va, uint8_t*)) != NULL) {
472 unsigned text_size = va_arg(va, unsigned);
473 md5sha_hash(&pre->hashed_key_xor_ipad, text, text_size);
474 }
475 len = sha_end(&pre->hashed_key_xor_ipad, out);
476
477 /* out = H((key XOR opad) + out) */
478 md5sha_hash(&pre->hashed_key_xor_opad, out, len);
479 return sha_end(&pre->hashed_key_xor_opad, out);
480}
481
482static unsigned hmac_sha_precomputed(hmac_precomputed_t *pre_init, uint8_t *out, ...)
483{
484 hmac_precomputed_t pre;
485 va_list va;
486 unsigned len;
487
488 va_start(va, out);
489 pre = *pre_init; /* struct copy */
490 len = hmac_sha_precomputed_v(&pre, out, va);
491 va_end(va);
492 return len;
493}
494
495#if !ENABLE_FEATURE_TLS_SHA1
496#define hmac(tls,out,key,key_size,...) \
497 hmac(out,key,key_size, __VA_ARGS__)
498#endif
499static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
500{
501 hmac_precomputed_t pre;
502 va_list va;
503 unsigned len;
504
505 va_start(va, key_size);
506
507 hmac_begin(&pre, key, key_size,
508 (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
509 ? sha1_begin
510 : sha256_begin
511 );
512 len = hmac_sha_precomputed_v(&pre, out, va);
513
514 va_end(va);
515 return len;
516}
517
518// RFC 5246: 367// RFC 5246:
519// 5. HMAC and the Pseudorandom Function 368// 5. HMAC and the Pseudorandom Function
520//... 369//...
@@ -559,7 +408,7 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
559 const char *label, 408 const char *label,
560 uint8_t *seed, unsigned seed_size) 409 uint8_t *seed, unsigned seed_size)
561{ 410{
562 hmac_precomputed_t pre; 411 hmac_ctx_t ctx;
563 uint8_t a[TLS_MAX_MAC_SIZE]; 412 uint8_t a[TLS_MAX_MAC_SIZE];
564 uint8_t *out_p = outbuf; 413 uint8_t *out_p = outbuf;
565 unsigned label_size = strlen(label); 414 unsigned label_size = strlen(label);
@@ -569,26 +418,27 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
569#define SEED label, label_size, seed, seed_size 418#define SEED label, label_size, seed, seed_size
570#define A a, MAC_size 419#define A a, MAC_size
571 420
572 hmac_begin(&pre, secret, secret_size, sha256_begin); 421 hmac_begin(&ctx, secret, secret_size, sha256_begin_hmac);
573 422
574 /* A(1) = HMAC_hash(secret, seed) */ 423 /* A(1) = HMAC_hash(secret, seed) */
575 hmac_sha_precomputed(&pre, a, SEED, NULL); 424 hmac_peek_hash(&ctx, a, SEED, NULL);
576 425
577 for (;;) { 426 for (;;) {
578 /* HMAC_hash(secret, A(1) + seed) */ 427 /* HMAC_hash(secret, A(1) + seed) */
579 if (outbuf_size <= MAC_size) { 428 if (outbuf_size <= MAC_size) {
580 /* Last, possibly incomplete, block */ 429 /* Last, possibly incomplete, block */
581 /* (use a[] as temp buffer) */ 430 /* (use a[] as temp buffer) */
582 hmac_sha_precomputed(&pre, a, A, SEED, NULL); 431 hmac_peek_hash(&ctx, a, A, SEED, NULL);
583 memcpy(out_p, a, outbuf_size); 432 memcpy(out_p, a, outbuf_size);
433 hmac_uninit(&ctx);
584 return; 434 return;
585 } 435 }
586 /* Not last block. Store directly to result buffer */ 436 /* Not last block. Store directly to result buffer */
587 hmac_sha_precomputed(&pre, out_p, A, SEED, NULL); 437 hmac_peek_hash(&ctx, out_p, A, SEED, NULL);
588 out_p += MAC_size; 438 out_p += MAC_size;
589 outbuf_size -= MAC_size; 439 outbuf_size -= MAC_size;
590 /* A(2) = HMAC_hash(secret, A(1)) */ 440 /* A(2) = HMAC_hash(secret, A(1)) */
591 hmac_sha_precomputed(&pre, a, A, NULL); 441 hmac_peek_hash(&ctx, a, A, NULL);
592 } 442 }
593#undef A 443#undef A
594#undef SECRET 444#undef SECRET
@@ -655,6 +505,32 @@ static void *tls_get_zeroed_outbuf(tls_state_t *tls, int len)
655 return record; 505 return record;
656} 506}
657 507
508/* Calculate the HMAC over the list of blocks */
509#if !ENABLE_FEATURE_TLS_SHA1
510#define hmac_blocks(tls,out,key,key_size,...) \
511 hmac_blocks(out,key,key_size, __VA_ARGS__)
512#endif
513static unsigned hmac_blocks(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
514{
515 hmac_ctx_t ctx;
516 va_list va;
517 unsigned len;
518
519 hmac_begin(&ctx, key, key_size,
520 (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
521 ? sha1_begin_hmac
522 : sha256_begin_hmac
523 );
524
525 va_start(va, key_size);
526 hmac_hash_v(&ctx, va);
527 va_end(va);
528
529 len = hmac_end(&ctx, out);
530 hmac_uninit(&ctx);
531 return len;
532}
533
658static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type) 534static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type)
659{ 535{
660 uint8_t *buf = tls->outbuf + OUTBUF_PFX; 536 uint8_t *buf = tls->outbuf + OUTBUF_PFX;
@@ -676,7 +552,7 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
676 xhdr->len16_lo = size & 0xff; 552 xhdr->len16_lo = size & 0xff;
677 553
678 /* Calculate MAC signature */ 554 /* Calculate MAC signature */
679 hmac(tls, buf + size, /* result */ 555 hmac_blocks(tls, buf + size, /* result */
680 tls->client_write_MAC_key, TLS_MAC_SIZE(tls), 556 tls->client_write_MAC_key, TLS_MAC_SIZE(tls),
681 &tls->write_seq64_be, sizeof(tls->write_seq64_be), 557 &tls->write_seq64_be, sizeof(tls->write_seq64_be),
682 xhdr, RECHDR_LEN, 558 xhdr, RECHDR_LEN,
@@ -865,8 +741,13 @@ static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned ty
865 cnt++; 741 cnt++;
866 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */ 742 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
867 aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch); 743 aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch);
868 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining; 744 if (remaining >= AES_BLOCK_SIZE) {
869 xorbuf(buf, scratch, n); 745 n = AES_BLOCK_SIZE;
746 xorbuf_AES_BLOCK_SIZE(buf, scratch);
747 } else {
748 n = remaining;
749 xorbuf(buf, scratch, n);
750 }
870 buf += n; 751 buf += n;
871 remaining -= n; 752 remaining -= n;
872 } 753 }
@@ -1024,7 +905,7 @@ static void tls_aesgcm_decrypt(tls_state_t *tls, uint8_t *buf, int size)
1024 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */ 905 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
1025 aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch); 906 aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch);
1026 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining; 907 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining;
1027 xorbuf3(buf, scratch, buf + 8, n); 908 xorbuf_3(buf, scratch, buf + 8, n);
1028 buf += n; 909 buf += n;
1029 remaining -= n; 910 remaining -= n;
1030 } 911 }
@@ -2481,3 +2362,490 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2481 } 2362 }
2482 } 2363 }
2483} 2364}
2365#else
2366#include <stdbool.h>
2367
2368#if ENABLE_FEATURE_TLS_SCHANNEL_1_3
2369#include <subauth.h>
2370#endif
2371
2372#define SCHANNEL_USE_BLACKLISTS
2373
2374#include <security.h>
2375#include <schannel.h>
2376
2377
2378#define BB_SCHANNEL_ISC_FLAGS \
2379 (ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT | \
2380 ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM | ISC_REQ_USE_SUPPLIED_CREDS)
2381
2382#define SEC_STATUS_FAIL(status) ((status) != SEC_E_OK)
2383
2384static char *hresult_to_error_string(HRESULT result) {
2385 char *output = NULL;
2386
2387 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
2388 | FORMAT_MESSAGE_IGNORE_INSERTS |
2389 FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, result,
2390 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2391 (char *) &output, 0, NULL);
2392 return output;
2393}
2394
2395static void init_sec_buffer(SecBuffer *buffer, void *pvBuffer, unsigned long cbBuffer, unsigned long BufferType) {
2396 buffer->BufferType = BufferType;
2397 buffer->cbBuffer = cbBuffer;
2398 buffer->pvBuffer = pvBuffer;
2399}
2400
2401#define init_sec_buffer_empty(buffer, BufferType) init_sec_buffer(buffer, NULL, 0, BufferType)
2402
2403static void init_sec_buffer_desc(SecBufferDesc *desc, SecBuffer *buffers, unsigned long buffer_count) {
2404 desc->ulVersion = SECBUFFER_VERSION;
2405 desc->cBuffers = buffer_count;
2406 desc->pBuffers = buffers;
2407}
2408
2409static ssize_t tls_read(tls_state_t *state, char *buf, ssize_t len) {
2410 ssize_t amount_read = 0;
2411
2412 if (state->connection_state > BB_SCHANNEL_OPEN) {
2413 goto Success;
2414 }
2415
2416 while (len > 0) {
2417 if (state->out_buffer) {
2418 unsigned long copy_amount =
2419 min(len, (ssize_t)state->out_buffer_length);
2420 memcpy(buf, state->out_buffer, copy_amount);
2421
2422 amount_read += copy_amount;
2423 buf += copy_amount;
2424 len -= copy_amount;
2425
2426 if (copy_amount == state->out_buffer_length) {
2427 if (state->out_buffer_extra > 0) {
2428 memmove(
2429 state->in_buffer,
2430 state->in_buffer +
2431 (state->in_buffer_offset - state->out_buffer_extra),
2432 state->in_buffer_offset - (state->in_buffer_offset -
2433 state->out_buffer_extra));
2434 state->in_buffer_offset = state->out_buffer_extra;
2435 } else {
2436 state->in_buffer_offset = 0;
2437 }
2438
2439 state->out_buffer = NULL;
2440 state->out_buffer_length = 0;
2441 state->out_buffer_extra = 0;
2442
2443 } else {
2444 state->out_buffer_length -= copy_amount;
2445 state->out_buffer += copy_amount;
2446 }
2447 } else {
2448 SecBuffer buffers[4];
2449 SecBufferDesc desc;
2450 SECURITY_STATUS status;
2451
2452 init_sec_buffer(&buffers[0],
2453 state->in_buffer,
2454 state->in_buffer_offset,
2455 SECBUFFER_DATA);
2456 init_sec_buffer_empty(&buffers[1], SECBUFFER_EMPTY);
2457 init_sec_buffer_empty(&buffers[2], SECBUFFER_EMPTY);
2458 init_sec_buffer_empty(&buffers[3], SECBUFFER_EMPTY);
2459
2460 init_sec_buffer_desc(&desc, buffers, _countof(buffers));
2461
2462 status = DecryptMessage(&state->ctx_handle, &desc, 0, NULL);
2463
2464 switch (status) {
2465 case SEC_E_OK: {
2466 state->out_buffer = buffers[1].pvBuffer;
2467 state->out_buffer_length = buffers[1].cbBuffer;
2468 state->out_buffer_extra = state->in_buffer_offset;
2469
2470 if (buffers[3].BufferType == SECBUFFER_EXTRA) {
2471 state->out_buffer_extra = buffers[3].cbBuffer;
2472 } else {
2473 state->out_buffer_extra = 0;
2474 }
2475
2476 continue;
2477 }
2478 case SEC_I_CONTEXT_EXPIRED: {
2479 // Shut down the connection
2480 state->connection_state = BB_SCHANNEL_CLOSED;
2481 goto Success;
2482 }
2483 case SEC_E_INCOMPLETE_MESSAGE: {
2484 int result = safe_read(state->ifd,
2485 state->in_buffer + state->in_buffer_offset,
2486 sizeof(state->in_buffer) - state->in_buffer_offset);
2487 if (result == 0) {
2488 state->connection_state = BB_SCHANNEL_CLOSED;
2489 goto Success;
2490 } else if (result < 0) {
2491 bb_error_msg_and_die("schannel: read() failed");
2492 }
2493
2494 state->in_buffer_offset += result;
2495 continue;
2496 }
2497 case SEC_I_RENEGOTIATE: {
2498 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2499
2500 SecBuffer in_buffers[2];
2501 SecBufferDesc in_buffers_desc;
2502
2503 SecBuffer out_buffers[2];
2504 SecBufferDesc out_buffers_desc;
2505
2506 init_sec_buffer(&in_buffers[0],
2507 buffers[3].pvBuffer,
2508 buffers[3].cbBuffer,
2509 SECBUFFER_TOKEN);
2510 init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY);
2511
2512 init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN);
2513 init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT);
2514
2515 init_sec_buffer_desc(
2516 &in_buffers_desc, in_buffers, _countof(in_buffers));
2517 init_sec_buffer_desc(
2518 &out_buffers_desc, out_buffers, _countof(out_buffers));
2519
2520 status = InitializeSecurityContextA(&state->cred_handle,
2521 &state->ctx_handle,
2522 state->hostname,
2523 flags,
2524 0,
2525 0,
2526 &in_buffers_desc,
2527 0,
2528 &state->ctx_handle,
2529 &out_buffers_desc,
2530 &flags,
2531 0);
2532
2533 if (SEC_STATUS_FAIL(status)) {
2534 bb_error_msg_and_die("schannel: renegotiate failed: (0x%08lx): %s",
2535 status, hresult_to_error_string(status));
2536 }
2537
2538 if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
2539 memmove(state->in_buffer,
2540 state->in_buffer + (state->in_buffer_offset -
2541 in_buffers[1].cbBuffer),
2542 in_buffers[1].cbBuffer);
2543 state->in_buffer_offset = in_buffers[1].cbBuffer;
2544 } else {
2545 state->in_buffer_offset = 0;
2546 }
2547
2548 continue;
2549 }
2550 default: {
2551 bb_error_msg_and_die("schannel: DecryptMessage failed: (0x%08lx): %s",
2552 status, hresult_to_error_string(status));
2553 }
2554 }
2555 }
2556 }
2557
2558Success:
2559 return amount_read;
2560}
2561
2562static void tls_write(tls_state_t *state, char *buf, size_t len) {
2563 if (state->connection_state > BB_SCHANNEL_OPEN) {
2564 bb_error_msg_and_die("schannel: attempted to write to a closed connection");
2565 }
2566
2567 while (len > 0) {
2568 SECURITY_STATUS status;
2569 unsigned long copy_amount =
2570 min(len, (size_t)state->stream_sizes.cbMaximumMessage);
2571 char *write_buffer = _alloca(sizeof(state->in_buffer));
2572
2573 SecBuffer buffers[4];
2574 SecBufferDesc desc;
2575
2576 init_sec_buffer(&buffers[0],
2577 write_buffer,
2578 state->stream_sizes.cbHeader,
2579 SECBUFFER_STREAM_HEADER);
2580 init_sec_buffer(&buffers[1],
2581 write_buffer + state->stream_sizes.cbHeader,
2582 copy_amount,
2583 SECBUFFER_DATA);
2584 init_sec_buffer(&buffers[2],
2585 write_buffer + state->stream_sizes.cbHeader +
2586 copy_amount,
2587 state->stream_sizes.cbTrailer,
2588 SECBUFFER_STREAM_TRAILER);
2589 init_sec_buffer_empty(&buffers[3], SECBUFFER_EMPTY);
2590
2591 init_sec_buffer_desc(&desc, buffers, _countof(buffers));
2592
2593 memcpy(buffers[1].pvBuffer, buf, copy_amount);
2594
2595 status = EncryptMessage(&state->ctx_handle, 0, &desc, 0);
2596 if (SEC_STATUS_FAIL(status)) {
2597 bb_error_msg_and_die("schannel: EncryptMessage failed: (0x%08lx): %s",
2598 status, hresult_to_error_string(status));
2599 }
2600
2601 xwrite(state->ofd, write_buffer,
2602 buffers[0].cbBuffer + buffers[1].cbBuffer +
2603 buffers[2].cbBuffer);
2604
2605 len -= copy_amount;
2606 }
2607}
2608
2609static void tls_disconnect(tls_state_t * state) {
2610 SECURITY_STATUS status;
2611
2612 DWORD token = SCHANNEL_SHUTDOWN;
2613 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2614
2615 SecBuffer buf_token;
2616 SecBufferDesc buf_token_desc;
2617
2618 SecBuffer out_buffer;
2619 SecBufferDesc out_buffer_desc;
2620
2621 if (state->connection_state == BB_SCHANNEL_CLOSED_AND_FREED)
2622 return;
2623 state->connection_state = BB_SCHANNEL_CLOSED_AND_FREED;
2624
2625 init_sec_buffer(&buf_token, &token, sizeof(token), SECBUFFER_TOKEN);
2626 init_sec_buffer_desc(&buf_token_desc, &buf_token, 1);
2627
2628 ApplyControlToken(&state->ctx_handle, &buf_token_desc);
2629
2630 init_sec_buffer_empty(&out_buffer, SECBUFFER_TOKEN);
2631 init_sec_buffer_desc(&out_buffer_desc, &out_buffer, 1);
2632
2633 status = InitializeSecurityContextA(&state->cred_handle,
2634 &state->ctx_handle,
2635 state->hostname,
2636 flags,
2637 0,
2638 0,
2639 NULL,
2640 0,
2641 &state->ctx_handle,
2642 &out_buffer_desc,
2643 &flags,
2644 0);
2645
2646 if ((status == SEC_E_OK) || (status == SEC_I_CONTEXT_EXPIRED)) {
2647 write(state->ofd, out_buffer.pvBuffer, out_buffer.cbBuffer);
2648 FreeContextBuffer(out_buffer.pvBuffer);
2649 }
2650
2651 DeleteSecurityContext(&state->ctx_handle);
2652 FreeCredentialsHandle(&state->cred_handle);
2653 free(state->hostname);
2654}
2655
2656
2657void FAST_FUNC tls_handshake(tls_state_t *state, const char *hostname) {
2658 SECURITY_STATUS status;
2659
2660#if ENABLE_FEATURE_TLS_SCHANNEL_1_3
2661 SCH_CREDENTIALS credential = {.dwVersion = SCH_CREDENTIALS_VERSION,
2662 .dwCredFormat = 0,
2663 .cCreds = 0,
2664 .paCred = NULL,
2665 .hRootStore = NULL,
2666 .cMappers = 0,
2667 .aphMappers = NULL,
2668 .dwSessionLifespan = 0,
2669 .dwFlags =
2670 SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
2671 SCH_USE_STRONG_CRYPTO,
2672 .cTlsParameters = 0,
2673 .pTlsParameters = NULL
2674 };
2675#else
2676 SCHANNEL_CRED credential = {.dwVersion = SCHANNEL_CRED_VERSION,
2677 .cCreds = 0,
2678 .paCred = NULL,
2679 .hRootStore = NULL,
2680 .cMappers = 0,
2681 .aphMappers = NULL,
2682 .cSupportedAlgs = 0,
2683 .palgSupportedAlgs = NULL,
2684 .grbitEnabledProtocols =
2685 SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT |
2686 SP_PROT_TLS1_2_CLIENT,
2687 .dwMinimumCipherStrength = 0,
2688 .dwMaximumCipherStrength = 0,
2689 .dwSessionLifespan = 0,
2690 .dwFlags =
2691 SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
2692 SCH_USE_STRONG_CRYPTO,
2693 .dwCredFormat = 0
2694 };
2695#endif
2696
2697 state->in_buffer_offset = 0;
2698
2699 state->out_buffer = NULL;
2700 state->out_buffer_length = 0;
2701 state->out_buffer_extra = 0;
2702
2703 state->hostname = xstrdup(hostname);
2704 state->initialized = false;
2705 state->connection_state = BB_SCHANNEL_OPEN;
2706
2707 if (SEC_STATUS_FAIL(status = AcquireCredentialsHandleA(NULL,
2708 (SEC_CHAR *)UNISP_NAME_A,
2709 SECPKG_CRED_OUTBOUND,
2710 NULL,
2711 &credential,
2712 NULL,
2713 NULL,
2714 &state->cred_handle,
2715 NULL))) {
2716 bb_error_msg_and_die("schannel: AcquireCredentialsHandleA failed: (0x%08lx): %s",
2717 status, hresult_to_error_string(status));
2718 }
2719
2720 // InitializeSecurityContext loop
2721 while (true) {
2722 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2723
2724 SecBuffer in_buffers[2];
2725 SecBufferDesc in_buffers_desc;
2726
2727 SecBuffer out_buffers[2];
2728 SecBufferDesc out_buffers_desc;
2729
2730 init_sec_buffer(&in_buffers[0],
2731 state->in_buffer,
2732 state->in_buffer_offset,
2733 SECBUFFER_TOKEN);
2734 init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY);
2735
2736 init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN);
2737 init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT);
2738
2739 init_sec_buffer_desc(
2740 &in_buffers_desc, in_buffers, _countof(in_buffers));
2741 init_sec_buffer_desc(
2742 &out_buffers_desc, out_buffers, _countof(out_buffers));
2743
2744 status = InitializeSecurityContextA(
2745 &state->cred_handle,
2746 state->initialized ? &state->ctx_handle : NULL,
2747 state->hostname,
2748 flags,
2749 0,
2750 0,
2751 state->initialized ? &in_buffers_desc : NULL,
2752 0,
2753 &state->ctx_handle,
2754 &out_buffers_desc,
2755 &flags,
2756 0);
2757
2758 state->initialized = true;
2759
2760 if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
2761 memmove(state->in_buffer,
2762 state->in_buffer +
2763 (state->in_buffer_offset - in_buffers[1].cbBuffer),
2764 in_buffers[1].cbBuffer);
2765 state->in_buffer_offset = in_buffers[1].cbBuffer;
2766 }
2767
2768 switch (status) {
2769 case SEC_E_OK: {
2770 state->in_buffer_offset =
2771 (in_buffers[1].BufferType == SECBUFFER_EXTRA)
2772 ? in_buffers[1].cbBuffer
2773 : 0;
2774
2775 if (out_buffers[0].cbBuffer > 0) {
2776 xwrite(state->ifd, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
2777 FreeContextBuffer(out_buffers[0].pvBuffer);
2778 }
2779
2780 goto Success;
2781 }
2782 case SEC_I_CONTINUE_NEEDED: {
2783 xwrite(state->ofd, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
2784 FreeContextBuffer(out_buffers[0].pvBuffer);
2785 if (in_buffers[1].BufferType != SECBUFFER_EXTRA) {
2786 state->in_buffer_offset = 0;
2787 }
2788 break;
2789 }
2790 case SEC_E_INCOMPLETE_MESSAGE: {
2791 int amount_read =
2792 safe_read(state->ifd, state->in_buffer + state->in_buffer_offset,
2793 sizeof(state->in_buffer) - state->in_buffer_offset);
2794 if (amount_read <= 0) {
2795 bb_error_msg_and_die("schannel: handshake read() failed");
2796 }
2797
2798 state->in_buffer_offset += amount_read;
2799 continue;
2800 }
2801 default: {
2802 bb_error_msg_and_die("schannel: handshake failed: (0x%08lx): %s",
2803 status, hresult_to_error_string(status));
2804 }
2805 }
2806 }
2807
2808Success:
2809 QueryContextAttributes(
2810 &state->ctx_handle, SECPKG_ATTR_STREAM_SIZES, &state->stream_sizes);
2811 return;
2812}
2813
2814void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags) {
2815 char buffer[65536];
2816
2817 struct pollfd pfds[2];
2818
2819 pfds[0].fd = STDIN_FILENO;
2820 pfds[0].events = POLLIN;
2821 pfds[1].fd = tls->ifd;
2822 pfds[1].events = POLLIN;
2823
2824 for (;;) {
2825 int nread;
2826
2827 if (safe_poll(pfds, 2, -1) < 0)
2828 bb_simple_perror_msg_and_die("poll");
2829
2830 if (pfds[0].revents) {
2831 nread = safe_read(STDIN_FILENO, buffer, sizeof(buffer));
2832 if (nread < 1) {
2833 pfds[0].fd = -1;
2834 tls_disconnect(tls);
2835 if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF)
2836 break;
2837 } else {
2838 tls_write(tls, buffer, nread);
2839 }
2840 }
2841 if (pfds[1].revents) {
2842 nread = tls_read(tls, buffer, sizeof(buffer));
2843 if (nread < 1) {
2844 tls_disconnect(tls);
2845 break;
2846 }
2847 xwrite(STDOUT_FILENO, buffer, nread);
2848 }
2849 }
2850}
2851#endif
diff --git a/networking/tls.h b/networking/tls.h
index 0173b87b2..eee5a7617 100644
--- a/networking/tls.h
+++ b/networking/tls.h
@@ -11,6 +11,7 @@
11#include "libbb.h" 11#include "libbb.h"
12 12
13 13
14#if !ENABLE_FEATURE_TLS_SCHANNEL
14/* Config tweaks */ 15/* Config tweaks */
15#define HAVE_NATIVE_INT64 16#define HAVE_NATIVE_INT64
16#undef USE_1024_KEY_SPEED_OPTIMIZATIONS 17#undef USE_1024_KEY_SPEED_OPTIMIZATIONS
@@ -82,10 +83,9 @@ typedef int16_t int16;
82 83
83void tls_get_random(void *buf, unsigned len) FAST_FUNC; 84void tls_get_random(void *buf, unsigned len) FAST_FUNC;
84 85
85void xorbuf(void* buf, const void* mask, unsigned count) FAST_FUNC;
86
87#define ALIGNED_long ALIGNED(sizeof(long)) 86#define ALIGNED_long ALIGNED(sizeof(long))
88void xorbuf_aligned_AES_BLOCK_SIZE(void* buf, const void* mask) FAST_FUNC; 87#define xorbuf_aligned_AES_BLOCK_SIZE(dst,src) xorbuf16_aligned_long(dst,src)
88#define xorbuf_AES_BLOCK_SIZE(dst,src) xorbuf16(dst,src)
89 89
90#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS) 90#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS)
91 91
@@ -120,3 +120,4 @@ void curve_P256_compute_pubkey_and_premaster(
120void curve_P256_compute_pubkey_and_premaster_NEW( 120void curve_P256_compute_pubkey_and_premaster_NEW(
121 uint8_t *pubkey2x32, uint8_t *premaster32, 121 uint8_t *pubkey2x32, uint8_t *premaster32,
122 const uint8_t *peerkey2x32) FAST_FUNC; 122 const uint8_t *peerkey2x32) FAST_FUNC;
123#endif
diff --git a/networking/tls_aesgcm.c b/networking/tls_aesgcm.c
index 5ddcdd2ad..9c2381a57 100644
--- a/networking/tls_aesgcm.c
+++ b/networking/tls_aesgcm.c
@@ -167,10 +167,7 @@ void FAST_FUNC aesgcm_GHASH(byte* h,
167 blocks = cSz / AES_BLOCK_SIZE; 167 blocks = cSz / AES_BLOCK_SIZE;
168 partial = cSz % AES_BLOCK_SIZE; 168 partial = cSz % AES_BLOCK_SIZE;
169 while (blocks--) { 169 while (blocks--) {
170 if (BB_UNALIGNED_MEMACCESS_OK) // c is not guaranteed to be aligned 170 xorbuf_AES_BLOCK_SIZE(x, c);
171 xorbuf_aligned_AES_BLOCK_SIZE(x, c);
172 else
173 xorbuf(x, c, AES_BLOCK_SIZE);
174 GMULT(x, h); 171 GMULT(x, h);
175 c += AES_BLOCK_SIZE; 172 c += AES_BLOCK_SIZE;
176 } 173 }
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 79cef1999..19c961d5c 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -148,10 +148,11 @@ enum {
148 OPT_o = 1 << 12, 148 OPT_o = 1 << 12,
149 OPT_x = 1 << 13, 149 OPT_x = 1 << 13,
150 OPT_f = 1 << 14, 150 OPT_f = 1 << 14,
151 OPT_l = 1 << 15, 151 OPT_m = 1 << 15,
152 OPT_d = 1 << 16, 152 OPT_l = 1 << 16,
153 OPT_d = 1 << 17,
153/* The rest has variable bit positions, need to be clever */ 154/* The rest has variable bit positions, need to be clever */
154 OPTBIT_d = 16, 155 OPTBIT_d = 17,
155 USE_FOR_MMU( OPTBIT_b,) 156 USE_FOR_MMU( OPTBIT_b,)
156 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) 157 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
157 IF_FEATURE_UDHCP_PORT( OPTBIT_P,) 158 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
@@ -268,6 +269,23 @@ static void option_to_env(const uint8_t *option, const uint8_t *option_end)
268 //case D6_OPT_SERVERID: 269 //case D6_OPT_SERVERID:
269 case D6_OPT_IA_NA: 270 case D6_OPT_IA_NA:
270 case D6_OPT_IA_PD: 271 case D6_OPT_IA_PD:
272/* 0 1 2 3
273 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
274 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
275 * | OPTION_IA_PD | option-length |
276 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
277 * | IAID (4 octets) |
278 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
279 * | T1 |
280 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
281 * | T2 |
282 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
283 * . .
284 * . IA_PD-options .
285 * . .
286 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
287 */
288 /* recurse to handle "IA_PD-options" field */
271 option_to_env(option + 16, option + 4 + option[3]); 289 option_to_env(option + 16, option + 4 + option[3]);
272 break; 290 break;
273 //case D6_OPT_IA_TA: 291 //case D6_OPT_IA_TA:
@@ -604,6 +622,31 @@ static NOINLINE int send_d6_info_request(void)
604 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr); 622 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
605} 623}
606 624
625/*
626 * RFC 3315 10. Identity Association
627 *
628 * An "identity-association" (IA) is a construct through which a server
629 * and a client can identify, group, and manage a set of related IPv6
630 * addresses. Each IA consists of an IAID and associated configuration
631 * information.
632 *
633 * A client must associate at least one distinct IA with each of its
634 * network interfaces for which it is to request the assignment of IPv6
635 * addresses from a DHCP server. The client uses the IAs assigned to an
636 * interface to obtain configuration information from a server for that
637 * interface. Each IA must be associated with exactly one interface.
638 *
639 * The IAID uniquely identifies the IA and must be chosen to be unique
640 * among the IAIDs on the client. The IAID is chosen by the client.
641 * For any given use of an IA by the client, the IAID for that IA MUST
642 * be consistent across restarts of the DHCP client...
643 */
644/* Generate IAID. We base it on our MAC address' last 4 bytes */
645static void generate_iaid(uint8_t *iaid)
646{
647 memcpy(iaid, &client_data.client_mac[2], 4);
648}
649
607/* Multicast a DHCPv6 Solicit packet to the network, with an optionally requested IP. 650/* Multicast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
608 * 651 *
609 * RFC 3315 17.1.1. Creation of Solicit Messages 652 * RFC 3315 17.1.1. Creation of Solicit Messages
@@ -703,7 +746,7 @@ static NOINLINE int send_d6_discover(struct in6_addr *requested_ipv6)
703 client6_data.ia_na = xzalloc(len); 746 client6_data.ia_na = xzalloc(len);
704 client6_data.ia_na->code = D6_OPT_IA_NA; 747 client6_data.ia_na->code = D6_OPT_IA_NA;
705 client6_data.ia_na->len = len - 4; 748 client6_data.ia_na->len = len - 4;
706 *(bb__aliased_uint32_t*)client6_data.ia_na->data = rand(); /* IAID */ 749 generate_iaid(client6_data.ia_na->data); /* IAID */
707 if (requested_ipv6) { 750 if (requested_ipv6) {
708 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4); 751 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
709 iaaddr->code = D6_OPT_IAADDR; 752 iaaddr->code = D6_OPT_IAADDR;
@@ -721,7 +764,7 @@ static NOINLINE int send_d6_discover(struct in6_addr *requested_ipv6)
721 client6_data.ia_pd = xzalloc(len); 764 client6_data.ia_pd = xzalloc(len);
722 client6_data.ia_pd->code = D6_OPT_IA_PD; 765 client6_data.ia_pd->code = D6_OPT_IA_PD;
723 client6_data.ia_pd->len = len - 4; 766 client6_data.ia_pd->len = len - 4;
724 *(bb__aliased_uint32_t*)client6_data.ia_pd->data = rand(); /* IAID */ 767 generate_iaid(client6_data.ia_pd->data); /* IAID */
725 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len); 768 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
726 } 769 }
727 770
@@ -1131,12 +1174,11 @@ static void client_background(void)
1131//usage:#endif 1174//usage:#endif
1132//usage:#define udhcpc6_trivial_usage 1175//usage:#define udhcpc6_trivial_usage
1133//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"R] [-t N] [-T SEC] [-A SEC|-n] [-i IFACE] [-s PROG]\n" 1176//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"R] [-t N] [-T SEC] [-A SEC|-n] [-i IFACE] [-s PROG]\n"
1134//usage: " [-p PIDFILE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-ldo] [-r IPv6] [-x OPT:VAL]... [-O OPT]..." 1177//usage: " [-p PIDFILE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-mldo] [-r IPv6] [-x OPT:VAL]... [-O OPT]..."
1135//usage:#define udhcpc6_full_usage "\n" 1178//usage:#define udhcpc6_full_usage "\n"
1136//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")" 1179//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")"
1137//usage: "\n -p FILE Create pidfile" 1180//usage: "\n -p FILE Create pidfile"
1138//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC6_DEFAULT_SCRIPT")" 1181//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC6_DEFAULT_SCRIPT")"
1139//usage: "\n -B Request broadcast replies"
1140//usage: "\n -t N Send up to N discover packets" 1182//usage: "\n -t N Send up to N discover packets"
1141//usage: "\n -T SEC Pause between packets (default 3)" 1183//usage: "\n -T SEC Pause between packets (default 3)"
1142//usage: "\n -A SEC Wait if lease is not obtained (default 20)" 1184//usage: "\n -A SEC Wait if lease is not obtained (default 20)"
@@ -1154,6 +1196,7 @@ static void client_background(void)
1154////usage: IF_FEATURE_UDHCPC_ARPING( 1196////usage: IF_FEATURE_UDHCPC_ARPING(
1155////usage: "\n -a Use arping to validate offered address" 1197////usage: "\n -a Use arping to validate offered address"
1156////usage: ) 1198////usage: )
1199//usage: "\n -m Send multicast renew requests rather than unicast ones"
1157//usage: "\n -l Send 'information request' instead of 'solicit'" 1200//usage: "\n -l Send 'information request' instead of 'solicit'"
1158//usage: "\n (used for servers which do not assign IPv6 addresses)" 1201//usage: "\n (used for servers which do not assign IPv6 addresses)"
1159//usage: "\n -r IPv6 Request this address ('no' to not request any IP)" 1202//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
@@ -1211,7 +1254,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1211 /* Parse command line */ 1254 /* Parse command line */
1212 opt = getopt32long(argv, "^" 1255 opt = getopt32long(argv, "^"
1213 /* O,x: list; -T,-t,-A take numeric param */ 1256 /* O,x: list; -T,-t,-A take numeric param */
1214 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fld" 1257 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fmld"
1215 USE_FOR_MMU("b") 1258 USE_FOR_MMU("b")
1216 ///IF_FEATURE_UDHCPC_ARPING("a") 1259 ///IF_FEATURE_UDHCPC_ARPING("a")
1217 IF_FEATURE_UDHCP_PORT("P:") 1260 IF_FEATURE_UDHCP_PORT("P:")
@@ -1464,7 +1507,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1464 if (opt & OPT_l) 1507 if (opt & OPT_l)
1465 send_d6_info_request(); 1508 send_d6_info_request();
1466 else 1509 else
1467 send_d6_renew(&srv6_buf, requested_ipv6); 1510 send_d6_renew(OPT_m ? NULL : &srv6_buf, requested_ipv6);
1468 timeout = discover_timeout; 1511 timeout = discover_timeout;
1469 packet_num++; 1512 packet_num++;
1470 continue; 1513 continue;
@@ -1606,62 +1649,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1606 case RENEW_REQUESTED: 1649 case RENEW_REQUESTED:
1607 case REBINDING: 1650 case REBINDING:
1608 if (packet.d6_msg_type == D6_MSG_REPLY) { 1651 if (packet.d6_msg_type == D6_MSG_REPLY) {
1609 unsigned start;
1610 uint32_t lease_seconds;
1611 struct d6_option *option;
1612 unsigned address_timeout;
1613 unsigned prefix_timeout;
1614 type_is_ok:
1615 change_listen_mode(LISTEN_NONE);
1616
1617 address_timeout = 0;
1618 prefix_timeout = 0;
1619 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
1620 if (option && (option->data[0] | option->data[1]) != 0) {
1621///FIXME:
1622// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1623// | OPTION_STATUS_CODE | option-len |
1624// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1625// | status-code | |
1626// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1627// . status-message .
1628// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1629// so why do we think it's NAK if data[0] is zero but data[1] is not? That's wrong...
1630// we should also check that option->len is ok (i.e. not 0), right?
1631 /* return to init state */
1632 bb_info_msg("received DHCP NAK (%u)", option->data[4]);
1633 d6_run_script(packet.d6_options,
1634 packet_end, "nak");
1635 if (client_data.state != REQUESTING)
1636 d6_run_script_no_option("deconfig");
1637 sleep(3); /* avoid excessive network traffic */
1638 client_data.state = INIT_SELECTING;
1639 client_data.first_secs = 0; /* make secs field count from 0 */
1640 requested_ipv6 = NULL;
1641 timeout = 0;
1642 packet_num = 0;
1643 continue;
1644 }
1645 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1646 if (!option) {
1647 bb_simple_info_msg("no server ID, ignoring packet");
1648 continue;
1649 /* still selecting - this server looks bad */
1650 }
1651//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1652//server_id variable is used solely for creation of proper server_id option
1653//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1654 free(client6_data.server_id);
1655 client6_data.server_id = option;
1656 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1657 /* enter requesting state */
1658 change_listen_mode(LISTEN_RAW);
1659 client_data.state = REQUESTING;
1660 timeout = 0;
1661 packet_num = 0;
1662 continue;
1663 }
1664 /* It's a D6_MSG_REPLY */
1665/* 1652/*
1666 * RFC 3315 18.1.8. Receipt of Reply Messages 1653 * RFC 3315 18.1.8. Receipt of Reply Messages
1667 * 1654 *
@@ -1747,6 +1734,67 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1747 * . . 1734 * . .
1748 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1735 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1749 */ 1736 */
1737 unsigned start;
1738 uint32_t lease_seconds;
1739 struct d6_option *option;
1740 unsigned address_timeout;
1741 unsigned prefix_timeout;
1742 type_is_ok:
1743 change_listen_mode(LISTEN_NONE);
1744
1745 address_timeout = 0;
1746 prefix_timeout = 0;
1747 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
1748 if (option) {
1749// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1750// | OPTION_STATUS_CODE | option-len |
1751// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1752// | status-code | |
1753// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1754// . status-message .
1755// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1756 unsigned len, status;
1757 len = ((unsigned)option->len_hi << 8) + option->len;
1758 if (len < 2) {
1759 bb_simple_error_msg("invalid OPTION_STATUS_CODE, ignoring packet");
1760 continue;
1761 }
1762 status = ((unsigned)option->data[0] << 8) + option->data[1];
1763 if (status != 0) {
1764//TODO: handle status == 5 (UseMulticast)?
1765 /* return to init state */
1766 bb_info_msg("received DHCP NAK: %u '%.*s'", status, len - 2, option->data + 2);
1767 d6_run_script(packet.d6_options, packet_end, "nak");
1768 if (client_data.state != REQUESTING)
1769 d6_run_script_no_option("deconfig");
1770 sleep(3); /* avoid excessive network traffic */
1771 client_data.state = INIT_SELECTING;
1772 client_data.first_secs = 0; /* make secs field count from 0 */
1773 requested_ipv6 = NULL;
1774 timeout = 0;
1775 packet_num = 0;
1776 continue;
1777 }
1778 }
1779 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1780 if (!option) {
1781 bb_simple_info_msg("no server ID, ignoring packet");
1782 continue;
1783 /* still selecting - this server looks bad */
1784 }
1785//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1786//server_id variable is used solely for creation of proper server_id option
1787//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1788 free(client6_data.server_id);
1789 client6_data.server_id = option;
1790 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1791 /* enter requesting state */
1792 change_listen_mode(LISTEN_RAW);
1793 client_data.state = REQUESTING;
1794 timeout = 0;
1795 packet_num = 0;
1796 continue;
1797 }
1750 if (option_mask32 & OPT_r) { 1798 if (option_mask32 & OPT_r) {
1751 struct d6_option *iaaddr; 1799 struct d6_option *iaaddr;
1752 1800
@@ -1790,6 +1838,21 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1790 1838
1791 free(client6_data.ia_pd); 1839 free(client6_data.ia_pd);
1792 client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD); 1840 client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD);
1841// 0 1 2 3
1842// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1843// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1844// | OPTION_IA_PD | option-length |
1845// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1846// | IAID (4 octets) |
1847// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1848// | T1 |
1849// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1850// | T2 |
1851// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1852// . .
1853// . IA_PD-options .
1854// . .
1855// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1793 if (!client6_data.ia_pd) { 1856 if (!client6_data.ia_pd) {
1794 bb_info_msg("no %s option%s", "IA_PD", ", ignoring packet"); 1857 bb_info_msg("no %s option%s", "IA_PD", ", ignoring packet");
1795 continue; 1858 continue;
diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c
index 142de9b43..1d7541948 100644
--- a/networking/udhcp/d6_packet.c
+++ b/networking/udhcp/d6_packet.c
@@ -153,13 +153,15 @@ int FAST_FUNC d6_send_kernel_packet_from_client_data_ifindex(
153 } 153 }
154 setsockopt_reuseaddr(fd); 154 setsockopt_reuseaddr(fd);
155 155
156 memset(&sa, 0, sizeof(sa)); 156 if (src_ipv6) {
157 sa.sin6_family = AF_INET6; 157 memset(&sa, 0, sizeof(sa));
158 sa.sin6_port = htons(source_port); 158 sa.sin6_family = AF_INET6;
159 sa.sin6_addr = *src_ipv6; /* struct copy */ 159 sa.sin6_port = htons(source_port);
160 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 160 sa.sin6_addr = *src_ipv6; /* struct copy */
161 msg = "bind(%s)"; 161 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
162 goto ret_close; 162 msg = "bind(%s)";
163 goto ret_close;
164 }
163 } 165 }
164 166
165 memset(&sa, 0, sizeof(sa)); 167 memset(&sa, 0, sizeof(sa));
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 2904119e5..b9cbd6464 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -575,29 +575,51 @@ static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadc
575 const uint8_t *chaddr; 575 const uint8_t *chaddr;
576 uint32_t ciaddr; 576 uint32_t ciaddr;
577 577
578 // Was: 578 // Logic:
579 //if (force_broadcast) { /* broadcast */ } 579 //if (force_broadcast) { /* broadcast */ }
580 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ } 580 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
581 // ^^^ dhcp_pkt->ciaddr comes from client's request packet.
582 // We expect such clients to have an UDP socket listening on that IP.
581 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ } 583 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
582 //else { /* unicast to dhcp_pkt->yiaddr */ } 584 //else { /* unicast to dhcp_pkt->yiaddr */ }
583 // But this is wrong: yiaddr is _our_ idea what client's IP is 585 // ^^^ The last case is confusing, but *should* work.
584 // (for example, from lease file). Client may not know that, 586 // It's a case where client have sent a DISCOVER
585 // and may not have UDP socket listening on that IP! 587 // and does not have a kernel UDP socket listening on the IP
586 // We should never unicast to dhcp_pkt->yiaddr! 588 // we are offering in yiaddr (it does not know the IP yet)!
587 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet, 589 // This *should* work because client *should* listen on a raw socket
588 // and can be used. 590 // instead at this time (IOW: it should examine ALL IPv4 packets
589 591 // "by hand", not relying on kernel's UDP stack.)
590 if (force_broadcast 592
591 || (dhcp_pkt->flags & htons(BROADCAST_FLAG)) 593 chaddr = dhcp_pkt->chaddr;
592 || dhcp_pkt->ciaddr == 0 594
595 if (dhcp_pkt->ciaddr == 0
596 || force_broadcast /* sending DHCPNAK pkt? */
593 ) { 597 ) {
594 log1s("broadcasting packet to client"); 598 if (dhcp_pkt->flags & htons(BROADCAST_FLAG)
595 ciaddr = INADDR_BROADCAST; 599 || force_broadcast /* sending DHCPNAK pkt? */
596 chaddr = MAC_BCAST_ADDR; 600 ) {
601// RFC 2131:
602// If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
603// set, then the server broadcasts DHCPOFFER and DHCPACK messages to
604// 0xffffffff. ...
605// In all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
606// messages to 0xffffffff.
607 ciaddr = INADDR_BROADCAST;
608 chaddr = MAC_BCAST_ADDR;
609 log1s("broadcasting packet to client");
610 } else {
611// If the broadcast bit is not set and 'giaddr' is zero and
612// 'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
613// messages to the client's hardware address and 'yiaddr' address.
614 ciaddr = dhcp_pkt->yiaddr;
615 log1("unicasting packet to client %ciaddr", 'y');
616 }
597 } else { 617 } else {
598 log1s("unicasting packet to client ciaddr"); 618// If the 'giaddr'
619// field is zero and the 'ciaddr' field is nonzero, then the server
620// unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
599 ciaddr = dhcp_pkt->ciaddr; 621 ciaddr = dhcp_pkt->ciaddr;
600 chaddr = dhcp_pkt->chaddr; 622 log1("unicasting packet to client %ciaddr", 'c');
601 } 623 }
602 624
603 udhcp_send_raw_packet(dhcp_pkt, 625 udhcp_send_raw_packet(dhcp_pkt,
@@ -624,6 +646,10 @@ static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
624static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast) 646static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
625{ 647{
626 if (dhcp_pkt->gateway_nip) 648 if (dhcp_pkt->gateway_nip)
649// RFC 2131:
650// If the 'giaddr' field in a DHCP message from a client is non-zero,
651// the server sends any return messages to the 'DHCP server' port on the
652// BOOTP relay agent whose address appears in 'giaddr'.
627 send_packet_to_relay(dhcp_pkt); 653 send_packet_to_relay(dhcp_pkt);
628 else 654 else
629 send_packet_to_client(dhcp_pkt, force_broadcast); 655 send_packet_to_client(dhcp_pkt, force_broadcast);
diff --git a/procps/pmap.c b/procps/pmap.c
index 49f7688d9..0cac265c8 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -29,10 +29,14 @@
29 29
30#if ULLONG_MAX == 0xffffffff 30#if ULLONG_MAX == 0xffffffff
31# define TABS "\t" 31# define TABS "\t"
32# define SIZEWIDTHx "7"
33# define SIZEWIDTH "9"
32# define AFMTLL "8" 34# define AFMTLL "8"
33# define DASHES "" 35# define DASHES ""
34#else 36#else
35# define TABS "\t\t" 37# define TABS "\t\t"
38# define SIZEWIDTHx "15"
39# define SIZEWIDTH "17"
36# define AFMTLL "16" 40# define AFMTLL "16"
37# define DASHES "--------" 41# define DASHES "--------"
38#endif 42#endif
@@ -42,49 +46,160 @@ enum {
42 OPT_q = 1 << 1, 46 OPT_q = 1 << 1,
43}; 47};
44 48
45static void print_smaprec(struct smaprec *currec, void *data) 49struct smaprec {
46{ 50 // For mixed 32/64 userspace, 32-bit pmap still needs
47 unsigned opt = (uintptr_t)data; 51 // 64-bit field here to correctly show 64-bit processes:
52 unsigned long long smap_start;
53 // Make size wider too:
54 // I've seen 1203765248 kb large "---p" mapping in a browser,
55 // this cuts close to 4 terabytes.
56 unsigned long long smap_size;
57 // (strictly speaking, other fields need to be wider too,
58 // but they are in kbytes, not bytes, and they hold sizes,
59 // not start addresses, sizes tend to be less than 4 terabytes)
60 unsigned long private_dirty;
61 unsigned long smap_pss, smap_swap;
62 char smap_mode[5];
63 char *smap_name;
64};
48 65
66// How long the filenames and command lines we want to handle?
67#define PMAP_BUFSZ 4096
68
69static void print_smaprec(struct smaprec *currec)
70{
49 printf("%0" AFMTLL "llx ", currec->smap_start); 71 printf("%0" AFMTLL "llx ", currec->smap_start);
50 72
51 if (opt & OPT_x) 73 if (option_mask32 & OPT_x)
52 printf("%7lu %7lu %7lu %7lu ", 74 printf("%7llu %7lu %7lu %7lu ",
53 currec->smap_size, 75 currec->smap_size,
54 currec->smap_pss, 76 currec->smap_pss,
55 currec->private_dirty, 77 currec->private_dirty,
56 currec->smap_swap); 78 currec->smap_swap);
57 else 79 else
58 printf("%7luK", currec->smap_size); 80 printf("%7lluK", currec->smap_size);
81
82 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]");
83}
84
85/* libbb's procps_read_smaps() looks somewhat similar,
86 * but the collected information is sufficiently different
87 * that merging them into one function is not a good idea
88 * (unless you feel masochistic today).
89 */
90static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total)
91{
92 FILE *file;
93 struct smaprec currec;
94 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
95
96 sprintf(filename, "/proc/%u/smaps", (int)pid);
97
98 file = fopen_for_read(filename);
99 if (!file)
100 return 1;
101
102 memset(&currec, 0, sizeof(currec));
103 while (fgets(buf, PMAP_BUFSZ, file)) {
104 // Each mapping datum has this form:
105 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
106 // Size: nnn kB
107 // Rss: nnn kB
108 // .....
109
110 char *tp, *p;
111
112#define bytes4 *(uint32_t*)buf
113#define Pss_ PACK32_BYTES('P','s','s',':')
114#define Swap PACK32_BYTES('S','w','a','p')
115#define Priv PACK32_BYTES('P','r','i','v')
116#define FETCH(X, N) \
117 tp = skip_whitespace(buf+4 + (N)); \
118 total->X += currec.X = fast_strtoul_10(&tp); \
119 continue
120#define SCAN(S, X) \
121if (memcmp(buf+4, S, sizeof(S)-1) == 0) { \
122 FETCH(X, sizeof(S)-1); \
123}
124 if (bytes4 == Pss_) {
125 FETCH(smap_pss, 0);
126 }
127 if (bytes4 == Swap && buf[4] == ':') {
128 FETCH(smap_swap, 1);
129 }
130 if (bytes4 == Priv) {
131 SCAN("ate_Dirty:", private_dirty);
132 }
133#undef bytes4
134#undef Pss_
135#undef Swap
136#undef Priv
137#undef FETCH
138#undef SCAN
139 tp = strchr(buf, '-');
140 if (tp) {
141 // We reached next mapping - the line of this form:
142 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
143
144 // If we have a previous record, there's nothing more
145 // for it, print and clear currec
146 if (currec.smap_size)
147 print_smaprec(&currec);
148 free(currec.smap_name);
149 memset(&currec, 0, sizeof(currec));
150
151 *tp = ' ';
152 tp = buf;
153 currec.smap_start = fast_strtoull_16(&tp);
154 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
155 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
156
157 // skipping "rw-s FILEOFS M:m INODE "
158 tp = skip_fields(tp, 4);
159 tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
160 p = strchrnul(tp, '\n');
161 if (p != tp) {
162 currec.smap_name = xstrndup(tp, p - tp);
163 }
164 total->smap_size += currec.smap_size;
165 }
166 } // while (got line)
167 fclose(file);
168
169 if (currec.smap_size)
170 print_smaprec(&currec);
171 free(currec.smap_name);
59 172
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); 173 return 0;
61} 174}
62 175
63static int procps_get_maps(pid_t pid, unsigned opt) 176static int procps_get_maps(pid_t pid, unsigned opt)
64{ 177{
65 struct smaprec total; 178 struct smaprec total;
66 int ret; 179 int ret;
67 char buf[256]; 180 char buf[PMAP_BUFSZ] ALIGN4;
181
182 ret = read_cmdline(buf, sizeof(buf), pid, NULL);
183 if (ret < 0)
184 return ret;
68 185
69 read_cmdline(buf, sizeof(buf), pid, NULL);
70 printf("%u: %s\n", (int)pid, buf); 186 printf("%u: %s\n", (int)pid, buf);
71 187
72 if (!(opt & OPT_q) && (opt & OPT_x)) 188 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); 189 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74 190
75 memset(&total, 0, sizeof(total)); 191 memset(&total, 0, sizeof(total));
76 192 ret = read_smaps(pid, buf, &total);
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
78 if (ret) 193 if (ret)
79 return ret; 194 return ret;
80 195
81 if (!(opt & OPT_q)) { 196 if (!(opt & OPT_q)) {
82 if (opt & OPT_x) 197 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n" 198 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n", 199 "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); 200 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else 201 else
87 printf("mapped: %luK\n", total.smap_size); 202 printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
88 } 203 }
89 204
90 return 0; 205 return 0;
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..7902e82f0 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -117,10 +117,15 @@
117 117
118#include "libbb.h" 118#include "libbb.h"
119 119
120#define ESC "\033" 120#define ESC "\033"
121#define HOME ESC"[H"
122#define CLREOS ESC"[J"
123#define CLREOL ESC"[K"
124#define REVERSE ESC"[7m"
125#define NORMAL ESC"[m"
121 126
122typedef struct top_status_t { 127typedef struct top_status_t {
123 unsigned long vsz; 128 unsigned long memsize;
124#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 129#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
125 unsigned long ticks; 130 unsigned long ticks;
126 unsigned pcpu; /* delta of ticks */ 131 unsigned pcpu; /* delta of ticks */
@@ -161,13 +166,16 @@ struct globals {
161 top_status_t *top; 166 top_status_t *top;
162 int ntop; 167 int ntop;
163 smallint inverted; 168 smallint inverted;
169 smallint first_line_printed;
164#if ENABLE_FEATURE_TOPMEM 170#if ENABLE_FEATURE_TOPMEM
165 smallint sort_field; 171 smallint sort_field;
166#endif 172#endif
167#if ENABLE_FEATURE_TOP_SMP_CPU 173#if ENABLE_FEATURE_TOP_SMP_CPU
168 smallint smp_cpu_info; /* one/many cpu info lines? */ 174 smallint smp_cpu_info; /* one/many cpu info lines? */
169#endif 175#endif
170 unsigned lines; /* screen height */ 176 int lines_remaining;
177 unsigned lines; /* screen height */
178 unsigned scr_width; /* width, clamped <= LINE_BUF_SIZE-2 */
171#if ENABLE_FEATURE_TOP_INTERACTIVE 179#if ENABLE_FEATURE_TOP_INTERACTIVE
172 struct termios initial_settings; 180 struct termios initial_settings;
173 int scroll_ofs; 181 int scroll_ofs;
@@ -212,7 +220,6 @@ struct globals {
212#define cpu_prev_jif (G.cpu_prev_jif ) 220#define cpu_prev_jif (G.cpu_prev_jif )
213#define num_cpus (G.num_cpus ) 221#define num_cpus (G.num_cpus )
214#define total_pcpu (G.total_pcpu ) 222#define total_pcpu (G.total_pcpu )
215#define line_buf (G.line_buf )
216#define INIT_G() do { \ 223#define INIT_G() do { \
217 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 224 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ 225 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
@@ -241,8 +248,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
241static int mem_sort(top_status_t *P, top_status_t *Q) 248static int mem_sort(top_status_t *P, top_status_t *Q)
242{ 249{
243 /* We want to avoid unsigned->signed and truncation errors */ 250 /* We want to avoid unsigned->signed and truncation errors */
244 if (Q->vsz < P->vsz) return -1; 251 if (Q->memsize < P->memsize) return -1;
245 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ 252 return Q->memsize != P->memsize; /* 0 if ==, 1 if > */
246} 253}
247 254
248 255
@@ -283,9 +290,9 @@ static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
283#endif 290#endif
284 int ret; 291 int ret;
285 292
286 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) 293 if (!fgets(G.line_buf, LINE_BUF_SIZE, fp) || G.line_buf[0] != 'c' /* not "cpu" */)
287 return 0; 294 return 0;
288 ret = sscanf(line_buf, fmt, 295 ret = sscanf(G.line_buf, fmt,
289 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, 296 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
290 &p_jif->iowait, &p_jif->irq, &p_jif->softirq, 297 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
291 &p_jif->steal); 298 &p_jif->steal);
@@ -362,7 +369,7 @@ static void do_stats(void)
362 369
363 get_jiffy_counts(); 370 get_jiffy_counts();
364 total_pcpu = 0; 371 total_pcpu = 0;
365 /* total_vsz = 0; */ 372 /* total_memsize = 0; */
366 new_hist = xmalloc(sizeof(new_hist[0]) * ntop); 373 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
367 /* 374 /*
368 * Make a pass through the data to get stats. 375 * Make a pass through the data to get stats.
@@ -394,7 +401,7 @@ static void do_stats(void)
394 i = (i+1) % prev_hist_count; 401 i = (i+1) % prev_hist_count;
395 /* hist_iterations++; */ 402 /* hist_iterations++; */
396 } while (i != last_i); 403 } while (i != last_i);
397 /* total_vsz += cur->vsz; */ 404 /* total_memsize += cur->memsize; */
398 } 405 }
399 406
400 /* 407 /*
@@ -407,6 +414,39 @@ static void do_stats(void)
407 414
408#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 415#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
409 416
417static void print_line_buf(void)
418{
419 const char *fmt;
420
421 G.lines_remaining--;
422 fmt = OPT_BATCH_MODE ? "\n""%.*s" : "\n""%.*s"CLREOL;
423 if (!G.first_line_printed) {
424 G.first_line_printed = 1;
425 /* Go to top */
426 fmt = OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL;
427 }
428 printf(fmt, G.scr_width - 1, G.line_buf);
429}
430
431static void print_line_bold(void)
432{
433 G.lines_remaining--;
434//we never print first line in bold
435// if (!G.first_line_printed) {
436// printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf);
437// G.first_line_printed = 1;
438// } else {
439 printf(OPT_BATCH_MODE ? "\n""%.*s" : "\n"REVERSE"%.*s"NORMAL CLREOL, G.scr_width - 1, G.line_buf);
440// }
441}
442
443static void print_end(void)
444{
445 fputs_stdout(OPT_BATCH_MODE ? "\n" : CLREOS"\r");
446 /* next print will be "first line" (will clear the screen) */
447 G.first_line_printed = 0;
448}
449
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 450#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411/* formats 7 char string (8 with terminating NUL) */ 451/* formats 7 char string (8 with terminating NUL) */
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) 452static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
@@ -433,7 +473,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
433#endif 473#endif
434 474
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 475#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) 476static void display_cpus(void)
437{ 477{
438 /* 478 /*
439 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% 479 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
@@ -469,8 +509,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
469# else 509# else
470 /* Loop thru CPU(s) */ 510 /* Loop thru CPU(s) */
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1; 511 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p) 512 if (n_cpu_lines > G.lines_remaining)
473 n_cpu_lines = *lines_rem_p; 513 n_cpu_lines = G.lines_remaining;
474 514
475 for (i = 0; i < n_cpu_lines; i++) { 515 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i]; 516 p_jif = &cpu_jif[i];
@@ -488,7 +528,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
488 CALC_STAT(softirq); 528 CALC_STAT(softirq);
489 /*CALC_STAT(steal);*/ 529 /*CALC_STAT(steal);*/
490 530
491 snprintf(scrbuf, scr_width, 531 sprintf(G.line_buf,
492 /* Barely fits in 79 chars when in "decimals" mode. */ 532 /* Barely fits in 79 chars when in "decimals" mode. */
493# if ENABLE_FEATURE_TOP_SMP_CPU 533# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", 534 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
@@ -501,16 +541,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
501 /*, SHOW_STAT(steal) - what is this 'steal' thing? */ 541 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
502 /* I doubt anyone wants to know it */ 542 /* I doubt anyone wants to know it */
503 ); 543 );
504 puts(scrbuf); 544 print_line_buf();
505 } 545 }
506 } 546 }
507# undef SHOW_STAT 547# undef SHOW_STAT
508# undef CALC_STAT 548# undef CALC_STAT
509# undef FMT 549# undef FMT
510 *lines_rem_p -= i;
511} 550}
512#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ 551#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) 552# define display_cpus() ((void)0)
514#endif 553#endif
515 554
516enum { 555enum {
@@ -564,52 +603,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX])
564 fclose(f); 603 fclose(f);
565} 604}
566 605
567static unsigned long display_header(int scr_width, int *lines_rem_p) 606static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm)
607{
608 int width = G.scr_width - offset;
609 if (width > 1) /* wider than to fit just the NUL? */
610 read_cmdline(G.line_buf + offset, width, pid, comm);
611//TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e
612 print_line_buf();
613}
614
615static unsigned long display_header(void)
568{ 616{
569 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
570 char *buf; 617 char *buf;
571 unsigned long meminfo[MI_MAX]; 618 unsigned long meminfo[MI_MAX];
572 619
573 parse_meminfo(meminfo); 620 parse_meminfo(meminfo);
574 621
575 /* Output memory info */ 622 /* Output memory info */
576 if (scr_width > (int)sizeof(scrbuf)) 623 sprintf(G.line_buf,
577 scr_width = sizeof(scrbuf);
578 snprintf(scrbuf, scr_width,
579 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", 624 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], 625 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE], 626 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], 627 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS], 628 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]); 629 meminfo[MI_CACHED]);
585 /* Go to top & clear to the end of screen */ 630 print_line_buf();
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588 631
589 /* Display CPU time split as percentage of total time. 632 /* Display CPU time split as percentage of total time.
590 * This displays either a cumulative line or one line per CPU. 633 * This displays either a cumulative line or one line per CPU.
591 */ 634 */
592 display_cpus(scr_width, scrbuf, lines_rem_p); 635 display_cpus();
593 636
594 /* Read load average as a string */ 637 /* Read load average as a string */
595 buf = stpcpy(scrbuf, "Load average: "); 638 buf = stpcpy(G.line_buf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); 639 open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0'; 640 G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */
598 strchrnul(buf, '\n')[0] = '\0'; 641 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf); 642 print_line_buf();
600 (*lines_rem_p)--;
601 643
602 return meminfo[MI_MEMTOTAL]; 644 return meminfo[MI_MEMTOTAL];
603} 645}
604 646
605static NOINLINE void display_process_list(int lines_rem, int scr_width) 647static NOINLINE void display_process_list(void)
606{ 648{
607 enum { 649 enum {
608 BITS_PER_INT = sizeof(int) * 8 650 BITS_PER_INT = sizeof(int) * 8
609 }; 651 };
610 652
611 top_status_t *s; 653 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ 654 unsigned long total_memory = display_header();
613 /* xxx_shift and xxx_scale variables allow us to replace 655 /* xxx_shift and xxx_scale variables allow us to replace
614 * expensive divides with multiply and shift */ 656 * expensive divides with multiply and shift */
615 unsigned pmem_shift, pmem_scale, pmem_half; 657 unsigned pmem_shift, pmem_scale, pmem_half;
@@ -621,7 +663,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
621 663
622#if ENABLE_FEATURE_TOP_DECIMALS 664#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000 665# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t; 666 typedef struct { unsigned quot, rem; } bb_div_t;
625/* Used to have "div_t name = div((val), 10)" here 667/* Used to have "div_t name = div((val), 10)" here
626 * (IOW: intended to use libc-compatible way to divide and use 668 * (IOW: intended to use libc-compatible way to divide and use
627 * both result and remainder, but musl does not inline div()...) 669 * both result and remainder, but musl does not inline div()...)
@@ -629,28 +671,34 @@ typedef struct { unsigned quot, rem; } bb_div_t;
629 */ 671 */
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } 672# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem 673# define SHOW_STAT(name) name.quot, '0'+name.rem
674# define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0')
632# define FMT "%3u.%c" 675# define FMT "%3u.%c"
633#else 676#else
634# define UPSCALE 100 677# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val) 678# define CALC_STAT(name, val) unsigned name = (val)
679# define SANITIZE(name) if (name > 99) name = 99
636# define SHOW_STAT(name) name 680# define SHOW_STAT(name) name
637# define FMT "%4u%%" 681# define FMT "%4u%%"
638#endif 682#endif
639 683
640 /* what info of the processes is shown */ 684 strcpy(G.line_buf, " PID PPID USER STAT RSS %RSS"
641 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
642 " PID PPID USER STAT VSZ %VSZ"
643 IF_FEATURE_TOP_SMP_PROCESS(" CPU") 685 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") 686 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND"); 687 " COMMAND");
646 lines_rem--; 688 print_line_bold();
647 689
648 /* 690 /* %RSS = s->memsize / MemTotal * 100%
649 * %VSZ = s->vsz/MemTotal 691 * Calculate this with multiply and shift. Example:
692 * shift = 12
693 * scale = 100 * 0x1000 / total_memory
694 * percent_mem = (size_mem * scale) >> shift
695 * ~= (size_mem >> shift) * scale
696 * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory
697 * ~= size_mem * 100 / total_memory
650 */ 698 */
651 pmem_shift = BITS_PER_INT-11; 699 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; 700 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
653 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ 701 /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */
654 while (pmem_scale >= 512) { 702 while (pmem_scale >= 512) {
655 pmem_scale /= 4; 703 pmem_scale /= 4;
656 pmem_shift -= 2; 704 pmem_shift -= 2;
@@ -689,25 +737,29 @@ typedef struct { unsigned quot, rem; } bb_div_t;
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); 737 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 738 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
691#endif 739#endif
740 if (G.lines_remaining > ntop - G_scroll_ofs)
741 G.lines_remaining = ntop - G_scroll_ofs;
692 742
693 /* Ok, all preliminary data is ready, go through the list */ 743 /* Ok, all preliminary data is ready, go through the list */
694 scr_width += 2; /* account for leading '\n' and trailing NUL */
695 if (lines_rem > ntop - G_scroll_ofs)
696 lines_rem = ntop - G_scroll_ofs;
697 s = top + G_scroll_ofs; 744 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) { 745 while (G.lines_remaining > 0) {
699 int n; 746 int n;
700 char *ppu; 747 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12]; 748 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8]; 749 char memsize_str_buf[8];
703 unsigned col; 750 unsigned col;
704 751
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 752 CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 753#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); 754 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif 755#endif
756 /* VSZ can be much larger than total memory
757 * (seen values close to 2Tbyte), don't try to display
758 * "uses 12345.6% of MemTotal" (won't fit the column)
759 */
760 SANITIZE(pmem);
709 761
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); 762 smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy");
711 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ 763 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
712 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); 764 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf; 765 ppu = ppubuf;
@@ -736,27 +788,23 @@ typedef struct { unsigned quot, rem; } bb_div_t;
736 ppu[6+6+8] = '\0'; /* truncate USER */ 788 ppu[6+6+8] = '\0'; /* truncate USER */
737 } 789 }
738 shortened: 790 shortened:
739 col = snprintf(line_buf, scr_width, 791 col = sprintf(G.line_buf,
740 "\n" "%s %s %.5s" FMT 792 "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d") 793 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) 794 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ", 795 " ",
744 ppu, 796 ppu,
745 s->state, vsz_str_buf, 797 s->state, memsize_str_buf,
746 SHOW_STAT(pmem) 798 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) 799 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) 800 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 ); 801 );
750 if ((int)(scr_width - col) > 1) 802 cmdline_to_line_buf_and_print(col, s->pid, s->comm);
751 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
752 fputs_stdout(line_buf);
753 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, 803 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
754 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ 804 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
755 s++; 805 s++;
756 } 806 }
757 /* printf(" %d", hist_iterations); */ 807 /* printf(" %d", hist_iterations); */
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760} 808}
761#undef UPSCALE 809#undef UPSCALE
762#undef SHOW_STAT 810#undef SHOW_STAT
@@ -828,36 +876,34 @@ static int topmem_sort(char *a, char *b)
828} 876}
829 877
830/* display header info (meminfo / loadavg) */ 878/* display header info (meminfo / loadavg) */
831static void display_topmem_header(int scr_width, int *lines_rem_p) 879static void display_topmem_header(void)
832{ 880{
833 unsigned long meminfo[MI_MAX]; 881 unsigned long meminfo[MI_MAX];
834 882
835 parse_meminfo(meminfo); 883 parse_meminfo(meminfo);
836 884
837 snprintf(line_buf, LINE_BUF_SIZE, 885 sprintf(G.line_buf,
838 "Mem total:%lu anon:%lu map:%lu free:%lu", 886 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL], 887 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES], 888 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED], 889 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]); 890 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); 891 print_line_buf();
844 892
845 snprintf(line_buf, LINE_BUF_SIZE, 893 sprintf(G.line_buf,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", 894 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB], 895 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS], 896 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED], 897 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY], 898 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]); 899 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf); 900 print_line_buf();
853 901
854 snprintf(line_buf, LINE_BUF_SIZE, 902 sprintf(G.line_buf,
855 "Swap total:%lu free:%lu", // TODO: % used? 903 "Swap total:%lu free:%lu", // TODO: % used?
856 meminfo[MI_SWAPTOTAL], 904 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]); 905 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf); 906 print_line_buf();
859
860 (*lines_rem_p) -= 3;
861} 907}
862 908
863/* see http://en.wikipedia.org/wiki/Tera */ 909/* see http://en.wikipedia.org/wiki/Tera */
@@ -870,75 +916,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5])
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; 916 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871} 917}
872 918
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) 919static NOINLINE void display_topmem_process_list(void)
874{ 920{
875#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
876#define MIN_WIDTH sizeof(HDR_STR)
877 const topmem_status_t *s = topmem + G_scroll_ofs; 921 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch; 922 char *cp, ch;
879 923
880 display_topmem_header(scr_width, &lines_rem); 924 display_topmem_header();
881 925
882 strcpy(line_buf, HDR_STR " COMMAND"); 926 strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND");
883 /* Mark the ^FIELD^ we sort by */ 927 /* Mark the ^FIELD^ we sort by */
884 cp = &line_buf[5 + sort_field * 6]; 928 cp = &G.line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted]; 929 ch = "^_"[inverted];
886 cp[6] = ch; 930 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' '); 931 do *cp++ = ch; while (*cp == ' ');
932 print_line_bold();
888 933
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); 934 if (G.lines_remaining > ntop - G_scroll_ofs)
890 lines_rem--; 935 G.lines_remaining = ntop - G_scroll_ofs;
891 936 while (G.lines_remaining > 0) {
892 if (lines_rem > ntop - G_scroll_ofs)
893 lines_rem = ntop - G_scroll_ofs;
894 while (--lines_rem >= 0) {
895 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ 937 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
896 int n = sprintf(line_buf, "%5u ", s->pid); 938 int n = sprintf(G.line_buf, "%5u ", s->pid);
897 if (n > 7) { 939 if (n > 7) {
898 /* PID is 7 chars long (up to 4194304) */ 940 /* PID is 7 chars long (up to 4194304) */
899 ulltoa4_and_space(s->vsz , &line_buf[8]); 941 ulltoa4_and_space(s->vsz , &G.line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]); 942 ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]);
901 /* the next field (RSS) starts at 8+10 = 3*6 */ 943 /* the next field (RSS) starts at 8+10 = 3*6 */
902 } else { 944 } else {
903 if (n == 7) /* PID is 6 chars long */ 945 if (n == 7) /* PID is 6 chars long */
904 ulltoa4_and_space(s->vsz, &line_buf[7]); 946 ulltoa4_and_space(s->vsz, &G.line_buf[7]);
905 /* the next field (VSZRW) starts at 7+5 = 2*6 */ 947 /* the next field (VSZRW) starts at 7+5 = 2*6 */
906 else /* PID is 5 chars or less */ 948 else /* PID is 5 chars or less */
907 ulltoa5_and_space(s->vsz, &line_buf[6]); 949 ulltoa5_and_space(s->vsz, &G.line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]); 950 ulltoa5_and_space(s->vszrw, &G.line_buf[2*6]);
909 }
910 ulltoa5_and_space(s->rss , &line_buf[3*6]);
911 ulltoa5_and_space(s->rss_sh , &line_buf[4*6]);
912 ulltoa5_and_space(s->dirty , &line_buf[5*6]);
913 ulltoa5_and_space(s->dirty_sh, &line_buf[6*6]);
914 ulltoa5_and_space(s->stack , &line_buf[7*6]);
915 line_buf[8*6] = '\0';
916 if (scr_width > (int)MIN_WIDTH) {
917 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
918 } 951 }
919 printf("\n""%.*s", scr_width, line_buf); 952 ulltoa5_and_space(s->rss , &G.line_buf[3*6]);
953 ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]);
954 ulltoa5_and_space(s->dirty , &G.line_buf[5*6]);
955 ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]);
956 ulltoa5_and_space(s->stack , &G.line_buf[7*6]);
957 G.line_buf[8*6] = '\0';
958 cmdline_to_line_buf_and_print(8*6, s->pid, s->comm);
920 s++; 959 s++;
921 } 960 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926} 961}
927 962
928#else 963#endif /* end TOPMEM support */
929void display_topmem_process_list(int lines_rem, int scr_width);
930int topmem_sort(char *a, char *b);
931#endif /* TOPMEM */
932
933/*
934 * end TOPMEM support
935 */
936 964
937enum { 965enum {
938 TOP_MASK = 0 966 TOP_MASK = 0
939 | PSSCAN_PID 967 | PSSCAN_PID
940 | PSSCAN_PPID 968 | PSSCAN_PPID
941 | PSSCAN_VSZ 969 | PSSCAN_RSS
942 | PSSCAN_STIME 970 | PSSCAN_STIME
943 | PSSCAN_UTIME 971 | PSSCAN_UTIME
944 | PSSCAN_STATE 972 | PSSCAN_STATE
@@ -950,7 +978,7 @@ enum {
950 | PSSCAN_SMAPS 978 | PSSCAN_SMAPS
951 | PSSCAN_COMM, 979 | PSSCAN_COMM,
952 EXIT_MASK = 0, 980 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1, 981 ONLY_REDRAW = (unsigned)-1,
954}; 982};
955 983
956#if ENABLE_FEATURE_TOP_INTERACTIVE 984#if ENABLE_FEATURE_TOP_INTERACTIVE
@@ -963,15 +991,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
963 } 991 }
964 992
965 while (1) { 993 while (1) {
966 int32_t c; 994 int32_t c, cc;
967 995
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); 996 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) { 997 if (c == -1) {
970 /* error/EOF */ 998 if (errno != EAGAIN)
971 option_mask32 |= OPT_EOF; 999 /* error/EOF */
1000 option_mask32 |= OPT_EOF;
1001 /* else: timeout - rescan and refresh */
972 break; 1002 break;
973 } 1003 }
974 interval = 0; 1004 interval = 0;
1005 /* "continue" statements below return to do one additional
1006 * quick attempt to read a key. This prevents
1007 * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn"
1008 * to cause lots of rescans.
1009 */
975 1010
976 if (c == initial_settings.c_cc[VINTR]) 1011 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK; 1012 return EXIT_MASK;
@@ -1005,9 +1040,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1005 G_scroll_ofs = ntop - 1; 1040 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0) 1041 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0; 1042 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK; 1043 return ONLY_REDRAW;
1009 } 1044 }
1010 1045
1046 cc = c;
1011 c |= 0x20; /* lowercase */ 1047 c |= 0x20; /* lowercase */
1012 if (c == 'q') 1048 if (c == 'q')
1013 return EXIT_MASK; 1049 return EXIT_MASK;
@@ -1055,9 +1091,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1055 continue; 1091 continue;
1056 } 1092 }
1057# if ENABLE_FEATURE_TOPMEM 1093# if ENABLE_FEATURE_TOPMEM
1094 if (cc == 'S') {
1095 if (--sort_field < 0)
1096 sort_field = NUM_SORT_FIELD - 1;
1097 if (--sort_field < 0)
1098 sort_field = NUM_SORT_FIELD - 1;
1099 }
1058 if (c == 's') { 1100 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD; 1101 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1102 if (scan_mask == TOPMEM_MASK)
1103 return ONLY_REDRAW;
1104 scan_mask = TOPMEM_MASK;
1061 free(prev_hist); 1105 free(prev_hist);
1062 prev_hist = NULL; 1106 prev_hist = NULL;
1063 prev_hist_count = 0; 1107 prev_hist_count = 0;
@@ -1066,7 +1110,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1066# endif 1110# endif
1067 if (c == 'r') { 1111 if (c == 'r') {
1068 inverted ^= 1; 1112 inverted ^= 1;
1069 continue; 1113 return ONLY_REDRAW;
1070 } 1114 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU 1115# if ENABLE_FEATURE_TOP_SMP_CPU
1072 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ 1116 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
@@ -1088,8 +1132,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1088 } 1132 }
1089# endif 1133# endif
1090# endif 1134# endif
1091 break; /* unknown key -> force refresh */ 1135 /* Unknown key. Eat remaining buffered input (if any) */
1092 } 1136 } /* while (1) */
1093 1137
1094 return scan_mask; 1138 return scan_mask;
1095} 1139}
@@ -1155,12 +1199,15 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1155{ 1199{
1156 duration_t interval; 1200 duration_t interval;
1157 int iterations; 1201 int iterations;
1158 unsigned col; 1202 unsigned opt;
1159 char *str_interval, *str_iterations; 1203 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK; 1204 unsigned scan_mask = TOP_MASK;
1161 1205
1162 INIT_G(); 1206 INIT_G();
1163 1207
1208//worth it?
1209// setvbuf(stdout, /*buf*/ NULL, _IOFBF, /*size*/ 0);
1210
1164 interval = 5; /* default update interval is 5 seconds */ 1211 interval = 5; /* default update interval is 5 seconds */
1165 iterations = 0; /* infinite */ 1212 iterations = 0; /* infinite */
1166#if ENABLE_FEATURE_TOP_SMP_CPU 1213#if ENABLE_FEATURE_TOP_SMP_CPU
@@ -1172,13 +1219,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1172 1219
1173 /* all args are options; -n NUM */ 1220 /* all args are options; -n NUM */
1174 make_all_argv_opts(argv); /* options can be specified w/o dash */ 1221 make_all_argv_opts(argv); /* options can be specified w/o dash */
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); 1222 opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176 /* NB: -m and -H are accepted even if not configured */ 1223 /* NB: -m and -H are accepted even if not configured */
1177#if ENABLE_FEATURE_TOPMEM 1224#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m) /* -m (busybox specific) */ 1225 if (opt & OPT_m) /* -m (busybox specific) */
1179 scan_mask = TOPMEM_MASK; 1226 scan_mask = TOPMEM_MASK;
1180#endif 1227#endif
1181 if (col & OPT_d) { 1228 if (opt & OPT_d) {
1182 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1229 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1183 if (str_interval[0] == '-') 1230 if (str_interval[0] == '-')
1184 str_interval++; 1231 str_interval++;
@@ -1187,18 +1234,17 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1187 if (interval > INT_MAX / 1000) 1234 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000; 1235 interval = INT_MAX / 1000;
1189 } 1236 }
1190 if (col & OPT_n) { 1237 if (opt & OPT_n) {
1191 if (str_iterations[0] == '-') 1238 if (str_iterations[0] == '-')
1192 str_iterations++; 1239 str_iterations++;
1193 iterations = xatou(str_iterations); 1240 iterations = xatou(str_iterations);
1194 } 1241 }
1195#if ENABLE_FEATURE_SHOW_THREADS 1242#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) { 1243 if (opt & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS; 1244 scan_mask |= PSSCAN_TASKS;
1198 } 1245 }
1199#endif 1246#endif
1200 1247
1201 /* change to /proc */
1202 xchdir("/proc"); 1248 xchdir("/proc");
1203 1249
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1250#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -1226,23 +1272,22 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1226#endif 1272#endif
1227 1273
1228 while (scan_mask != EXIT_MASK) { 1274 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) 1275 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;)
1230 procps_status_t *p = NULL; 1276 procps_status_t *p = NULL;
1231 1277
1232 if (OPT_BATCH_MODE) { 1278 G.lines = INT_MAX;
1233 G.lines = INT_MAX; 1279 G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1234 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ 1280 if (!OPT_BATCH_MODE) {
1235 } else {
1236 G.lines = 24; /* default */ 1281 G.lines = 24; /* default */
1237 col = 79; 1282 G.scr_width = 80;
1238 /* We output to stdout, we need size of stdout (not stdin)! */ 1283 /* We output to stdout, we need size of stdout (not stdin)! */
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1284 get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines);
1240 if (G.lines < 5 || col < 10) { 1285 if (G.lines < 5 || G.scr_width < 10) {
1241 sleep_for_duration(interval); 1286 sleep_for_duration(interval);
1242 continue; 1287 continue;
1243 } 1288 }
1244 if (col > LINE_BUF_SIZE - 2) 1289 if (G.scr_width > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2; 1290 G.scr_width = LINE_BUF_SIZE - 2;
1246 } 1291 }
1247 1292
1248 /* read process IDs & status for all the processes */ 1293 /* read process IDs & status for all the processes */
@@ -1255,7 +1300,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1255 top = xrealloc_vector(top, 6, ntop++); 1300 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid; 1301 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid; 1302 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz; 1303 top[n].memsize = p->rss;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1304#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime; 1305 top[n].ticks = p->stime + p->utime;
1261#endif 1306#endif
@@ -1268,20 +1313,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1268 } 1313 }
1269#if ENABLE_FEATURE_TOPMEM 1314#if ENABLE_FEATURE_TOPMEM
1270 else { /* TOPMEM */ 1315 else { /* TOPMEM */
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) 1316 if (!(p->mapped_ro | p->mapped_rw))
1272 continue; /* kernel threads are ignored */ 1317 continue; /* kernel threads are ignored */
1273 n = ntop; 1318 n = ntop;
1274 /* No bug here - top and topmem are the same */ 1319 /* No bug here - top and topmem are the same */
1275 top = xrealloc_vector(topmem, 6, ntop++); 1320 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm); 1321 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid; 1322 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; 1323 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw; 1324 topmem[n].vszrw = p->mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; 1325 topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
1281 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; 1326 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
1282 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; 1327 topmem[n].dirty = p->private_dirty + p->shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty; 1328 topmem[n].dirty_sh = p->shared_dirty;
1284 topmem[n].stack = p->smaps.stack; 1329 topmem[n].stack = p->stack;
1285 } 1330 }
1286#endif 1331#endif
1287 } /* end of "while we read /proc" */ 1332 } /* end of "while we read /proc" */
@@ -1310,30 +1355,40 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1355 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 } 1356 }
1312#endif 1357#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:) 1358 IF_FEATURE_TOP_INTERACTIVE(redraw:)
1359 G.lines_remaining = G.lines;
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { 1360 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col); 1361 display_process_list();
1316 } 1362 }
1317#if ENABLE_FEATURE_TOPMEM 1363#if ENABLE_FEATURE_TOPMEM
1318 else { /* TOPMEM */ 1364 else { /* TOPMEM */
1319 display_topmem_process_list(G.lines, col); 1365 display_topmem_process_list();
1320 } 1366 }
1321#endif 1367#endif
1368 print_end();
1369 fflush_all();
1322 if (iterations >= 0 && !--iterations) 1370 if (iterations >= 0 && !--iterations)
1323 break; 1371 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE 1372#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems(); 1373 clearmems();
1326 sleep_for_duration(interval); 1374 sleep_for_duration(interval);
1327#else 1375#else
1328 new_mask = handle_input(scan_mask, interval); 1376 new_mask = handle_input(scan_mask,
1329 if (new_mask == NO_RESCAN_MASK) 1377 /* After "redraw with no rescan", have one
1330 goto display; 1378 * key timeout shorter that normal
1379 * (IOW: rescan sooner):
1380 */
1381 (new_mask == ONLY_REDRAW ? 1 : interval)
1382 );
1383 if (new_mask == ONLY_REDRAW)
1384 goto redraw;
1331 scan_mask = new_mask; 1385 scan_mask = new_mask;
1332 clearmems(); 1386 clearmems();
1333#endif 1387#endif
1334 } /* end of "while (not Q)" */ 1388 } /* end of "while (not Q)" */
1335 1389
1336 bb_putchar('\n'); 1390 if (!OPT_BATCH_MODE)
1391 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE 1392#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term(); 1393 reset_term();
1339#endif 1394#endif
diff --git a/runit/chpst.c b/runit/chpst.c
index 4e3d613b7..3d04ee50d 100644
--- a/runit/chpst.c
+++ b/runit/chpst.c
@@ -38,7 +38,8 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38//config: bool "setuidgid (4.2 kb)" 38//config: bool "setuidgid (4.2 kb)"
39//config: default y 39//config: default y
40//config: help 40//config: help
41//config: Sets soft resource limits as specified by options 41//config: Sets UID and GID to those of the given account, and execs
42//config: specified program.
42//config: 43//config:
43//config:config ENVUIDGID 44//config:config ENVUIDGID
44//config: bool "envuidgid (4.1 kb)" 45//config: bool "envuidgid (4.1 kb)"
diff --git a/scripts/kconfig/libcurses/addch.c b/scripts/kconfig/libcurses/addch.c
index f3c25d389..3d1f9c04b 100644
--- a/scripts/kconfig/libcurses/addch.c
+++ b/scripts/kconfig/libcurses/addch.c
@@ -90,23 +90,25 @@ addch
90 All functions return OK on success and ERR on error. 90 All functions return OK on success and ERR on error.
91 91
92### Portability 92### Portability
93 X/Open ncurses NetBSD 93
94 addch Y Y Y 94 Function | X/Open | ncurses | NetBSD
95 waddch Y Y Y 95 :---------------------|:------:|:-------:|:------:
96 mvaddch Y Y Y 96 addch | Y | Y | Y
97 mvwaddch Y Y Y 97 waddch | Y | Y | Y
98 echochar Y Y Y 98 mvaddch | Y | Y | Y
99 wechochar Y Y Y 99 mvwaddch | Y | Y | Y
100 add_wch Y Y Y 100 echochar | Y | Y | Y
101 wadd_wch Y Y Y 101 wechochar | Y | Y | Y
102 mvadd_wch Y Y Y 102 add_wch | Y | Y | Y
103 mvwadd_wch Y Y Y 103 wadd_wch | Y | Y | Y
104 echo_wchar Y Y Y 104 mvadd_wch | Y | Y | Y
105 wecho_wchar Y Y Y 105 mvwadd_wch | Y | Y | Y
106 addrawch - - - 106 echo_wchar | Y | Y | Y
107 waddrawch - - - 107 wecho_wchar | Y | Y | Y
108 mvaddrawch - - - 108 addrawch | - | - | -
109 mvwaddrawch - - - 109 waddrawch | - | - | -
110 mvaddrawch | - | - | -
111 mvwaddrawch | - | - | -
110 112
111**man-end****************************************************************/ 113**man-end****************************************************************/
112 114
diff --git a/scripts/kconfig/libcurses/addstr.c b/scripts/kconfig/libcurses/addstr.c
index 47f8f4e10..bc286d4b5 100644
--- a/scripts/kconfig/libcurses/addstr.c
+++ b/scripts/kconfig/libcurses/addstr.c
@@ -43,23 +43,25 @@ addstr
43 All functions return OK or ERR. 43 All functions return OK or ERR.
44 44
45### Portability 45### Portability
46 X/Open ncurses NetBSD 46
47 addstr Y Y Y 47 Function | X/Open | ncurses | NetBSD
48 waddstr Y Y Y 48 :---------------------|:------:|:-------:|:------:
49 mvaddstr Y Y Y 49 addstr | Y | Y | Y
50 mvwaddstr Y Y Y 50 waddstr | Y | Y | Y
51 addnstr Y Y Y 51 mvaddstr | Y | Y | Y
52 waddnstr Y Y Y 52 mvwaddstr | Y | Y | Y
53 mvaddnstr Y Y Y 53 addnstr | Y | Y | Y
54 mvwaddnstr Y Y Y 54 waddnstr | Y | Y | Y
55 addwstr Y Y Y 55 mvaddnstr | Y | Y | Y
56 waddwstr Y Y Y 56 mvwaddnstr | Y | Y | Y
57 mvaddwstr Y Y Y 57 addwstr | Y | Y | Y
58 mvwaddwstr Y Y Y 58 waddwstr | Y | Y | Y
59 addnwstr Y Y Y 59 mvaddwstr | Y | Y | Y
60 waddnwstr Y Y Y 60 mvwaddwstr | Y | Y | Y
61 mvaddnwstr Y Y Y 61 addnwstr | Y | Y | Y
62 mvwaddnwstr Y Y Y 62 waddnwstr | Y | Y | Y
63 mvaddnwstr | Y | Y | Y
64 mvwaddnwstr | Y | Y | Y
63 65
64**man-end****************************************************************/ 66**man-end****************************************************************/
65 67
diff --git a/scripts/kconfig/libcurses/attr.c b/scripts/kconfig/libcurses/attr.c
index 3ab5a5637..d7bc36c73 100644
--- a/scripts/kconfig/libcurses/attr.c
+++ b/scripts/kconfig/libcurses/attr.c
@@ -96,36 +96,38 @@ attr
96 All functions return OK on success and ERR on error. 96 All functions return OK on success and ERR on error.
97 97
98### Portability 98### Portability
99 X/Open ncurses NetBSD 99
100 attroff Y Y Y 100 Function | X/Open | ncurses | NetBSD
101 wattroff Y Y Y 101 :---------------------|:------:|:-------:|:------:
102 attron Y Y Y 102 attroff | Y | Y | Y
103 wattron Y Y Y 103 wattroff | Y | Y | Y
104 attrset Y Y Y 104 attron | Y | Y | Y
105 wattrset Y Y Y 105 wattron | Y | Y | Y
106 standend Y Y Y 106 attrset | Y | Y | Y
107 wstandend Y Y Y 107 wattrset | Y | Y | Y
108 standout Y Y Y 108 standend | Y | Y | Y
109 wstandout Y Y Y 109 wstandend | Y | Y | Y
110 color_set Y Y Y 110 standout | Y | Y | Y
111 wcolor_set Y Y Y 111 wstandout | Y | Y | Y
112 attr_get Y Y Y 112 color_set | Y | Y | Y
113 wattr_get Y Y Y 113 wcolor_set | Y | Y | Y
114 attr_on Y Y Y 114 attr_get | Y | Y | Y
115 wattr_on Y Y Y 115 wattr_get | Y | Y | Y
116 attr_off Y Y Y 116 attr_on | Y | Y | Y
117 wattr_off Y Y Y 117 wattr_on | Y | Y | Y
118 attr_set Y Y Y 118 attr_off | Y | Y | Y
119 wattr_set Y Y Y 119 wattr_off | Y | Y | Y
120 chgat Y Y Y 120 attr_set | Y | Y | Y
121 wchgat Y Y Y 121 wattr_set | Y | Y | Y
122 mvchgat Y Y Y 122 chgat | Y | Y | Y
123 mvwchgat Y Y Y 123 wchgat | Y | Y | Y
124 getattrs - Y Y 124 mvchgat | Y | Y | Y
125 underend - - Y 125 mvwchgat | Y | Y | Y
126 wunderend - - Y 126 getattrs | - | Y | Y
127 underscore - - Y 127 underend | - | - | Y
128 wunderscore - - Y 128 wunderend | - | - | Y
129 underscore | - | - | Y
130 wunderscore | - | - | Y
129 131
130**man-end****************************************************************/ 132**man-end****************************************************************/
131 133
diff --git a/scripts/kconfig/libcurses/beep.c b/scripts/kconfig/libcurses/beep.c
index 0b97137fd..b679eed3c 100644
--- a/scripts/kconfig/libcurses/beep.c
+++ b/scripts/kconfig/libcurses/beep.c
@@ -26,9 +26,11 @@ beep
26 These functions return ERR if called before initscr(), otherwise OK. 26 These functions return ERR if called before initscr(), otherwise OK.
27 27
28### Portability 28### Portability
29 X/Open ncurses NetBSD 29
30 beep Y Y Y 30 Function | X/Open | ncurses | NetBSD
31 flash Y Y Y 31 :---------------------|:------:|:-------:|:------:
32 beep | Y | Y | Y
33 flash | Y | Y | Y
32 34
33**man-end****************************************************************/ 35**man-end****************************************************************/
34 36
diff --git a/scripts/kconfig/libcurses/bkgd.c b/scripts/kconfig/libcurses/bkgd.c
index af2d0e054..c92df28be 100644
--- a/scripts/kconfig/libcurses/bkgd.c
+++ b/scripts/kconfig/libcurses/bkgd.c
@@ -48,18 +48,20 @@ bkgd
48 case they return ERR. 48 case they return ERR.
49 49
50### Portability 50### Portability
51 X/Open ncurses NetBSD 51
52 bkgd Y Y Y 52 Function | X/Open | ncurses | NetBSD
53 bkgdset Y Y Y 53 :---------------------|:------:|:-------:|:------:
54 getbkgd Y Y Y 54 bkgd | Y | Y | Y
55 wbkgd Y Y Y 55 bkgdset | Y | Y | Y
56 wbkgdset Y Y Y 56 getbkgd | Y | Y | Y
57 bkgrnd Y Y Y 57 wbkgd | Y | Y | Y
58 bkgrndset Y Y Y 58 wbkgdset | Y | Y | Y
59 getbkgrnd Y Y Y 59 bkgrnd | Y | Y | Y
60 wbkgrnd Y Y Y 60 bkgrndset | Y | Y | Y
61 wbkgrndset Y Y Y 61 getbkgrnd | Y | Y | Y
62 wgetbkgrnd Y Y Y 62 wbkgrnd | Y | Y | Y
63 wbkgrndset | Y | Y | Y
64 wgetbkgrnd | Y | Y | Y
63 65
64**man-end****************************************************************/ 66**man-end****************************************************************/
65 67
diff --git a/scripts/kconfig/libcurses/border.c b/scripts/kconfig/libcurses/border.c
index 302c46ea2..409f2351a 100644
--- a/scripts/kconfig/libcurses/border.c
+++ b/scripts/kconfig/libcurses/border.c
@@ -46,14 +46,16 @@ border
46 border(), wborder(), and box() draw a border around the edge of the 46 border(), wborder(), and box() draw a border around the edge of the
47 window. If any argument is zero, an appropriate default is used: 47 window. If any argument is zero, an appropriate default is used:
48 48
49 ls left side of border ACS_VLINE 49 Name | Element | Default
50 rs right side of border ACS_VLINE 50 :----|:------------------------------|:------------
51 ts top side of border ACS_HLINE 51 ls | left side of border | ACS_VLINE
52 bs bottom side of border ACS_HLINE 52 rs | right side of border | ACS_VLINE
53 tl top left corner of border ACS_ULCORNER 53 ts | top side of border | ACS_HLINE
54 tr top right corner of border ACS_URCORNER 54 bs | bottom side of border | ACS_HLINE
55 bl bottom left corner of border ACS_LLCORNER 55 tl | top left corner of border | ACS_ULCORNER
56 br bottom right corner of border ACS_LRCORNER 56 tr | top right corner of border | ACS_URCORNER
57 bl | bottom left corner of border | ACS_LLCORNER
58 br | bottom right corner of border | ACS_LRCORNER
57 59
58 hline() and whline() draw a horizontal line, using ch, starting from 60 hline() and whline() draw a horizontal line, using ch, starting from
59 the current cursor position. The cursor position does not change. The 61 the current cursor position. The cursor position does not change. The
@@ -74,29 +76,31 @@ border
74 These functions return OK on success and ERR on error. 76 These functions return OK on success and ERR on error.
75 77
76### Portability 78### Portability
77 X/Open ncurses NetBSD 79
78 border Y Y Y 80 Function | X/Open | ncurses | NetBSD
79 wborder Y Y Y 81 :---------------------|:------:|:-------:|:------:
80 box Y Y Y 82 border | Y | Y | Y
81 hline Y Y Y 83 wborder | Y | Y | Y
82 vline Y Y Y 84 box | Y | Y | Y
83 whline Y Y Y 85 hline | Y | Y | Y
84 wvline Y Y Y 86 vline | Y | Y | Y
85 mvhline Y Y Y 87 whline | Y | Y | Y
86 mvvline Y Y Y 88 wvline | Y | Y | Y
87 mvwhline Y Y Y 89 mvhline | Y | Y | Y
88 mvwvline Y Y Y 90 mvvline | Y | Y | Y
89 border_set Y Y Y 91 mvwhline | Y | Y | Y
90 wborder_set Y Y Y 92 mvwvline | Y | Y | Y
91 box_set Y Y Y 93 border_set | Y | Y | Y
92 hline_set Y Y Y 94 wborder_set | Y | Y | Y
93 vline_set Y Y Y 95 box_set | Y | Y | Y
94 whline_set Y Y Y 96 hline_set | Y | Y | Y
95 wvline_set Y Y Y 97 vline_set | Y | Y | Y
96 mvhline_set Y Y Y 98 whline_set | Y | Y | Y
97 mvvline_set Y Y Y 99 wvline_set | Y | Y | Y
98 mvwhline_set Y Y Y 100 mvhline_set | Y | Y | Y
99 mvwvline_set Y Y Y 101 mvvline_set | Y | Y | Y
102 mvwhline_set | Y | Y | Y
103 mvwvline_set | Y | Y | Y
100 104
101**man-end****************************************************************/ 105**man-end****************************************************************/
102 106
diff --git a/scripts/kconfig/libcurses/clear.c b/scripts/kconfig/libcurses/clear.c
index acb8edf81..e10b321c3 100644
--- a/scripts/kconfig/libcurses/clear.c
+++ b/scripts/kconfig/libcurses/clear.c
@@ -38,15 +38,17 @@ clear
38 All functions return OK on success and ERR on error. 38 All functions return OK on success and ERR on error.
39 39
40### Portability 40### Portability
41 X/Open ncurses NetBSD 41
42 clear Y Y Y 42 Function | X/Open | ncurses | NetBSD
43 wclear Y Y Y 43 :---------------------|:------:|:-------:|:------:
44 erase Y Y Y 44 clear | Y | Y | Y
45 werase Y Y Y 45 wclear | Y | Y | Y
46 clrtobot Y Y Y 46 erase | Y | Y | Y
47 wclrtobot Y Y Y 47 werase | Y | Y | Y
48 clrtoeol Y Y Y 48 clrtobot | Y | Y | Y
49 wclrtoeol Y Y Y 49 wclrtobot | Y | Y | Y
50 clrtoeol | Y | Y | Y
51 wclrtoeol | Y | Y | Y
50 52
51**man-end****************************************************************/ 53**man-end****************************************************************/
52 54
diff --git a/scripts/kconfig/libcurses/color.c b/scripts/kconfig/libcurses/color.c
index 3335ae082..372dad948 100644
--- a/scripts/kconfig/libcurses/color.c
+++ b/scripts/kconfig/libcurses/color.c
@@ -92,20 +92,22 @@ color
92 find_pair() return a pair number, or -1 on error. 92 find_pair() return a pair number, or -1 on error.
93 93
94### Portability 94### Portability
95 X/Open ncurses NetBSD 95
96 has_colors Y Y Y 96 Function | X/Open | ncurses | NetBSD
97 start_color Y Y Y 97 :---------------------|:------:|:-------:|:------:
98 init_pair Y Y Y 98 has_colors | Y | Y | Y
99 pair_content Y Y Y 99 start_color | Y | Y | Y
100 can_change_color Y Y Y 100 init_pair | Y | Y | Y
101 init_color Y Y Y 101 pair_content | Y | Y | Y
102 color_content Y Y Y 102 can_change_color | Y | Y | Y
103 alloc_pair - Y - 103 init_color | Y | Y | Y
104 assume_default_colors - Y Y 104 color_content | Y | Y | Y
105 find_pair - Y - 105 alloc_pair | - | Y | -
106 free_pair - Y - 106 assume_default_colors | - | Y | Y
107 use_default_colors - Y Y 107 find_pair | - | Y | -
108 PDC_set_line_color - - - 108 free_pair | - | Y | -
109 use_default_colors | - | Y | Y
110 PDC_set_line_color | - | - | -
109 111
110**man-end****************************************************************/ 112**man-end****************************************************************/
111 113
diff --git a/scripts/kconfig/libcurses/curses.h b/scripts/kconfig/libcurses/curses.h
index e4ba3776a..f58fe66df 100644
--- a/scripts/kconfig/libcurses/curses.h
+++ b/scripts/kconfig/libcurses/curses.h
@@ -9,26 +9,28 @@
9 9
10Define before inclusion (only those needed): 10Define before inclusion (only those needed):
11 11
12 XCURSES if building / built for X11 12 Macro | Meaning / value
13 PDC_RGB if you want to use RGB color definitions 13 :-------------|-------------------------------------------------
14 (Red = 1, Green = 2, Blue = 4) instead of BGR 14 XCURSES | if building / built for X11
15 PDC_WIDE if building / built with wide-character support 15 PDC_RGB | RGB color (Red = 1, Green = 2, Blue = 4) vs. BGR
16 PDC_DLL_BUILD if building / built as a Windows DLL 16 PDC_WIDE | if building / built with wide-character support
17 PDC_NCMOUSE to use the ncurses mouse API instead 17 PDC_DLL_BUILD | if building / built as a Windows DLL
18 of PDCurses' traditional mouse API 18 PDC_NCMOUSE | use ncurses mouse API vs. traditional PDCurses
19 19
20Defined by this header: 20Defined by this header:
21 21
22 PDCURSES PDCurses-only features are available 22 Macro | Meaning / value
23 PDC_BUILD API build version 23 :-------------|-------------------------------------------------
24 PDC_VER_MAJOR major version number 24 PDCURSES | PDCurses-only features are available
25 PDC_VER_MINOR minor version number 25 PDC_BUILD | API build version
26 PDC_VERDOT version string 26 PDC_VER_MAJOR | major version number
27 PDC_VER_MINOR | minor version number
28 PDC_VERDOT | version string
27 29
28**man-end****************************************************************/ 30**man-end****************************************************************/
29 31
30#define PDCURSES 1 32#define PDCURSES 1
31#define PDC_BUILD 3907 33#define PDC_BUILD 3908
32#define PDC_VER_MAJOR 3 34#define PDC_VER_MAJOR 3
33#define PDC_VER_MINOR 9 35#define PDC_VER_MINOR 9
34#define PDC_VERDOT "3.9" 36#define PDC_VERDOT "3.9"
@@ -420,10 +422,9 @@ Text Attributes
420 422
421PDCurses uses a 32-bit integer for its chtype: 423PDCurses uses a 32-bit integer for its chtype:
422 424
423 +--------------------------------------------------------------------+ 425 color pair | modifiers | character eg 'a'
424 |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|..| 2| 1| 0| 426 -----------------------|-----------------------|--------------------
425 +--------------------------------------------------------------------+ 427 31 30 29 28 27 26 25 24|23 22 21 20 19 18 17 16|15 14 13 .. 2 1 0
426 color pair | modifiers | character eg 'a'
427 428
428There are 256 color pairs (8 bits), 8 bits for modifiers, and 16 bits 429There are 256 color pairs (8 bits), 8 bits for modifiers, and 16 bits
429for character data. The modifiers are bold, underline, right-line, 430for character data. The modifiers are bold, underline, right-line,
diff --git a/scripts/kconfig/libcurses/getch.c b/scripts/kconfig/libcurses/getch.c
index 8719ca39c..9d771ec0e 100644
--- a/scripts/kconfig/libcurses/getch.c
+++ b/scripts/kconfig/libcurses/getch.c
@@ -75,19 +75,21 @@ getch
75 character or function key token. 75 character or function key token.
76 76
77### Portability 77### Portability
78 X/Open ncurses NetBSD 78
79 getch Y Y Y 79 Function | X/Open | ncurses | NetBSD
80 wgetch Y Y Y 80 :---------------------|:------:|:-------:|:------:
81 mvgetch Y Y Y 81 getch | Y | Y | Y
82 mvwgetch Y Y Y 82 wgetch | Y | Y | Y
83 ungetch Y Y Y 83 mvgetch | Y | Y | Y
84 flushinp Y Y Y 84 mvwgetch | Y | Y | Y
85 get_wch Y Y Y 85 ungetch | Y | Y | Y
86 wget_wch Y Y Y 86 flushinp | Y | Y | Y
87 mvget_wch Y Y Y 87 get_wch | Y | Y | Y
88 mvwget_wch Y Y Y 88 wget_wch | Y | Y | Y
89 unget_wch Y Y Y 89 mvget_wch | Y | Y | Y
90 PDC_get_key_modifiers - - - 90 mvwget_wch | Y | Y | Y
91 unget_wch | Y | Y | Y
92 PDC_get_key_modifiers | - | - | -
91 93
92**man-end****************************************************************/ 94**man-end****************************************************************/
93 95
diff --git a/scripts/kconfig/libcurses/getyx.c b/scripts/kconfig/libcurses/getyx.c
index 5f104df99..6aa894ed0 100644
--- a/scripts/kconfig/libcurses/getyx.c
+++ b/scripts/kconfig/libcurses/getyx.c
@@ -54,21 +54,23 @@ getyx
54 values, or ERR in the case of a NULL window. 54 values, or ERR in the case of a NULL window.
55 55
56### Portability 56### Portability
57 X/Open ncurses NetBSD 57
58 getyx Y Y Y 58 Function | X/Open | ncurses | NetBSD
59 getparyx Y Y Y 59 :---------------------|:------:|:-------:|:------:
60 getbegyx Y Y Y 60 getyx | Y | Y | Y
61 getmaxyx Y Y Y 61 getparyx | Y | Y | Y
62 getsyx - Y Y 62 getbegyx | Y | Y | Y
63 setsyx - Y Y 63 getmaxyx | Y | Y | Y
64 getbegy - Y Y 64 getsyx | - | Y | Y
65 getbegx - Y Y 65 setsyx | - | Y | Y
66 getcury - Y Y 66 getbegy | - | Y | Y
67 getcurx - Y Y 67 getbegx | - | Y | Y
68 getpary - Y Y 68 getcury | - | Y | Y
69 getparx - Y Y 69 getcurx | - | Y | Y
70 getmaxy - Y Y 70 getpary | - | Y | Y
71 getmaxx - Y Y 71 getparx | - | Y | Y
72 getmaxy | - | Y | Y
73 getmaxx | - | Y | Y
72 74
73**man-end****************************************************************/ 75**man-end****************************************************************/
74 76
diff --git a/scripts/kconfig/libcurses/inch.c b/scripts/kconfig/libcurses/inch.c
index 2bd4430f6..28b57e222 100644
--- a/scripts/kconfig/libcurses/inch.c
+++ b/scripts/kconfig/libcurses/inch.c
@@ -31,15 +31,17 @@ inch
31 returned.) Note that in PDCurses, chtype and cchar_t are the same. 31 returned.) Note that in PDCurses, chtype and cchar_t are the same.
32 32
33### Portability 33### Portability
34 X/Open ncurses NetBSD 34
35 inch Y Y Y 35 Function | X/Open | ncurses | NetBSD
36 winch Y Y Y 36 :---------------------|:------:|:-------:|:------:
37 mvinch Y Y Y 37 inch | Y | Y | Y
38 mvwinch Y Y Y 38 winch | Y | Y | Y
39 in_wch Y Y Y 39 mvinch | Y | Y | Y
40 win_wch Y Y Y 40 mvwinch | Y | Y | Y
41 mvin_wch Y Y Y 41 in_wch | Y | Y | Y
42 mvwin_wch Y Y Y 42 win_wch | Y | Y | Y
43 mvin_wch | Y | Y | Y
44 mvwin_wch | Y | Y | Y
43 45
44**man-end****************************************************************/ 46**man-end****************************************************************/
45 47
diff --git a/scripts/kconfig/libcurses/initscr.c b/scripts/kconfig/libcurses/initscr.c
index 66940a3cf..a4208642a 100644
--- a/scripts/kconfig/libcurses/initscr.c
+++ b/scripts/kconfig/libcurses/initscr.c
@@ -83,17 +83,19 @@ initscr
83 returns OK, and resize_term(), which returns either OK or ERR. 83 returns OK, and resize_term(), which returns either OK or ERR.
84 84
85### Portability 85### Portability
86 X/Open ncurses NetBSD 86
87 initscr Y Y Y 87 Function | X/Open | ncurses | NetBSD
88 endwin Y Y Y 88 :---------------------|:------:|:-------:|:------:
89 isendwin Y Y Y 89 initscr | Y | Y | Y
90 newterm Y Y Y 90 endwin | Y | Y | Y
91 set_term Y Y Y 91 isendwin | Y | Y | Y
92 delscreen Y Y Y 92 newterm | Y | Y | Y
93 resize_term - Y Y 93 set_term | Y | Y | Y
94 set_tabsize - Y Y 94 delscreen | Y | Y | Y
95 curses_version - Y - 95 resize_term | - | Y | Y
96 is_termresized - - - 96 set_tabsize | - | Y | Y
97 curses_version | - | Y | -
98 is_termresized | - | - | -
97 99
98**man-end****************************************************************/ 100**man-end****************************************************************/
99 101
diff --git a/scripts/kconfig/libcurses/inopts.c b/scripts/kconfig/libcurses/inopts.c
index 30e18f611..a1c4b49f8 100644
--- a/scripts/kconfig/libcurses/inopts.c
+++ b/scripts/kconfig/libcurses/inopts.c
@@ -106,32 +106,34 @@ inopts
106 always returns FALSE. All others return OK on success and ERR on error. 106 always returns FALSE. All others return OK on success and ERR on error.
107 107
108### Portability 108### Portability
109 X/Open ncurses NetBSD 109
110 cbreak Y Y Y 110 Function | X/Open | ncurses | NetBSD
111 nocbreak Y Y Y 111 :---------------------|:------:|:-------:|:------:
112 echo Y Y Y 112 cbreak | Y | Y | Y
113 noecho Y Y Y 113 nocbreak | Y | Y | Y
114 halfdelay Y Y Y 114 echo | Y | Y | Y
115 intrflush Y Y Y 115 noecho | Y | Y | Y
116 keypad Y Y Y 116 halfdelay | Y | Y | Y
117 meta Y Y Y 117 intrflush | Y | Y | Y
118 nl Y Y Y 118 keypad | Y | Y | Y
119 nonl Y Y Y 119 meta | Y | Y | Y
120 nodelay Y Y Y 120 nl | Y | Y | Y
121 notimeout Y Y Y 121 nonl | Y | Y | Y
122 raw Y Y Y 122 nodelay | Y | Y | Y
123 noraw Y Y Y 123 notimeout | Y | Y | Y
124 noqiflush Y Y Y 124 raw | Y | Y | Y
125 qiflush Y Y Y 125 noraw | Y | Y | Y
126 timeout Y Y Y 126 noqiflush | Y | Y | Y
127 wtimeout Y Y Y 127 qiflush | Y | Y | Y
128 wgetdelay - Y - 128 timeout | Y | Y | Y
129 typeahead Y Y Y 129 wtimeout | Y | Y | Y
130 crmode Y Y Y 130 wgetdelay | - | Y | -
131 nocrmode Y Y Y 131 typeahead | Y | Y | Y
132 is_keypad - Y Y 132 crmode | Y | Y | Y
133 is_nodelay - Y - 133 nocrmode | Y | Y | Y
134 is_notimeout - Y - 134 is_keypad | - | Y | Y
135 is_nodelay | - | Y | -
136 is_notimeout | - | Y | -
135 137
136**man-end****************************************************************/ 138**man-end****************************************************************/
137 139
diff --git a/scripts/kconfig/libcurses/kernel.c b/scripts/kconfig/libcurses/kernel.c
index afb2d0661..0f5a73c21 100644
--- a/scripts/kconfig/libcurses/kernel.c
+++ b/scripts/kconfig/libcurses/kernel.c
@@ -71,20 +71,22 @@ kernel
71 curs_set(), which returns the previous visibility. 71 curs_set(), which returns the previous visibility.
72 72
73### Portability 73### Portability
74 X/Open ncurses NetBSD 74
75 def_prog_mode Y Y Y 75 Function | X/Open | ncurses | NetBSD
76 def_shell_mode Y Y Y 76 :---------------------|:------:|:-------:|:------:
77 reset_prog_mode Y Y Y 77 def_prog_mode | Y | Y | Y
78 reset_shell_mode Y Y Y 78 def_shell_mode | Y | Y | Y
79 resetty Y Y Y 79 reset_prog_mode | Y | Y | Y
80 savetty Y Y Y 80 reset_shell_mode | Y | Y | Y
81 ripoffline Y Y Y 81 resetty | Y | Y | Y
82 curs_set Y Y Y 82 savetty | Y | Y | Y
83 napms Y Y Y 83 ripoffline | Y | Y | Y
84 fixterm - Y - 84 curs_set | Y | Y | Y
85 resetterm - Y - 85 napms | Y | Y | Y
86 saveterm - Y - 86 fixterm | - | Y | -
87 draino - - - 87 resetterm | - | Y | -
88 saveterm | - | Y | -
89 draino | - | - | -
88 90
89**man-end****************************************************************/ 91**man-end****************************************************************/
90 92
diff --git a/scripts/kconfig/libcurses/move.c b/scripts/kconfig/libcurses/move.c
index 96496445e..38db1d19a 100644
--- a/scripts/kconfig/libcurses/move.c
+++ b/scripts/kconfig/libcurses/move.c
@@ -28,10 +28,12 @@ move
28 All functions return OK on success and ERR on error. 28 All functions return OK on success and ERR on error.
29 29
30### Portability 30### Portability
31 X/Open ncurses NetBSD 31
32 move Y Y Y 32 Function | X/Open | ncurses | NetBSD
33 mvcur Y Y Y 33 :---------------------|:------:|:-------:|:------:
34 wmove Y Y Y 34 move | Y | Y | Y
35 mvcur | Y | Y | Y
36 wmove | Y | Y | Y
35 37
36**man-end****************************************************************/ 38**man-end****************************************************************/
37 39
diff --git a/scripts/kconfig/libcurses/outopts.c b/scripts/kconfig/libcurses/outopts.c
index e0a55e437..4c8950959 100644
--- a/scripts/kconfig/libcurses/outopts.c
+++ b/scripts/kconfig/libcurses/outopts.c
@@ -75,23 +75,25 @@ outopts
75 return OK on success and ERR on error. 75 return OK on success and ERR on error.
76 76
77### Portability 77### Portability
78 X/Open ncurses NetBSD 78
79 clearok Y Y Y 79 Function | X/Open | ncurses | NetBSD
80 idlok Y Y Y 80 :---------------------|:------:|:-------:|:------:
81 idcok Y Y Y 81 clearok | Y | Y | Y
82 immedok Y Y Y 82 idlok | Y | Y | Y
83 leaveok Y Y Y 83 idcok | Y | Y | Y
84 setscrreg Y Y Y 84 immedok | Y | Y | Y
85 wsetscrreg Y Y Y 85 leaveok | Y | Y | Y
86 wgetscrreg - Y - 86 setscrreg | Y | Y | Y
87 scrollok Y Y Y 87 wsetscrreg | Y | Y | Y
88 is_cleared - Y - 88 wgetscrreg | - | Y | -
89 is_idlok - Y - 89 scrollok | Y | Y | Y
90 is_idcok - Y - 90 is_cleared | - | Y | -
91 is_immedok - Y - 91 is_idlok | - | Y | -
92 is_leaveok - Y Y 92 is_idcok | - | Y | -
93 is_scrollok - Y - 93 is_immedok | - | Y | -
94 raw_output - - - 94 is_leaveok | - | Y | Y
95 is_scrollok | - | Y | -
96 raw_output | - | - | -
95 97
96**man-end****************************************************************/ 98**man-end****************************************************************/
97 99
diff --git a/scripts/kconfig/libcurses/overlay.c b/scripts/kconfig/libcurses/overlay.c
index ae1903da8..75a93d83c 100644
--- a/scripts/kconfig/libcurses/overlay.c
+++ b/scripts/kconfig/libcurses/overlay.c
@@ -39,10 +39,12 @@ overlay
39 All functions return OK on success and ERR on error. 39 All functions return OK on success and ERR on error.
40 40
41### Portability 41### Portability
42 X/Open ncurses NetBSD 42
43 overlay Y Y Y 43 Function | X/Open | ncurses | NetBSD
44 overwrite Y Y Y 44 :---------------------|:------:|:-------:|:------:
45 copywin Y Y Y 45 overlay | Y | Y | Y
46 overwrite | Y | Y | Y
47 copywin | Y | Y | Y
46 48
47**man-end****************************************************************/ 49**man-end****************************************************************/
48 50
diff --git a/scripts/kconfig/libcurses/pad.c b/scripts/kconfig/libcurses/pad.c
index 3dfdfe5d6..8b44dabd4 100644
--- a/scripts/kconfig/libcurses/pad.c
+++ b/scripts/kconfig/libcurses/pad.c
@@ -62,14 +62,16 @@ pad
62 All functions except is_pad() return OK on success and ERR on error. 62 All functions except is_pad() return OK on success and ERR on error.
63 63
64### Portability 64### Portability
65 X/Open ncurses NetBSD 65
66 newpad Y Y Y 66 Function | X/Open | ncurses | NetBSD
67 subpad Y Y Y 67 :---------------------|:------:|:-------:|:------:
68 prefresh Y Y Y 68 newpad | Y | Y | Y
69 pnoutrefresh Y Y Y 69 subpad | Y | Y | Y
70 pechochar Y Y Y 70 prefresh | Y | Y | Y
71 pecho_wchar Y Y Y 71 pnoutrefresh | Y | Y | Y
72 is_pad - Y Y 72 pechochar | Y | Y | Y
73 pecho_wchar | Y | Y | Y
74 is_pad | - | Y | Y
73 75
74**man-end****************************************************************/ 76**man-end****************************************************************/
75 77
diff --git a/scripts/kconfig/libcurses/pdcclip.c b/scripts/kconfig/libcurses/pdcclip.c
index 740221280..6184ad2d9 100644
--- a/scripts/kconfig/libcurses/pdcclip.c
+++ b/scripts/kconfig/libcurses/pdcclip.c
@@ -24,26 +24,27 @@ clipboard
24 memory returned, via PDC_freeclipboard(). The length of the clipboard 24 memory returned, via PDC_freeclipboard(). The length of the clipboard
25 contents is returned in the length argument. 25 contents is returned in the length argument.
26 26
27 PDC_setclipboard copies the supplied text into the system's 27 PDC_setclipboard() copies the supplied text into the system's
28 clipboard, emptying the clipboard prior to the copy. 28 clipboard, emptying the clipboard prior to the copy.
29 29
30 PDC_clearclipboard() clears the internal clipboard. 30 PDC_clearclipboard() clears the internal clipboard.
31 31
32### Return Values 32### Return Values
33 33
34 indicator of success/failure of call. 34 PDC_CLIP_SUCCESS the call was successful
35 PDC_CLIP_SUCCESS the call was successful 35 PDC_CLIP_MEMORY_ERROR unable to allocate sufficient memory for
36 PDC_CLIP_MEMORY_ERROR unable to allocate sufficient memory for 36 the clipboard contents
37 the clipboard contents 37 PDC_CLIP_EMPTY the clipboard contains no text
38 PDC_CLIP_EMPTY the clipboard contains no text 38 PDC_CLIP_ACCESS_ERROR no clipboard support
39 PDC_CLIP_ACCESS_ERROR no clipboard support
40 39
41### Portability 40### Portability
42 X/Open ncurses NetBSD 41
43 PDC_getclipboard - - - 42 Function | X/Open | ncurses | NetBSD
44 PDC_setclipboard - - - 43 :---------------------|:------:|:-------:|:------:
45 PDC_freeclipboard - - - 44 PDC_getclipboard | - | - | -
46 PDC_clearclipboard - - - 45 PDC_setclipboard | - | - | -
46 PDC_freeclipboard | - | - | -
47 PDC_clearclipboard | - | - | -
47 48
48**man-end****************************************************************/ 49**man-end****************************************************************/
49 50
@@ -143,7 +144,12 @@ int PDC_clearclipboard(void)
143{ 144{
144 PDC_LOG(("PDC_clearclipboard() - called\n")); 145 PDC_LOG(("PDC_clearclipboard() - called\n"));
145 146
146 EmptyClipboard(); 147 if (OpenClipboard(NULL))
148 if (EmptyClipboard())
149 {
150 CloseClipboard();
151 return PDC_CLIP_SUCCESS;
152 }
147 153
148 return PDC_CLIP_SUCCESS; 154 return PDC_CLIP_ACCESS_ERROR;
149} 155}
diff --git a/scripts/kconfig/libcurses/pdcsetsc.c b/scripts/kconfig/libcurses/pdcsetsc.c
index a2d1b6dc3..1e9fc9b0d 100644
--- a/scripts/kconfig/libcurses/pdcsetsc.c
+++ b/scripts/kconfig/libcurses/pdcsetsc.c
@@ -31,9 +31,12 @@ pdcsetsc
31 platforms. 31 platforms.
32 32
33### Portability 33### Portability
34 X/Open ncurses NetBSD 34
35 PDC_set_blink - - - 35 Function | X/Open | ncurses | NetBSD
36 PDC_set_title - - - 36 :---------------------|:------:|:-------:|:------:
37 PDC_set_blink | - | - | -
38 PDC_set_bold | - | - | -
39 PDC_set_title | - | - | -
37 40
38**man-end****************************************************************/ 41**man-end****************************************************************/
39 42
diff --git a/scripts/kconfig/libcurses/printw.c b/scripts/kconfig/libcurses/printw.c
index 38e7fd112..a753638a5 100644
--- a/scripts/kconfig/libcurses/printw.c
+++ b/scripts/kconfig/libcurses/printw.c
@@ -32,13 +32,15 @@ printw
32 error. 32 error.
33 33
34### Portability 34### Portability
35 X/Open ncurses NetBSD 35
36 printw Y Y Y 36 Function | X/Open | ncurses | NetBSD
37 wprintw Y Y Y 37 :---------------------|:------:|:-------:|:------:
38 mvprintw Y Y Y 38 printw | Y | Y | Y
39 mvwprintw Y Y Y 39 wprintw | Y | Y | Y
40 vwprintw Y Y Y 40 mvprintw | Y | Y | Y
41 vw_printw Y Y Y 41 mvwprintw | Y | Y | Y
42 vwprintw | Y | Y | Y
43 vw_printw | Y | Y | Y
42 44
43**man-end****************************************************************/ 45**man-end****************************************************************/
44 46
diff --git a/scripts/kconfig/libcurses/refresh.c b/scripts/kconfig/libcurses/refresh.c
index 306f4efb3..1dce414e5 100644
--- a/scripts/kconfig/libcurses/refresh.c
+++ b/scripts/kconfig/libcurses/refresh.c
@@ -45,13 +45,15 @@ refresh
45 All functions return OK on success and ERR on error. 45 All functions return OK on success and ERR on error.
46 46
47### Portability 47### Portability
48 X/Open ncurses NetBSD 48
49 refresh Y Y Y 49 Function | X/Open | ncurses | NetBSD
50 wrefresh Y Y Y 50 :---------------------|:------:|:-------:|:------:
51 wnoutrefresh Y Y Y 51 refresh | Y | Y | Y
52 doupdate Y Y Y 52 wrefresh | Y | Y | Y
53 redrawwin Y Y Y 53 wnoutrefresh | Y | Y | Y
54 wredrawln Y Y Y 54 doupdate | Y | Y | Y
55 redrawwin | Y | Y | Y
56 wredrawln | Y | Y | Y
55 57
56**man-end****************************************************************/ 58**man-end****************************************************************/
57 59
diff --git a/scripts/kconfig/libcurses/scroll.c b/scripts/kconfig/libcurses/scroll.c
index d2f3d1704..a53d71bad 100644
--- a/scripts/kconfig/libcurses/scroll.c
+++ b/scripts/kconfig/libcurses/scroll.c
@@ -31,10 +31,12 @@ scroll
31 All functions return OK on success and ERR on error. 31 All functions return OK on success and ERR on error.
32 32
33### Portability 33### Portability
34 X/Open ncurses NetBSD 34
35 scroll Y Y Y 35 Function | X/Open | ncurses | NetBSD
36 scrl Y Y Y 36 :---------------------|:------:|:-------:|:------:
37 wscrl Y Y Y 37 scroll | Y | Y | Y
38 scrl | Y | Y | Y
39 wscrl | Y | Y | Y
38 40
39**man-end****************************************************************/ 41**man-end****************************************************************/
40 42
diff --git a/scripts/kconfig/libcurses/slk.c b/scripts/kconfig/libcurses/slk.c
index a9fca13d3..e8dde752c 100644
--- a/scripts/kconfig/libcurses/slk.c
+++ b/scripts/kconfig/libcurses/slk.c
@@ -46,12 +46,12 @@ slk
46 slk_init() requires a single parameter which describes the format of 46 slk_init() requires a single parameter which describes the format of
47 the SLKs as follows: 47 the SLKs as follows:
48 48
49 0 3-2-3 format 49 0 3-2-3 format
50 1 4-4 format 50 1 4-4 format
51 2 4-4-4 format (ncurses extension) 51 2 4-4-4 format (ncurses extension)
52 3 4-4-4 format with index line (ncurses extension) 52 3 4-4-4 format with index line (ncurses extension)
53 2 lines used 53 2 lines used
54 55 5-5 format (pdcurses format) 54 55 5-5 format (pdcurses format)
55 55
56 slk_refresh(), slk_noutrefresh() and slk_touch() are analogous to 56 slk_refresh(), slk_noutrefresh() and slk_touch() are analogous to
57 refresh(), noutrefresh() and touch(). 57 refresh(), noutrefresh() and touch().
@@ -61,26 +61,28 @@ slk
61 All functions return OK on success and ERR on error. 61 All functions return OK on success and ERR on error.
62 62
63### Portability 63### Portability
64 X/Open ncurses NetBSD 64
65 slk_init Y Y Y 65 Function | X/Open | ncurses | NetBSD
66 slk_set Y Y Y 66 :---------------------|:------:|:-------:|:------:
67 slk_refresh Y Y Y 67 slk_init | Y | Y | Y
68 slk_noutrefresh Y Y Y 68 slk_set | Y | Y | Y
69 slk_label Y Y Y 69 slk_refresh | Y | Y | Y
70 slk_clear Y Y Y 70 slk_noutrefresh | Y | Y | Y
71 slk_restore Y Y Y 71 slk_label | Y | Y | Y
72 slk_touch Y Y Y 72 slk_clear | Y | Y | Y
73 slk_attron Y Y Y 73 slk_restore | Y | Y | Y
74 slk_attrset Y Y Y 74 slk_touch | Y | Y | Y
75 slk_attroff Y Y Y 75 slk_attron | Y | Y | Y
76 slk_attr_on Y Y Y 76 slk_attrset | Y | Y | Y
77 slk_attr_set Y Y Y 77 slk_attroff | Y | Y | Y
78 slk_attr_off Y Y Y 78 slk_attr_on | Y | Y | Y
79 slk_wset Y Y Y 79 slk_attr_set | Y | Y | Y
80 PDC_mouse_in_slk - - - 80 slk_attr_off | Y | Y | Y
81 PDC_slk_free - - - 81 slk_wset | Y | Y | Y
82 PDC_slk_initialize - - - 82 PDC_mouse_in_slk | - | - | -
83 slk_wlabel - - - 83 PDC_slk_free | - | - | -
84 PDC_slk_initialize | - | - | -
85 slk_wlabel | - | - | -
84 86
85**man-end****************************************************************/ 87**man-end****************************************************************/
86 88
diff --git a/scripts/kconfig/libcurses/touch.c b/scripts/kconfig/libcurses/touch.c
index 2fd03cce5..7ea0b64b2 100644
--- a/scripts/kconfig/libcurses/touch.c
+++ b/scripts/kconfig/libcurses/touch.c
@@ -49,14 +49,16 @@ touch
49 is_wintouched() and is_linetouched(). 49 is_wintouched() and is_linetouched().
50 50
51### Portability 51### Portability
52 X/Open ncurses NetBSD 52
53 touchwin Y Y Y 53 Function | X/Open | ncurses | NetBSD
54 touchline Y Y Y 54 :---------------------|:------:|:-------:|:------:
55 untouchwin Y Y Y 55 touchwin | Y | Y | Y
56 wtouchln Y Y Y 56 touchline | Y | Y | Y
57 is_linetouched Y Y Y 57 untouchwin | Y | Y | Y
58 is_wintouched Y Y Y 58 wtouchln | Y | Y | Y
59 touchoverlap - - Y 59 is_linetouched | Y | Y | Y
60 is_wintouched | Y | Y | Y
61 touchoverlap | - | - | Y
60 62
61**man-end****************************************************************/ 63**man-end****************************************************************/
62 64
diff --git a/scripts/kconfig/libcurses/window.c b/scripts/kconfig/libcurses/window.c
index 4ae5b0861..891d0c05b 100644
--- a/scripts/kconfig/libcurses/window.c
+++ b/scripts/kconfig/libcurses/window.c
@@ -124,26 +124,28 @@ window
124 NOT cancelled for those windows. 124 NOT cancelled for those windows.
125 125
126### Portability 126### Portability
127 X/Open ncurses NetBSD 127
128 newwin Y Y Y 128 Function | X/Open | ncurses | NetBSD
129 delwin Y Y Y 129 :---------------------|:------:|:-------:|:------:
130 mvwin Y Y Y 130 newwin | Y | Y | Y
131 subwin Y Y Y 131 delwin | Y | Y | Y
132 derwin Y Y Y 132 mvwin | Y | Y | Y
133 mvderwin Y Y Y 133 subwin | Y | Y | Y
134 dupwin Y Y Y 134 derwin | Y | Y | Y
135 wgetparent - Y - 135 mvderwin | Y | Y | Y
136 wsyncup Y Y Y 136 dupwin | Y | Y | Y
137 syncok Y Y Y 137 wgetparent | - | Y | -
138 is_subwin - Y - 138 wsyncup | Y | Y | Y
139 is_syncok - Y - 139 syncok | Y | Y | Y
140 wcursyncup Y Y Y 140 is_subwin | - | Y | -
141 wsyncdown Y Y Y 141 is_syncok | - | Y | -
142 wresize - Y Y 142 wcursyncup | Y | Y | Y
143 resize_window - - - 143 wsyncdown | Y | Y | Y
144 PDC_makelines - - - 144 wresize | - | Y | Y
145 PDC_makenew - - - 145 resize_window | - | - | -
146 PDC_sync - - - 146 PDC_makelines | - | - | -
147 PDC_makenew | - | - | -
148 PDC_sync | - | - | -
147 149
148**man-end****************************************************************/ 150**man-end****************************************************************/
149 151
diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh
index a608d4c5e..885586865 100755
--- a/scripts/kconfig/lxdialog/check-lxdialog.sh
+++ b/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -53,7 +53,7 @@ trap "rm -f $tmp" 0 1 2 3 15
53check() { 53check() {
54 $cc -x c - -o $tmp 2>/dev/null <<'EOF' 54 $cc -x c - -o $tmp 2>/dev/null <<'EOF'
55#include CURSES_LOC 55#include CURSES_LOC
56int main() {} 56int main() { return 0; }
57EOF 57EOF
58 if [ $? != 0 ]; then 58 if [ $? != 0 ]; then
59 echo " *** Unable to find the ncurses libraries or the" 1>&2 59 echo " *** Unable to find the ncurses libraries or the" 1>&2
diff --git a/scripts/mk_mingw64u_defconfig b/scripts/mk_mingw64u_defconfig
index 19124d735..5561a0900 100755
--- a/scripts/mk_mingw64u_defconfig
+++ b/scripts/mk_mingw64u_defconfig
@@ -32,6 +32,7 @@ set_build_opts \
32 CONFIG_LAST_SUPPORTED_WCHAR=1114111 \ 32 CONFIG_LAST_SUPPORTED_WCHAR=1114111 \
33 CONFIG_UNICODE_COMBINING_WCHARS=y \ 33 CONFIG_UNICODE_COMBINING_WCHARS=y \
34 CONFIG_UNICODE_WIDE_WCHARS=y \ 34 CONFIG_UNICODE_WIDE_WCHARS=y \
35 CONFIG_FEATURE_USE_CNG_API=y \
35 < "$configs"/mingw64_defconfig \ 36 < "$configs"/mingw64_defconfig \
36 > "$configs"/mingw64u_defconfig 37 > "$configs"/mingw64u_defconfig
37 38
diff --git a/shell/Config.src b/shell/Config.src
index 5efbf9995..5b3fe08f3 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -166,9 +166,10 @@ config FEATURE_SH_HISTFILESIZE
166 default y 166 default y
167 depends on SHELL_ASH || SHELL_HUSH 167 depends on SHELL_ASH || SHELL_HUSH
168 help 168 help
169 This option makes busybox shells to use $HISTFILESIZE variable 169 This option makes busybox shells to use $HISTSIZE and
170 to set shell history size. Note that its max value is capped 170 $HISTFILESIZE variables to set shell history size.
171 by "History size" setting in library tuning section. 171 Note that its max value is capped by "History size" setting
172 in library tuning section.
172 173
173config FEATURE_SH_EMBEDDED_SCRIPTS 174config FEATURE_SH_EMBEDDED_SCRIPTS
174 bool "Embed scripts in the binary" 175 bool "Embed scripts in the binary"
diff --git a/shell/ash.c b/shell/ash.c
index 3919118f0..61f10d74e 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -384,10 +384,16 @@ union node;
384struct strlist; 384struct strlist;
385struct job; 385struct job;
386 386
387#if defined(_WIN64)
388# define ALIGN64 ALIGNED(16)
389#else
390# define ALIGN64
391#endif
392
387struct forkshell { 393struct forkshell {
388 /* filled by forkshell_copy() */ 394 /* filled by forkshell_copy() */
389 struct globals_var *gvp;
390 struct globals_misc *gmp; 395 struct globals_misc *gmp;
396 struct globals_var *gvp;
391 struct tblentry **cmdtable; 397 struct tblentry **cmdtable;
392#if ENABLE_ASH_ALIAS 398#if ENABLE_ASH_ALIAS
393 struct alias **atab; 399 struct alias **atab;
@@ -401,7 +407,6 @@ struct forkshell {
401 unsigned njobs; 407 unsigned njobs;
402 struct job *curjob; 408 struct job *curjob;
403#endif 409#endif
404 /* struct parsefile *g_parsefile; */
405 HANDLE hMapFile; 410 HANDLE hMapFile;
406 char *old_base; 411 char *old_base;
407 int size; 412 int size;
@@ -426,8 +431,8 @@ struct forkshell {
426 int fd[3]; 431 int fd[3];
427 union node *n; 432 union node *n;
428 char **argv; 433 char **argv;
429 char *path; 434 const char *path;
430}; 435} ALIGN64;
431 436
432enum { 437enum {
433 FS_OPENHERE, 438 FS_OPENHERE,
@@ -461,7 +466,7 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
461 466
462/* ============ Shell options */ 467/* ============ Shell options */
463 468
464/* If you add/change options hare, update --help text too */ 469/* If you add/change options here, update --help text too */
465static const char *const optletters_optnames[] ALIGN_PTR = { 470static const char *const optletters_optnames[] ALIGN_PTR = {
466 "e" "errexit", 471 "e" "errexit",
467 "f" "noglob", 472 "f" "noglob",
@@ -534,6 +539,65 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
534enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; 539enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
535 540
536 541
542/* ============ Parser data */
543
544struct strlist {
545 struct strlist *next;
546 char *text;
547};
548
549struct alias;
550
551struct strpush {
552 struct strpush *prev; /* preceding string on stack */
553 char *prev_string;
554 int prev_left_in_line;
555#if ENABLE_ASH_ALIAS
556 struct alias *ap; /* if push was associated with an alias */
557#endif
558 char *string; /* remember the string since it may change */
559
560 /* Delay freeing so we can stop nested aliases. */
561 struct strpush *spfree;
562
563 /* Remember last two characters for pungetc. */
564 int lastc[2];
565
566 /* Number of outstanding calls to pungetc. */
567 int unget;
568};
569
570/*
571 * The parsefile structure pointed to by the global variable parsefile
572 * contains information about the current file being read.
573 */
574struct parsefile {
575 struct parsefile *prev; /* preceding file on stack */
576 int linno; /* current line */
577 int pf_fd; /* file descriptor (or -1 if string) */
578 int left_in_line; /* number of chars left in this line */
579 int left_in_buffer; /* number of chars left in this buffer past the line */
580 char *next_to_pgetc; /* next char in buffer */
581 char *buf; /* input buffer */
582 struct strpush *strpush; /* for pushing strings at this level */
583 struct strpush basestrpush; /* so pushing one is fast */
584
585 /* Delay freeing so we can stop nested aliases. */
586 struct strpush *spfree;
587
588 /* Remember last two characters for pungetc. */
589 int lastc[2];
590
591 /* Number of outstanding calls to pungetc. */
592 int unget;
593
594#if ENABLE_PLATFORM_MINGW32
595 /* True if a trailing CR from a previous read was left unprocessed. */
596 int cr;
597#endif
598};
599
600
537/* ============ Misc data */ 601/* ============ Misc data */
538 602
539#define msg_illnum "Illegal number: %s" 603#define msg_illnum "Illegal number: %s"
@@ -558,6 +622,9 @@ struct globals_misc {
558 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 622 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
559#endif 623#endif
560 smallint inps4; /* Prevent PS4 nesting. */ 624 smallint inps4; /* Prevent PS4 nesting. */
625#if !ENABLE_PLATFORM_MINGW32
626 smallint vforked;
627#endif
561 int savestatus; /* exit status of last command outside traps */ 628 int savestatus; /* exit status of last command outside traps */
562 int rootpid; /* pid of main shell */ 629 int rootpid; /* pid of main shell */
563 /* shell level: 0 for the main shell, 1 for its children, and so on */ 630 /* shell level: 0 for the main shell, 1 for its children, and so on */
@@ -581,9 +648,6 @@ struct globals_misc {
581 char *physdir; // = nullstr; /* physical working directory */ 648 char *physdir; // = nullstr; /* physical working directory */
582 649
583 char *arg0; /* value of $0 */ 650 char *arg0; /* value of $0 */
584#if ENABLE_PLATFORM_MINGW32
585 char *commandname;
586#endif
587 651
588 struct jmploc *exception_handler; 652 struct jmploc *exception_handler;
589 653
@@ -595,7 +659,7 @@ struct globals_misc {
595 */ 659 */
596 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 660 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
597#if !ENABLE_PLATFORM_MINGW32 661#if !ENABLE_PLATFORM_MINGW32
598 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 662 volatile /*sig_atomic_t*/ smallint gotsigchld; /* 1 = got SIGCHLD */
599 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 663 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
600#endif 664#endif
601 smallint exception_type; /* kind of exception: */ 665 smallint exception_type; /* kind of exception: */
@@ -669,8 +733,14 @@ struct globals_misc {
669 733
670 char **trap_ptr; /* used only by "trap hack" */ 734 char **trap_ptr; /* used only by "trap hack" */
671 735
736 char *commandname; /* currently executing command */
737 struct parsefile *g_parsefile; /* = &basepf, current input file */
738 struct parsefile basepf; /* top level input file */
739
672 /* Rarely referenced stuff */ 740 /* Rarely referenced stuff */
673 741
742 struct jmploc main_handler;
743
674 /* Cached supplementary group array (for testing executable'ity of files) */ 744 /* Cached supplementary group array (for testing executable'ity of files) */
675 struct cached_groupinfo groupinfo; 745 struct cached_groupinfo groupinfo;
676 746
@@ -685,6 +755,11 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
685#define back_exitstatus (G_misc.back_exitstatus ) 755#define back_exitstatus (G_misc.back_exitstatus )
686#define job_warning (G_misc.job_warning) 756#define job_warning (G_misc.job_warning)
687#define inps4 (G_misc.inps4 ) 757#define inps4 (G_misc.inps4 )
758#if !ENABLE_PLATFORM_MINGW32
759#define vforked (G_misc.vforked )
760#else
761#define vforked 0
762#endif
688#define savestatus (G_misc.savestatus ) 763#define savestatus (G_misc.savestatus )
689#define rootpid (G_misc.rootpid ) 764#define rootpid (G_misc.rootpid )
690#define shlvl (G_misc.shlvl ) 765#define shlvl (G_misc.shlvl )
@@ -701,14 +776,11 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
701#define curdir (G_misc.curdir ) 776#define curdir (G_misc.curdir )
702#define physdir (G_misc.physdir ) 777#define physdir (G_misc.physdir )
703#define arg0 (G_misc.arg0 ) 778#define arg0 (G_misc.arg0 )
704#if ENABLE_PLATFORM_MINGW32
705#define commandname (G_misc.commandname)
706#endif
707#define exception_handler (G_misc.exception_handler) 779#define exception_handler (G_misc.exception_handler)
708#define exception_type (G_misc.exception_type ) 780#define exception_type (G_misc.exception_type )
709#define suppress_int (G_misc.suppress_int ) 781#define suppress_int (G_misc.suppress_int )
710#define pending_int (G_misc.pending_int ) 782#define pending_int (G_misc.pending_int )
711#define got_sigchld (G_misc.got_sigchld ) 783#define gotsigchld (G_misc.gotsigchld )
712#define pending_sig (G_misc.pending_sig ) 784#define pending_sig (G_misc.pending_sig )
713#define nullstr (G_misc.nullstr ) 785#define nullstr (G_misc.nullstr )
714#define optlist (G_misc.optlist ) 786#define optlist (G_misc.optlist )
@@ -717,6 +789,10 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
717#define may_have_traps (G_misc.may_have_traps ) 789#define may_have_traps (G_misc.may_have_traps )
718#define trap (G_misc.trap ) 790#define trap (G_misc.trap )
719#define trap_ptr (G_misc.trap_ptr ) 791#define trap_ptr (G_misc.trap_ptr )
792#define commandname (G_misc.commandname)
793#define g_parsefile (G_misc.g_parsefile )
794#define basepf (G_misc.basepf )
795#define main_handler (G_misc.main_handler )
720#define groupinfo (G_misc.groupinfo ) 796#define groupinfo (G_misc.groupinfo )
721#define random_gen (G_misc.random_gen ) 797#define random_gen (G_misc.random_gen )
722#define backgndpid (G_misc.backgndpid ) 798#define backgndpid (G_misc.backgndpid )
@@ -733,6 +809,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
733 curdir = nullstr; \ 809 curdir = nullstr; \
734 physdir = nullstr; \ 810 physdir = nullstr; \
735 trap_ptr = trap; \ 811 trap_ptr = trap; \
812 g_parsefile = &basepf; \
736 groupinfo.euid = -1; \ 813 groupinfo.euid = -1; \
737 groupinfo.egid = -1; \ 814 groupinfo.egid = -1; \
738} while (0) 815} while (0)
@@ -780,73 +857,26 @@ var_end(const char *var)
780 return var; 857 return var;
781} 858}
782 859
783 860#if !ENABLE_PLATFORM_MINGW32
784/* ============ Parser data */ 861/* Our signal logic never blocks individual signals
785 862 * using signal mask - only by setting SIG_IGN handler.
786/* 863 * Therefore just unmasking all of them instead of "restore old mask"
787 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. 864 * approach is safe, modulo a case where the shell itself inherited
788 */ 865 * a _masked_ signal.
789struct strlist {
790 struct strlist *next;
791 char *text;
792};
793
794struct alias;
795
796struct strpush {
797 struct strpush *prev; /* preceding string on stack */
798 char *prev_string;
799 int prev_left_in_line;
800#if ENABLE_ASH_ALIAS
801 struct alias *ap; /* if push was associated with an alias */
802#endif
803 char *string; /* remember the string since it may change */
804
805 /* Delay freeing so we can stop nested aliases. */
806 struct strpush *spfree;
807
808 /* Remember last two characters for pungetc. */
809 int lastc[2];
810
811 /* Number of outstanding calls to pungetc. */
812 int unget;
813};
814
815/*
816 * The parsefile structure pointed to by the global variable parsefile
817 * contains information about the current file being read.
818 */ 866 */
819struct parsefile { 867static void
820 struct parsefile *prev; /* preceding file on stack */ 868sigclearmask(void)
821 int linno; /* current line */ 869{
822 int pf_fd; /* file descriptor (or -1 if string) */ 870 sigprocmask_allsigs(SIG_UNBLOCK);
823 int left_in_line; /* number of chars left in this line */ 871}
824 int left_in_buffer; /* number of chars left in this buffer past the line */
825 char *next_to_pgetc; /* next char in buffer */
826 char *buf; /* input buffer */
827 struct strpush *strpush; /* for pushing strings at this level */
828 struct strpush basestrpush; /* so pushing one is fast */
829
830 /* Delay freeing so we can stop nested aliases. */
831 struct strpush *spfree;
832
833 /* Remember last two characters for pungetc. */
834 int lastc[2];
835
836 /* Number of outstanding calls to pungetc. */
837 int unget;
838
839#if ENABLE_PLATFORM_MINGW32
840 /* True if a trailing CR from a previous read was left unprocessed. */
841 int cr;
842#endif 872#endif
843};
844 873
845static struct parsefile basepf; /* top level input file */ 874/* Reset handler when entering a subshell */
846static struct parsefile *g_parsefile = &basepf; /* current input file */ 875static void
847#if ENABLE_PLATFORM_POSIX 876reset_exception_handler(void)
848static char *commandname; /* currently executing command */ 877{
849#endif 878 exception_handler = &main_handler;
879}
850 880
851 881
852/* ============ Interrupts / exceptions */ 882/* ============ Interrupts / exceptions */
@@ -860,13 +890,13 @@ static void exitshell(void) NORETURN;
860 * more fun than worrying about efficiency and portability. :-)) 890 * more fun than worrying about efficiency and portability. :-))
861 */ 891 */
862#if DEBUG_INTONOFF 892#if DEBUG_INTONOFF
863# define INT_OFF do { \ 893# define INTOFF do { \
864 TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \ 894 TRACE(("%s:%d INTOFF(%d)\n", __func__, __LINE__, suppress_int)); \
865 suppress_int++; \ 895 suppress_int++; \
866 barrier(); \ 896 barrier(); \
867} while (0) 897} while (0)
868#else 898#else
869# define INT_OFF do { \ 899# define INTOFF do { \
870 suppress_int++; \ 900 suppress_int++; \
871 barrier(); \ 901 barrier(); \
872} while (0) 902} while (0)
@@ -885,7 +915,11 @@ raise_exception(int e)
885 if (exception_handler == NULL) 915 if (exception_handler == NULL)
886 abort(); 916 abort();
887#endif 917#endif
888 INT_OFF; 918
919 if (vforked)
920 _exit(exitstatus);
921
922 INTOFF;
889 exception_type = e; 923 exception_type = e;
890 longjmp(exception_handler->loc, 1); 924 longjmp(exception_handler->loc, 1);
891} 925}
@@ -900,7 +934,7 @@ raise_exception(int e)
900 * Called when a SIGINT is received. (If the user specifies 934 * Called when a SIGINT is received. (If the user specifies
901 * that SIGINT is to be trapped or ignored using the trap builtin, then 935 * that SIGINT is to be trapped or ignored using the trap builtin, then
902 * this routine is not called.) suppress_int is nonzero when interrupts 936 * this routine is not called.) suppress_int is nonzero when interrupts
903 * are held using the INT_OFF macro. (The test for iflag is just 937 * are held using the INTOFF macro. (The test for iflag is just
904 * defensive programming.) 938 * defensive programming.)
905 */ 939 */
906static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN); 940static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN);
@@ -929,6 +963,7 @@ raise_interrupt(void)
929 raise(SIGINT); 963 raise(SIGINT);
930#else 964#else
931 fflush_all(); 965 fflush_all();
966 kill(-getpid(), SIGINT);
932 _exit(SIGINT << 24); 967 _exit(SIGINT << 24);
933#endif 968#endif
934 } 969 }
@@ -957,12 +992,12 @@ int_on(void)
957 barrier(); 992 barrier();
958} 993}
959#if DEBUG_INTONOFF 994#if DEBUG_INTONOFF
960# define INT_ON do { \ 995# define INTON do { \
961 TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \ 996 TRACE(("%s:%d INTON(%d)\n", __func__, __LINE__, suppress_int-1)); \
962 int_on(); \ 997 int_on(); \
963} while (0) 998} while (0)
964#else 999#else
965# define INT_ON int_on() 1000# define INTON int_on()
966#endif 1001#endif
967static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void 1002static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void
968force_int_on(void) 1003force_int_on(void)
@@ -973,7 +1008,7 @@ force_int_on(void)
973 raise_interrupt(); /* does not return */ 1008 raise_interrupt(); /* does not return */
974 barrier(); 1009 barrier();
975} 1010}
976#define FORCE_INT_ON force_int_on() 1011#define FORCEINTON force_int_on()
977 1012
978#define SAVE_INT(v) ((v) = suppress_int) 1013#define SAVE_INT(v) ((v) = suppress_int)
979 1014
@@ -991,27 +1026,27 @@ force_int_on(void)
991static void 1026static void
992outstr(const char *p, FILE *file) 1027outstr(const char *p, FILE *file)
993{ 1028{
994 INT_OFF; 1029 INTOFF;
995 fputs(p, file); 1030 fputs(p, file);
996 INT_ON; 1031 INTON;
997} 1032}
998 1033
999static void 1034static void
1000flush_stdout_stderr(void) 1035flush_stdout_stderr(void)
1001{ 1036{
1002 INT_OFF; 1037 INTOFF;
1003 fflush_all(); 1038 fflush_all();
1004 INT_ON; 1039 INTON;
1005} 1040}
1006 1041
1007/* Was called outcslow(c,FILE*), but c was always '\n' */ 1042/* Was called outcslow(c,FILE*), but c was always '\n' */
1008static void 1043static void
1009newline_and_flush(FILE *dest) 1044newline_and_flush(FILE *dest)
1010{ 1045{
1011 INT_OFF; 1046 INTOFF;
1012 putc('\n', dest); 1047 putc('\n', dest);
1013 fflush(dest); 1048 fflush(dest);
1014 INT_ON; 1049 INTON;
1015} 1050}
1016 1051
1017static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2))); 1052static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
@@ -1021,11 +1056,11 @@ out1fmt(const char *fmt, ...)
1021 va_list ap; 1056 va_list ap;
1022 int r; 1057 int r;
1023 1058
1024 INT_OFF; 1059 INTOFF;
1025 va_start(ap, fmt); 1060 va_start(ap, fmt);
1026 r = vprintf(fmt, ap); 1061 r = vprintf(fmt, ap);
1027 va_end(ap); 1062 va_end(ap);
1028 INT_ON; 1063 INTON;
1029 return r; 1064 return r;
1030} 1065}
1031 1066
@@ -1036,11 +1071,11 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...)
1036 va_list ap; 1071 va_list ap;
1037 int ret; 1072 int ret;
1038 1073
1039 INT_OFF; 1074 INTOFF;
1040 va_start(ap, fmt); 1075 va_start(ap, fmt);
1041 ret = vsnprintf(outbuf, length, fmt, ap); 1076 ret = vsnprintf(outbuf, length, fmt, ap);
1042 va_end(ap); 1077 va_end(ap);
1043 INT_ON; 1078 INTON;
1044 return ret > (int)length ? length : ret; 1079 return ret > (int)length ? length : ret;
1045} 1080}
1046 1081
@@ -1077,28 +1112,47 @@ out2str(const char *p)
1077# define CTL_LAST CTLFROMPROC 1112# define CTL_LAST CTLFROMPROC
1078#endif 1113#endif
1079 1114
1080/* variable substitution byte (follows CTLVAR) */ 1115/* ${VAR[ops]} encoding is CTLVAR,<type_byte>,"VARNAME=",<ops_encoded(details?)>,CTLENDVAR */
1081#define VSTYPE 0x0f /* type of variable substitution */ 1116/* variable type byte (follows CTLVAR) */
1082#define VSNUL 0x10 /* colon--treat the empty string as unset */ 1117#define VSTYPE 0x0f /* type of variable substitution */
1083 1118#define VSNUL 0x10 /* colon: the op is one of :- :+ :? := */
1084/* values of VSTYPE field */ 1119/* values of VSTYPE field. The first 5 must be in this order, "}-+?=" string is used elsewhere to index into them */
1085#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ 1120#define VSNORMAL 0x1 /* $var or ${var} */
1086#define VSMINUS 0x2 /* ${var-text} */ 1121#define VSMINUS 0x2 /* ${var[:]-text} */
1087#define VSPLUS 0x3 /* ${var+text} */ 1122#define VSPLUS 0x3 /* ${var[:]+text} */
1088#define VSQUESTION 0x4 /* ${var?message} */ 1123#define VSQUESTION 0x4 /* ${var[:]?message} */
1089#define VSASSIGN 0x5 /* ${var=text} */ 1124#define VSASSIGN 0x5 /* ${var[:]=text} */
1090#define VSTRIMRIGHT 0x6 /* ${var%pattern} */ 1125#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
1091#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ 1126#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
1092#define VSTRIMLEFT 0x8 /* ${var#pattern} */ 1127#define VSTRIMLEFT 0x8 /* ${var#pattern} */
1093#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ 1128#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
1094#define VSLENGTH 0xa /* ${#var} */ 1129#define VSLENGTH 0xa /* ${#var} */
1095#if BASH_SUBSTR 1130#if BASH_SUBSTR
1096#define VSSUBSTR 0xc /* ${var:position:length} */ 1131#define VSSUBSTR 0xb /* ${var:position:length} */
1132#endif
1133#if BASH_PATTERN_SUBST
1134#define VSREPLACE 0xc /* ${var/pattern/replacement} */
1135#define VSREPLACEALL 0xd /* ${var//pattern/replacement} */
1136#endif
1137static const char vstype_suffix[][3] ALIGN1 = {
1138 [VSNORMAL - VSNORMAL] = "}", // $var or ${var}
1139 [VSMINUS - VSNORMAL] = "-", // ${var-text}
1140 [VSPLUS - VSNORMAL] = "+", // ${var+text}
1141 [VSQUESTION - VSNORMAL] = "?", // ${var?message}
1142 [VSASSIGN - VSNORMAL] = "=", // ${var=text}
1143 [VSTRIMRIGHT - VSNORMAL] = "%", // ${var%pattern}
1144 [VSTRIMRIGHTMAX - VSNORMAL] = "%%",// ${var%%pattern}
1145 [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern}
1146 [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern}
1147 [VSLENGTH - VSNORMAL] = "", // ${#var}
1148#if BASH_SUBSTR
1149 [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length}
1097#endif 1150#endif
1098#if BASH_PATTERN_SUBST 1151#if BASH_PATTERN_SUBST
1099#define VSREPLACE 0xd /* ${var/pattern/replacement} */ 1152 [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement}
1100#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ 1153 [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement}
1101#endif 1154#endif
1155};
1102 1156
1103static const char dolatstr[] ALIGN1 = { 1157static const char dolatstr[] ALIGN1 = {
1104 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' 1158 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
@@ -1127,14 +1181,15 @@ static const char dolatstr[] ALIGN1 = {
1127#endif 1181#endif
1128#define NCLOBBER 18 1182#define NCLOBBER 18
1129#define NFROM 19 1183#define NFROM 19
1130#define NFROMTO 20 1184#define NFROMSTR 20
1131#define NAPPEND 21 1185#define NFROMTO 21
1132#define NTOFD 22 1186#define NAPPEND 22
1133#define NFROMFD 23 1187#define NTOFD 23
1134#define NHERE 24 1188#define NFROMFD 24
1135#define NXHERE 25 1189#define NHERE 25
1136#define NNOT 26 1190#define NXHERE 26
1137#define N_NUMBER 27 1191#define NNOT 27
1192#define N_NUMBER 28
1138 1193
1139union node; 1194union node;
1140 1195
@@ -1472,7 +1527,9 @@ sharg(union node *arg, FILE *fp)
1472 1527
1473 if (subtype & VSNUL) 1528 if (subtype & VSNUL)
1474 putc(':', fp); 1529 putc(':', fp);
1475 1530#if 1
1531 fputs(vstype_suffix[(subtype & VSTYPE) - VSNORMAL], fp);
1532#else
1476 switch (subtype & VSTYPE) { 1533 switch (subtype & VSTYPE) {
1477 case VSNORMAL: 1534 case VSNORMAL:
1478 putc('}', fp); 1535 putc('}', fp);
@@ -1508,6 +1565,7 @@ sharg(union node *arg, FILE *fp)
1508 default: 1565 default:
1509 out1fmt("<subtype %d>", subtype); 1566 out1fmt("<subtype %d>", subtype);
1510 } 1567 }
1568#endif
1511 break; 1569 break;
1512 case CTLENDVAR: 1570 case CTLENDVAR:
1513 putc('}', fp); 1571 putc('}', fp);
@@ -1562,6 +1620,7 @@ shcmd(union node *cmd, FILE *fp)
1562#endif 1620#endif
1563 case NTOFD: s = ">&"; dftfd = 1; break; 1621 case NTOFD: s = ">&"; dftfd = 1; break;
1564 case NFROM: s = "<"; break; 1622 case NFROM: s = "<"; break;
1623 case NFROMSTR: s = "<<<"; break;
1565 case NFROMFD: s = "<&"; break; 1624 case NFROMFD: s = "<&"; break;
1566 case NFROMTO: s = "<>"; break; 1625 case NFROMTO: s = "<>"; break;
1567 default: s = "*error*"; break; 1626 default: s = "*error*"; break;
@@ -1648,13 +1707,14 @@ showtree(union node *n)
1648static void 1707static void
1649ash_vmsg(const char *msg, va_list ap) 1708ash_vmsg(const char *msg, va_list ap)
1650{ 1709{
1710//In dash, the order/format is different:
1711// arg0: LINENO: [commandname:] MSG
1712//If you fix it, change testsuite to match
1651 fprintf(stderr, "%s: ", arg0); 1713 fprintf(stderr, "%s: ", arg0);
1652 if (commandname) { 1714 if (commandname && strcmp(arg0, commandname) != 0)
1653 if (strcmp(arg0, commandname)) 1715 fprintf(stderr, "%s: ", commandname);
1654 fprintf(stderr, "%s: ", commandname); 1716 if (!iflag || g_parsefile->pf_fd > 0)
1655 if (!iflag || g_parsefile->pf_fd > 0) 1717 fprintf(stderr, "line %d: ", errlinno);
1656 fprintf(stderr, "line %d: ", errlinno);
1657 }
1658 vfprintf(stderr, msg, ap); 1718 vfprintf(stderr, msg, ap);
1659 newline_and_flush(stderr); 1719 newline_and_flush(stderr);
1660} 1720}
@@ -1683,6 +1743,7 @@ ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
1683 /* NOTREACHED */ 1743 /* NOTREACHED */
1684} 1744}
1685 1745
1746/* This function is called sh_error() in dash */
1686static void ash_msg_and_raise_error(const char *, ...) NORETURN; 1747static void ash_msg_and_raise_error(const char *, ...) NORETURN;
1687static void 1748static void
1688ash_msg_and_raise_error(const char *msg, ...) 1749ash_msg_and_raise_error(const char *msg, ...)
@@ -1737,17 +1798,60 @@ ash_msg(const char *fmt, ...)
1737} 1798}
1738 1799
1739/* 1800/*
1801 * Types of operations (passed to the errmsg routine).
1802 */
1803#define E_OPEN 01 /* opening a file */
1804#define E_CREAT 02 /* creating a file */
1805#define E_EXEC 04 /* executing a program */
1806/*
1740 * Return a string describing an error. The returned string may be a 1807 * Return a string describing an error. The returned string may be a
1741 * pointer to a static buffer that will be overwritten on the next call. 1808 * pointer to a static buffer that will be overwritten on the next call.
1742 * Action describes the operation that got the error. 1809 * Action describes the operation that got the error.
1743 */ 1810 */
1744static const char * 1811static const char *
1745errmsg(int e, const char *em) 1812errmsg(int e, int action)
1746{ 1813{
1747 if (e == ENOENT || e == ENOTDIR) { 1814 if (e != ENOENT && e != ENOTDIR)
1748 return em; 1815 return strerror(e);
1816
1817 if (action & E_OPEN)
1818 return "no such file";
1819 else if (action & E_CREAT)
1820 return "nonexistent directory";
1821 else
1822 return "not found";
1823}
1824
1825static int sh_open_fail(const char *, int, int) NORETURN;
1826static int sh_open_fail(const char *pathname, int flags, int e)
1827{
1828 const char *word;
1829 int action;
1830
1831 word = "open";
1832 action = E_OPEN;
1833 if (flags & O_CREAT) {
1834 word = "create";
1835 action = E_CREAT;
1749 } 1836 }
1750 return strerror(e); 1837
1838 ash_msg_and_raise_error("can't %s %s: %s", word, pathname, errmsg(e, action));
1839}
1840
1841static int sh_open(const char *pathname, int flags, int mayfail)
1842{
1843 int fd;
1844 int e;
1845
1846 do {
1847 fd = open(pathname, flags, 0666);
1848 e = errno;
1849 } while (fd < 0 && e == EINTR && !pending_sig);
1850
1851 if (mayfail || fd >= 0)
1852 return fd;
1853
1854 sh_open_fail(pathname, flags, e);
1751} 1855}
1752 1856
1753 1857
@@ -1821,7 +1925,6 @@ struct stackmark {
1821 size_t stacknleft; 1925 size_t stacknleft;
1822}; 1926};
1823 1927
1824
1825struct globals_memstack { 1928struct globals_memstack {
1826 struct stack_block *g_stackp; // = &stackbase; 1929 struct stack_block *g_stackp; // = &stackbase;
1827 char *g_stacknxt; // = stackbase.space; 1930 char *g_stacknxt; // = stackbase.space;
@@ -1844,7 +1947,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1844 sstrend = stackbase.space + MINSIZE; \ 1947 sstrend = stackbase.space + MINSIZE; \
1845} while (0) 1948} while (0)
1846 1949
1847
1848#define stackblock() ((void *)g_stacknxt) 1950#define stackblock() ((void *)g_stacknxt)
1849#define stackblocksize() g_stacknleft 1951#define stackblocksize() g_stacknleft
1850 1952
@@ -1874,14 +1976,14 @@ stalloc(size_t nbytes)
1874 len = sizeof(struct stack_block) - MINSIZE + blocksize; 1976 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1875 if (len < blocksize) 1977 if (len < blocksize)
1876 ash_msg_and_raise_error(bb_msg_memory_exhausted); 1978 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1877 INT_OFF; 1979 INTOFF;
1878 sp = ckmalloc(len); 1980 sp = ckmalloc(len);
1879 sp->prev = g_stackp; 1981 sp->prev = g_stackp;
1880 g_stacknxt = sp->space; 1982 g_stacknxt = sp->space;
1881 g_stacknleft = blocksize; 1983 g_stacknleft = blocksize;
1882 sstrend = g_stacknxt + blocksize; 1984 sstrend = g_stacknxt + blocksize;
1883 g_stackp = sp; 1985 g_stackp = sp;
1884 INT_ON; 1986 INTON;
1885 } 1987 }
1886 p = g_stacknxt; 1988 p = g_stacknxt;
1887 g_stacknxt += aligned; 1989 g_stacknxt += aligned;
@@ -1947,7 +2049,7 @@ popstackmark(struct stackmark *mark)
1947 if (!mark->stackp) 2049 if (!mark->stackp)
1948 return; 2050 return;
1949 2051
1950 INT_OFF; 2052 INTOFF;
1951 while (g_stackp != mark->stackp) { 2053 while (g_stackp != mark->stackp) {
1952 sp = g_stackp; 2054 sp = g_stackp;
1953 g_stackp = sp->prev; 2055 g_stackp = sp->prev;
@@ -1956,7 +2058,7 @@ popstackmark(struct stackmark *mark)
1956 g_stacknxt = mark->stacknxt; 2058 g_stacknxt = mark->stacknxt;
1957 g_stacknleft = mark->stacknleft; 2059 g_stacknleft = mark->stacknleft;
1958 sstrend = mark->stacknxt + mark->stacknleft; 2060 sstrend = mark->stacknxt + mark->stacknleft;
1959 INT_ON; 2061 INTON;
1960} 2062}
1961 2063
1962/* 2064/*
@@ -1985,7 +2087,7 @@ growstackblock(size_t min)
1985 struct stack_block *prevstackp; 2087 struct stack_block *prevstackp;
1986 size_t grosslen; 2088 size_t grosslen;
1987 2089
1988 INT_OFF; 2090 INTOFF;
1989 sp = g_stackp; 2091 sp = g_stackp;
1990 prevstackp = sp->prev; 2092 prevstackp = sp->prev;
1991 grosslen = newlen + sizeof(struct stack_block) - MINSIZE; 2093 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
@@ -1995,7 +2097,7 @@ growstackblock(size_t min)
1995 g_stacknxt = sp->space; 2097 g_stacknxt = sp->space;
1996 g_stacknleft = newlen; 2098 g_stacknleft = newlen;
1997 sstrend = sp->space + newlen; 2099 sstrend = sp->space + newlen;
1998 INT_ON; 2100 INTON;
1999 } else { 2101 } else {
2000 char *oldspace = g_stacknxt; 2102 char *oldspace = g_stacknxt;
2001 size_t oldlen = g_stacknleft; 2103 size_t oldlen = g_stacknleft;
@@ -2361,7 +2463,6 @@ struct localvar {
2361# define VIMPORT 0x400 /* variable was imported from environment */ 2463# define VIMPORT 0x400 /* variable was imported from environment */
2362#endif 2464#endif
2363 2465
2364
2365/* Need to be before varinit_data[] */ 2466/* Need to be before varinit_data[] */
2366#if ENABLE_LOCALE_SUPPORT 2467#if ENABLE_LOCALE_SUPPORT
2367static void FAST_FUNC 2468static void FAST_FUNC
@@ -2856,7 +2957,7 @@ setvar(const char *name, const char *val, int flags)
2856 vallen = strlen(val); 2957 vallen = strlen(val);
2857 } 2958 }
2858 2959
2859 INT_OFF; 2960 INTOFF;
2860 nameeq = ckzalloc(namelen + vallen + 2); 2961 nameeq = ckzalloc(namelen + vallen + 2);
2861 p = mempcpy(nameeq, name, namelen); 2962 p = mempcpy(nameeq, name, namelen);
2862 if (val) { 2963 if (val) {
@@ -2864,7 +2965,7 @@ setvar(const char *name, const char *val, int flags)
2864 strcpy(p, val); 2965 strcpy(p, val);
2865 } 2966 }
2866 vp = setvareq(nameeq, flags | VNOSAVE); 2967 vp = setvareq(nameeq, flags | VNOSAVE);
2867 INT_ON; 2968 INTON;
2868 2969
2869 return vp; 2970 return vp;
2870} 2971}
@@ -3419,7 +3520,7 @@ setpwd(const char *val, int setold)
3419 if (setold) { 3520 if (setold) {
3420 setvar("OLDPWD", oldcur, VEXPORT); 3521 setvar("OLDPWD", oldcur, VEXPORT);
3421 } 3522 }
3422 INT_OFF; 3523 INTOFF;
3423 if (physdir != nullstr) { 3524 if (physdir != nullstr) {
3424 if (physdir != oldcur) 3525 if (physdir != oldcur)
3425 free(physdir); 3526 free(physdir);
@@ -3436,7 +3537,7 @@ setpwd(const char *val, int setold)
3436 free(oldcur); 3537 free(oldcur);
3437 } 3538 }
3438 curdir = dir; 3539 curdir = dir;
3439 INT_ON; 3540 INTON;
3440 setvar("PWD", dir, VEXPORT); 3541 setvar("PWD", dir, VEXPORT);
3441} 3542}
3442 3543
@@ -3454,7 +3555,7 @@ docd(const char *dest, int flags)
3454 3555
3455 TRACE(("docd(\"%s\", %d) called\n", dest, flags)); 3556 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
3456 3557
3457 INT_OFF; 3558 INTOFF;
3458 if (!(flags & CD_PHYSICAL)) { 3559 if (!(flags & CD_PHYSICAL)) {
3459 dir = updatepwd(dest); 3560 dir = updatepwd(dest);
3460 if (dir) 3561 if (dir)
@@ -3466,7 +3567,7 @@ docd(const char *dest, int flags)
3466 setpwd(dir, 1); 3567 setpwd(dir, 1);
3467 hashcd(); 3568 hashcd();
3468 out: 3569 out:
3469 INT_ON; 3570 INTON;
3470 return err; 3571 return err;
3471} 3572}
3472 3573
@@ -3581,7 +3682,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3581 3682
3582/* ============ ... */ 3683/* ============ ... */
3583 3684
3584
3585#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3685#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3586 3686
3587/* Syntax classes */ 3687/* Syntax classes */
@@ -3988,13 +4088,11 @@ struct alias {
3988 int flag; 4088 int flag;
3989}; 4089};
3990 4090
3991
3992static struct alias **atab; // [ATABSIZE]; 4091static struct alias **atab; // [ATABSIZE];
3993#define INIT_G_alias() do { \ 4092#define INIT_G_alias() do { \
3994 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 4093 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3995} while (0) 4094} while (0)
3996 4095
3997
3998static struct alias ** 4096static struct alias **
3999__lookupalias(const char *name) 4097__lookupalias(const char *name)
4000{ 4098{
@@ -4056,7 +4154,7 @@ setalias(const char *name, const char *val)
4056 4154
4057 app = __lookupalias(name); 4155 app = __lookupalias(name);
4058 ap = *app; 4156 ap = *app;
4059 INT_OFF; 4157 INTOFF;
4060 if (ap) { 4158 if (ap) {
4061 if (!(ap->flag & ALIASINUSE)) { 4159 if (!(ap->flag & ALIASINUSE)) {
4062 free(ap->val); 4160 free(ap->val);
@@ -4072,7 +4170,7 @@ setalias(const char *name, const char *val)
4072 /*ap->next = NULL;*/ 4170 /*ap->next = NULL;*/
4073 *app = ap; 4171 *app = ap;
4074 } 4172 }
4075 INT_ON; 4173 INTON;
4076} 4174}
4077 4175
4078static int 4176static int
@@ -4083,9 +4181,9 @@ unalias(const char *name)
4083 app = __lookupalias(name); 4181 app = __lookupalias(name);
4084 4182
4085 if (*app) { 4183 if (*app) {
4086 INT_OFF; 4184 INTOFF;
4087 *app = freealias(*app); 4185 *app = freealias(*app);
4088 INT_ON; 4186 INTON;
4089 return 0; 4187 return 0;
4090 } 4188 }
4091 4189
@@ -4098,7 +4196,7 @@ rmaliases(void)
4098 struct alias *ap, **app; 4196 struct alias *ap, **app;
4099 int i; 4197 int i;
4100 4198
4101 INT_OFF; 4199 INTOFF;
4102 for (i = 0; i < ATABSIZE; i++) { 4200 for (i = 0; i < ATABSIZE; i++) {
4103 app = &atab[i]; 4201 app = &atab[i];
4104 for (ap = *app; ap; ap = *app) { 4202 for (ap = *app; ap; ap = *app) {
@@ -4108,7 +4206,7 @@ rmaliases(void)
4108 } 4206 }
4109 } 4207 }
4110 } 4208 }
4111 INT_ON; 4209 INTON;
4112} 4210}
4113 4211
4114static void 4212static void
@@ -4176,7 +4274,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
4176 4274
4177#endif /* ASH_ALIAS */ 4275#endif /* ASH_ALIAS */
4178 4276
4179
4180/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4277/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
4181#define FORK_FG 0 4278#define FORK_FG 0
4182#define FORK_BG 1 4279#define FORK_BG 1
@@ -4230,20 +4327,19 @@ struct job {
4230 struct job *prev_job; /* previous job */ 4327 struct job *prev_job; /* previous job */
4231}; 4328};
4232 4329
4233static struct job *makejob(/*union node *,*/ int);
4234#if !ENABLE_PLATFORM_MINGW32 4330#if !ENABLE_PLATFORM_MINGW32
4235static int forkshell(struct job *, union node *, int); 4331static int forkshell(struct job *, union node *, int);
4236#endif 4332#endif
4237static int waitforjob(struct job *); 4333static int waitforjob(struct job *);
4238 4334
4239#if !JOBS && !JOBS_WIN32 4335#if !JOBS && !JOBS_WIN32
4240enum { doing_jobctl = 0 }; 4336enum { jobctl = 0 };
4241#define setjobctl(on) do {} while (0) 4337#define setjobctl(on) do {} while (0)
4242#elif JOBS_WIN32 4338#elif JOBS_WIN32
4243static smallint doing_jobctl; //references:8 4339static smallint jobctl; //references:8
4244#define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0) 4340#define setjobctl(on) do { if (rootshell) jobctl = on; } while (0)
4245#else /* JOBS */ 4341#else /* JOBS */
4246static smallint doing_jobctl; //references:8 4342static smallint jobctl; //references:8
4247static void setjobctl(int); 4343static void setjobctl(int);
4248#endif 4344#endif
4249 4345
@@ -4259,22 +4355,30 @@ ignoresig(int signo)
4259 /* No, need to do it */ 4355 /* No, need to do it */
4260 signal(signo, SIG_IGN); 4356 signal(signo, SIG_IGN);
4261 } 4357 }
4262 sigmode[signo - 1] = S_HARD_IGN; 4358 if (!vforked)
4359 sigmode[signo - 1] = S_HARD_IGN;
4263} 4360}
4264 4361
4265/* 4362/*
4266 * Only one usage site - in setsignal() 4363 * Only one usage site - in setsignal()
4364 * This function is called onsig() in dash
4267 */ 4365 */
4268static void 4366static void
4269signal_handler(int signo) 4367signal_handler(int signo)
4270{ 4368{
4369 // parent momentarily has vforked == 1 too, but it masks
4370 // all signals until after it resets vforked to 0.
4371 if (vforked)
4372 /* We are vfork child, DO NOT MODIFY ANY VARIABLES! */
4373 return;
4374
4271 if (signo == SIGCHLD) { 4375 if (signo == SIGCHLD) {
4272 got_sigchld = 1; 4376 gotsigchld = 1;
4273 if (!trap[SIGCHLD]) 4377 if (!trap[SIGCHLD])
4274 return; 4378 return;
4275 } 4379 }
4276#if ENABLE_FEATURE_EDITING 4380#if ENABLE_FEATURE_EDITING
4277 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 4381 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
4278#endif 4382#endif
4279 gotsig[signo - 1] = 1; 4383 gotsig[signo - 1] = 1;
4280 pending_sig = signo; 4384 pending_sig = signo;
@@ -4295,10 +4399,13 @@ signal_handler(int signo)
4295static void 4399static void
4296setsignal(int signo) 4400setsignal(int signo)
4297{ 4401{
4402 int lvforked;
4298 char *t; 4403 char *t;
4299 char cur_act, new_act; 4404 char cur_act, new_act;
4300 struct sigaction act; 4405 struct sigaction act;
4301 4406
4407 lvforked = vforked;
4408
4302 t = trap[signo]; 4409 t = trap[signo];
4303 new_act = S_DFL; 4410 new_act = S_DFL;
4304 if (t != NULL) { /* trap for this sig is set */ 4411 if (t != NULL) { /* trap for this sig is set */
@@ -4307,7 +4414,7 @@ setsignal(int signo)
4307 new_act = S_IGN; 4414 new_act = S_IGN;
4308 } 4415 }
4309 4416
4310 if (rootshell && new_act == S_DFL) { 4417 if (rootshell && new_act == S_DFL && !lvforked) {
4311 switch (signo) { 4418 switch (signo) {
4312 case SIGINT: 4419 case SIGINT:
4313 if (iflag || minusc || sflag == 0) 4420 if (iflag || minusc || sflag == 0)
@@ -4377,8 +4484,8 @@ setsignal(int signo)
4377 if (cur_act == S_HARD_IGN || cur_act == new_act) 4484 if (cur_act == S_HARD_IGN || cur_act == new_act)
4378 return; 4485 return;
4379 4486
4380 *t = new_act; 4487 if (!lvforked)
4381 4488 *t = new_act;
4382 act.sa_handler = SIG_DFL; 4489 act.sa_handler = SIG_DFL;
4383 switch (new_act) { 4490 switch (new_act) {
4384 case S_CATCH: 4491 case S_CATCH:
@@ -4641,7 +4748,7 @@ freejob(struct job *jp)
4641 int i; 4748 int i;
4642#endif 4749#endif
4643 4750
4644 INT_OFF; 4751 INTOFF;
4645#if ENABLE_PLATFORM_POSIX || JOBS_WIN32 4752#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4646 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4753 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4647 if (ps->ps_cmd != nullstr) 4754 if (ps->ps_cmd != nullstr)
@@ -4652,14 +4759,29 @@ freejob(struct job *jp)
4652 free(jp->ps); 4759 free(jp->ps);
4653 jp->used = 0; 4760 jp->used = 0;
4654 set_curjob(jp, CUR_DELETE); 4761 set_curjob(jp, CUR_DELETE);
4655 INT_ON; 4762 INTON;
4656} 4763}
4657 4764
4658#if JOBS 4765#if JOBS
4659static void 4766static void
4660xtcsetpgrp(int fd, pid_t pgrp) 4767xtcsetpgrp(int fd, pid_t pgrp)
4661{ 4768{
4662 if (tcsetpgrp(fd, pgrp)) 4769 int err;
4770
4771 sigblockall(NULL);
4772 err = tcsetpgrp(fd, pgrp);
4773 sigclearmask();
4774 // Unmasking signals would cause any arrived signal to trigger, so why?
4775 // Generally yes, but there are exceptions. Such as:
4776 // """
4777 // Attempts to use tcsetpgrp() from a process which is a member of
4778 // a background process group on a fd associated with its controlling
4779 // terminal shall cause the process group to be sent a SIGTTOU signal.
4780 // If the calling thread is blocking SIGTTOU signals or the process
4781 // is ignoring SIGTTOU signals, the process shall be allowed
4782 // to perform the operation, and no signal is sent."""
4783
4784 if (err)
4663 ash_msg_and_raise_perror("can't set tty process group"); 4785 ash_msg_and_raise_perror("can't set tty process group");
4664} 4786}
4665 4787
@@ -4678,11 +4800,11 @@ setjobctl(int on)
4678 int fd; 4800 int fd;
4679 int pgrp; 4801 int pgrp;
4680 4802
4681 if (on == doing_jobctl || rootshell == 0) 4803 if (on == jobctl || rootshell == 0)
4682 return; 4804 return;
4683 if (on) { 4805 if (on) {
4684 int ofd; 4806 int ofd;
4685 ofd = fd = open(_PATH_TTY, O_RDWR); 4807 ofd = fd = sh_open(_PATH_TTY, O_RDWR, 1);
4686 if (fd < 0) { 4808 if (fd < 0) {
4687 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails. 4809 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
4688 * That sometimes helps to acquire controlling tty. 4810 * That sometimes helps to acquire controlling tty.
@@ -4738,7 +4860,7 @@ setjobctl(int on)
4738 fd = -1; 4860 fd = -1;
4739 } 4861 }
4740 ttyfd = fd; 4862 ttyfd = fd;
4741 doing_jobctl = on; 4863 jobctl = on;
4742} 4864}
4743#endif 4865#endif
4744 4866
@@ -4827,7 +4949,7 @@ restartjob(struct job *jp, int mode)
4827 int status; 4949 int status;
4828 pid_t pgid; 4950 pid_t pgid;
4829 4951
4830 INT_OFF; 4952 INTOFF;
4831 if (jp->state == JOBDONE) 4953 if (jp->state == JOBDONE)
4832 goto out; 4954 goto out;
4833 jp->state = JOBRUNNING; 4955 jp->state = JOBRUNNING;
@@ -4847,7 +4969,7 @@ restartjob(struct job *jp, int mode)
4847 } while (--i); 4969 } while (--i);
4848 out: 4970 out:
4849 status = (mode == FORK_FG) ? waitforjob(jp) : 0; 4971 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4850 INT_ON; 4972 INTON;
4851 return status; 4973 return status;
4852} 4974}
4853 4975
@@ -4968,11 +5090,12 @@ waitpid_child(int *status, int wait_flags)
4968#define waitpid(p, s, f) waitpid_child(s, f) 5090#define waitpid(p, s, f) waitpid_child(s, f)
4969#endif 5091#endif
4970 5092
4971#define DOWAIT_NONBLOCK 0 5093/* Inside dowait(): */
4972#define DOWAIT_BLOCK 1 5094#define DOWAIT_NONBLOCK 0 /* waitpid() will use WNOHANG and won't wait for signals */
4973#define DOWAIT_BLOCK_OR_SIG 2 5095#define DOWAIT_BLOCK 1 /* waitpid() will NOT use WNOHANG */
5096#define DOWAIT_CHILD_OR_SIG 2 /* waitpid() will use WNOHANG and if got 0, will wait for signals, then loop back */
4974#if BASH_WAIT_N 5097#if BASH_WAIT_N
4975# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ 5098# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4976#endif 5099#endif
4977 5100
4978static int 5101static int
@@ -4984,29 +5107,38 @@ waitproc(int block, int *status)
4984 int err; 5107 int err;
4985 5108
4986#if JOBS 5109#if JOBS
4987 if (doing_jobctl) 5110 if (jobctl)
4988 flags |= WUNTRACED; 5111 flags |= WUNTRACED;
4989#endif 5112#endif
4990 5113
4991 do { 5114 do {
4992 got_sigchld = 0; 5115 gotsigchld = 0;
4993 do 5116 do
4994 err = waitpid(-1, status, flags); 5117 err = waitpid(-1, status, flags);
4995 while (err < 0 && errno == EINTR); 5118 while (err < 0 && errno == EINTR);
4996 5119
5120 /* Return if error (for example, ECHILD); or if pid found;
5121 * or if "block" is DOWAIT_NONBLOCK (=0), in this case return -1.
5122 */
4997 if (err || (err = -!block)) 5123 if (err || (err = -!block))
4998 break; 5124 break;
4999 5125
5000 sigfillset(&oldmask); 5126 /* "block" is DOWAIT_CHILD_OR_SIG. All children are running
5001 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */ 5127 * (waitpid(WNOHAG) above returned 0), wait for signals:
5002 while (!got_sigchld && !pending_sig) 5128 */
5003 sigsuspend(&oldmask); 5129
5004 sigprocmask(SIG_SETMASK, &oldmask, NULL);
5005 //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): 5130 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
5006 //while (!got_sigchld && !pending_sig) 5131 //while (!gotsigchld && !pending_sig)
5007 // pause(); 5132 // pause();
5008 5133
5009 } while (got_sigchld); 5134 sigblockall(&oldmask);
5135
5136 while (!gotsigchld && !pending_sig)
5137 sigsuspend(&oldmask);
5138
5139 sigclearmask();
5140 } while (gotsigchld);
5141 /* If we fall off the loop, err is 0, which means we got a !SIGCHLD signal */
5010 5142
5011 return err; 5143 return err;
5012#else 5144#else
@@ -5016,8 +5148,7 @@ waitproc(int block, int *status)
5016#endif 5148#endif
5017} 5149}
5018 5150
5019static int 5151static int waitone(int block, struct job *job)
5020waitone(int block, struct job *job)
5021{ 5152{
5022 int pid; 5153 int pid;
5023 int status; 5154 int status;
@@ -5030,25 +5161,25 @@ waitone(int block, struct job *job)
5030 5161
5031 TRACE(("dowait(0x%x) called\n", block)); 5162 TRACE(("dowait(0x%x) called\n", block));
5032 5163
5033 /* It's wrong to call waitpid() outside of INT_OFF region: 5164 /* It's wrong to call waitpid() outside of INTOFF region:
5034 * signal can arrive just after syscall return and handler can 5165 * signal can arrive just after syscall return and handler can
5035 * longjmp away, losing stop/exit notification processing. 5166 * longjmp away, losing stop/exit notification processing.
5036 * Thus, for "jobs" builtin, and for waiting for a fg job, 5167 * Thus, for "jobs" builtin, and for waiting for a fg job,
5037 * we call waitpid() (blocking or non-blocking) inside INT_OFF. 5168 * we call waitpid() (blocking or non-blocking) inside INTOFF.
5038 * 5169 *
5039 * However, for "wait" builtin it is wrong to simply call waitpid() 5170 * However, for "wait" builtin it is wrong to simply call waitpid()
5040 * in INT_OFF region: "wait" needs to wait for any running job 5171 * in INTOFF region: "wait" needs to wait for any running job
5041 * to change state, but should exit on any trap too. 5172 * to change state, but should exit on any trap too.
5042 * In INT_OFF region, a signal just before syscall entry can set 5173 * In INTOFF region, a signal just before syscall entry can set
5043 * pending_sig variables, but we can't check them, and we would 5174 * pending_sig variables, but we can't check them, and we would
5044 * either enter a sleeping waitpid() (BUG), or need to busy-loop. 5175 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
5045 * 5176 *
5046 * Because of this, we run inside INT_OFF, but use a special routine 5177 * Because of this, we run inside INTOFF, but use a special routine
5047 * which combines waitpid() and sigsuspend(). 5178 * which combines waitpid() and sigsuspend().
5048 * This is the reason why we need to have a handler for SIGCHLD: 5179 * This is the reason why we need to have a handler for SIGCHLD:
5049 * SIG_DFL handler does not wake sigsuspend(). 5180 * SIG_DFL handler does not wake sigsuspend().
5050 */ 5181 */
5051 INT_OFF; 5182 INTOFF;
5052 pid = waitproc(block, &status); 5183 pid = waitproc(block, &status);
5053 TRACE(("wait returns pid %d, status=%d\n", pid, status)); 5184 TRACE(("wait returns pid %d, status=%d\n", pid, status));
5054 if (pid <= 0) 5185 if (pid <= 0)
@@ -5056,36 +5187,34 @@ waitone(int block, struct job *job)
5056 5187
5057 for (jp = curjob; jp; jp = jp->prev_job) { 5188 for (jp = curjob; jp; jp = jp->prev_job) {
5058 int jobstate; 5189 int jobstate;
5059 struct procstat *ps; 5190 struct procstat *sp;
5060 struct procstat *psend; 5191 struct procstat *spend;
5061 if (jp->state == JOBDONE) 5192 if (jp->state == JOBDONE)
5062 continue; 5193 continue;
5063 jobstate = JOBDONE; 5194 jobstate = JOBDONE;
5064 ps = jp->ps; 5195 spend = jp->ps + jp->nprocs;
5065 psend = ps + jp->nprocs; 5196 sp = jp->ps;
5066 do { 5197 do {
5067 if (ps->ps_pid == pid) { 5198 if (sp->ps_pid == pid) {
5068 TRACE(("Job %d: changing status of proc %d " 5199 TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->ps_status, status));
5069 "from 0x%x to 0x%x\n", 5200 sp->ps_status = status;
5070 jobno(jp), pid, ps->ps_status, status));
5071 ps->ps_status = status;
5072 thisjob = jp; 5201 thisjob = jp;
5073#if ENABLE_PLATFORM_MINGW32 5202#if ENABLE_PLATFORM_MINGW32
5074 CloseHandle(ps->ps_proc); 5203 CloseHandle(sp->ps_proc);
5075 ps->ps_proc = NULL; 5204 sp->ps_proc = NULL;
5076#endif 5205#endif
5077 } 5206 }
5078 if (ps->ps_status == -1) 5207 if (sp->ps_status == -1)
5079 jobstate = JOBRUNNING; 5208 jobstate = JOBRUNNING;
5080#if JOBS 5209#if JOBS
5081 if (jobstate == JOBRUNNING) 5210 if (jobstate == JOBRUNNING)
5082 continue; 5211 continue;
5083 if (WIFSTOPPED(ps->ps_status)) { 5212 if (WIFSTOPPED(sp->ps_status)) {
5084 jp->stopstatus = ps->ps_status; 5213 jp->stopstatus = sp->ps_status;
5085 jobstate = JOBSTOPPED; 5214 jobstate = JOBSTOPPED;
5086 } 5215 }
5087#endif 5216#endif
5088 } while (++ps < psend); 5217 } while (++sp < spend);
5089 if (!thisjob) 5218 if (!thisjob)
5090 continue; 5219 continue;
5091 5220
@@ -5097,8 +5226,7 @@ waitone(int block, struct job *job)
5097 */ 5226 */
5098 thisjob->changed = 1; 5227 thisjob->changed = 1;
5099 if (thisjob->state != jobstate) { 5228 if (thisjob->state != jobstate) {
5100 TRACE(("Job %d: changing state from %d to %d\n", 5229 TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, jobstate));
5101 jobno(thisjob), thisjob->state, jobstate));
5102 thisjob->state = jobstate; 5230 thisjob->state = jobstate;
5103#if JOBS 5231#if JOBS
5104 if (jobstate == JOBSTOPPED) 5232 if (jobstate == JOBSTOPPED)
@@ -5110,7 +5238,7 @@ waitone(int block, struct job *job)
5110 } 5238 }
5111 /* The process wasn't found in job list */ 5239 /* The process wasn't found in job list */
5112 out: 5240 out:
5113 INT_ON; 5241 INTON;
5114 5242
5115#if BASH_WAIT_N 5243#if BASH_WAIT_N
5116 if (want_jobexitstatus) { 5244 if (want_jobexitstatus) {
@@ -5133,11 +5261,10 @@ waitone(int block, struct job *job)
5133 return pid; 5261 return pid;
5134} 5262}
5135 5263
5136static int 5264static int dowait(int block, struct job *jp)
5137dowait(int block, struct job *jp)
5138{ 5265{
5139#if !ENABLE_PLATFORM_MINGW32 5266#if !ENABLE_PLATFORM_MINGW32
5140 smallint gotchld = *(volatile smallint *)&got_sigchld; 5267 smallint gotchld = *(volatile smallint *)&gotsigchld;
5141 int rpid; 5268 int rpid;
5142 int pid; 5269 int pid;
5143 5270
@@ -5375,9 +5502,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
5375 * the trap is executed." 5502 * the trap is executed."
5376 */ 5503 */
5377#if BASH_WAIT_N 5504#if BASH_WAIT_N
5378 status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL); 5505 status = dowait(DOWAIT_CHILD_OR_SIG | DOWAIT_JOBSTATUS, NULL);
5379#else 5506#else
5380 dowait(DOWAIT_BLOCK_OR_SIG, NULL); 5507 dowait(DOWAIT_CHILD_OR_SIG, NULL);
5381#endif 5508#endif
5382 /* if child sends us a signal *and immediately exits*, 5509 /* if child sends us a signal *and immediately exits*,
5383 * dowait() returns pid > 0. Check this case, 5510 * dowait() returns pid > 0. Check this case,
@@ -5418,7 +5545,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
5418 job = getjob(*argv, 0); 5545 job = getjob(*argv, 0);
5419 } 5546 }
5420 /* loop until process terminated or stopped */ 5547 /* loop until process terminated or stopped */
5421 dowait(DOWAIT_BLOCK_OR_SIG, job); 5548 dowait(DOWAIT_CHILD_OR_SIG, job);
5422 if (pending_sig) 5549 if (pending_sig)
5423 goto sigout; 5550 goto sigout;
5424 job->waited = 1; 5551 job->waited = 1;
@@ -5481,7 +5608,7 @@ growjobtab(void)
5481 * Called with interrupts off. 5608 * Called with interrupts off.
5482 */ 5609 */
5483static struct job * 5610static struct job *
5484makejob(/*union node *node,*/ int nprocs) 5611makejob(int nprocs)
5485{ 5612{
5486 int i; 5613 int i;
5487 struct job *jp; 5614 struct job *jp;
@@ -5496,7 +5623,7 @@ makejob(/*union node *node,*/ int nprocs)
5496 if (jp->state != JOBDONE || !jp->waited) 5623 if (jp->state != JOBDONE || !jp->waited)
5497 continue; 5624 continue;
5498#if JOBS || JOBS_WIN32 5625#if JOBS || JOBS_WIN32
5499 if (doing_jobctl) 5626 if (jobctl)
5500 continue; 5627 continue;
5501#endif 5628#endif
5502 freejob(jp); 5629 freejob(jp);
@@ -5505,8 +5632,8 @@ makejob(/*union node *node,*/ int nprocs)
5505 memset(jp, 0, sizeof(*jp)); 5632 memset(jp, 0, sizeof(*jp));
5506#if JOBS 5633#if JOBS
5507 /* jp->jobctl is a bitfield. 5634 /* jp->jobctl is a bitfield.
5508 * "jp->jobctl |= doing_jobctl" likely to give awful code */ 5635 * "jp->jobctl |= jobctl" likely to give awful code */
5509 if (doing_jobctl) 5636 if (jobctl)
5510 jp->jobctl = 1; 5637 jp->jobctl = 1;
5511#endif 5638#endif
5512 jp->prev_job = curjob; 5639 jp->prev_job = curjob;
@@ -5531,13 +5658,6 @@ static char *cmdnextc;
5531static void 5658static void
5532cmdputs(const char *s) 5659cmdputs(const char *s)
5533{ 5660{
5534 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
5535 "", "}", "-", "+", "?", "=",
5536 "%", "%%", "#", "##"
5537 IF_BASH_SUBSTR(, ":")
5538 IF_BASH_PATTERN_SUBST(, "/", "//")
5539 };
5540
5541 const char *p, *str; 5661 const char *p, *str;
5542 char cc[2]; 5662 char cc[2];
5543 char *nextc; 5663 char *nextc;
@@ -5600,32 +5720,34 @@ cmdputs(const char *s)
5600 case '=': 5720 case '=':
5601 if (subtype == 0) 5721 if (subtype == 0)
5602 break; 5722 break;
5723 /* We are in variable name */
5603 if ((subtype & VSTYPE) != VSNORMAL) 5724 if ((subtype & VSTYPE) != VSNORMAL)
5604 quoted <<= 1; 5725 quoted <<= 1;
5605 str = vstype[subtype & VSTYPE]; 5726 str = vstype_suffix[(subtype & VSTYPE) - VSNORMAL];
5606 if (subtype & VSNUL) 5727 if (!(subtype & VSNUL))
5607 c = ':'; 5728 goto dostr;
5608 else 5729 c = ':';
5609 goto checkstr;
5610 break; 5730 break;
5611 case '\'': 5731 case '$':
5732 /* Can happen inside quotes, or in variable name $$ */
5733 if (subtype != 0)
5734 // Testcase:
5735 // $ true $$ &
5736 // $ <cr>
5737 // [1]+ Done true ${$} // shows ${\$} without "if (subtype)" check
5738 break;
5739 /* Not in variable name - show as \$ */
5740 case '\'': /* These can only happen inside quotes */
5612 case '\\': 5741 case '\\':
5613 case '"': 5742 case '"':
5614 case '$':
5615 /* These can only happen inside quotes */
5616 cc[0] = c; 5743 cc[0] = c;
5617 str = cc; 5744 str = cc;
5618//FIXME:
5619// $ true $$ &
5620// $ <cr>
5621// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok)
5622 c = '\\'; 5745 c = '\\';
5623 break; 5746 break;
5624 default: 5747 default:
5625 break; 5748 break;
5626 } 5749 }
5627 USTPUTC(c, nextc); 5750 USTPUTC(c, nextc);
5628 checkstr:
5629 if (!str) 5751 if (!str)
5630 continue; 5752 continue;
5631 dostr: 5753 dostr:
@@ -5637,7 +5759,7 @@ cmdputs(const char *s)
5637 if (quoted & 1) { 5759 if (quoted & 1) {
5638 USTPUTC('"', nextc); 5760 USTPUTC('"', nextc);
5639 } 5761 }
5640 *nextc = 0; 5762 *nextc = '\0';
5641 cmdnextc = nextc; 5763 cmdnextc = nextc;
5642} 5764}
5643 5765
@@ -5761,6 +5883,9 @@ cmdtxt(union node *n)
5761 case NXHERE: 5883 case NXHERE:
5762 p = "<<..."; 5884 p = "<<...";
5763 goto dotail2; 5885 goto dotail2;
5886 case NFROMSTR:
5887 p = "<<<";
5888 goto dotail2;
5764 case NCASE: 5889 case NCASE:
5765 cmdputs("case "); 5890 cmdputs("case ");
5766 cmdputs(n->ncase.expr->narg.text); 5891 cmdputs(n->ncase.expr->narg.text);
@@ -5848,7 +5973,7 @@ clear_traps(void)
5848{ 5973{
5849 char **tp; 5974 char **tp;
5850 5975
5851 INT_OFF; 5976 INTOFF;
5852 for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) { 5977 for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) {
5853 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ 5978 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
5854 if (trap_ptr == trap) 5979 if (trap_ptr == trap)
@@ -5860,7 +5985,7 @@ clear_traps(void)
5860 } 5985 }
5861 } 5986 }
5862 may_have_traps = 0; 5987 may_have_traps = 0;
5863 INT_ON; 5988 INTON;
5864} 5989}
5865 5990
5866#if !ENABLE_PLATFORM_MINGW32 5991#if !ENABLE_PLATFORM_MINGW32
@@ -5872,18 +5997,28 @@ static void closescript(void);
5872static NOINLINE void 5997static NOINLINE void
5873forkchild(struct job *jp, union node *n, int mode) 5998forkchild(struct job *jp, union node *n, int mode)
5874{ 5999{
6000 int lvforked;
5875 int oldlvl; 6001 int oldlvl;
5876 6002
5877 TRACE(("Child shell %d\n", getpid())); 6003 TRACE(("Child shell %d\n", getpid()));
5878 oldlvl = shlvl; 6004 oldlvl = shlvl;
5879 shlvl++; 6005 lvforked = vforked;
6006
6007 if (!lvforked) {
6008 shlvl++;
6009
6010 closescript();
6011
6012#if JOBS
6013 /* do job control only in root shell */
6014 jobctl = 0;
6015#endif
6016 }
5880 6017
5881 /* man bash: "Non-builtin commands run by bash have signal handlers 6018 /* man bash: "Non-builtin commands run by bash have signal handlers
5882 * set to the values inherited by the shell from its parent". 6019 * set to the values inherited by the shell from its parent".
5883 * Do we do it correctly? */ 6020 * Do we do it correctly? */
5884 6021
5885 closescript();
5886
5887 if (n && n->type == NCMD /* is it single cmd? */ 6022 if (n && n->type == NCMD /* is it single cmd? */
5888 /* && n->ncmd.args->type == NARG - always true? */ 6023 /* && n->ncmd.args->type == NARG - always true? */
5889 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 6024 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
@@ -5929,10 +6064,9 @@ forkchild(struct job *jp, union node *n, int mode)
5929 trap_ptr = xmemdup(trap, sizeof(trap)); 6064 trap_ptr = xmemdup(trap, sizeof(trap));
5930 /* Fall through into clearing traps */ 6065 /* Fall through into clearing traps */
5931 } 6066 }
5932 clear_traps(); 6067 if (!lvforked)
6068 clear_traps();
5933#if JOBS 6069#if JOBS
5934 /* do job control only in root shell */
5935 doing_jobctl = 0;
5936 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { 6070 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
5937 pid_t pgrp; 6071 pid_t pgrp;
5938 6072
@@ -5955,8 +6089,7 @@ forkchild(struct job *jp, union node *n, int mode)
5955 ignoresig(SIGQUIT); 6089 ignoresig(SIGQUIT);
5956 if (jp->nprocs == 0) { 6090 if (jp->nprocs == 0) {
5957 close(0); 6091 close(0);
5958 if (open(bb_dev_null, O_RDONLY) != 0) 6092 sh_open(_PATH_DEVNULL, O_RDONLY, 0);
5959 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
5960 } 6093 }
5961 } 6094 }
5962 if (oldlvl == 0) { 6095 if (oldlvl == 0) {
@@ -5987,6 +6120,10 @@ forkchild(struct job *jp, union node *n, int mode)
5987 return; 6120 return;
5988 } 6121 }
5989#endif 6122#endif
6123
6124 if (lvforked)
6125 return;
6126
5990 for (jp = curjob; jp; jp = jp->prev_job) 6127 for (jp = curjob; jp; jp = jp->prev_job)
5991 freejob(jp); 6128 freejob(jp);
5992} 6129}
@@ -6005,8 +6142,17 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
6005{ 6142{
6006#if ENABLE_PLATFORM_MINGW32 6143#if ENABLE_PLATFORM_MINGW32
6007 pid_t pid = GetProcessId(proc); 6144 pid_t pid = GetProcessId(proc);
6145#else
6146 if (pid < 0) {
6147 TRACE(("Fork failed, errno=%d", errno));
6148 if (jp)
6149 freejob(jp);
6150 ash_msg_and_raise_perror("can't fork");
6151 /* NOTREACHED */
6152 }
6008#endif 6153#endif
6009 TRACE(("In parent shell: child = %d\n", pid)); 6154
6155 TRACE(("In parent shell: child = %d\n", pid));
6010 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 6156 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
6011 return; 6157 return;
6012#if JOBS 6158#if JOBS
@@ -6040,7 +6186,7 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
6040 ps->ps_proc = proc; 6186 ps->ps_proc = proc;
6041#endif 6187#endif
6042#if JOBS || JOBS_WIN32 6188#if JOBS || JOBS_WIN32
6043 if (doing_jobctl && n) 6189 if (jobctl && n)
6044 ps->ps_cmd = commandtext(n); 6190 ps->ps_cmd = commandtext(n);
6045#endif 6191#endif
6046 } 6192 }
@@ -6055,20 +6201,43 @@ forkshell(struct job *jp, union node *n, int mode)
6055 6201
6056 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); 6202 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
6057 pid = fork(); 6203 pid = fork();
6058 if (pid < 0) {
6059 TRACE(("Fork failed, errno=%d", errno));
6060 if (jp)
6061 freejob(jp);
6062 ash_msg_and_raise_perror("can't fork");
6063 }
6064 if (pid == 0) { 6204 if (pid == 0) {
6065 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ 6205 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
6066 forkchild(jp, n, mode); 6206 forkchild(jp, n, mode);
6067 } else { 6207 } else
6068 forkparent(jp, n, mode, pid); 6208 forkparent(jp, n, mode, pid);
6069 } 6209
6070 return pid; 6210 return pid;
6071} 6211}
6212
6213static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
6214
6215static struct job*
6216vforkexec(union node *n, char **argv, const char *path, int idx)
6217{
6218 struct job *jp;
6219 int pid;
6220
6221 jp = makejob(1);
6222
6223 sigblockall(NULL);
6224 vforked = 1;
6225
6226 pid = vfork();
6227
6228 if (!pid) {
6229 forkchild(jp, n, FORK_FG);
6230 sigclearmask();
6231 shellexec(argv[0], argv, path, idx);
6232 /* NOTREACHED */
6233 }
6234
6235 vforked = 0;
6236 sigclearmask();
6237 forkparent(jp, n, FORK_FG, pid);
6238
6239 return jp;
6240}
6072#endif 6241#endif
6073 6242
6074/* 6243/*
@@ -6193,7 +6362,6 @@ stoppedjobs(void)
6193} 6362}
6194#endif 6363#endif
6195 6364
6196
6197/* 6365/*
6198 * Code for dealing with input/output redirection. 6366 * Code for dealing with input/output redirection.
6199 */ 6367 */
@@ -6208,26 +6376,11 @@ stoppedjobs(void)
6208 * data to a pipe. If the document is short, we can stuff the data in 6376 * data to a pipe. If the document is short, we can stuff the data in
6209 * the pipe without forking. 6377 * the pipe without forking.
6210 */ 6378 */
6211/* openhere needs this forward reference */
6212static void expandhere(union node *arg);
6213static int 6379static int
6214openhere(union node *redir) 6380write2pipe(int pip[2], const char *p, size_t len)
6215{ 6381{
6216 char *p;
6217 int pip[2];
6218 size_t len = 0;
6219 IF_PLATFORM_MINGW32(struct forkshell fs); 6382 IF_PLATFORM_MINGW32(struct forkshell fs);
6220 6383
6221 if (pipe(pip) < 0)
6222 ash_msg_and_raise_perror("can't create pipe");
6223
6224 p = redir->nhere.doc->narg.text;
6225 if (redir->type == NXHERE) {
6226 expandhere(redir->nhere.doc);
6227 p = stackblock();
6228 }
6229
6230 len = strlen(p);
6231 if (len <= PIPE_BUF) { 6384 if (len <= PIPE_BUF) {
6232 xwrite(pip[1], p, len); 6385 xwrite(pip[1], p, len);
6233 goto out; 6386 goto out;
@@ -6258,89 +6411,113 @@ openhere(union node *redir)
6258 return pip[0]; 6411 return pip[0];
6259} 6412}
6260 6413
6414/* openhere needs this forward reference */
6415static void expandhere(union node *arg);
6416static int
6417openhere(union node *redir)
6418{
6419 char *p;
6420 int pip[2];
6421
6422 if (pipe(pip) < 0)
6423 ash_msg_and_raise_perror("can't create pipe");
6424
6425 p = redir->nhere.doc->narg.text;
6426 if (redir->type == NXHERE) {
6427 expandhere(redir->nhere.doc);
6428 p = stackblock();
6429 }
6430
6431 return write2pipe(pip, p, strlen(p));
6432}
6433
6434static int
6435openherestr(char *str)
6436{
6437 int pip[2];
6438 size_t len;
6439
6440 if (pipe(pip) < 0)
6441 ash_msg_and_raise_perror("can't create pipe");
6442
6443 len = strlen(str);
6444 str[len] = '\n';
6445 write2pipe(pip, str, len + 1);
6446 str[len] = '\0';
6447 return pip[0];
6448}
6449
6261static int 6450static int
6262openredirect(union node *redir) 6451openredirect(union node *redir)
6263{ 6452{
6264 struct stat sb; 6453 struct stat sb;
6265 char *fname; 6454 char *fname;
6455 int flags;
6266 int f; 6456 int f;
6267 6457
6268 switch (redir->nfile.type) { 6458 switch (redir->nfile.type) {
6269/* Can't happen, our single caller does this itself */
6270// case NTOFD:
6271// case NFROMFD:
6272// return -1;
6273 case NHERE:
6274 case NXHERE:
6275 return openhere(redir);
6276 }
6277
6278 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
6279 * allocated space. Do it only when we know it is safe.
6280 */
6281 fname = redir->nfile.expfname;
6282
6283 switch (redir->nfile.type) {
6284 default:
6285#if DEBUG
6286 abort();
6287#endif
6288 case NFROM: 6459 case NFROM:
6289 f = open(fname, O_RDONLY); 6460 flags = O_RDONLY;
6290 if (f < 0) 6461 do_open:
6291 goto eopen; 6462 f = sh_open(redir->nfile.expfname, flags, 0);
6463#if ENABLE_PLATFORM_MINGW32
6464 if (redir->nfile.type == NAPPEND)
6465 lseek(f, 0, SEEK_END);
6466#endif
6292 break; 6467 break;
6293 case NFROMTO: 6468 case NFROMSTR:
6294 f = open(fname, O_RDWR|O_CREAT, 0666); 6469 f = openherestr(redir->nfile.expfname);
6295 if (f < 0)
6296 goto ecreate;
6297 break; 6470 break;
6471 case NFROMTO:
6472 flags = O_RDWR|O_CREAT;
6473 goto do_open;
6298 case NTO: 6474 case NTO:
6299#if BASH_REDIR_OUTPUT 6475#if BASH_REDIR_OUTPUT
6300 case NTO2: 6476 case NTO2:
6301#endif 6477#endif
6302 /* Take care of noclobber mode. */ 6478 /* Take care of noclobber mode. */
6303 if (Cflag) { 6479 if (Cflag) {
6480 fname = redir->nfile.expfname;
6304 if (stat(fname, &sb) < 0) { 6481 if (stat(fname, &sb) < 0) {
6305 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); 6482 flags = O_WRONLY|O_CREAT|O_EXCL;
6306 if (f < 0) 6483 goto do_open;
6307 goto ecreate; 6484 }
6308 } else if (!S_ISREG(sb.st_mode)) { 6485
6309 f = open(fname, O_WRONLY, 0666); 6486 if (S_ISREG(sb.st_mode))
6310 if (f < 0) 6487 goto ecreate;
6311 goto ecreate; 6488
6312 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) { 6489 f = sh_open(fname, O_WRONLY, 0);
6313 close(f); 6490 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
6314 errno = EEXIST; 6491 close(f);
6315 goto ecreate;
6316 }
6317 } else {
6318 errno = EEXIST;
6319 goto ecreate; 6492 goto ecreate;
6320 } 6493 }
6321 break; 6494 break;
6322 } 6495 }
6323 /* FALLTHROUGH */ 6496 /* FALLTHROUGH */
6324 case NCLOBBER: 6497 case NCLOBBER:
6325 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666); 6498 flags = O_WRONLY|O_CREAT|O_TRUNC;
6326 if (f < 0) 6499 goto do_open;
6327 goto ecreate;
6328 break;
6329 case NAPPEND: 6500 case NAPPEND:
6330 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 6501 flags = O_WRONLY|O_CREAT|O_APPEND;
6331 if (f < 0) 6502 goto do_open;
6332 goto ecreate; 6503/* Can't happen, our single caller does this itself */
6333#if ENABLE_PLATFORM_MINGW32 6504// case NTOFD:
6334 lseek(f, 0, SEEK_END); 6505// case NFROMFD:
6506// return -1;
6507 default:
6508#ifdef DEBUG
6509 abort();
6335#endif 6510#endif
6511 /* Fall through to eliminate warning. */
6512 case NHERE:
6513 case NXHERE:
6514 f = openhere(redir);
6336 break; 6515 break;
6337 } 6516 }
6338 6517
6339 return f; 6518 return f;
6340 ecreate: 6519 ecreate:
6341 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory")); 6520 sh_open_fail(fname, O_CREAT, EEXIST);
6342 eopen:
6343 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
6344} 6521}
6345 6522
6346/* 6523/*
@@ -6593,7 +6770,7 @@ redirect(union node *redir, int flags)
6593 return; 6770 return;
6594 6771
6595 sv = NULL; 6772 sv = NULL;
6596 INT_OFF; 6773 INTOFF;
6597 if (flags & REDIR_PUSH) 6774 if (flags & REDIR_PUSH)
6598 sv = redirlist; 6775 sv = redirlist;
6599 do { 6776 do {
@@ -6658,7 +6835,7 @@ redirect(union node *redir, int flags)
6658#endif 6835#endif
6659 } 6836 }
6660 } while ((redir = redir->nfile.next) != NULL); 6837 } while ((redir = redir->nfile.next) != NULL);
6661 INT_ON; 6838 INTON;
6662 6839
6663//dash:#define REDIR_SAVEFD2 03 /* set preverrout */ 6840//dash:#define REDIR_SAVEFD2 03 /* set preverrout */
6664#define REDIR_SAVEFD2 0 6841#define REDIR_SAVEFD2 0
@@ -6746,7 +6923,7 @@ popredir(int drop)
6746 6923
6747 if (redirlist == NULL) 6924 if (redirlist == NULL)
6748 return; 6925 return;
6749 INT_OFF; 6926 INTOFF;
6750 rp = redirlist; 6927 rp = redirlist;
6751 for (i = 0; i < rp->pair_count; i++) { 6928 for (i = 0; i < rp->pair_count; i++) {
6752 int fd = rp->two_fd[i].orig_fd; 6929 int fd = rp->two_fd[i].orig_fd;
@@ -6766,7 +6943,7 @@ popredir(int drop)
6766 } 6943 }
6767 redirlist = rp->next; 6944 redirlist = rp->next;
6768 free(rp); 6945 free(rp);
6769 INT_ON; 6946 INTON;
6770} 6947}
6771 6948
6772static void 6949static void
@@ -6793,11 +6970,11 @@ ash_arith(const char *s)
6793 math_state.setvar = setvar0; 6970 math_state.setvar = setvar0;
6794 //math_state.endofname = endofname; 6971 //math_state.endofname = endofname;
6795 6972
6796 INT_OFF; 6973 INTOFF;
6797 result = arith(&math_state, s); 6974 result = arith(&math_state, s);
6798 if (math_state.errmsg) 6975 if (math_state.errmsg)
6799 ash_msg_and_raise_error(math_state.errmsg); 6976 ash_msg_and_raise_error(math_state.errmsg);
6800 INT_ON; 6977 INTON;
6801 6978
6802 return result; 6979 return result;
6803} 6980}
@@ -7018,7 +7195,7 @@ ifsfree(void)
7018 if (!p) 7195 if (!p)
7019 goto out; 7196 goto out;
7020 7197
7021 INT_OFF; 7198 INTOFF;
7022 do { 7199 do {
7023 struct ifsregion *ifsp; 7200 struct ifsregion *ifsp;
7024 ifsp = p->next; 7201 ifsp = p->next;
@@ -7026,7 +7203,7 @@ ifsfree(void)
7026 p = ifsp; 7203 p = ifsp;
7027 } while (p); 7204 } while (p);
7028 ifsfirst.next = NULL; 7205 ifsfirst.next = NULL;
7029 INT_ON; 7206 INTON;
7030 out: 7207 out:
7031 ifslastp = NULL; 7208 ifslastp = NULL;
7032} 7209}
@@ -7241,11 +7418,11 @@ recordregion(int start, int end, int nulonly)
7241 if (ifslastp == NULL) { 7418 if (ifslastp == NULL) {
7242 ifsp = &ifsfirst; 7419 ifsp = &ifsfirst;
7243 } else { 7420 } else {
7244 INT_OFF; 7421 INTOFF;
7245 ifsp = ckzalloc(sizeof(*ifsp)); 7422 ifsp = ckzalloc(sizeof(*ifsp));
7246 /*ifsp->next = NULL; - ckzalloc did it */ 7423 /*ifsp->next = NULL; - ckzalloc did it */
7247 ifslastp->next = ifsp; 7424 ifslastp->next = ifsp;
7248 INT_ON; 7425 INTON;
7249 } 7426 }
7250 ifslastp = ifsp; 7427 ifslastp = ifsp;
7251 ifslastp->begoff = start; 7428 ifslastp->begoff = start;
@@ -7262,11 +7439,11 @@ removerecordregions(int endoff)
7262 if (ifsfirst.endoff > endoff) { 7439 if (ifsfirst.endoff > endoff) {
7263 while (ifsfirst.next) { 7440 while (ifsfirst.next) {
7264 struct ifsregion *ifsp; 7441 struct ifsregion *ifsp;
7265 INT_OFF; 7442 INTOFF;
7266 ifsp = ifsfirst.next->next; 7443 ifsp = ifsfirst.next->next;
7267 free(ifsfirst.next); 7444 free(ifsfirst.next);
7268 ifsfirst.next = ifsp; 7445 ifsfirst.next = ifsp;
7269 INT_ON; 7446 INTON;
7270 } 7447 }
7271 if (ifsfirst.begoff > endoff) { 7448 if (ifsfirst.begoff > endoff) {
7272 ifslastp = NULL; 7449 ifslastp = NULL;
@@ -7282,11 +7459,11 @@ removerecordregions(int endoff)
7282 ifslastp = ifslastp->next; 7459 ifslastp = ifslastp->next;
7283 while (ifslastp->next) { 7460 while (ifslastp->next) {
7284 struct ifsregion *ifsp; 7461 struct ifsregion *ifsp;
7285 INT_OFF; 7462 INTOFF;
7286 ifsp = ifslastp->next->next; 7463 ifsp = ifslastp->next->next;
7287 free(ifslastp->next); 7464 free(ifslastp->next);
7288 ifslastp->next = ifsp; 7465 ifslastp->next = ifsp;
7289 INT_ON; 7466 INTON;
7290 } 7467 }
7291 if (ifslastp->endoff > endoff) 7468 if (ifslastp->endoff > endoff)
7292 ifslastp->endoff = endoff; 7469 ifslastp->endoff = endoff;
@@ -7399,7 +7576,7 @@ evalbackcmd(union node *n, struct backcmd *result
7399 if (pipe(pip) < 0) 7576 if (pipe(pip) < 0)
7400 ash_msg_and_raise_perror("can't create pipe"); 7577 ash_msg_and_raise_perror("can't create pipe");
7401 /* process substitution uses NULL job, like openhere() */ 7578 /* process substitution uses NULL job, like openhere() */
7402 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; 7579 jp = (ctl == CTLBACKQ) ? makejob(1) : NULL;
7403#if ENABLE_PLATFORM_MINGW32 7580#if ENABLE_PLATFORM_MINGW32
7404 memset(&fs, 0, sizeof(fs)); 7581 memset(&fs, 0, sizeof(fs));
7405 fs.fpid = FS_EVALBACKCMD; 7582 fs.fpid = FS_EVALBACKCMD;
@@ -7411,7 +7588,8 @@ evalbackcmd(union node *n, struct backcmd *result
7411#else 7588#else
7412 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7589 if (forkshell(jp, n, FORK_NOJOB) == 0) {
7413 /* child */ 7590 /* child */
7414 FORCE_INT_ON; 7591 reset_exception_handler();
7592 FORCEINTON;
7415 close(pip[ip]); 7593 close(pip[ip]);
7416 /* ic is index of child end of pipe *and* fd to connect it to */ 7594 /* ic is index of child end of pipe *and* fd to connect it to */
7417 if (pip[ic] != ic) { 7595 if (pip[ic] != ic) {
@@ -7473,7 +7651,7 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
7473 if (flag & EXP_DISCARD) 7651 if (flag & EXP_DISCARD)
7474 goto out; 7652 goto out;
7475 7653
7476 INT_OFF; 7654 INTOFF;
7477 startloc = expdest - (char *)stackblock(); 7655 startloc = expdest - (char *)stackblock();
7478 pushstackmark(&smark, startloc); 7656 pushstackmark(&smark, startloc);
7479 evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl)); 7657 evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl));
@@ -7507,7 +7685,7 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
7507 back_exitstatus = waitforjob(in.jp); 7685 back_exitstatus = waitforjob(in.jp);
7508 } 7686 }
7509 done: 7687 done:
7510 INT_ON; 7688 INTON;
7511 7689
7512 /* Eat all trailing newlines */ 7690 /* Eat all trailing newlines */
7513 dest = expdest; 7691 dest = expdest;
@@ -8611,7 +8789,7 @@ expandmeta(struct strlist *str /*, int flag*/)
8611 if (!hasmeta(str->text)) 8789 if (!hasmeta(str->text))
8612 goto nometa; 8790 goto nometa;
8613 8791
8614 INT_OFF; 8792 INTOFF;
8615 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); 8793 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
8616// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match 8794// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
8617// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) 8795// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
@@ -8642,12 +8820,12 @@ expandmeta(struct strlist *str /*, int flag*/)
8642#endif 8820#endif
8643 addglob(&pglob); 8821 addglob(&pglob);
8644 globfree(&pglob); 8822 globfree(&pglob);
8645 INT_ON; 8823 INTON;
8646 break; 8824 break;
8647 case GLOB_NOMATCH: 8825 case GLOB_NOMATCH:
8648 //nometa2: 8826 //nometa2:
8649 globfree(&pglob); 8827 globfree(&pglob);
8650 INT_ON; 8828 INTON;
8651 nometa: 8829 nometa:
8652 *exparg.lastp = str; 8830 *exparg.lastp = str;
8653 rmescapes(str->text, 0, NULL); 8831 rmescapes(str->text, 0, NULL);
@@ -8655,7 +8833,7 @@ expandmeta(struct strlist *str /*, int flag*/)
8655 break; 8833 break;
8656 default: /* GLOB_NOSPACE */ 8834 default: /* GLOB_NOSPACE */
8657 globfree(&pglob); 8835 globfree(&pglob);
8658 INT_ON; 8836 INTON;
8659 ash_msg_and_raise_error(bb_msg_memory_exhausted); 8837 ash_msg_and_raise_error(bb_msg_memory_exhausted);
8660 } 8838 }
8661 str = str->next; 8839 str = str->next;
@@ -8915,7 +9093,7 @@ expandmeta(struct strlist *str /*, int flag*/)
8915 goto nometa; 9093 goto nometa;
8916 savelastp = exparg.lastp; 9094 savelastp = exparg.lastp;
8917 9095
8918 INT_OFF; 9096 INTOFF;
8919 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); 9097 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
8920 len = strlen(p); 9098 len = strlen(p);
8921 exp.dir_max = len + PATH_MAX; 9099 exp.dir_max = len + PATH_MAX;
@@ -8925,7 +9103,7 @@ expandmeta(struct strlist *str /*, int flag*/)
8925 free(exp.dir); 9103 free(exp.dir);
8926 if (p != str->text) 9104 if (p != str->text)
8927 free(p); 9105 free(p);
8928 INT_ON; 9106 INTON;
8929 if (exparg.lastp == savelastp) { 9107 if (exparg.lastp == savelastp) {
8930 /* 9108 /*
8931 * no matches 9109 * no matches
@@ -9128,7 +9306,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
9128 argv[0] = (char *)"Which"; 9306 argv[0] = (char *)"Which";
9129 } 9307 }
9130# else 9308# else
9131 if (APPLET_IS_NOEXEC(applet_no)) { 9309 if (!vforked && APPLET_IS_NOEXEC(applet_no)) {
9132# endif 9310# endif
9133#if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT) 9311#if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT)
9134 /* If building for UCRT move this up into shellexec() to 9312 /* If building for UCRT move this up into shellexec() to
@@ -9232,9 +9410,11 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
9232 * have to change the find_command routine as well. 9410 * have to change the find_command routine as well.
9233 * argv[-1] must exist and be writable! See tryexec() for why. 9411 * argv[-1] must exist and be writable! See tryexec() for why.
9234 */ 9412 */
9413#if ENABLE_PLATFORM_MINGW32
9235static struct builtincmd *find_builtin(const char *name); 9414static struct builtincmd *find_builtin(const char *name);
9236static void shellexec(char *prog, char **argv, const char *path, int idx, 9415static void shellexec(char *prog, char **argv, const char *path, int idx,
9237 int noexec) NORETURN; 9416 int noexec) NORETURN;
9417#endif
9238static void shellexec(char *prog, char **argv, const char *path, int idx, 9418static void shellexec(char *prog, char **argv, const char *path, int idx,
9239 int noexec) 9419 int noexec)
9240{ 9420{
@@ -9325,7 +9505,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx,
9325 exitstatus = exerrno; 9505 exitstatus = exerrno;
9326 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", 9506 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
9327 prog, e, suppress_int)); 9507 prog, e, suppress_int));
9328 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found")); 9508 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, E_EXEC));
9329 /* NOTREACHED */ 9509 /* NOTREACHED */
9330} 9510}
9331 9511
@@ -9358,7 +9538,7 @@ clearcmdentry(void)
9358 struct tblentry **pp; 9538 struct tblentry **pp;
9359 struct tblentry *cmdp; 9539 struct tblentry *cmdp;
9360 9540
9361 INT_OFF; 9541 INTOFF;
9362 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { 9542 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
9363 pp = tblp; 9543 pp = tblp;
9364 while ((cmdp = *pp) != NULL) { 9544 while ((cmdp = *pp) != NULL) {
@@ -9375,7 +9555,7 @@ clearcmdentry(void)
9375 } 9555 }
9376 } 9556 }
9377 } 9557 }
9378 INT_ON; 9558 INTON;
9379} 9559}
9380 9560
9381/* 9561/*
@@ -9429,13 +9609,13 @@ delete_cmd_entry(void)
9429{ 9609{
9430 struct tblentry *cmdp; 9610 struct tblentry *cmdp;
9431 9611
9432 INT_OFF; 9612 INTOFF;
9433 cmdp = *lastcmdentry; 9613 cmdp = *lastcmdentry;
9434 *lastcmdentry = cmdp->next; 9614 *lastcmdentry = cmdp->next;
9435 if (cmdp->cmdtype == CMDFUNCTION) 9615 if (cmdp->cmdtype == CMDFUNCTION)
9436 freefunc(cmdp->param.func); 9616 freefunc(cmdp->param.func);
9437 free(cmdp); 9617 free(cmdp);
9438 INT_ON; 9618 INTON;
9439} 9619}
9440 9620
9441/* 9621/*
@@ -9903,7 +10083,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9903} 10083}
9904#endif 10084#endif
9905 10085
9906
9907/*static int funcblocksize; // size of structures in function */ 10086/*static int funcblocksize; // size of structures in function */
9908/*static int funcstringsize; // size of strings in node */ 10087/*static int funcstringsize; // size of strings in node */
9909static void *funcblock; /* block to allocate function from */ 10088static void *funcblock; /* block to allocate function from */
@@ -10026,6 +10205,7 @@ calcsize(int funcblocksize, union node *n)
10026#endif 10205#endif
10027 case NCLOBBER: 10206 case NCLOBBER:
10028 case NFROM: 10207 case NFROM:
10208 case NFROMSTR:
10029 case NFROMTO: 10209 case NFROMTO:
10030 case NAPPEND: 10210 case NAPPEND:
10031 funcblocksize = calcsize(funcblocksize, n->nfile.fname); 10211 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
@@ -10242,6 +10422,7 @@ copynode(union node *n)
10242#endif 10422#endif
10243 case NCLOBBER: 10423 case NCLOBBER:
10244 case NFROM: 10424 case NFROM:
10425 case NFROMSTR:
10245 case NFROMTO: 10426 case NFROMTO:
10246 case NAPPEND: 10427 case NAPPEND:
10247 new->nfile.fname = copynode(n->nfile.fname); 10428 new->nfile.fname = copynode(n->nfile.fname);
@@ -10304,11 +10485,11 @@ defun(union node *func)
10304{ 10485{
10305 struct cmdentry entry; 10486 struct cmdentry entry;
10306 10487
10307 INT_OFF; 10488 INTOFF;
10308 entry.cmdtype = CMDFUNCTION; 10489 entry.cmdtype = CMDFUNCTION;
10309 entry.u.func = copyfunc(func); 10490 entry.u.func = copyfunc(func);
10310 addcmdentry(func->ndefun.text, &entry); 10491 addcmdentry(func->ndefun.text, &entry);
10311 INT_ON; 10492 INTON;
10312} 10493}
10313 10494
10314/* Reasons for skipping commands (see comment on breakcmd routine) */ 10495/* Reasons for skipping commands (see comment on breakcmd routine) */
@@ -10470,7 +10651,7 @@ evaltree(union node *n, int flags)
10470#endif 10651#endif
10471 case NNOT: 10652 case NNOT:
10472 status = !evaltree(n->nnot.com, EV_TESTED); 10653 status = !evaltree(n->nnot.com, EV_TESTED);
10473 goto setstatus; 10654 break;
10474 case NREDIR: 10655 case NREDIR:
10475 errlinno = lineno = n->nredir.linno; 10656 errlinno = lineno = n->nredir.linno;
10476 expredir(n->nredir.redirect); 10657 expredir(n->nredir.redirect);
@@ -10481,7 +10662,7 @@ evaltree(union node *n, int flags)
10481 } 10662 }
10482 if (n->nredir.redirect) 10663 if (n->nredir.redirect)
10483 popredir(/*drop:*/ 0); 10664 popredir(/*drop:*/ 0);
10484 goto setstatus; 10665 break;
10485 case NCMD: 10666 case NCMD:
10486 evalfn = evalcommand; 10667 evalfn = evalcommand;
10487 checkexit: 10668 checkexit:
@@ -10525,7 +10706,7 @@ evaltree(union node *n, int flags)
10525 evalfn = evaltree; 10706 evalfn = evaltree;
10526 calleval: 10707 calleval:
10527 status = evalfn(n, flags); 10708 status = evalfn(n, flags);
10528 goto setstatus; 10709 break;
10529 } 10710 }
10530 case NIF: 10711 case NIF:
10531 status = evaltree(n->nif.test, EV_TESTED); 10712 status = evaltree(n->nif.test, EV_TESTED);
@@ -10539,17 +10720,18 @@ evaltree(union node *n, int flags)
10539 goto evaln; 10720 goto evaln;
10540 } 10721 }
10541 status = 0; 10722 status = 0;
10542 goto setstatus; 10723 break;
10543 case NDEFUN: 10724 case NDEFUN:
10544 defun(n); 10725 defun(n);
10545 /* Not necessary. To test it: 10726 /* Not necessary. To test it:
10546 * "false; f() { qwerty; }; echo $?" should print 0. 10727 * "false; f() { qwerty; }; echo $?" should print 0.
10547 */ 10728 */
10548 /* status = 0; */ 10729 /* status = 0; */
10549 setstatus:
10550 exitstatus = status;
10551 break; 10730 break;
10552 } 10731 }
10732
10733 exitstatus = status;
10734
10553 out: 10735 out:
10554 /* Order of checks below is important: 10736 /* Order of checks below is important:
10555 * signal handlers trigger before exit caused by "set -e". 10737 * signal handlers trigger before exit caused by "set -e".
@@ -10718,6 +10900,7 @@ evalsubshell(union node *n, int flags)
10718 10900
10719#if ENABLE_PLATFORM_MINGW32 10901#if ENABLE_PLATFORM_MINGW32
10720 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) { 10902 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) {
10903 reset_exception_handler();
10721 expredir(n->nredir.redirect); 10904 expredir(n->nredir.redirect);
10722 redirect(n->nredir.redirect, 0); 10905 redirect(n->nredir.redirect, 0);
10723 evaltreenr(n->nredir.n, flags); 10906 evaltreenr(n->nredir.n, flags);
@@ -10728,10 +10911,10 @@ evalsubshell(union node *n, int flags)
10728 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10911 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
10729 goto nofork; 10912 goto nofork;
10730#endif 10913#endif
10731 INT_OFF; 10914 INTOFF;
10732 if (backgnd == FORK_FG) 10915 if (backgnd == FORK_FG)
10733 get_tty_state(); 10916 get_tty_state();
10734 jp = makejob(/*n,*/ 1); 10917 jp = makejob(1);
10735#if ENABLE_PLATFORM_MINGW32 10918#if ENABLE_PLATFORM_MINGW32
10736 memset(&fs, 0, sizeof(fs)); 10919 memset(&fs, 0, sizeof(fs));
10737 fs.fpid = FS_EVALSUBSHELL; 10920 fs.fpid = FS_EVALSUBSHELL;
@@ -10741,11 +10924,12 @@ evalsubshell(union node *n, int flags)
10741#else 10924#else
10742 if (forkshell(jp, n, backgnd) == 0) { 10925 if (forkshell(jp, n, backgnd) == 0) {
10743 /* child */ 10926 /* child */
10744 INT_ON; 10927 INTON;
10745 flags |= EV_EXIT; 10928 flags |= EV_EXIT;
10746 if (backgnd) 10929 if (backgnd)
10747 flags &= ~EV_TESTED; 10930 flags &= ~EV_TESTED;
10748 nofork: 10931 nofork:
10932 reset_exception_handler();
10749 redirect(n->nredir.redirect, 0); 10933 redirect(n->nredir.redirect, 0);
10750 evaltreenr(n->nredir.n, flags); 10934 evaltreenr(n->nredir.n, flags);
10751 /* never returns */ 10935 /* never returns */
@@ -10755,7 +10939,7 @@ evalsubshell(union node *n, int flags)
10755 status = 0; 10939 status = 0;
10756 if (backgnd == FORK_FG) 10940 if (backgnd == FORK_FG)
10757 status = waitforjob(jp); 10941 status = waitforjob(jp);
10758 INT_ON; 10942 INTON;
10759 return status; 10943 return status;
10760} 10944}
10761 10945
@@ -10776,6 +10960,7 @@ expredir(union node *n)
10776 switch (redir->type) { 10960 switch (redir->type) {
10777 case NFROMTO: 10961 case NFROMTO:
10778 case NFROM: 10962 case NFROM:
10963 case NFROMSTR:
10779 case NTO: 10964 case NTO:
10780#if BASH_REDIR_OUTPUT 10965#if BASH_REDIR_OUTPUT
10781 case NTO2: 10966 case NTO2:
@@ -10844,10 +11029,10 @@ evalpipe(union node *n, int flags)
10844 for (lp = n->npipe.cmdlist; lp; lp = lp->next) 11029 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
10845 pipelen++; 11030 pipelen++;
10846 flags |= EV_EXIT; 11031 flags |= EV_EXIT;
10847 INT_OFF; 11032 INTOFF;
10848 if (n->npipe.pipe_backgnd == 0) 11033 if (n->npipe.pipe_backgnd == 0)
10849 get_tty_state(); 11034 get_tty_state();
10850 jp = makejob(/*n,*/ pipelen); 11035 jp = makejob(pipelen);
10851 prevfd = -1; 11036 prevfd = -1;
10852 for (lp = n->npipe.cmdlist; lp; lp = lp->next) { 11037 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
10853 prehash(lp->n); 11038 prehash(lp->n);
@@ -10870,7 +11055,8 @@ evalpipe(union node *n, int flags)
10870#else 11055#else
10871 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 11056 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
10872 /* child */ 11057 /* child */
10873 INT_ON; 11058 reset_exception_handler();
11059 INTON;
10874 if (pip[1] >= 0) { 11060 if (pip[1] >= 0) {
10875 close(pip[0]); 11061 close(pip[0]);
10876 } 11062 }
@@ -10898,7 +11084,7 @@ evalpipe(union node *n, int flags)
10898 status = waitforjob(jp); 11084 status = waitforjob(jp);
10899 TRACE(("evalpipe: job done exit status %d\n", status)); 11085 TRACE(("evalpipe: job done exit status %d\n", status));
10900 } 11086 }
10901 INT_ON; 11087 INTON;
10902 11088
10903 return status; 11089 return status;
10904} 11090}
@@ -11002,7 +11188,7 @@ poplocalvars(int keep)
11002 int var_type; 11188 int var_type;
11003#endif 11189#endif
11004 11190
11005 INT_OFF; 11191 INTOFF;
11006 ll = localvar_stack; 11192 ll = localvar_stack;
11007 localvar_stack = ll->next; 11193 localvar_stack = ll->next;
11008 11194
@@ -11057,7 +11243,7 @@ poplocalvars(int keep)
11057 } 11243 }
11058 free(lvp); 11244 free(lvp);
11059 } 11245 }
11060 INT_ON; 11246 INTON;
11061} 11247}
11062 11248
11063/* 11249/*
@@ -11073,12 +11259,12 @@ pushlocalvars(int push)
11073 if (!push) 11259 if (!push)
11074 goto out; 11260 goto out;
11075 11261
11076 INT_OFF; 11262 INTOFF;
11077 ll = ckzalloc(sizeof(*ll)); 11263 ll = ckzalloc(sizeof(*ll));
11078 /*ll->lv = NULL; - zalloc did it */ 11264 /*ll->lv = NULL; - zalloc did it */
11079 ll->next = top; 11265 ll->next = top;
11080 localvar_stack = ll; 11266 localvar_stack = ll;
11081 INT_ON; 11267 INTON;
11082 out: 11268 out:
11083 return top; 11269 return top;
11084} 11270}
@@ -11115,13 +11301,13 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
11115 if (e) { 11301 if (e) {
11116 goto funcdone; 11302 goto funcdone;
11117 } 11303 }
11118 INT_OFF; 11304 INTOFF;
11119 exception_handler = &jmploc; 11305 exception_handler = &jmploc;
11120 shellparam.malloced = 0; 11306 shellparam.malloced = 0;
11121 func->count++; 11307 func->count++;
11122 funcname = func->n.ndefun.text; 11308 funcname = func->n.ndefun.text;
11123 funcline = func->n.ndefun.linno; 11309 funcline = func->n.ndefun.linno;
11124 INT_ON; 11310 INTON;
11125 shellparam.nparam = argc - 1; 11311 shellparam.nparam = argc - 1;
11126 shellparam.p = argv + 1; 11312 shellparam.p = argv + 1;
11127#if ENABLE_ASH_GETOPTS 11313#if ENABLE_ASH_GETOPTS
@@ -11130,7 +11316,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
11130#endif 11316#endif
11131 evaltree(func->n.ndefun.body, flags & EV_TESTED); 11317 evaltree(func->n.ndefun.body, flags & EV_TESTED);
11132 funcdone: 11318 funcdone:
11133 INT_OFF; 11319 INTOFF;
11134 funcname = savefuncname; 11320 funcname = savefuncname;
11135 if (savetrap) { 11321 if (savetrap) {
11136 if (!trap[NTRAP_ERR]) 11322 if (!trap[NTRAP_ERR])
@@ -11144,7 +11330,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
11144 freeparam(&shellparam); 11330 freeparam(&shellparam);
11145 shellparam = saveparam; 11331 shellparam = saveparam;
11146 exception_handler = savehandler; 11332 exception_handler = savehandler;
11147 INT_ON; 11333 INTON;
11148 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF); 11334 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
11149 return e; 11335 return e;
11150} 11336}
@@ -11163,7 +11349,7 @@ mklocal(char *name, int flags)
11163 struct var *vp; 11349 struct var *vp;
11164 char *eq = strchr(name, '='); 11350 char *eq = strchr(name, '=');
11165 11351
11166 INT_OFF; 11352 INTOFF;
11167 /* Cater for duplicate "local". Examples: 11353 /* Cater for duplicate "local". Examples:
11168 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x 11354 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
11169 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x 11355 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
@@ -11214,7 +11400,7 @@ mklocal(char *name, int flags)
11214 lvp->next = localvar_stack->lv; 11400 lvp->next = localvar_stack->lv;
11215 localvar_stack->lv = lvp; 11401 localvar_stack->lv = lvp;
11216 ret: 11402 ret:
11217 INT_ON; 11403 INTON;
11218} 11404}
11219 11405
11220/* 11406/*
@@ -11772,7 +11958,7 @@ evalcommand(union node *cmd, int flags)
11772 char *sv_argv0; 11958 char *sv_argv0;
11773#endif 11959#endif
11774 11960
11775 INT_OFF; 11961 INTOFF;
11776 sv_environ = environ; 11962 sv_environ = environ;
11777 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL); 11963 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
11778 /* 11964 /*
@@ -11802,10 +11988,11 @@ evalcommand(union node *cmd, int flags)
11802 * our signals to SA_RESTART? 11988 * our signals to SA_RESTART?
11803 */ 11989 */
11804 /*clearerr(stdout);*/ 11990 /*clearerr(stdout);*/
11805 INT_ON; 11991 INTON;
11806 break; 11992 break;
11807 } 11993 }
11808#endif 11994#endif
11995 /* Fork off a child process if necessary. */
11809 /* Can we avoid forking? For example, very last command 11996 /* Can we avoid forking? For example, very last command
11810 * in a script or a subshell does not need forking, 11997 * in a script or a subshell does not need forking,
11811 * we can just exec it. 11998 * we can just exec it.
@@ -11815,7 +12002,7 @@ evalcommand(union node *cmd, int flags)
11815 /* No, forking off a child is necessary */ 12002 /* No, forking off a child is necessary */
11816 struct forkshell fs; 12003 struct forkshell fs;
11817 12004
11818 INT_OFF; 12005 INTOFF;
11819 memset(&fs, 0, sizeof(fs)); 12006 memset(&fs, 0, sizeof(fs));
11820 fs.fpid = FS_SHELLEXEC; 12007 fs.fpid = FS_SHELLEXEC;
11821 fs.argv = argv; 12008 fs.argv = argv;
@@ -11828,16 +12015,10 @@ evalcommand(union node *cmd, int flags)
11828#else 12015#else
11829 if (!(flags & EV_EXIT) || may_have_traps) { 12016 if (!(flags & EV_EXIT) || may_have_traps) {
11830 /* No, forking off a child is necessary */ 12017 /* No, forking off a child is necessary */
11831 INT_OFF; 12018 INTOFF;
11832 get_tty_state(); 12019 get_tty_state();
11833 jp = makejob(/*cmd,*/ 1); 12020 jp = vforkexec(cmd, argv, path, cmdentry.u.index);
11834 if (forkshell(jp, cmd, FORK_FG) != 0) { 12021 break;
11835 /* parent */
11836 break;
11837 }
11838 /* child */
11839 FORCE_INT_ON;
11840 /* fall through to exec'ing external program */
11841 } 12022 }
11842#endif 12023#endif
11843 shellexec(argv[0], argv, path, cmdentry.u.index, FALSE); 12024 shellexec(argv[0], argv, path, cmdentry.u.index, FALSE);
@@ -11861,7 +12042,7 @@ evalcommand(union node *cmd, int flags)
11861 status = waitforjob(jp); 12042 status = waitforjob(jp);
11862 if (jp) 12043 if (jp)
11863 TRACE(("forked child exited with %d\n", status)); 12044 TRACE(("forked child exited with %d\n", status));
11864 FORCE_INT_ON; 12045 FORCEINTON;
11865 12046
11866 out: 12047 out:
11867 if (cmd->ncmd.redirect) 12048 if (cmd->ncmd.redirect)
@@ -11919,7 +12100,6 @@ goodname(const char *p)
11919 return endofname(p)[0] == '\0'; 12100 return endofname(p)[0] == '\0';
11920} 12101}
11921 12102
11922
11923/* 12103/*
11924 * Search for a command. This is called before we fork so that the 12104 * Search for a command. This is called before we fork so that the
11925 * location of the command will be available in the parent as well as 12105 * location of the command will be available in the parent as well as
@@ -11999,7 +12179,7 @@ pushstring(char *s, struct alias *ap)
11999 int len; 12179 int len;
12000 12180
12001 len = strlen(s); 12181 len = strlen(s);
12002 INT_OFF; 12182 INTOFF;
12003 if (g_parsefile->strpush || g_parsefile->spfree) { 12183 if (g_parsefile->strpush || g_parsefile->spfree) {
12004 sp = ckzalloc(sizeof(*sp)); 12184 sp = ckzalloc(sizeof(*sp));
12005 sp->prev = g_parsefile->strpush; 12185 sp->prev = g_parsefile->strpush;
@@ -12023,14 +12203,14 @@ pushstring(char *s, struct alias *ap)
12023 g_parsefile->left_in_line = len; 12203 g_parsefile->left_in_line = len;
12024 g_parsefile->unget = 0; 12204 g_parsefile->unget = 0;
12025 g_parsefile->spfree = NULL; 12205 g_parsefile->spfree = NULL;
12026 INT_ON; 12206 INTON;
12027} 12207}
12028 12208
12029static void popstring(void) 12209static void popstring(void)
12030{ 12210{
12031 struct strpush *sp = g_parsefile->strpush; 12211 struct strpush *sp = g_parsefile->strpush;
12032 12212
12033 INT_OFF; 12213 INTOFF;
12034#if ENABLE_ASH_ALIAS 12214#if ENABLE_ASH_ALIAS
12035 if (sp->ap) { 12215 if (sp->ap) {
12036 if (g_parsefile->next_to_pgetc[-1] == ' ' 12216 if (g_parsefile->next_to_pgetc[-1] == ' '
@@ -12049,7 +12229,7 @@ static void popstring(void)
12049 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); 12229 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
12050 g_parsefile->strpush = sp->prev; 12230 g_parsefile->strpush = sp->prev;
12051 g_parsefile->spfree = sp; 12231 g_parsefile->spfree = sp;
12052 INT_ON; 12232 INTON;
12053} 12233}
12054 12234
12055#if ENABLE_PLATFORM_MINGW32 12235#if ENABLE_PLATFORM_MINGW32
@@ -12146,11 +12326,11 @@ preadfd(void)
12146 * #(bash 5.0.17 exits after first "T", looks like a bug) 12326 * #(bash 5.0.17 exits after first "T", looks like a bug)
12147 */ 12327 */
12148 bb_got_signal = 0; 12328 bb_got_signal = 0;
12149 INT_OFF; /* no longjmp'ing out of read_line_input please */ 12329 INTOFF; /* no longjmp'ing out of read_line_input please */
12150 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ); 12330 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
12151 if (bb_got_signal == SIGINT) 12331 if (bb_got_signal == SIGINT)
12152 write(STDOUT_FILENO, "^C\n", 3); 12332 write(STDOUT_FILENO, "^C\n", 3);
12153 INT_ON; /* here non-blocked SIGINT will longjmp */ 12333 INTON; /* here non-blocked SIGINT will longjmp */
12154 if (nr == 0) { 12334 if (nr == 0) {
12155 /* ^C pressed, "convert" to SIGINT */ 12335 /* ^C pressed, "convert" to SIGINT */
12156# if !ENABLE_PLATFORM_MINGW32 12336# if !ENABLE_PLATFORM_MINGW32
@@ -12316,7 +12496,7 @@ nlnoprompt(void)
12316 12496
12317static void freestrings(struct strpush *sp) 12497static void freestrings(struct strpush *sp)
12318{ 12498{
12319 INT_OFF; 12499 INTOFF;
12320 do { 12500 do {
12321 struct strpush *psp; 12501 struct strpush *psp;
12322#if ENABLE_ASH_ALIAS 12502#if ENABLE_ASH_ALIAS
@@ -12335,7 +12515,7 @@ static void freestrings(struct strpush *sp)
12335 } while (sp); 12515 } while (sp);
12336 12516
12337 g_parsefile->spfree = NULL; 12517 g_parsefile->spfree = NULL;
12338 INT_ON; 12518 INTON;
12339} 12519}
12340 12520
12341static int __pgetc(void) 12521static int __pgetc(void)
@@ -12468,7 +12648,7 @@ popfile(void)
12468 if (pf == &basepf) 12648 if (pf == &basepf)
12469 return; 12649 return;
12470 12650
12471 INT_OFF; 12651 INTOFF;
12472 if (pf->pf_fd >= 0) 12652 if (pf->pf_fd >= 0)
12473 close(pf->pf_fd); 12653 close(pf->pf_fd);
12474 free(pf->buf); 12654 free(pf->buf);
@@ -12480,7 +12660,7 @@ popfile(void)
12480 } 12660 }
12481 g_parsefile = pf->prev; 12661 g_parsefile = pf->prev;
12482 free(pf); 12662 free(pf);
12483 INT_ON; 12663 INTON;
12484} 12664}
12485 12665
12486static void 12666static void
@@ -12543,14 +12723,10 @@ setinputfile(const char *fname, int flags)
12543{ 12723{
12544 int fd; 12724 int fd;
12545 12725
12546 INT_OFF; 12726 INTOFF;
12547 fd = open(fname, O_RDONLY | O_CLOEXEC); 12727 fd = sh_open(fname, O_RDONLY, flags & INPUT_NOFILE_OK);
12548 if (fd < 0) { 12728 if (fd < 0)
12549 if (flags & INPUT_NOFILE_OK) 12729 goto out;
12550 goto out;
12551 exitstatus = 127;
12552 ash_msg_and_raise_perror("can't open '%s'", fname);
12553 }
12554 if (fd < 10) 12730 if (fd < 10)
12555 fd = savefd(fd); 12731 fd = savefd(fd);
12556 else if (O_CLOEXEC == 0) /* old libc */ 12732 else if (O_CLOEXEC == 0) /* old libc */
@@ -12558,7 +12734,7 @@ setinputfile(const char *fname, int flags)
12558 12734
12559 setinputfd(fd, flags & INPUT_PUSH_FILE); 12735 setinputfd(fd, flags & INPUT_PUSH_FILE);
12560 out: 12736 out:
12561 INT_ON; 12737 INTON;
12562 return fd; 12738 return fd;
12563} 12739}
12564 12740
@@ -12568,13 +12744,13 @@ setinputfile(const char *fname, int flags)
12568static void 12744static void
12569setinputstring(char *string) 12745setinputstring(char *string)
12570{ 12746{
12571 INT_OFF; 12747 INTOFF;
12572 pushfile(); 12748 pushfile();
12573 g_parsefile->next_to_pgetc = string; 12749 g_parsefile->next_to_pgetc = string;
12574 g_parsefile->left_in_line = strlen(string); 12750 g_parsefile->left_in_line = strlen(string);
12575 g_parsefile->buf = NULL; 12751 g_parsefile->buf = NULL;
12576 g_parsefile->linno = lineno; 12752 g_parsefile->linno = lineno;
12577 INT_ON; 12753 INTON;
12578} 12754}
12579 12755
12580 12756
@@ -12870,7 +13046,7 @@ shiftcmd(int argc UNUSED_PARAM, char **argv)
12870 n = number(argv[1]); 13046 n = number(argv[1]);
12871 if (n > shellparam.nparam) 13047 if (n > shellparam.nparam)
12872 return 1; 13048 return 1;
12873 INT_OFF; 13049 INTOFF;
12874 shellparam.nparam -= n; 13050 shellparam.nparam -= n;
12875 for (ap1 = shellparam.p; --n >= 0; ap1++) { 13051 for (ap1 = shellparam.p; --n >= 0; ap1++) {
12876 if (shellparam.malloced) 13052 if (shellparam.malloced)
@@ -12883,7 +13059,7 @@ shiftcmd(int argc UNUSED_PARAM, char **argv)
12883 shellparam.optind = 1; 13059 shellparam.optind = 1;
12884 shellparam.optoff = -1; 13060 shellparam.optoff = -1;
12885#endif 13061#endif
12886 INT_ON; 13062 INTON;
12887 return 0; 13063 return 0;
12888} 13064}
12889 13065
@@ -12937,7 +13113,7 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12937 if (!argv[1]) 13113 if (!argv[1])
12938 return showvars(nullstr, 0, VUNSET); 13114 return showvars(nullstr, 0, VUNSET);
12939 13115
12940 INT_OFF; 13116 INTOFF;
12941 retval = options(/*login_sh:*/ NULL); 13117 retval = options(/*login_sh:*/ NULL);
12942 if (retval == 0) { /* if no parse error... */ 13118 if (retval == 0) { /* if no parse error... */
12943 optschanged(); 13119 optschanged();
@@ -12945,7 +13121,7 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12945 setparam(argptr); 13121 setparam(argptr);
12946 } 13122 }
12947 } 13123 }
12948 INT_ON; 13124 INTON;
12949 return retval; 13125 return retval;
12950} 13126}
12951 13127
@@ -13355,7 +13531,7 @@ fixredir(union node *n, const char *text, int err)
13355 * silently truncate results to word width. 13531 * silently truncate results to word width.
13356 */ 13532 */
13357 if (err) 13533 if (err)
13358 raise_error_syntax("bad fd number"); 13534 ash_msg_and_raise_error("bad fd number");
13359 n->ndup.vname = makename(); 13535 n->ndup.vname = makename();
13360 } 13536 }
13361} 13537}
@@ -13788,12 +13964,12 @@ decode_dollar_squote(void)
13788#endif 13964#endif
13789 13965
13790/* Used by expandstr to get here-doc like behaviour. */ 13966/* Used by expandstr to get here-doc like behaviour. */
13791#define FAKEEOFMARK ((char*)(uintptr_t)1) 13967#define FAKEEOFMARK ((struct heredoc*)(uintptr_t)1)
13792 13968
13793static ALWAYS_INLINE int 13969static ALWAYS_INLINE int
13794realeofmark(const char *eofmark) 13970realeofmark(struct heredoc *here)
13795{ 13971{
13796 return eofmark && eofmark != FAKEEOFMARK; 13972 return here && here != FAKEEOFMARK;
13797} 13973}
13798 13974
13799/* 13975/*
@@ -13815,7 +13991,7 @@ realeofmark(const char *eofmark)
13815#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} 13991#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;}
13816#define PARSEARITH() {goto parsearith; parsearith_return:;} 13992#define PARSEARITH() {goto parsearith; parsearith_return:;}
13817static int 13993static int
13818readtoken1(int c, int syntax, char *eofmark, int striptabs) 13994readtoken1(int c, int syntax, struct heredoc *eofmark)
13819{ 13995{
13820 /* NB: syntax parameter fits into smallint */ 13996 /* NB: syntax parameter fits into smallint */
13821 /* c parameter is an unsigned char or PEOF */ 13997 /* c parameter is an unsigned char or PEOF */
@@ -14067,23 +14243,30 @@ checkend: {
14067 int markloc; 14243 int markloc;
14068 char *p; 14244 char *p;
14069 14245
14070 if (striptabs) { 14246 if (eofmark->striptabs) {
14071 while (c == '\t') 14247 while (c == '\t')
14072 c = pgetc(); 14248 if (eofmark->here->type == NHERE)
14249 c = pgetc(); /* dash always does pgetc() */
14250 else /* NXHERE */
14251 c = pgetc_eatbnl();
14252 /* (see heredoc_bkslash_newline3a.tests) */
14073 } 14253 }
14074 14254
14075 markloc = out - (char *)stackblock(); 14255 markloc = out - (char *)stackblock();
14076 for (p = eofmark; STPUTC(c, out), *p; p++) { 14256 for (p = eofmark->eofmark; STPUTC(c, out), *p; p++) {
14077 if (c != *p) 14257 if (c != *p)
14078 goto more_heredoc; 14258 goto more_heredoc;
14079 /* FIXME: fails for backslash-newlined terminator: 14259 /* dash still has this not fixed (as of 2025-08)
14080 * cat <<EOF 14260 * cat <<EOF
14081 * ... 14261 * ...
14082 * EO\ 14262 * EO\
14083 * F 14263 * F
14084 * (see heredoc_bkslash_newline2.tests) 14264 * (see heredoc_bkslash_newline2.tests)
14085 */ 14265 */
14086 c = pgetc(); 14266 if (eofmark->here->type == NHERE)
14267 c = pgetc(); /* dash always does pgetc() */
14268 else /* NXHERE */
14269 c = pgetc_eatbnl();
14087 } 14270 }
14088 14271
14089 if (c == '\n' || c == PEOF) { 14272 if (c == '\n' || c == PEOF) {
@@ -14093,7 +14276,6 @@ checkend: {
14093 needprompt = doprompt; 14276 needprompt = doprompt;
14094 } else { 14277 } else {
14095 int len_here; 14278 int len_here;
14096
14097 more_heredoc: 14279 more_heredoc:
14098 p = (char *)stackblock() + markloc + 1; 14280 p = (char *)stackblock() + markloc + 1;
14099 len_here = out - p; 14281 len_here = out - p;
@@ -14156,6 +14338,11 @@ parseredir: {
14156 c = pgetc_eatbnl(); 14338 c = pgetc_eatbnl();
14157 switch (c) { 14339 switch (c) {
14158 case '<': 14340 case '<':
14341 c = pgetc_eatbnl();
14342 if (c == '<') {
14343 np->type = NFROMSTR;
14344 break;
14345 }
14159 if (sizeof(struct nfile) != sizeof(struct nhere)) { 14346 if (sizeof(struct nfile) != sizeof(struct nhere)) {
14160 np = stzalloc(sizeof(struct nhere)); 14347 np = stzalloc(sizeof(struct nhere));
14161 /*np->nfile.fd = 0; - stzalloc did it */ 14348 /*np->nfile.fd = 0; - stzalloc did it */
@@ -14163,7 +14350,6 @@ parseredir: {
14163 np->type = NHERE; 14350 np->type = NHERE;
14164 heredoc = stzalloc(sizeof(struct heredoc)); 14351 heredoc = stzalloc(sizeof(struct heredoc));
14165 heredoc->here = np; 14352 heredoc->here = np;
14166 c = pgetc_eatbnl();
14167 if (c == '-') { 14353 if (c == '-') {
14168 heredoc->striptabs = 1; 14354 heredoc->striptabs = 1;
14169 } else { 14355 } else {
@@ -14602,7 +14788,7 @@ xxreadtoken(void)
14602 } 14788 }
14603 } /* for (;;) */ 14789 } /* for (;;) */
14604 14790
14605 return readtoken1(c, BASESYNTAX, (char *) NULL, 0); 14791 return readtoken1(c, BASESYNTAX, NULL);
14606} 14792}
14607#else /* old xxreadtoken */ 14793#else /* old xxreadtoken */
14608#define RETURN(token) return lasttoken = token 14794#define RETURN(token) return lasttoken = token
@@ -14653,7 +14839,7 @@ xxreadtoken(void)
14653 } 14839 }
14654 break; 14840 break;
14655 } 14841 }
14656 return readtoken1(c, BASESYNTAX, (char *)NULL, 0); 14842 return readtoken1(c, BASESYNTAX, NULL);
14657#undef RETURN 14843#undef RETURN
14658} 14844}
14659#endif /* old xxreadtoken */ 14845#endif /* old xxreadtoken */
@@ -14759,9 +14945,9 @@ parseheredoc(void)
14759 tokpushback = 0; 14945 tokpushback = 0;
14760 setprompt_if(needprompt, 2); 14946 setprompt_if(needprompt, 2);
14761 if (here->here->type == NHERE) 14947 if (here->here->type == NHERE)
14762 readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs); 14948 readtoken1(pgetc(), SQSYNTAX, here);
14763 else 14949 else
14764 readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs); 14950 readtoken1(pgetc_eatbnl(), DQSYNTAX, here);
14765 n = stzalloc(sizeof(struct narg)); 14951 n = stzalloc(sizeof(struct narg));
14766 n->narg.type = NARG; 14952 n->narg.type = NARG;
14767 /*n->narg.next = NULL; - stzalloc did it */ 14953 /*n->narg.next = NULL; - stzalloc did it */
@@ -14772,7 +14958,6 @@ parseheredoc(void)
14772 } 14958 }
14773} 14959}
14774 14960
14775
14776static const char * 14961static const char *
14777expandstr(const char *ps, int syntax_type) 14962expandstr(const char *ps, int syntax_type)
14778{ 14963{
@@ -14805,8 +14990,7 @@ expandstr(const char *ps, int syntax_type)
14805 * PS1='$(date "+%H:%M:%S) > ' 14990 * PS1='$(date "+%H:%M:%S) > '
14806 */ 14991 */
14807 exception_handler = &jmploc; 14992 exception_handler = &jmploc;
14808 readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0); 14993 readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK);
14809
14810 n.narg.type = NARG; 14994 n.narg.type = NARG;
14811 n.narg.next = NULL; 14995 n.narg.next = NULL;
14812 n.narg.text = wordtext; 14996 n.narg.text = wordtext;
@@ -14943,7 +15127,7 @@ cmdloop(int top)
14943 15127
14944 setstackmark(&smark); 15128 setstackmark(&smark);
14945#if JOBS || JOBS_WIN32 15129#if JOBS || JOBS_WIN32
14946 if (doing_jobctl) 15130 if (jobctl)
14947 showjobs(SHOW_CHANGED|SHOW_STDERR); 15131 showjobs(SHOW_CHANGED|SHOW_STDERR);
14948#endif 15132#endif
14949 inter = 0; 15133 inter = 0;
@@ -15355,11 +15539,11 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15355 entry->u.index = idx; 15539 entry->u.index = idx;
15356 return; 15540 return;
15357 } 15541 }
15358 INT_OFF; 15542 INTOFF;
15359 cmdp = cmdlookup(name, 1); 15543 cmdp = cmdlookup(name, 1);
15360 cmdp->cmdtype = CMDNORMAL; 15544 cmdp->cmdtype = CMDNORMAL;
15361 cmdp->param.index = idx; 15545 cmdp->param.index = idx;
15362 INT_ON; 15546 INTON;
15363 goto success; 15547 goto success;
15364 } 15548 }
15365 15549
@@ -15379,7 +15563,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15379 return; 15563 return;
15380 } 15564 }
15381#endif 15565#endif
15382 ash_msg("%s: %s", name, errmsg(e, "not found")); 15566 ash_msg("%s: %s", name, errmsg(e, E_EXEC));
15383 } 15567 }
15384 fail: 15568 fail:
15385 entry->cmdtype = CMDUNKNOWN; 15569 entry->cmdtype = CMDUNKNOWN;
@@ -15391,18 +15575,17 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15391 entry->u.cmd = bcmd; 15575 entry->u.cmd = bcmd;
15392 return; 15576 return;
15393 } 15577 }
15394 INT_OFF; 15578 INTOFF;
15395 cmdp = cmdlookup(name, 1); 15579 cmdp = cmdlookup(name, 1);
15396 cmdp->cmdtype = CMDBUILTIN; 15580 cmdp->cmdtype = CMDBUILTIN;
15397 cmdp->param.cmd = bcmd; 15581 cmdp->param.cmd = bcmd;
15398 INT_ON; 15582 INTON;
15399 success: 15583 success:
15400 cmdp->rehash = 0; 15584 cmdp->rehash = 0;
15401 entry->cmdtype = cmdp->cmdtype; 15585 entry->cmdtype = cmdp->cmdtype;
15402 entry->u = cmdp->param; 15586 entry->u = cmdp->param;
15403} 15587}
15404 15588
15405
15406/* 15589/*
15407 * The trap builtin. 15590 * The trap builtin.
15408 */ 15591 */
@@ -15458,7 +15641,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15458 exitcode = 1; 15641 exitcode = 1;
15459 goto next; 15642 goto next;
15460 } 15643 }
15461 INT_OFF; 15644 INTOFF;
15462 if (action) { 15645 if (action) {
15463 if (LONE_DASH(action)) 15646 if (LONE_DASH(action))
15464 action = NULL; 15647 action = NULL;
@@ -15495,7 +15678,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15495 if (signo != 0 && signo < NSIG) 15678 if (signo != 0 && signo < NSIG)
15496 setsignal(signo); 15679 setsignal(signo);
15497#endif 15680#endif
15498 INT_ON; 15681 INTON;
15499 next: 15682 next:
15500 ap++; 15683 ap++;
15501 } 15684 }
@@ -15808,10 +15991,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15808 * to jump out of it. 15991 * to jump out of it.
15809 */ 15992 */
15810 again: 15993 again:
15811 INT_OFF; 15994 INTOFF;
15812 r = shell_builtin_read(&params); 15995 r = shell_builtin_read(&params);
15813 INT_ON; 15996 INTON;
15814 15997
15998#if !ENABLE_PLATFORM_MINGW32
15815 if ((uintptr_t)r == 1 && errno == EINTR) { 15999 if ((uintptr_t)r == 1 && errno == EINTR) {
15816 /* To get SIGCHLD: sleep 1 & read x; echo $x 16000 /* To get SIGCHLD: sleep 1 & read x; echo $x
15817 * Correct behavior is to not exit "read" 16001 * Correct behavior is to not exit "read"
@@ -15820,8 +16004,15 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15820 goto again; 16004 goto again;
15821 } 16005 }
15822 16006
15823#if ENABLE_PLATFORM_MINGW32 16007 if ((uintptr_t)r == 2) /* -t SEC timeout? */
16008 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
16009 /* The actual value observed with bash 5.2.15: */
16010 return 128 + SIGALRM;
16011#else /* ENABLE_PLATFORM_MINGW32 */
15824 if ((uintptr_t)r == 2) { 16012 if ((uintptr_t)r == 2) {
16013 /* Timeout, return 128 + SIGALRM */
16014 return 142;
16015 } else if ((uintptr_t)r == 3) {
15825 /* ^C pressed, propagate event */ 16016 /* ^C pressed, propagate event */
15826 if (trap[SIGINT]) { 16017 if (trap[SIGINT]) {
15827 write(STDOUT_FILENO, "^C", 2); 16018 write(STDOUT_FILENO, "^C", 2);
@@ -15859,10 +16050,10 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15859 symbolic_mode = 1; 16050 symbolic_mode = 1;
15860 } 16051 }
15861 16052
15862 INT_OFF; 16053 INTOFF;
15863 mask = umask(0); 16054 mask = umask(0);
15864 umask(mask); 16055 umask(mask);
15865 INT_ON; 16056 INTON;
15866 16057
15867 if (*argptr == NULL) { 16058 if (*argptr == NULL) {
15868 if (symbolic_mode) { 16059 if (symbolic_mode) {
@@ -15969,8 +16160,25 @@ exitshell(void)
15969 char *p; 16160 char *p;
15970 16161
15971#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 16162#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
15972 save_history(line_input_state); /* may be NULL */ 16163 if (line_input_state) {
16164 const char *hp;
16165# if ENABLE_FEATURE_SH_HISTFILESIZE
16166// in bash:
16167// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
16168// "When this variable is assigned a value, the history file is truncated, if necessary"
16169// but we do it only at exit, not on assignment:
16170 /* Use HISTFILESIZE to limit file size */
16171 hp = lookupvar("HISTFILESIZE");
16172 if (hp)
16173 line_input_state->max_history = size_from_HISTFILESIZE(hp);
16174# endif
16175 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
16176 hp = lookupvar("HISTFILE");
16177 line_input_state->hist_file = hp;
16178 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
16179 }
15973#endif 16180#endif
16181
15974 savestatus = exitstatus; 16182 savestatus = exitstatus;
15975 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 16183 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
15976 if (setjmp(loc.loc)) 16184 if (setjmp(loc.loc))
@@ -16020,11 +16228,13 @@ init(void)
16020{ 16228{
16021#if ENABLE_PLATFORM_MINGW32 16229#if ENABLE_PLATFORM_MINGW32
16022 int import = 0; 16230 int import = 0;
16023#else 16231#endif
16232
16024 /* we will never free this */ 16233 /* we will never free this */
16025 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); 16234 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
16026 basepf.linno = 1; 16235 basepf.linno = 1;
16027 16236
16237#if !ENABLE_PLATFORM_MINGW32
16028 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 16238 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
16029 setsignal(SIGCHLD); 16239 setsignal(SIGCHLD);
16030#endif 16240#endif
@@ -16144,7 +16354,6 @@ init(void)
16144 } 16354 }
16145} 16355}
16146 16356
16147
16148//usage:#define ash_trivial_usage 16357//usage:#define ash_trivial_usage
16149//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16358//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
16150//////// comes from ^^^^^^^^^^optletters 16359//////// comes from ^^^^^^^^^^optletters
@@ -16166,7 +16375,7 @@ procargs(char **argv)
16166#if ENABLE_PLATFORM_MINGW32 16375#if ENABLE_PLATFORM_MINGW32
16167 login_sh = applet_name[0] == 'l'; 16376 login_sh = applet_name[0] == 'l';
16168#else 16377#else
16169 login_sh = xargv[0] && xargv[0][0] == '-'; 16378 login_sh = /*xargv[0] &&*/ xargv[0][0] == '-';
16170#endif 16379#endif
16171#if NUM_SCRIPTS > 0 16380#if NUM_SCRIPTS > 0
16172 if (minusc) 16381 if (minusc)
@@ -16218,7 +16427,6 @@ procargs(char **argv)
16218#endif 16427#endif
16219 setarg0: 16428 setarg0:
16220 arg0 = *xargv++; 16429 arg0 = *xargv++;
16221 commandname = arg0;
16222 } 16430 }
16223 16431
16224 shellparam.p = xargv; 16432 shellparam.p = xargv;
@@ -16283,17 +16491,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16283/* note: 'argc' is used only if embedded scripts are enabled */ 16491/* note: 'argc' is used only if embedded scripts are enabled */
16284{ 16492{
16285 volatile smallint state; 16493 volatile smallint state;
16286 struct jmploc jmploc;
16287 struct stackmark smark; 16494 struct stackmark smark;
16288 int login_sh; 16495 int login_sh;
16289 16496
16290#if ENABLE_PLATFORM_MINGW32 16497#if ENABLE_PLATFORM_MINGW32
16291 INIT_G_memstack(); 16498 INIT_G_memstack();
16292 16499
16293 /* from init() */
16294 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
16295 basepf.linno = 1;
16296
16297 if (argc == 3 && !strcmp(argv[1], "--fs")) { 16500 if (argc == 3 && !strcmp(argv[1], "--fs")) {
16298 forkshell_init(argv[2]); 16501 forkshell_init(argv[2]);
16299 /* only reached in case of error */ 16502 /* only reached in case of error */
@@ -16317,7 +16520,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16317#endif 16520#endif
16318 16521
16319 state = 0; 16522 state = 0;
16320 if (setjmp(jmploc.loc)) { 16523 if (setjmp(main_handler.loc)) {
16321 smallint e; 16524 smallint e;
16322 smallint s; 16525 smallint s;
16323 16526
@@ -16336,7 +16539,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16336 } 16539 }
16337 16540
16338 popstackmark(&smark); 16541 popstackmark(&smark);
16339 FORCE_INT_ON; /* enable interrupts */ 16542 FORCEINTON; /* enable interrupts */
16340 if (s == 1) 16543 if (s == 1)
16341 goto state1; 16544 goto state1;
16342 if (s == 2) 16545 if (s == 2)
@@ -16345,7 +16548,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16345 goto state3; 16548 goto state3;
16346 goto state4; 16549 goto state4;
16347 } 16550 }
16348 exception_handler = &jmploc; 16551 exception_handler = &main_handler;
16349 rootpid = getpid(); 16552 rootpid = getpid();
16350 16553
16351 init(); 16554 init();
@@ -16463,18 +16666,23 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16463 if (!hp) { 16666 if (!hp) {
16464 hp = lookupvar("HOME"); 16667 hp = lookupvar("HOME");
16465 if (hp) { 16668 if (hp) {
16466 INT_OFF; 16669 INTOFF;
16467 hp = concat_path_file(hp, ".ash_history"); 16670 hp = concat_path_file(hp, ".ash_history");
16468 setvar0("HISTFILE", hp); 16671 setvar0("HISTFILE", hp);
16469 free((char*)hp); 16672 free((char*)hp);
16470 INT_ON; 16673 INTON;
16471 hp = lookupvar("HISTFILE"); 16674 hp = lookupvar("HISTFILE");
16472 } 16675 }
16473 } 16676 }
16474 if (hp) 16677 if (hp)
16475 line_input_state->hist_file = xstrdup(hp); 16678 line_input_state->hist_file = xstrdup(hp);
16476# if ENABLE_FEATURE_SH_HISTFILESIZE 16679# if ENABLE_FEATURE_SH_HISTFILESIZE
16477 hp = lookupvar("HISTFILESIZE"); 16680 hp = lookupvar("HISTSIZE");
16681 /* Using HISTFILESIZE above to limit max_history would be WRONG:
16682 * users may set HISTFILESIZE=0 in their profile scripts
16683 * to prevent _saving_ of history files, but still want to have
16684 * non-zero history limit for in-memory list.
16685 */
16478 line_input_state->max_history = size_from_HISTFILESIZE(hp); 16686 line_input_state->max_history = size_from_HISTFILESIZE(hp);
16479# endif 16687# endif
16480 } 16688 }
@@ -16537,7 +16745,7 @@ forkshell_evalbackcmd(struct forkshell *fs)
16537 pip[ip] = fs->fd[ip]; 16745 pip[ip] = fs->fd[ip];
16538 pip[ic] = fs->fd[ic]; 16746 pip[ic] = fs->fd[ic];
16539 16747
16540 FORCE_INT_ON; 16748 FORCEINTON;
16541 close(pip[ip]); 16749 close(pip[ip]);
16542 if (pip[ic] != ic) { 16750 if (pip[ic] != ic) {
16543 /*close(ic);*/ 16751 /*close(ic);*/
@@ -16557,7 +16765,7 @@ forkshell_evalsubshell(struct forkshell *fs)
16557 int flags = fs->flags; 16765 int flags = fs->flags;
16558 16766
16559 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); 16767 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16560 INT_ON; 16768 INTON;
16561 flags |= EV_EXIT; 16769 flags |= EV_EXIT;
16562 if (fs->mode) 16770 if (fs->mode)
16563 flags &= ~EV_TESTED; 16771 flags &= ~EV_TESTED;
@@ -16576,7 +16784,7 @@ forkshell_evalpipe(struct forkshell *fs)
16576 int pip[2] = {fs->fd[0], fs->fd[1]}; 16784 int pip[2] = {fs->fd[0], fs->fd[1]};
16577 16785
16578 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); 16786 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16579 INT_ON; 16787 INTON;
16580 if (pip[1] >= 0) { 16788 if (pip[1] >= 0) {
16581 close(pip[0]); 16789 close(pip[0]);
16582 } 16790 }
@@ -16596,9 +16804,9 @@ forkshell_shellexec(struct forkshell *fs)
16596{ 16804{
16597 int idx = fs->fd[0]; 16805 int idx = fs->fd[0];
16598 char **argv = fs->argv; 16806 char **argv = fs->argv;
16599 char *path = fs->path; 16807 const char *path = fs->path;
16600 16808
16601 FORCE_INT_ON; 16809 FORCEINTON;
16602 shellexec(argv[0], argv, path, idx, TRUE); 16810 shellexec(argv[0], argv, path, idx, TRUE);
16603} 16811}
16604 16812
@@ -17148,6 +17356,8 @@ globals_misc_size(struct datasize ds)
17148#undef physdir 17356#undef physdir
17149#undef arg0 17357#undef arg0
17150#undef commandname 17358#undef commandname
17359#undef g_parsefile
17360#undef basepf
17151#undef nullstr 17361#undef nullstr
17152#undef trap 17362#undef trap
17153static struct globals_misc * 17363static struct globals_misc *
@@ -17176,6 +17386,8 @@ globals_misc_copy(void)
17176 new->trap[i] = nodeckstrdup(p->trap[i]); 17386 new->trap[i] = nodeckstrdup(p->trap[i]);
17177 SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE); 17387 SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE);
17178 } 17388 }
17389 new->g_parsefile = NULL;
17390 memset(&new->basepf, 0, sizeof(struct parsefile));
17179 return new; 17391 return new;
17180} 17392}
17181 17393
@@ -17188,8 +17400,8 @@ forkshell_size(struct forkshell *fs)
17188 if (fs->fpid == FS_OPENHERE) 17400 if (fs->fpid == FS_OPENHERE)
17189 return ds; 17401 return ds;
17190 17402
17191 ds = globals_var_size(ds);
17192 ds = globals_misc_size(ds); 17403 ds = globals_misc_size(ds);
17404 ds = globals_var_size(ds);
17193 ds = cmdtable_size(ds); 17405 ds = cmdtable_size(ds);
17194 17406
17195 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n); 17407 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
@@ -17221,11 +17433,11 @@ forkshell_copy(struct forkshell *fs, struct forkshell *new)
17221 if (fs->fpid == FS_OPENHERE) 17433 if (fs->fpid == FS_OPENHERE)
17222 return; 17434 return;
17223 17435
17224 new->gvp = globals_var_copy();
17225 new->gmp = globals_misc_copy(); 17436 new->gmp = globals_misc_copy();
17437 new->gvp = globals_var_copy();
17226 new->cmdtable = cmdtable_copy(); 17438 new->cmdtable = cmdtable_copy();
17227 SAVE_PTR(new->gvp, "gvp", NO_FREE);
17228 SAVE_PTR(new->gmp, "gmp", NO_FREE); 17439 SAVE_PTR(new->gmp, "gmp", NO_FREE);
17440 SAVE_PTR(new->gvp, "gvp", NO_FREE);
17229 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE); 17441 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
17230 17442
17231 new->n = copynode(fs->n); 17443 new->n = copynode(fs->n);
@@ -17262,7 +17474,7 @@ forkshell_copy(struct forkshell *fs, struct forkshell *new)
17262 17474
17263#if FORKSHELL_DEBUG 17475#if FORKSHELL_DEBUG
17264#define NUM_BLOCKS FUNCSTRING 17476#define NUM_BLOCKS FUNCSTRING
17265enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING}; 17477enum {GMP, GVP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING};
17266 17478
17267/* fp0 and notes can each be NULL */ 17479/* fp0 and notes can each be NULL */
17268static void 17480static void
@@ -17327,8 +17539,8 @@ forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
17327 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB]; 17539 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
17328 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV]; 17540 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
17329 lptr[CMDTABLE] = (char *)fs->cmdtable; 17541 lptr[CMDTABLE] = (char *)fs->cmdtable;
17330 lptr[GMP] = (char *)fs->gmp;
17331 lptr[GVP] = (char *)fs->gvp; 17542 lptr[GVP] = (char *)fs->gvp;
17543 lptr[GMP] = (char *)fs->gmp;
17332 17544
17333 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize); 17545 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
17334 total = 0; 17546 total = 0;
@@ -17470,7 +17682,6 @@ forkshell_init(const char *idstr)
17470 int i; 17682 int i;
17471 char **ptr; 17683 char **ptr;
17472 char *lrelocate; 17684 char *lrelocate;
17473 struct jmploc jmploc;
17474 17685
17475 if (sscanf(idstr, "%p", &map_handle) != 1) 17686 if (sscanf(idstr, "%p", &map_handle) != 1)
17476 return; 17687 return;
@@ -17508,9 +17719,14 @@ forkshell_init(const char *idstr)
17508 } 17719 }
17509 fs->gmp->trap_ptr = fs->gmp->trap; 17720 fs->gmp->trap_ptr = fs->gmp->trap;
17510 17721
17722 /* from init() */
17723 fs->gmp->basepf.next_to_pgetc = fs->gmp->basepf.buf = ckzalloc(IBUFSIZ);
17724 fs->gmp->basepf.linno = 1;
17725 fs->gmp->g_parsefile = &fs->gmp->basepf;
17726
17511 /* Set global variables */ 17727 /* Set global variables */
17512 ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp);
17513 ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp); 17728 ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp);
17729 ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp);
17514 cmdtable = fs->cmdtable; 17730 cmdtable = fs->cmdtable;
17515#if ENABLE_ASH_ALIAS 17731#if ENABLE_ASH_ALIAS
17516 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */ 17732 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
@@ -17533,11 +17749,11 @@ forkshell_init(const char *idstr)
17533 17749
17534 reinitvar(); 17750 reinitvar();
17535 17751
17536 if (setjmp(jmploc.loc)) { 17752 if (setjmp(main_handler.loc)) {
17537 exitreset(); 17753 exitreset();
17538 exitshell(); 17754 exitshell();
17539 } 17755 }
17540 exception_handler = &jmploc; 17756 exception_handler = &main_handler;
17541 17757
17542 shlvl++; 17758 shlvl++;
17543 if (fs->mode == FORK_BG) { 17759 if (fs->mode == FORK_BG) {
@@ -17566,7 +17782,7 @@ forkshell_init(const char *idstr)
17566 clear_traps(); 17782 clear_traps();
17567#if JOBS_WIN32 17783#if JOBS_WIN32
17568 /* do job control only in root shell */ 17784 /* do job control only in root shell */
17569 doing_jobctl = 0; 17785 jobctl = 0;
17570 17786
17571 if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args && 17787 if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args &&
17572 strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) { 17788 strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) {
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right
new file mode 100644
index 000000000..3d79316d7
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right
@@ -0,0 +1 @@
Ok1
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests
new file mode 100755
index 000000000..eb2223031
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests
@@ -0,0 +1,4 @@
1cat <<-EOF
2 Ok1
3 EO\
4F
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right
new file mode 100644
index 000000000..3d79316d7
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right
@@ -0,0 +1 @@
Ok1
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests
new file mode 100755
index 000000000..de21132d1
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests
@@ -0,0 +1,4 @@
1cat <<EOF
2Ok1
3\
4EOF
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right
new file mode 100644
index 000000000..3d79316d7
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right
@@ -0,0 +1 @@
Ok1
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests
new file mode 100755
index 000000000..da3860804
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests
@@ -0,0 +1,4 @@
1cat <<-EOF
2Ok1
3 \
4 EOF
diff --git a/shell/ash_test/ash-heredoc/herestring1.right b/shell/ash_test/ash-heredoc/herestring1.right
new file mode 100644
index 000000000..555937daa
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/herestring1.right
@@ -0,0 +1,33 @@
1one
2_two
3\_three
4\_four
5\_two
6\_three
7\\_four
8two_newlines
9
10/bin/c*
11star_*
12star_*
13star_*
14star_\*
15star_*
16star_\*
17line1
18line2
19 line3
20line1
21line2
22 line3
23512
24256
25128
2664
2732
2816
298
304
312
32v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
33v-$a-\t-\-"-\x-`-\--\z-\*-\?-
diff --git a/shell/ash_test/ash-heredoc/herestring1.tests b/shell/ash_test/ash-heredoc/herestring1.tests
new file mode 100755
index 000000000..fb7bd0dda
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/herestring1.tests
@@ -0,0 +1,35 @@
1cat <<<one
2cat <<<\_two
3cat <<<"\_three"
4cat <<<'\_four'
5cat <<<\\_two
6cat <<<"\\_three"
7cat <<<'\\_four'
8
9cat <<<$'two_newlines\n'
10
11cat <<</bin/c*
12
13cat <<<star_*
14cat <<<star_\*
15cat <<<"star_*"
16cat <<<"star_\*"
17cat <<<'star_*'
18cat <<<'star_\*'
19
20var=$'line1
21line2
22 line3'
23
24cat <<<$var
25
26cat <<<"$var"
27
28i=10
29until test $((--i)) = 0; do
30 cat <<<$((2**i))
31done
32
33a=qwerty
34cat <<<`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
35cat <<<"`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`"
diff --git a/shell/ash_test/ash-misc/assignment2.right b/shell/ash_test/ash-misc/assignment2.right
index 179c71c5a..246694462 100644
--- a/shell/ash_test/ash-misc/assignment2.right
+++ b/shell/ash_test/ash-misc/assignment2.right
@@ -1,2 +1,4 @@
1./assignment2.tests: line 2: a=b: not found 1./assignment2.tests: line 2: a=b: not found
2127 2127
3./assignment2.tests: line 4: ab=c: not found
4127
diff --git a/shell/ash_test/ash-misc/assignment2.tests b/shell/ash_test/ash-misc/assignment2.tests
index f6938434c..a93a48d09 100755
--- a/shell/ash_test/ash-misc/assignment2.tests
+++ b/shell/ash_test/ash-misc/assignment2.tests
@@ -1,3 +1,5 @@
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 $?
4a'b'=c true
5echo $?
diff --git a/shell/ash_test/ash-misc/func6.right b/shell/ash_test/ash-misc/func6.right
new file mode 100644
index 000000000..01e79c32a
--- /dev/null
+++ b/shell/ash_test/ash-misc/func6.right
@@ -0,0 +1,3 @@
11
22
33
diff --git a/shell/ash_test/ash-misc/func6.tests b/shell/ash_test/ash-misc/func6.tests
new file mode 100755
index 000000000..5f1699c42
--- /dev/null
+++ b/shell/ash_test/ash-misc/func6.tests
@@ -0,0 +1,8 @@
1{ f() { echo $1; } }
2f 1
3
4{ f() ( echo $1; )}
5f 2
6
7{ f()(echo $1)}
8f 3
diff --git a/shell/ash_test/ash-misc/func7.right b/shell/ash_test/ash-misc/func7.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/ash_test/ash-misc/func7.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/ash_test/ash-misc/func7.tests b/shell/ash_test/ash-misc/func7.tests
new file mode 100755
index 000000000..f5e03b6e1
--- /dev/null
+++ b/shell/ash_test/ash-misc/func7.tests
@@ -0,0 +1 @@
if f() { echo Ok:$?; } then f; fi
diff --git a/shell/ash_test/ash-read/read_ifs2.right b/shell/ash_test/ash-read/read_ifs2.right
new file mode 100644
index 000000000..797137dae
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.right
@@ -0,0 +1,9 @@
1|X|Y:Z:|
2|X|Y:Z|
3|X|Y|
4|X|Y|
5|X||
6|X||
7|||
8Whitespace should be trimmed too:
9|X|Y|
diff --git a/shell/ash_test/ash-read/read_ifs2.tests b/shell/ash_test/ash-read/read_ifs2.tests
new file mode 100755
index 000000000..f01a68978
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.tests
@@ -0,0 +1,9 @@
1echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|")
2echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|")
3echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|")
4echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|")
5echo "X:" | (IFS=": " read x y; echo "|$x|$y|")
6echo "X" | (IFS=": " read x y; echo "|$x|$y|")
7echo "" | (IFS=": " read x y; echo "|$x|$y|")
8echo Whitespace should be trimmed too:
9echo "X:Y : " | (IFS=": " read x y; echo "|$x|$y|")
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/ash_test/ash-read/read_t.right
+++ b/shell/ash_test/ash-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/ash_test/ash-read/read_t.tests
+++ b/shell/ash_test/ash-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/ash_test/ash-redir/redir_EINTR1.right b/shell/ash_test/ash-redir/redir_EINTR1.right
new file mode 100644
index 000000000..287d91f67
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_EINTR1.right
@@ -0,0 +1,2 @@
1Hello
2Done:0
diff --git a/shell/ash_test/ash-redir/redir_EINTR1.tests b/shell/ash_test/ash-redir/redir_EINTR1.tests
new file mode 100755
index 000000000..cede4d4d8
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_EINTR1.tests
@@ -0,0 +1,13 @@
1rm -f test.fifo
2mkfifo test.fifo
3
4(sleep 1; kill -chld $$) &
5(sleep 2; echo Hello >test.fifo) &
6
7# We get open("test.fifo") interrupted by SIGCHLD from the first subshell.
8# The shell MUST retry the open (no printing of error messages).
9# Then, the second subshell opens fifo for writing and open unblocks and succeeds.
10cat <test.fifo
11
12echo "Done:$?"
13rm -f test.fifo
diff --git a/shell/ash_test/ash-redir/redir_EINTR2.right b/shell/ash_test/ash-redir/redir_EINTR2.right
new file mode 100644
index 000000000..287d91f67
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_EINTR2.right
@@ -0,0 +1,2 @@
1Hello
2Done:0
diff --git a/shell/ash_test/ash-redir/redir_EINTR2.tests b/shell/ash_test/ash-redir/redir_EINTR2.tests
new file mode 100755
index 000000000..3d343c7ea
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_EINTR2.tests
@@ -0,0 +1,14 @@
1rm -f test.fifo
2mkfifo test.fifo
3
4(sleep 1; kill -chld $$) &
5(sleep 2; echo Hello >test.fifo) &
6
7# We get open("test.fifo") interrupted by SIGCHLD from the first subshell.
8# The shell MUST retry the open (no printing of error messages).
9# Then, the second subshell opens fifo for writing and open unblocks and succeeds.
10read HELLO <test.fifo
11echo "$HELLO"
12
13echo "Done:$?"
14rm -f test.fifo
diff --git a/shell/ash_test/ash-vars/var_backslash1.right b/shell/ash_test/ash-vars/var_backslash1.right
new file mode 100644
index 000000000..3f2c21289
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_backslash1.right
@@ -0,0 +1,35 @@
1a is '\*bc'
2b is '\'
3c is '*'
4${a##?*} removes everything: || - matches one char, then all
5${a##?"*"} removes \*: |bc| - matches one char, then *
6${a##\*} removes nothing: |\*bc| - first char is not *
7${a##\\*} removes everything: || - matches \, then all
8${a##\\\*} removes \*: |bc| - matches \, then *
9${a##?$c} removes everything: || - matches one char, then all
10${a##?"$c"} removes \*: |bc| - matches one char, then *
11${a##\\$c} removes everything: || - matches \, then all
12${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $
13${a##\\"$c"} removes \*: |bc| - matches \, then *
14${a##$b} removes \: |*bc| - matches \
15${a##"$b"} removes \: |*bc| - matches \
16
17Single quote tests:
18${a##?'*'} removes \*: |bc| - matches one char, then *
19${a##'\'*} removes everything: || - matches \, then all
20${a##'\'\*} removes \*: |bc| - matches \, then *
21${a##'\*'} removes \*: |bc| - matches \, then *
22${a##'\'$c} removes everything: || - matches \, then all
23${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $
24${a##'\'"$c"} removes \*: |bc| - matches \, then *
25
26${a##"$b"?} removes \*: |bc| - matches \, then one char
27${a##"$b"*} removes everything: || - matches \, then all
28${a##"$b""?"} removes nothing: |\*bc| - second char is not ?
29${a##"$b""*"} removes \*: |bc| - matches \, then *
30${a##"$b"\*} removes \*: |bc| - matches \, then *
31${a##"$b"$c} removes everything:|| - matches \, then all
32${a##"$b""$c"} removes \*: |bc| - matches \, then *
33${a##"$b?"} removes nothing: |\*bc| - second char is not ?
34${a##"$b*"} removes \*: |bc| - matches \, then *
35${a##"$b$c"} removes \*: |bc| - matches \, then *
diff --git a/shell/ash_test/ash-vars/var_backslash1.tests b/shell/ash_test/ash-vars/var_backslash1.tests
new file mode 100755
index 000000000..015a6107b
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_backslash1.tests
@@ -0,0 +1,48 @@
1a='\*bc'
2b='\'
3c='*'
4echo "a is '$a'"
5echo "b is '$b'"
6echo "c is '$c'"
7echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all'
8echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *'
9echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *'
10echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all'
11echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *'
12echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all'
13echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *'
14echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all'
15echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $'
16echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *'
17echo '${a##$b} removes \: '"|${a##$b}|"' - matches \'
18echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \'
19echo
20sq="'"
21echo 'Single quote tests:'
22echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *'
23echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all'
24echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *'
25echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *'
26echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all'
27echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $'
28echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *'
29echo
30## In bash, this isn't working as expected
31#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc|
32#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc|
33#echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc|
34#echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc|
35## the cause seems to be that $b emits backslash that "glues" onto next character if there is one:
36## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *)
37## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc|
38#echo
39echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char'
40echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all'
41echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc|
42echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *'
43echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *'
44echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all'
45echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *'
46echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc|
47echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints ||
48echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *'
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c86308d3b..f0f41984d 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -31,9 +31,7 @@
31extern char **environ; 31extern char **environ;
32 32
33int 33int
34main (argc, argv) 34main (int argc, char **argv)
35 int argc;
36 char **argv;
37{ 35{
38 register char **envp, *eval; 36 register char **envp, *eval;
39 int len; 37 int len;
diff --git a/shell/ash_test/recho.c b/shell/ash_test/recho.c
index 42a5feafd..7e96b14cc 100644
--- a/shell/ash_test/recho.c
+++ b/shell/ash_test/recho.c
@@ -27,7 +27,7 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <stdlib.h> 28#include <stdlib.h>
29 29
30void strprint(); 30void strprint(char *);
31 31
32int main(int argc, char **argv) 32int main(int argc, char **argv)
33{ 33{
diff --git a/shell/hush.c b/shell/hush.c
index 4a97293cc..da0db7948 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -44,23 +44,20 @@
44 * make complex ${var%...} constructs support optional 44 * make complex ${var%...} constructs support optional
45 * make here documents optional 45 * make here documents optional
46 * special variables (done: PWD, PPID, RANDOM) 46 * special variables (done: PWD, PPID, RANDOM)
47 * follow IFS rules more precisely, including update semantics
48 * tilde expansion 47 * tilde expansion
49 * aliases
50 * "command" missing features: 48 * "command" missing features:
51 * command -p CMD: run CMD using default $PATH 49 * command -p CMD: run CMD using default $PATH
52 * (can use this to override standalone shell as well?) 50 * (can use this to override standalone shell as well?)
53 * command BLTIN: disables special-ness (e.g. errors do not abort) 51 * command BLTIN: disables special-ness (e.g. errors do not abort)
54 * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard) 52 * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard)
55 * builtins mandated by standards we don't support: 53 * builtins mandated by standards we don't support:
56 * [un]alias, fc:
57 * fc -l[nr] [BEG] [END]: list range of commands in history 54 * fc -l[nr] [BEG] [END]: list range of commands in history
58 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands 55 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands
59 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP 56 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP
60 * 57 *
61 * Bash compat TODO: 58 * Bash compat TODO:
62 * redirection of stdout+stderr: &> and >& 59 * redirection of stdout+stderr: &> and >&
63 * reserved words: function select 60 * reserved words: select
64 * advanced test: [[ ]] 61 * advanced test: [[ ]]
65 * process substitution: <(list) and >(list) 62 * process substitution: <(list) and >(list)
66 * let EXPR [EXPR...] 63 * let EXPR [EXPR...]
@@ -102,7 +99,7 @@
102//config: 99//config:
103//config: It will compile and work on no-mmu systems. 100//config: It will compile and work on no-mmu systems.
104//config: 101//config:
105//config: It does not handle select, aliases, tilde expansion, 102//config: It does not handle select, tilde expansion,
106//config: &>file and >&file redirection of stdout+stderr. 103//config: &>file and >&file redirection of stdout+stderr.
107//config: 104//config:
108// This option is visible (has a description) to make it possible to select 105// This option is visible (has a description) to make it possible to select
@@ -115,6 +112,11 @@
115//config:# It's only needed to get "nice" menuconfig indenting. 112//config:# It's only needed to get "nice" menuconfig indenting.
116//config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH 113//config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH
117//config: 114//config:
115//config:config HUSH_NEED_FOR_SPEED
116//config: bool "Faster, but larger code"
117//config: default y
118//config: depends on SHELL_HUSH
119//config:
118//config:config HUSH_BASH_COMPAT 120//config:config HUSH_BASH_COMPAT
119//config: bool "bash-compatible extensions" 121//config: bool "bash-compatible extensions"
120//config: default y 122//config: default y
@@ -189,6 +191,13 @@
189//config: help 191//config: help
190//config: Enable case ... esac statement. +400 bytes. 192//config: Enable case ... esac statement. +400 bytes.
191//config: 193//config:
194//config:config HUSH_ALIAS
195//config: bool "Support aliases"
196//config: default y
197//config: depends on SHELL_HUSH
198//config: help
199//config: Enable aliases.
200//config:
192//config:config HUSH_FUNCTIONS 201//config:config HUSH_FUNCTIONS
193//config: bool "Support funcname() { commands; } syntax" 202//config: bool "Support funcname() { commands; } syntax"
194//config: default y 203//config: default y
@@ -196,6 +205,13 @@
196//config: help 205//config: help
197//config: Enable support for shell functions. +800 bytes. 206//config: Enable support for shell functions. +800 bytes.
198//config: 207//config:
208//config:config HUSH_FUNCTION_KEYWORD
209//config: bool "Support function keyword"
210//config: default y
211//config: depends on HUSH_FUNCTIONS
212//config: help
213//config: Support "function FUNCNAME { CMD; }" syntax.
214//config:
199//config:config HUSH_LOCAL 215//config:config HUSH_LOCAL
200//config: bool "local builtin" 216//config: bool "local builtin"
201//config: default y 217//config: default y
@@ -392,6 +408,8 @@
392 408
393/* Build knobs */ 409/* Build knobs */
394#define LEAK_HUNTING 0 410#define LEAK_HUNTING 0
411#define LEAK_PRINTF(...) fdprintf(__VA_ARGS__)
412//#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0)
395#define BUILD_AS_NOMMU 0 413#define BUILD_AS_NOMMU 0
396/* Enable/disable sanity checks. Ok to enable in production, 414/* Enable/disable sanity checks. Ok to enable in production,
397 * only adds a bit of bloat. Set to >1 to get non-production level verbosity. 415 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
@@ -551,25 +569,22 @@ typedef struct o_string {
551 smallint has_empty_slot; 569 smallint has_empty_slot;
552 smallint ended_in_ifs; 570 smallint ended_in_ifs;
553} o_string; 571} o_string;
572#define IS_NULL_WORD(str) \
573 ((str).length == 0 && !(str).has_quoted_part)
554enum { 574enum {
555 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ 575 /* Protect all newly added chars against globbing by prepending \ to *, ?, [, -, \ */
556 EXP_FLAG_GLOB = 0x2, 576 EXP_FLAG_GLOBPROTECT_CHARS = 0x1,
557 /* Protect newly added chars against globbing 577 /* Protect quoted vars against globbing */
558 * by prepending \ to *, ?, [, \ */ 578 EXP_FLAG_GLOBPROTECT_VARS = 0x2,
559 EXP_FLAG_ESC_GLOB_CHARS = 0x1, 579 /* On word-split, perform globbing (one word may become many) */
580 EXP_FLAG_DO_GLOBBING = 0x4,
581 /* Do not word-split */
582 EXP_FLAG_SINGLEWORD = 0x80,
583 /* ^^^^ EXP_FLAG_SINGLEWORD must be 0x80 */
560}; 584};
561/* Used for initialization: o_string foo = NULL_O_STRING; */ 585/* Used for initialization: o_string foo = NULL_O_STRING; */
562#define NULL_O_STRING { NULL } 586#define NULL_O_STRING { NULL }
563 587
564#ifndef debug_printf_parse
565static const char *const assignment_flag[] ALIGN_PTR = {
566 "MAYBE_ASSIGNMENT",
567 "DEFINITELY_ASSIGNMENT",
568 "NOT_ASSIGNMENT",
569 "WORD_IS_KEYWORD",
570};
571#endif
572
573/* We almost can use standard FILE api, but we need an ability to move 588/* We almost can use standard FILE api, but we need an ability to move
574 * its fd when redirects coincide with it. No api exists for that 589 * its fd when redirects coincide with it. No api exists for that
575 * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902). 590 * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902).
@@ -587,22 +602,28 @@ typedef struct HFILE {
587 602
588typedef struct in_str { 603typedef struct in_str {
589 const char *p; 604 const char *p;
605#if ENABLE_HUSH_ALIAS
606 /* During alias expansion, p is saved in saved_ibuf, and replaced by alias expansion */
607 const char *saved_ibuf;
608 char *albuf; /* malloced */
609#endif
590 int peek_buf[2]; 610 int peek_buf[2];
591 int last_char; 611 int last_char;
592 HFILE *file; 612 HFILE *file;
593} in_str; 613} in_str;
594 614
595/* The descrip member of this structure is only used to make 615/* The descrip3 member of this structure is only used to make
596 * debugging output pretty */ 616 * debugging output pretty */
597static const struct { 617static const struct {
598 int32_t mode; 618 int32_t mode;
599 signed char default_fd; 619 signed char default_fd;
600 char descrip[3]; 620 char descrip3[3];
601} redir_table[] ALIGN4 = { 621} redir_table[] ALIGN4 = {
602 { O_RDONLY, 0, "<" }, 622 { O_RDONLY, 0, "<" },
603 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, 623 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
604 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, 624 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
605 { O_CREAT|O_RDWR, 1, "<>" }, 625 { O_CREAT|O_RDWR, 1, "<>" },
626 { O_RDONLY, 0, "<<<" },
606 { O_RDONLY, 0, "<<" }, 627 { O_RDONLY, 0, "<<" },
607/* Should not be needed. Bogus default_fd helps in debugging */ 628/* Should not be needed. Bogus default_fd helps in debugging */
608/* { O_RDONLY, 77, "<<" }, */ 629/* { O_RDONLY, 77, "<<" }, */
@@ -622,12 +643,13 @@ struct redir_struct {
622 */ 643 */
623}; 644};
624typedef enum redir_type { 645typedef enum redir_type {
625 REDIRECT_INPUT = 0, 646 REDIRECT_INPUT = 0,
626 REDIRECT_OVERWRITE = 1, 647 REDIRECT_OVERWRITE = 1,
627 REDIRECT_APPEND = 2, 648 REDIRECT_APPEND = 2,
628 REDIRECT_IO = 3, 649 REDIRECT_IO = 3,
629 REDIRECT_HEREDOC = 4, 650 REDIRECT_HERESTRING = 4,
630 REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */ 651 REDIRECT_HEREDOC = 5,
652 REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */
631 653
632 REDIRFD_CLOSE = -3, 654 REDIRFD_CLOSE = -3,
633 REDIRFD_SYNTAX_ERR = -2, 655 REDIRFD_SYNTAX_ERR = -2,
@@ -656,8 +678,12 @@ struct command {
656# define CMD_SINGLEWORD_NOGLOB 3 678# define CMD_SINGLEWORD_NOGLOB 3
657#endif 679#endif
658#if ENABLE_HUSH_FUNCTIONS 680#if ENABLE_HUSH_FUNCTIONS
659# define CMD_FUNCDEF 4 681# if ENABLE_HUSH_FUNCTION_KEYWORD
682# define CMD_FUNCTION_KWORD 4
683# endif
684# define CMD_FUNCDEF 5
660#endif 685#endif
686/* ^^^ if you change this, update CMDTYPE[] array too */
661 687
662 smalluint cmd_exitcode; 688 smalluint cmd_exitcode;
663 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ 689 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
@@ -693,7 +719,9 @@ struct command {
693}; 719};
694/* Is there anything in this command at all? */ 720/* Is there anything in this command at all? */
695#define IS_NULL_CMD(cmd) \ 721#define IS_NULL_CMD(cmd) \
696 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects) 722 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects \
723 /* maybe? IF_HUSH_FUNCTION_KEYWORD(&& !(cmd)->cmd_type) */ \
724 )
697 725
698struct pipe { 726struct pipe {
699 struct pipe *next; 727 struct pipe *next;
@@ -734,13 +762,9 @@ struct parse_context {
734#if !BB_MMU 762#if !BB_MMU
735 o_string as_string; 763 o_string as_string;
736#endif 764#endif
737 smallint is_assignment; /* 0:maybe, 1:yes, 2:no, 3:keyword */ 765 smallint is_assignment;
738#if HAS_KEYWORDS 766#if HAS_KEYWORDS
739 smallint ctx_res_w; 767 smallint ctx_res_w;
740 smallint ctx_inverted; /* "! cmd | cmd" */
741#if ENABLE_HUSH_CASE
742 smallint ctx_dsemicolon; /* ";;" seen */
743#endif
744 /* bitmask of FLAG_xxx, for figuring out valid reserved words */ 768 /* bitmask of FLAG_xxx, for figuring out valid reserved words */
745 int old_flag; 769 int old_flag;
746 /* group we are enclosed in: 770 /* group we are enclosed in:
@@ -759,9 +783,14 @@ enum {
759 MAYBE_ASSIGNMENT = 0, 783 MAYBE_ASSIGNMENT = 0,
760 DEFINITELY_ASSIGNMENT = 1, 784 DEFINITELY_ASSIGNMENT = 1,
761 NOT_ASSIGNMENT = 2, 785 NOT_ASSIGNMENT = 2,
762 /* Not an assignment, but next word may be: "if v=xyz cmd;" */
763 WORD_IS_KEYWORD = 3,
764}; 786};
787#ifndef debug_printf_parse
788static const char *const assignment_flag[] ALIGN_PTR = {
789 "MAYBE_ASSIGNMENT",
790 "DEFINITELY_ASSIGNMENT",
791 "NOT_ASSIGNMENT",
792};
793#endif
765 794
766/* On program start, environ points to initial environment. 795/* On program start, environ points to initial environment.
767 * putenv adds new pointers into it, unsetenv removes them. 796 * putenv adds new pointers into it, unsetenv removes them.
@@ -795,6 +824,14 @@ struct function {
795}; 824};
796#endif 825#endif
797 826
827#if ENABLE_HUSH_ALIAS
828struct alias {
829 struct alias *next;
830 char *str;
831 smallint dont_recurse;
832};
833#endif
834
798/* set -/+o OPT support. (TODO: make it optional) 835/* set -/+o OPT support. (TODO: make it optional)
799 * bash supports the following opts: 836 * bash supports the following opts:
800 * allexport off 837 * allexport off
@@ -916,6 +953,7 @@ struct globals {
916#endif 953#endif
917 /* set by signal handler if SIGINT is received _and_ its trap is not set */ 954 /* set by signal handler if SIGINT is received _and_ its trap is not set */
918 smallint flag_SIGINT; 955 smallint flag_SIGINT;
956 smallint flag_startup_done;
919#if ENABLE_HUSH_LOOPS 957#if ENABLE_HUSH_LOOPS
920 smallint flag_break_continue; 958 smallint flag_break_continue;
921#endif 959#endif
@@ -930,6 +968,12 @@ struct globals {
930# define G_flag_return_in_progress 0 968# define G_flag_return_in_progress 0
931#endif 969#endif
932 smallint exiting; /* used to prevent EXIT trap recursion */ 970 smallint exiting; /* used to prevent EXIT trap recursion */
971#if !BB_MMU
972 smallint reexeced_on_NOMMU;
973# define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU)
974#else
975# define G_reexeced_on_NOMMU 0
976#endif
933 /* These support $? */ 977 /* These support $? */
934 smalluint last_exitcode; 978 smalluint last_exitcode;
935 smalluint expand_exitcode; 979 smalluint expand_exitcode;
@@ -970,6 +1014,9 @@ struct globals {
970# endif 1014# endif
971 struct function *top_func; 1015 struct function *top_func;
972#endif 1016#endif
1017#if ENABLE_HUSH_ALIAS
1018 struct alias *top_alias;
1019#endif
973 /* Signal and trap handling */ 1020 /* Signal and trap handling */
974#if ENABLE_HUSH_FAST 1021#if ENABLE_HUSH_FAST
975 unsigned count_SIGCHLD; 1022 unsigned count_SIGCHLD;
@@ -1072,6 +1119,10 @@ static int builtin_jobs(char **argv) FAST_FUNC;
1072#if ENABLE_HUSH_GETOPTS 1119#if ENABLE_HUSH_GETOPTS
1073static int builtin_getopts(char **argv) FAST_FUNC; 1120static int builtin_getopts(char **argv) FAST_FUNC;
1074#endif 1121#endif
1122#if ENABLE_HUSH_ALIAS
1123static int builtin_alias(char **argv) FAST_FUNC;
1124static int builtin_unalias(char **argv) FAST_FUNC;
1125#endif
1075#if ENABLE_HUSH_HELP 1126#if ENABLE_HUSH_HELP
1076static int builtin_help(char **argv) FAST_FUNC; 1127static int builtin_help(char **argv) FAST_FUNC;
1077#endif 1128#endif
@@ -1149,6 +1200,9 @@ struct built_in_command {
1149static const struct built_in_command bltins1[] ALIGN_PTR = { 1200static const struct built_in_command bltins1[] ALIGN_PTR = {
1150 BLTIN("." , builtin_source , "Run commands in file"), 1201 BLTIN("." , builtin_source , "Run commands in file"),
1151 BLTIN(":" , builtin_true , NULL), 1202 BLTIN(":" , builtin_true , NULL),
1203#if ENABLE_HUSH_ALIAS
1204 BLTIN("alias" , builtin_alias , "Define or display aliases"),
1205#endif
1152#if ENABLE_HUSH_JOB 1206#if ENABLE_HUSH_JOB
1153 BLTIN("bg" , builtin_fg_bg , "Resume job in background"), 1207 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
1154#endif 1208#endif
@@ -1200,7 +1254,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = {
1200 BLTIN("return" , builtin_return , "Return from function"), 1254 BLTIN("return" , builtin_return , "Return from function"),
1201#endif 1255#endif
1202#if ENABLE_HUSH_SET 1256#if ENABLE_HUSH_SET
1203 BLTIN("set" , builtin_set , "Set positional parameters"), 1257 BLTIN("set" , builtin_set , "Set options or positional parameters"),
1204#endif 1258#endif
1205 BLTIN("shift" , builtin_shift , "Shift positional parameters"), 1259 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
1206#if BASH_SOURCE 1260#if BASH_SOURCE
@@ -1210,7 +1264,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = {
1210 BLTIN("times" , builtin_times , NULL), 1264 BLTIN("times" , builtin_times , NULL),
1211#endif 1265#endif
1212#if ENABLE_HUSH_TRAP 1266#if ENABLE_HUSH_TRAP
1213 BLTIN("trap" , builtin_trap , "Trap signals"), 1267 BLTIN("trap" , builtin_trap , "Define or display signal handlers"),
1214#endif 1268#endif
1215 BLTIN("true" , builtin_true , NULL), 1269 BLTIN("true" , builtin_true , NULL),
1216#if ENABLE_HUSH_TYPE 1270#if ENABLE_HUSH_TYPE
@@ -1222,6 +1276,9 @@ static const struct built_in_command bltins1[] ALIGN_PTR = {
1222#if ENABLE_HUSH_UMASK 1276#if ENABLE_HUSH_UMASK
1223 BLTIN("umask" , builtin_umask , "Set file creation mask"), 1277 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1224#endif 1278#endif
1279#if ENABLE_HUSH_ALIAS
1280 BLTIN("unalias" , builtin_unalias , "Delete aliases"),
1281#endif
1225#if ENABLE_HUSH_UNSET 1282#if ENABLE_HUSH_UNSET
1226 BLTIN("unset" , builtin_unset , "Unset variables"), 1283 BLTIN("unset" , builtin_unset , "Unset variables"),
1227#endif 1284#endif
@@ -1352,30 +1409,67 @@ static void debug_print_strings(const char *prefix, char **vv)
1352static void *xxmalloc(int lineno, size_t size) 1409static void *xxmalloc(int lineno, size_t size)
1353{ 1410{
1354 void *ptr = xmalloc((size + 0xff) & ~0xff); 1411 void *ptr = xmalloc((size + 0xff) & ~0xff);
1355 fdprintf(2, "line %d: malloc %p\n", lineno, ptr); 1412 LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr);
1413 return ptr;
1414}
1415static void *xxzalloc(int lineno, size_t size)
1416{
1417 void *ptr = xzalloc((size + 0xff) & ~0xff);
1418 LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr);
1356 return ptr; 1419 return ptr;
1357} 1420}
1358static void *xxrealloc(int lineno, void *ptr, size_t size) 1421static void *xxrealloc(int lineno, void *ptr, size_t size)
1359{ 1422{
1423 char *p = ptr;
1360 ptr = xrealloc(ptr, (size + 0xff) & ~0xff); 1424 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1361 fdprintf(2, "line %d: realloc %p\n", lineno, ptr); 1425 if (p != ptr)
1426 LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr);
1427 return ptr;
1428}
1429static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr)
1430{
1431 char *p = ptr;
1432 ptr = xrealloc_getcwd_or_warn(ptr);
1433 if (p != ptr)
1434 LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr);
1362 return ptr; 1435 return ptr;
1363} 1436}
1364static char *xxstrdup(int lineno, const char *str) 1437static char *xxstrdup(int lineno, const char *str)
1365{ 1438{
1366 char *ptr = xstrdup(str); 1439 char *ptr = xstrdup(str);
1367 fdprintf(2, "line %d: strdup %p\n", lineno, ptr); 1440 LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr);
1441 return ptr;
1442}
1443static char *xxstrndup(int lineno, const char *str, size_t n)
1444{
1445 char *ptr = xstrndup(str, n);
1446 LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr);
1447 return ptr;
1448}
1449static char *xxasprintf(int lineno, const char *f, ...)
1450{
1451 char *ptr;
1452 va_list args;
1453 va_start(args, f);
1454 if (vasprintf(&ptr, f, args) < 0)
1455 bb_die_memory_exhausted();
1456 va_end(args);
1457 LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr);
1368 return ptr; 1458 return ptr;
1369} 1459}
1370static void xxfree(void *ptr) 1460static void xxfree(void *ptr)
1371{ 1461{
1372 fdprintf(2, "free %p\n", ptr); 1462 LEAK_PRINTF(2, "free %p\n", ptr);
1373 free(ptr); 1463 free(ptr);
1374} 1464}
1375# define xmalloc(s) xxmalloc(__LINE__, s) 1465# define xmalloc(s) xxmalloc(__LINE__, s)
1376# define xrealloc(p, s) xxrealloc(__LINE__, p, s) 1466# define xzalloc(s) xxzalloc(__LINE__, s)
1377# define xstrdup(s) xxstrdup(__LINE__, s) 1467# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1378# define free(p) xxfree(p) 1468# define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p)
1469# define xstrdup(s) xxstrdup(__LINE__, s)
1470# define xstrndup(s, n) xxstrndup(__LINE__, s, n)
1471# define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__)
1472# define free(p) xxfree(p)
1379#endif 1473#endif
1380 1474
1381/* 1475/*
@@ -1391,11 +1485,14 @@ static void xxfree(void *ptr)
1391# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) 1485# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
1392# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s) 1486# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
1393# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) 1487# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
1488# define syntax_error_unexpected_str(lineno, s) syntax_error_unexpected_str(s)
1394#endif 1489#endif
1395 1490
1396static void die_if_script(void) 1491static void die_if_script(void)
1397{ 1492{
1398 if (!G_interactive_fd) { 1493 if (G.flag_startup_done /* when not yet set, allows "hush -l" to not die on errors in /etc/profile */
1494 && !G_interactive_fd
1495 ) {
1399 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */ 1496 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
1400 xfunc_error_retval = G.last_exitcode; 1497 xfunc_error_retval = G.last_exitcode;
1401 xfunc_die(); 1498 xfunc_die();
@@ -1438,24 +1535,27 @@ static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
1438// die_if_script(); 1535// die_if_script();
1439} 1536}
1440 1537
1441static void syntax_error_unterm_ch(unsigned lineno, char ch) 1538static void syntax_error_unterm_ch(unsigned lineno, int ch)
1442{ 1539{
1443 char msg[2] = { ch, '\0' }; 1540 char msg[2] = { ch, '\0' };
1444 syntax_error_unterm_str(lineno, msg); 1541 syntax_error_unterm_str(lineno, ch == EOF ? "EOF" : msg);
1445} 1542}
1446 1543
1447static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) 1544static void syntax_error_unexpected_str(unsigned lineno UNUSED_PARAM, const char *s)
1448{ 1545{
1449 char msg[2];
1450 msg[0] = ch;
1451 msg[1] = '\0';
1452#if HUSH_DEBUG >= 2 1546#if HUSH_DEBUG >= 2
1453 bb_error_msg("hush.c:%u", lineno); 1547 bb_error_msg("hush.c:%u", lineno);
1454#endif 1548#endif
1455 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); 1549 bb_error_msg("syntax error: unexpected %s", s);
1456 die_if_script(); 1550 die_if_script();
1457} 1551}
1458 1552
1553static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1554{
1555 char msg[2] = { ch, '\0' };
1556 syntax_error_unexpected_str(lineno, ch == EOF ? "EOF" : msg);
1557}
1558
1459#if HUSH_DEBUG < 2 1559#if HUSH_DEBUG < 2
1460# undef msg_and_die_if_script 1560# undef msg_and_die_if_script
1461# undef syntax_error 1561# undef syntax_error
@@ -1463,6 +1563,7 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1463# undef syntax_error_unterm_ch 1563# undef syntax_error_unterm_ch
1464# undef syntax_error_unterm_str 1564# undef syntax_error_unterm_str
1465# undef syntax_error_unexpected_ch 1565# undef syntax_error_unexpected_ch
1566# undef syntax_error_unexpected_str
1466#else 1567#else
1467# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__) 1568# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
1468# define syntax_error(msg) syntax_error(__LINE__, msg) 1569# define syntax_error(msg) syntax_error(__LINE__, msg)
@@ -1470,6 +1571,7 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1470# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) 1571# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
1471# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s) 1572# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
1472# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch) 1573# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
1574# define syntax_error_unexpected_str(s) syntax_error_unexpected_str(__LINE__, s)
1473#endif 1575#endif
1474 1576
1475/* 1577/*
@@ -1619,6 +1721,21 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1619 return newfd; 1721 return newfd;
1620} 1722}
1621 1723
1724#if ENABLE_HUSH_ALIAS
1725static void enable_all_aliases(void)
1726{
1727 if (G_interactive_fd) {
1728 struct alias *alias = G.top_alias;
1729 while (alias) {
1730 alias->dont_recurse = 0;
1731 alias = alias->next;
1732 }
1733 }
1734}
1735#else
1736#define enable_all_aliases() ((void)0)
1737#endif
1738
1622/* 1739/*
1623 * Manipulating HFILEs 1740 * Manipulating HFILEs
1624 */ 1741 */
@@ -1929,7 +2046,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1929 * "trap - SIGxxx": 2046 * "trap - SIGxxx":
1930 * if sig is in special_sig_mask, set handler back to: 2047 * if sig is in special_sig_mask, set handler back to:
1931 * record_pending_signo, or to IGN if it's a tty stop signal 2048 * record_pending_signo, or to IGN if it's a tty stop signal
1932 * if sig is in fatal_sig_mask, set handler back to sigexit. 2049 * if sig is in fatal_sig_mask, set handler back to restore_ttypgrp_and_killsig_or__exit.
1933 * else: set handler back to SIG_DFL 2050 * else: set handler back to SIG_DFL
1934 * "trap 'cmd' SIGxxx": 2051 * "trap 'cmd' SIGxxx":
1935 * set handler to record_pending_signo. 2052 * set handler to record_pending_signo.
@@ -1973,7 +2090,7 @@ static void record_pending_signo(int sig)
1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) 2090 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ 2091 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
1975 ) { 2092 ) {
1976 bb_got_signal = sig; /* for read_line_input: "we got a signal" */ 2093 bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */
1977 } 2094 }
1978#endif 2095#endif
1979#if ENABLE_HUSH_FAST 2096#if ENABLE_HUSH_FAST
@@ -2002,19 +2119,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
2002 return old_sa.sa_handler; 2119 return old_sa.sa_handler;
2003} 2120}
2004 2121
2005static void hush_exit(int exitcode) NORETURN;
2006
2007static void restore_ttypgrp_and__exit(void) NORETURN;
2008static void restore_ttypgrp_and__exit(void)
2009{
2010 /* xfunc has failed! die die die */
2011 /* no EXIT traps, this is an escape hatch! */
2012 G.exiting = 1;
2013 hush_exit(xfunc_error_retval);
2014}
2015
2016#if ENABLE_HUSH_JOB
2017
2018/* Needed only on some libc: 2122/* Needed only on some libc:
2019 * It was observed that on exit(), fgetc'ed buffered data 2123 * It was observed that on exit(), fgetc'ed buffered data
2020 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 2124 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -2028,26 +2132,20 @@ static void restore_ttypgrp_and__exit(void)
2028 * and in `cmd` handling. 2132 * and in `cmd` handling.
2029 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 2133 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2030 */ 2134 */
2031static void fflush_and__exit(void) NORETURN; 2135static NORETURN void fflush_and__exit(void)
2032static void fflush_and__exit(void)
2033{ 2136{
2034 fflush_all(); 2137 fflush_all();
2035 _exit(xfunc_error_retval); 2138 _exit(xfunc_error_retval);
2036} 2139}
2037 2140
2038/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 2141#if ENABLE_HUSH_JOB
2039# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2040/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2041# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2042
2043/* Restores tty foreground process group, and exits. 2142/* Restores tty foreground process group, and exits.
2044 * May be called as signal handler for fatal signal 2143 * May be called as signal handler for fatal signal
2045 * (will resend signal to itself, producing correct exit state) 2144 * (will resend signal to itself, producing correct exit state)
2046 * or called directly with -EXITCODE. 2145 * or called directly with -EXITCODE.
2047 * We also call it if xfunc is exiting. 2146 * We also call it if xfunc is exiting.
2048 */ 2147 */
2049static void sigexit(int sig) NORETURN; 2148static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig)
2050static void sigexit(int sig)
2051{ 2149{
2052 /* Careful: we can end up here after [v]fork. Do not restore 2150 /* Careful: we can end up here after [v]fork. Do not restore
2053 * tty pgrp then, only top-level shell process does that */ 2151 * tty pgrp then, only top-level shell process does that */
@@ -2065,6 +2163,19 @@ static void sigexit(int sig)
2065 2163
2066 kill_myself_with_sig(sig); /* does not return */ 2164 kill_myself_with_sig(sig); /* does not return */
2067} 2165}
2166
2167static NORETURN void fflush_restore_ttypgrp_and__exit(void)
2168{
2169 /* xfunc has failed! die die die */
2170 fflush_all();
2171 restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval);
2172}
2173
2174/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2175# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2176/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2177# define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit)
2178
2068#else 2179#else
2069 2180
2070# define disable_restore_tty_pgrp_on_exit() ((void)0) 2181# define disable_restore_tty_pgrp_on_exit() ((void)0)
@@ -2081,7 +2192,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2081#if ENABLE_HUSH_JOB 2192#if ENABLE_HUSH_JOB
2082 /* is sig fatal? */ 2193 /* is sig fatal? */
2083 if (G_fatal_sig_mask & sigmask) 2194 if (G_fatal_sig_mask & sigmask)
2084 handler = sigexit; 2195 handler = restore_ttypgrp_and_killsig_or__exit;
2085 else 2196 else
2086#endif 2197#endif
2087 /* sig has special handling? */ 2198 /* sig has special handling? */
@@ -2099,11 +2210,33 @@ static sighandler_t pick_sighandler(unsigned sig)
2099 return handler; 2210 return handler;
2100} 2211}
2101 2212
2102/* Restores tty foreground process group, and exits. */ 2213static const char* FAST_FUNC get_local_var_value(const char *name);
2103static void hush_exit(int exitcode) 2214
2215/* Self-explanatory.
2216 * Restores tty foreground process group too.
2217 */
2218static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2104{ 2219{
2105#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2220#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2106 save_history(G.line_input_state); /* may be NULL */ 2221 if (G.line_input_state
2222 && getpid() == G.root_pid /* exits in subshells do not save history */
2223 ) {
2224 const char *hp;
2225# if ENABLE_FEATURE_SH_HISTFILESIZE
2226// in bash:
2227// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
2228// "When this variable is assigned a value, the history file is truncated, if necessary"
2229// but we do it only at exit, not on every assignment:
2230 /* Use HISTFILESIZE to limit file size */
2231 hp = get_local_var_value("HISTFILESIZE");
2232 if (hp)
2233 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
2234# endif
2235 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
2236 hp = get_local_var_value("HISTFILE");
2237 G.line_input_state->hist_file = hp;
2238 save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */
2239 }
2107#endif 2240#endif
2108 2241
2109 fflush_all(); 2242 fflush_all();
@@ -2137,7 +2270,7 @@ static void hush_exit(int exitcode)
2137 2270
2138 fflush_all(); 2271 fflush_all();
2139#if ENABLE_HUSH_JOB 2272#if ENABLE_HUSH_JOB
2140 sigexit(- (exitcode & 0xff)); 2273 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2141#else 2274#else
2142 _exit(exitcode); 2275 _exit(exitcode);
2143#endif 2276#endif
@@ -2222,7 +2355,7 @@ static int check_and_run_traps(void)
2222 } 2355 }
2223 } 2356 }
2224 /* this restores tty pgrp, then kills us with SIGHUP */ 2357 /* this restores tty pgrp, then kills us with SIGHUP */
2225 sigexit(SIGHUP); 2358 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2226 } 2359 }
2227#endif 2360#endif
2228#if ENABLE_HUSH_FAST 2361#if ENABLE_HUSH_FAST
@@ -2375,7 +2508,7 @@ static int set_local_var(char *str, unsigned flags)
2375 if (cur->flg_read_only) { 2508 if (cur->flg_read_only) {
2376 bb_error_msg("%s: readonly variable", str); 2509 bb_error_msg("%s: readonly variable", str);
2377 free(str); 2510 free(str);
2378//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1, 2511//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $? to 1,
2379//but export per se succeeds (does put the var in env). We don't mimic that. 2512//but export per se succeeds (does put the var in env). We don't mimic that.
2380 return -1; 2513 return -1;
2381 } 2514 }
@@ -2433,10 +2566,10 @@ static int set_local_var(char *str, unsigned flags)
2433 2566
2434 /* Replace the value in the found "struct variable" */ 2567 /* Replace the value in the found "struct variable" */
2435 if (cur->max_len != 0) { 2568 if (cur->max_len != 0) {
2436 if (cur->max_len >= strnlen(str, cur->max_len + 1)) { 2569 /* This one is from startup env, try to reuse space */
2437 /* This one is from startup env, reuse space */ 2570 int new_len = stpncpy(cur->varstr, str, cur->max_len + 1) - cur->varstr;
2438 debug_printf_env("reusing startup env for '%s'\n", str); 2571 if (new_len <= cur->max_len) {
2439 strcpy(cur->varstr, str); 2572 debug_printf_env("reused startup env for '%s'\n", str);
2440 goto free_and_exp; 2573 goto free_and_exp;
2441 } 2574 }
2442 /* Can't reuse */ 2575 /* Can't reuse */
@@ -2453,7 +2586,7 @@ static int set_local_var(char *str, unsigned flags)
2453 goto set_str_and_exp; 2586 goto set_str_and_exp;
2454 } 2587 }
2455 2588
2456 /* Not found or shadowed - create new variable struct */ 2589 /* Not found, or found but shadowed - create new variable struct */
2457 debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl); 2590 debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl);
2458 cur = xzalloc(sizeof(*cur)); 2591 cur = xzalloc(sizeof(*cur));
2459 cur->var_nest_level = local_lvl; 2592 cur->var_nest_level = local_lvl;
@@ -2669,20 +2802,16 @@ static const char *setup_prompt_string(void)
2669 debug_printf("prompt_str '%s'\n", prompt_str); 2802 debug_printf("prompt_str '%s'\n", prompt_str);
2670 return prompt_str; 2803 return prompt_str;
2671} 2804}
2672static int get_user_input(struct in_str *i) 2805static int show_prompt_and_get_stdin(struct in_str *i)
2673{ 2806{
2674# if ENABLE_FEATURE_EDITING 2807# if ENABLE_FEATURE_EDITING
2675 /* In EDITING case, this function reads next input line, 2808 /* In EDITING case, this function reads next input line,
2676 * saves it in i->p, then returns 1st char of it. 2809 * saves it in i->p, then returns 1st char of it.
2677 */ 2810 */
2678 int r;
2679 const char *prompt_str;
2680
2681 prompt_str = setup_prompt_string();
2682 for (;;) { 2811 for (;;) {
2683 reinit_unicode_for_hush(); 2812 int r;
2684 G.flag_SIGINT = 0;
2685 2813
2814 G.flag_SIGINT = 0;
2686 bb_got_signal = 0; 2815 bb_got_signal = 0;
2687 if (!sigisemptyset(&G.pending_set)) { 2816 if (!sigisemptyset(&G.pending_set)) {
2688 /* Whoops, already got a signal, do not call read_line_input */ 2817 /* Whoops, already got a signal, do not call read_line_input */
@@ -2702,6 +2831,8 @@ static int get_user_input(struct in_str *i)
2702 * #^^^ prints "T", prints prompt, repeats 2831 * #^^^ prints "T", prints prompt, repeats
2703 * #(bash 5.0.17 exits after first "T", looks like a bug) 2832 * #(bash 5.0.17 exits after first "T", looks like a bug)
2704 */ 2833 */
2834 const char *prompt_str = setup_prompt_string();
2835 reinit_unicode_for_hush();
2705 r = read_line_input(G.line_input_state, prompt_str, 2836 r = read_line_input(G.line_input_state, prompt_str,
2706 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 2837 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
2707 ); 2838 );
@@ -2732,7 +2863,7 @@ static int get_user_input(struct in_str *i)
2732 /* it was a signal: go back, read another input line */ 2863 /* it was a signal: go back, read another input line */
2733 } 2864 }
2734 i->p = G.user_input_buf; 2865 i->p = G.user_input_buf;
2735 return (unsigned char)*i->p++; 2866 return (unsigned char)*i->p++; /* can't be NUL */
2736# else 2867# else
2737 /* In !EDITING case, this function gets called for every char. 2868 /* In !EDITING case, this function gets called for every char.
2738 * Buffering happens deeper in the call chain, in hfgetc(i->file). 2869 * Buffering happens deeper in the call chain, in hfgetc(i->file).
@@ -2773,13 +2904,13 @@ static int get_user_input(struct in_str *i)
2773} 2904}
2774/* This is the magic location that prints prompts 2905/* This is the magic location that prints prompts
2775 * and gets data back from the user */ 2906 * and gets data back from the user */
2776static int fgetc_interactive(struct in_str *i) 2907static int i_getch_interactive(struct in_str *i)
2777{ 2908{
2778 int ch; 2909 int ch;
2779 /* If it's interactive stdin, get new line. */ 2910 /* If it's interactive stdin, get new line. */
2780 if (G_interactive_fd && i->file == G.HFILE_stdin) { 2911 if (G_interactive_fd && i->file == G.HFILE_stdin) {
2781 /* Returns first char (or EOF), the rest is in i->p[] */ 2912 /* Returns first char (or EOF), the rest is in i->p[] */
2782 ch = get_user_input(i); 2913 ch = show_prompt_and_get_stdin(i);
2783 G.promptmode = 1; /* PS2 */ 2914 G.promptmode = 1; /* PS2 */
2784 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); 2915 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
2785 } else { 2916 } else {
@@ -2789,7 +2920,7 @@ static int fgetc_interactive(struct in_str *i)
2789 return ch; 2920 return ch;
2790} 2921}
2791#else /* !INTERACTIVE */ 2922#else /* !INTERACTIVE */
2792static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) 2923static ALWAYS_INLINE int i_getch_interactive(struct in_str *i)
2793{ 2924{
2794 int ch; 2925 int ch;
2795 do ch = hfgetc(i->file); while (ch == '\0'); 2926 do ch = hfgetc(i->file); while (ch == '\0');
@@ -2797,37 +2928,88 @@ static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
2797} 2928}
2798#endif /* !INTERACTIVE */ 2929#endif /* !INTERACTIVE */
2799 2930
2931#if ENABLE_HUSH_ALIAS
2932static ALWAYS_INLINE int i_has_alias_buffer(struct in_str *i)
2933{
2934 return i->saved_ibuf && i->p && *i->p;
2935}
2936static void i_prepend_to_alias_buffer(struct in_str *i, char *prepend, char ch)
2937{
2938 if (i->saved_ibuf) {
2939 /* Nested alias expansion example:
2940 * alias a='b c'; alias b='echo A:'
2941 * a
2942 * ^^^ runs "echo A: c"
2943 */
2944 char *old = i->albuf;
2945 //bb_error_msg("before'%s' p'%s'", i->albuf, i->p);
2946 i->albuf = xasprintf("%s%c%s", prepend, ch, i->p);
2947 i->p = i->albuf;
2948 //bb_error_msg("after'%s' p'%s'", i->albuf, i->p);
2949 free(old);
2950 return;
2951 }
2952 i->saved_ibuf = i->p;
2953 i->p = i->albuf = xasprintf("%s%c", prepend, ch);
2954 //bb_error_msg("albuf'%s'", i->albuf);
2955}
2956static void i_free_alias_buffer(struct in_str *i)
2957{
2958 if (i->saved_ibuf) {
2959 /* We are here if alias expansion has ended just now */
2960 free(i->albuf);
2961 i->p = i->saved_ibuf;
2962 i->saved_ibuf = NULL;
2963/* We re-enable aliases only if expansion has finished, not on command boundaries.
2964 * Example:
2965 * alias a="nice&&a"
2966 * a;a
2967 * This should run "nice" and then "can't execute 'a': No such file or directory",
2968 * then should run "nice" again and then "can't execute 'a': No such file or directory" again
2969 * because the second "a" in alias definition must not expand (to prevent infinite expansion),
2970 * but the "a" after ; must expand (there is no danger of infinite expansion).
2971 * alias a="nice&&nice"
2972 * a;a&&a
2973 * should execute "nice" six times.
2974 */
2975 debug_printf_parse("end of alias\n");
2976 enable_all_aliases();
2977 }
2978}
2979#else
2980# define i_has_alias_buffer(i) 0
2981# define i_free_alias_buffer(i) ((void)0)
2982#endif
2983
2800static int i_getch(struct in_str *i) 2984static int i_getch(struct in_str *i)
2801{ 2985{
2802 int ch; 2986 int ch;
2803 2987
2804 if (!i->file) { 2988 IF_HUSH_ALIAS(again:)
2805 /* string-based in_str */ 2989 if (i->p) {
2990 /* string-based in_str, or line editing buffer, or alias buffer */
2806 ch = (unsigned char)*i->p; 2991 ch = (unsigned char)*i->p;
2807 if (ch != '\0') { 2992 if (ch != '\0') {
2808 i->p++; 2993 i->p++;
2809 i->last_char = ch; 2994 goto out;
2810#if ENABLE_HUSH_LINENO_VAR 2995 }
2811 if (ch == '\n') { 2996#if ENABLE_HUSH_ALIAS
2812 G.parse_lineno++; 2997 if (i->saved_ibuf) {
2813 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); 2998 i_free_alias_buffer(i);
2814 } 2999 goto again;
3000 }
2815#endif 3001#endif
2816 return ch; 3002 /* If string-based in_str, end-of-string is EOF */
3003 if (!i->file) {
3004 debug_printf("i_getch: got EOF from string\n");
3005 return EOF;
2817 } 3006 }
2818 return EOF;
2819 } 3007 }
2820 3008
2821 /* FILE-based in_str */ 3009 /* FILE-based in_str */
2822 3010
2823#if ENABLE_FEATURE_EDITING 3011 /* Use what i_peek / i_peek2 saved (if anything) */
2824 /* This can be stdin, check line editing char[] buffer */ 3012 /* peek_buf[] is an int array, not char - can contain EOF */
2825 if (i->p && *i->p != '\0') {
2826 ch = (unsigned char)*i->p++;
2827 goto out;
2828 }
2829#endif
2830 /* peek_buf[] is an int array, not char. Can contain EOF. */
2831 ch = i->peek_buf[0]; 3013 ch = i->peek_buf[0];
2832 if (ch != 0) { 3014 if (ch != 0) {
2833 int ch2 = i->peek_buf[1]; 3015 int ch2 = i->peek_buf[1];
@@ -2838,70 +3020,71 @@ static int i_getch(struct in_str *i)
2838 goto out; 3020 goto out;
2839 } 3021 }
2840 3022
2841 ch = fgetc_interactive(i); 3023 ch = i_getch_interactive(i);
2842 out: 3024 out:
2843 debug_printf("file_get: got '%c' %d\n", ch, ch); 3025 debug_printf("i_getch: got '%c' %d\n", ch, ch);
2844 i->last_char = ch;
2845#if ENABLE_HUSH_LINENO_VAR 3026#if ENABLE_HUSH_LINENO_VAR
2846 if (ch == '\n') { 3027 if (ch == '\n') {
2847 G.parse_lineno++; 3028 G.parse_lineno++;
2848 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); 3029 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
2849 } 3030 }
2850#endif 3031#endif
3032 i->last_char = ch;
2851 return ch; 3033 return ch;
2852} 3034}
2853 3035
3036/* Called often, has optimizations to make it faster:
3037 * = May return NUL instead of EOF.
3038 * It's ok because use cases are "got '&', peek next ch to see whether it is '&'"
3039 * = Must not be called after '\n' (it would cause unexpected line editing prompt).
3040 */
2854static int i_peek(struct in_str *i) 3041static int i_peek(struct in_str *i)
2855{ 3042{
2856 int ch; 3043 int ch;
2857 3044
2858 if (!i->file) { 3045 if (i->p) {
2859 /* string-based in_str */ 3046 /* string-based in_str, or line editing buffer, or alias buffer */
2860 /* Doesn't report EOF on NUL. None of the callers care. */ 3047#if ENABLE_HUSH_ALIAS
3048 if (*i->p == '\0' && i->saved_ibuf) {
3049 /* corner case: "an_alias&&..." expansion will have
3050 * i->p = "<alias_expansion>&", i->saved_ibuf = "&..."
3051 * and at the end of it, we must not return NUL,
3052 * this would logically split && into & & during parsing.
3053 */
3054 return (unsigned char)*i->saved_ibuf;
3055 }
3056#endif
2861 return (unsigned char)*i->p; 3057 return (unsigned char)*i->p;
2862 } 3058 }
2863 3059
2864 /* FILE-based in_str */ 3060 /* Now we know it is a file-based in_str. */
2865 3061
2866#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
2867 /* This can be stdin, check line editing char[] buffer */
2868 if (i->p && *i->p != '\0')
2869 return (unsigned char)*i->p;
2870#endif
2871 /* peek_buf[] is an int array, not char. Can contain EOF. */ 3062 /* peek_buf[] is an int array, not char. Can contain EOF. */
2872 ch = i->peek_buf[0]; 3063 ch = i->peek_buf[0];
2873 if (ch != 0) 3064 if (ch == 0) {
2874 return ch; 3065 /* We did not read it yet, get it now */
3066 do ch = hfgetc(i->file); while (ch == '\0');
3067 i->peek_buf[0] = ch;
3068 }
2875 3069
2876 /* Need to get a new char */
2877 ch = fgetc_interactive(i);
2878 debug_printf("file_peek: got '%c' %d\n", ch, ch); 3070 debug_printf("file_peek: got '%c' %d\n", ch, ch);
2879
2880 /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
2881#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
2882 if (i->p) {
2883 i->p -= 1;
2884 return ch;
2885 }
2886#endif
2887 i->peek_buf[0] = ch;
2888 /*i->peek_buf[1] = 0; - already is */
2889 return ch; 3071 return ch;
2890} 3072}
2891 3073
2892/* Only ever called if i_peek() was called, and did not return EOF. 3074/* Called if i_peek() was called, and saw an ordinary char
2893 * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, 3075 * (not EOF, not NUL, not end-of-line).
2894 * not end-of-line. Therefore we never need to read a new editing line here. 3076 * Therefore we never need to read a new editing line here.
2895 */ 3077 */
2896static int i_peek2(struct in_str *i) 3078static int i_peek2(struct in_str *i)
2897{ 3079{
2898 int ch; 3080 int ch;
2899 3081
2900 /* There are two cases when i->p[] buffer exists. 3082 /* There are three cases when i->p[] buffer exists.
3083 * (0) alias expansion in progress.
2901 * (1) it's a string in_str. 3084 * (1) it's a string in_str.
2902 * (2) It's a file, and we have a saved line editing buffer. 3085 * (2) It's a file, and we have a saved line editing buffer.
2903 * In both cases, we know that i->p[0] exists and not NUL, and 3086 * In all cases, we know that i->p[0] exists and not NUL.
2904 * the peek2 result is in i->p[1]. 3087 * The peek2 result is in i->p[1].
2905 */ 3088 */
2906 if (i->p) 3089 if (i->p)
2907 return (unsigned char)i->p[1]; 3090 return (unsigned char)i->p[1];
@@ -3003,7 +3186,7 @@ static void o_grow_by(o_string *o, int len)
3003 } 3186 }
3004} 3187}
3005 3188
3006static void o_addchr(o_string *o, int ch) 3189static ALWAYS_INLINE void INLINED_o_addchr(o_string *o, int ch)
3007{ 3190{
3008 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); 3191 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
3009 if (o->length < o->maxlen) { 3192 if (o->length < o->maxlen) {
@@ -3017,6 +3200,10 @@ static void o_addchr(o_string *o, int ch)
3017 o_grow_by(o, 1); 3200 o_grow_by(o, 1);
3018 goto add; 3201 goto add;
3019} 3202}
3203static void o_addchr(o_string *o, int ch)
3204{
3205 INLINED_o_addchr(o, ch);
3206}
3020 3207
3021#if 0 3208#if 0
3022/* Valid only if we know o_string is not empty */ 3209/* Valid only if we know o_string is not empty */
@@ -3127,7 +3314,7 @@ static void o_addqchr(o_string *o, int ch)
3127static void o_addQchr(o_string *o, int ch) 3314static void o_addQchr(o_string *o, int ch)
3128{ 3315{
3129 int sz = 1; 3316 int sz = 1;
3130 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) 3317 if ((o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)
3131 && strchr("*?[-\\" MAYBE_BRACES, ch) 3318 && strchr("*?[-\\" MAYBE_BRACES, ch)
3132 ) { 3319 ) {
3133 sz++; 3320 sz++;
@@ -3170,7 +3357,7 @@ static void o_addqblock(o_string *o, const char *str, int len)
3170 3357
3171static void o_addQblock(o_string *o, const char *str, int len) 3358static void o_addQblock(o_string *o, const char *str, int len)
3172{ 3359{
3173 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) { 3360 if (!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)) {
3174 o_addblock(o, str, len); 3361 o_addblock(o, str, len);
3175 return; 3362 return;
3176 } 3363 }
@@ -3182,6 +3369,11 @@ static void o_addQstr(o_string *o, const char *str)
3182 o_addQblock(o, str, strlen(str)); 3369 o_addQblock(o, str, strlen(str));
3183} 3370}
3184 3371
3372static void o_addqstr(o_string *o, const char *str)
3373{
3374 o_addqblock(o, str, strlen(str));
3375}
3376
3185/* A special kind of o_string for $VAR and `cmd` expansion. 3377/* A special kind of o_string for $VAR and `cmd` expansion.
3186 * It contains char* list[] at the beginning, which is grown in 16 element 3378 * It contains char* list[] at the beginning, which is grown in 16 element
3187 * increments. Actual string data starts at the next multiple of 16 * (char*). 3379 * increments. Actual string data starts at the next multiple of 16 * (char*).
@@ -3200,11 +3392,11 @@ static void debug_print_list(const char *prefix, o_string *o, int n)
3200 int i = 0; 3392 int i = 0;
3201 3393
3202 indent(); 3394 indent();
3203 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", 3395 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d do_glob:%d has_quoted:%d globprotect:%d\n",
3204 prefix, list, n, string_start, o->length, o->maxlen, 3396 prefix, list, n, string_start, o->length, o->maxlen,
3205 !!(o->o_expflags & EXP_FLAG_GLOB), 3397 !!(o->o_expflags & EXP_FLAG_DO_GLOBBING),
3206 o->has_quoted_part, 3398 o->has_quoted_part,
3207 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 3399 !!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS));
3208 while (i < n) { 3400 while (i < n) {
3209 indent(); 3401 indent();
3210 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i], 3402 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
@@ -3327,7 +3519,19 @@ static int glob_needed(const char *s)
3327 s += 2; 3519 s += 2;
3328 continue; 3520 continue;
3329 } 3521 }
3330 if (*s == '*' || *s == '[' || *s == '?' || *s == '{') 3522 if (*s == '*' || *s == '?')
3523 return 1;
3524 /* Only force glob if "..[..].." detected.
3525 * Not merely "[", "[[", "][" etc.
3526 * Optimization to avoid glob()
3527 * on "[ COND ]" and "[[ COND ]]":
3528 * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done'
3529 * shouldn't be doing 50000 stat("[").
3530 * (Can do it for "{" too, but it's not a common case).
3531 */
3532 if (*s == '[' && strchr(s+1, ']'))
3533 return 1;
3534 if (*s == '{' /* && strchr(s+1, '}')*/)
3331 return 1; 3535 return 1;
3332 s++; 3536 s++;
3333 } 3537 }
@@ -3518,7 +3722,16 @@ static int glob_needed(const char *s)
3518 s += 2; 3722 s += 2;
3519 continue; 3723 continue;
3520 } 3724 }
3521 if (*s == '*' || *s == '[' || *s == '?') 3725 if (*s == '*' || *s == '?')
3726 return 1;
3727 /* Only force glob if "..[..].." detected.
3728 * Not merely "[", "[[", "][" etc.
3729 * Optimization to avoid glob()
3730 * on "[ COND ]" and "[[ COND ]]":
3731 * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done'
3732 * shouldn't be doing 50000 stat("[").
3733 */
3734 if (*s == '[' && strchr(s+1, ']'))
3522 return 1; 3735 return 1;
3523 s++; 3736 s++;
3524 } 3737 }
@@ -3585,11 +3798,11 @@ static int perform_glob(o_string *o, int n)
3585 3798
3586#endif /* !HUSH_BRACE_EXPANSION */ 3799#endif /* !HUSH_BRACE_EXPANSION */
3587 3800
3588/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. 3801/* If o->o_expflags & EXP_FLAG_DO_GLOBBING, glob the string so far remembered.
3589 * Otherwise, just finish current list[] and start new */ 3802 * Otherwise, just finish current list[] and start new */
3590static int o_save_ptr(o_string *o, int n) 3803static int o_save_ptr(o_string *o, int n)
3591{ 3804{
3592 if (o->o_expflags & EXP_FLAG_GLOB) { 3805 if (o->o_expflags & EXP_FLAG_DO_GLOBBING) {
3593 /* If o->has_empty_slot, list[n] was already globbed 3806 /* If o->has_empty_slot, list[n] was already globbed
3594 * (if it was requested back then when it was filled) 3807 * (if it was requested back then when it was filled)
3595 * so don't do that again! */ 3808 * so don't do that again! */
@@ -3665,8 +3878,8 @@ static struct pipe *free_pipe(struct pipe *pi)
3665 //command->group_as_string = NULL; 3878 //command->group_as_string = NULL;
3666#endif 3879#endif
3667 for (r = command->redirects; r; r = rnext) { 3880 for (r = command->redirects; r; r = rnext) {
3668 debug_printf_clean(" redirect %d%s", 3881 debug_printf_clean(" redirect %d%.3s",
3669 r->rd_fd, redir_table[r->rd_type].descrip); 3882 r->rd_fd, redir_table[r->rd_type].descrip3);
3670 /* guard against the case >$FOO, where foo is unset or blank */ 3883 /* guard against the case >$FOO, where foo is unset or blank */
3671 if (r->rd_filename) { 3884 if (r->rd_filename) {
3672 debug_printf_clean(" fname:'%s'\n", r->rd_filename); 3885 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
@@ -3735,7 +3948,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
3735# endif 3948# endif
3736# if ENABLE_HUSH_CASE 3949# if ENABLE_HUSH_CASE
3737 [RES_CASE ] = "CASE" , 3950 [RES_CASE ] = "CASE" ,
3738 [RES_CASE_IN ] = "CASE_IN" , 3951 [RES_CASE_IN] = "CASE_IN",
3739 [RES_MATCH] = "MATCH", 3952 [RES_MATCH] = "MATCH",
3740 [RES_CASE_BODY] = "CASE_BODY", 3953 [RES_CASE_BODY] = "CASE_BODY",
3741 [RES_ESAC ] = "ESAC" , 3954 [RES_ESAC ] = "ESAC" ,
@@ -3744,11 +3957,13 @@ static void debug_print_tree(struct pipe *pi, int lvl)
3744 [RES_SNTX ] = "SNTX" , 3957 [RES_SNTX ] = "SNTX" ,
3745 }; 3958 };
3746 static const char *const CMDTYPE[] ALIGN_PTR = { 3959 static const char *const CMDTYPE[] ALIGN_PTR = {
3747 "{}", 3960 "{}", //CMD_NORMAL
3748 "()", 3961 "()", //CMD_SUBSHELL
3749 "[noglob]", 3962 "[test2]", //CMD_TEST2_SINGLEWORD_NOGLOB
3963 "[noglob]", //CMD_SINGLEWORD_NOGLOB
3750# if ENABLE_HUSH_FUNCTIONS 3964# if ENABLE_HUSH_FUNCTIONS
3751 "func()", 3965 "func()", //CMD_FUNCTION_KWORD
3966 "funcdef", //CMD_FUNCDEF
3752# endif 3967# endif
3753 }; 3968 };
3754 3969
@@ -3812,9 +4027,11 @@ static struct pipe *new_pipe(void)
3812 return pi; 4027 return pi;
3813} 4028}
3814 4029
3815/* Command (member of a pipe) is complete, or we start a new pipe 4030/* Parsing of command (member of a pipe) is completed.
3816 * if ctx->command is NULL. 4031 * If it's not null, a new empty command structure is added
3817 * No errors possible here. 4032 * to the current pipe, and ctx->command is set to it.
4033 * Return the current number of already parsed commands in the pipe.
4034 * No errors are possible here.
3818 */ 4035 */
3819static int done_command(struct parse_context *ctx) 4036static int done_command(struct parse_context *ctx)
3820{ 4037{
@@ -3830,17 +4047,16 @@ static int done_command(struct parse_context *ctx)
3830 ctx->pending_redirect = NULL; 4047 ctx->pending_redirect = NULL;
3831 } 4048 }
3832#endif 4049#endif
3833
3834 if (command) { 4050 if (command) {
3835 if (IS_NULL_CMD(command)) { 4051 if (IS_NULL_CMD(command)) {
3836 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); 4052 debug_printf_parse("done_command: skipping null cmd, num_cmds:%d\n", pi->num_cmds);
3837 goto clear_and_ret; 4053 goto clear_and_ret;
3838 } 4054 }
3839 pi->num_cmds++; 4055 pi->num_cmds++;
3840 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); 4056 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
3841 //debug_print_tree(ctx->list_head, 20); 4057 //debug_print_tree(ctx->list_head, 20);
3842 } else { 4058 } else {
3843 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); 4059 debug_printf_parse("done_command: initializing, num_cmds:%d\n", pi->num_cmds);
3844 } 4060 }
3845 4061
3846 /* Only real trickiness here is that the uncommitted 4062 /* Only real trickiness here is that the uncommitted
@@ -3851,21 +4067,36 @@ static int done_command(struct parse_context *ctx)
3851 memset(command, 0, sizeof(*command)); 4067 memset(command, 0, sizeof(*command));
3852#if ENABLE_HUSH_LINENO_VAR 4068#if ENABLE_HUSH_LINENO_VAR
3853 command->lineno = G.parse_lineno; 4069 command->lineno = G.parse_lineno;
3854 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno); 4070 debug_printf_parse("command->lineno=G.parse_lineno (%u)\n", G.parse_lineno);
3855#endif 4071#endif
3856 return pi->num_cmds; /* used only for 0/nonzero check */ 4072 return pi->num_cmds;
3857} 4073}
3858 4074
3859static void done_pipe(struct parse_context *ctx, pipe_style type) 4075/* Parsing of a pipe is completed.
4076 * Finish parsing current command via done_command().
4077 * (If the pipe is not empty, but done_command() did not change the number
4078 * of commands in pipe, return value is 1. Used for catching syntax errors)
4079 * Then append a new pipe with one empty command.
4080 */
4081static int done_pipe(struct parse_context *ctx, pipe_style type)
3860{ 4082{
3861 int not_null; 4083 int num_cmds;
4084 int oldnum;
4085 int last_cmd_is_null;
3862 4086
3863 debug_printf_parse("done_pipe entered, followup %d\n", type); 4087 debug_printf_parse("done_pipe entered, followup %d\n", type);
3864 /* Close previous command */ 4088 /* Close previous command */
3865 not_null = done_command(ctx); 4089 oldnum = ctx->pipe->num_cmds;
4090 num_cmds = done_command(ctx);
4091
4092 /* This is true if this was a non-empty pipe,
4093 * but done_command didn't add a new member to it.
4094 * Usually it is a syntax error.
4095 * Examples: "date | | ...", "date | ; ..."
4096 */
4097 last_cmd_is_null = (oldnum != 0 && num_cmds == oldnum);
4098
3866#if HAS_KEYWORDS 4099#if HAS_KEYWORDS
3867 ctx->pipe->pi_inverted = ctx->ctx_inverted;
3868 ctx->ctx_inverted = 0;
3869 ctx->pipe->res_word = ctx->ctx_res_w; 4100 ctx->pipe->res_word = ctx->ctx_res_w;
3870#endif 4101#endif
3871 if (type == PIPE_BG && ctx->list_head != ctx->pipe) { 4102 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
@@ -3904,7 +4135,7 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
3904 ctx->list_head = ctx->pipe = pi; 4135 ctx->list_head = ctx->pipe = pi;
3905 /* for cases like "cmd && &", do not be tricked by last command 4136 /* for cases like "cmd && &", do not be tricked by last command
3906 * being null - the entire {...} & is NOT null! */ 4137 * being null - the entire {...} & is NOT null! */
3907 not_null = 1; 4138 num_cmds = 1;
3908 } else { 4139 } else {
3909 no_conv: 4140 no_conv:
3910 ctx->pipe->followup = type; 4141 ctx->pipe->followup = type;
@@ -3912,8 +4143,8 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
3912 4143
3913 /* Without this check, even just <enter> on command line generates 4144 /* Without this check, even just <enter> on command line generates
3914 * tree of three NOPs (!). Which is harmless but annoying. 4145 * tree of three NOPs (!). Which is harmless but annoying.
3915 * IOW: it is safe to do it unconditionally. */ 4146 * IOW: it is safe to do the following unconditionally. */
3916 if (not_null 4147 if (num_cmds != 0
3917#if ENABLE_HUSH_IF 4148#if ENABLE_HUSH_IF
3918 || ctx->ctx_res_w == RES_FI 4149 || ctx->ctx_res_w == RES_FI
3919#endif 4150#endif
@@ -3928,37 +4159,39 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
3928 ) { 4159 ) {
3929 struct pipe *new_p; 4160 struct pipe *new_p;
3930 debug_printf_parse("done_pipe: adding new pipe: " 4161 debug_printf_parse("done_pipe: adding new pipe: "
3931 "not_null:%d ctx->ctx_res_w:%d\n", 4162 "num_cmds:%d ctx->ctx_res_w:%d\n",
3932 not_null, ctx->ctx_res_w); 4163 num_cmds, ctx->ctx_res_w);
3933 new_p = new_pipe(); 4164 new_p = new_pipe();
3934 ctx->pipe->next = new_p; 4165 ctx->pipe->next = new_p;
3935 ctx->pipe = new_p; 4166 ctx->pipe = new_p;
3936 /* RES_THEN, RES_DO etc are "sticky" - 4167 /* RES_THEN, RES_DO etc are "sticky" -
3937 * they remain set for pipes inside if/while. 4168 * they remain set for pipes inside if/while.
3938 * This is used to control execution. 4169 * This is used to control execution.
3939 * RES_FOR and RES_IN are NOT sticky (needed to support
3940 * cases where variable or value happens to match a keyword):
3941 */ 4170 */
3942#if ENABLE_HUSH_LOOPS 4171#if ENABLE_HUSH_LOOPS
4172 /* RES_FOR and RES_IN are NOT sticky (needed to support
4173 * cases where variable or value happens to match a keyword):
4174 */
3943 if (ctx->ctx_res_w == RES_FOR 4175 if (ctx->ctx_res_w == RES_FOR
3944 || ctx->ctx_res_w == RES_IN) 4176 || ctx->ctx_res_w == RES_IN)
3945 ctx->ctx_res_w = RES_NONE; 4177 ctx->ctx_res_w = RES_NONE;
3946#endif 4178#endif
3947#if ENABLE_HUSH_CASE 4179#if ENABLE_HUSH_CASE
3948 if (ctx->ctx_res_w == RES_MATCH)
3949 ctx->ctx_res_w = RES_CASE_BODY;
3950 if (ctx->ctx_res_w == RES_CASE) 4180 if (ctx->ctx_res_w == RES_CASE)
3951 ctx->ctx_res_w = RES_CASE_IN; 4181 ctx->ctx_res_w = RES_CASE_IN;
4182 if (ctx->ctx_res_w == RES_MATCH)
4183 ctx->ctx_res_w = RES_CASE_BODY;
3952#endif 4184#endif
3953 ctx->command = NULL; /* trick done_command below */
3954 /* Create the memory for command, roughly: 4185 /* Create the memory for command, roughly:
3955 * ctx->pipe->cmds = new struct command; 4186 * ctx->pipe->cmds = new struct command;
3956 * ctx->command = &ctx->pipe->cmds[0]; 4187 * ctx->command = &ctx->pipe->cmds[0];
3957 */ 4188 */
4189 ctx->command = NULL;
3958 done_command(ctx); 4190 done_command(ctx);
3959 //debug_print_tree(ctx->list_head, 10); 4191 //debug_print_tree(ctx->list_head, 10);
3960 } 4192 }
3961 debug_printf_parse("done_pipe return\n"); 4193 debug_printf_parse("done_pipe return: last_cmd_is_null:%d\n", last_cmd_is_null);
4194 return last_cmd_is_null;
3962} 4195}
3963 4196
3964static void initialize_context(struct parse_context *ctx) 4197static void initialize_context(struct parse_context *ctx)
@@ -3972,6 +4205,10 @@ static void initialize_context(struct parse_context *ctx)
3972 * ctx->command = &ctx->pipe->cmds[0]; 4205 * ctx->command = &ctx->pipe->cmds[0];
3973 */ 4206 */
3974 done_command(ctx); 4207 done_command(ctx);
4208 /* If very first arg is "" or '', ctx.word.data may end up NULL.
4209 * Prevent this:
4210 */
4211 ctx->word.data = xzalloc(1); /* start as "", not as NULL */
3975} 4212}
3976 4213
3977/* If a reserved word is found and processed, parse context is modified 4214/* If a reserved word is found and processed, parse context is modified
@@ -4008,39 +4245,39 @@ enum {
4008 FLAG_START = (1 << RES_XXXX ), 4245 FLAG_START = (1 << RES_XXXX ),
4009}; 4246};
4010 4247
4011static const struct reserved_combo* match_reserved_word(o_string *word) 4248/* Mostly a list of accepted follow-up reserved words.
4012{ 4249 * FLAG_END means we are done with the sequence, and are ready
4013 /* Mostly a list of accepted follow-up reserved words. 4250 * to turn the compound list into a command.
4014 * FLAG_END means we are done with the sequence, and are ready 4251 * FLAG_START means the word must start a new compound list.
4015 * to turn the compound list into a command. 4252 */
4016 * FLAG_START means the word must start a new compound list. 4253static const struct reserved_combo reserved_list[] ALIGN4 = {
4017 */
4018 static const struct reserved_combo reserved_list[] ALIGN4 = {
4019# if ENABLE_HUSH_IF 4254# if ENABLE_HUSH_IF
4020 { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, 4255 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
4021 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, 4256 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
4022 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, 4257 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
4023 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, 4258 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
4024 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, 4259 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
4025 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, 4260 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
4026# endif 4261# endif
4027# if ENABLE_HUSH_LOOPS 4262# if ENABLE_HUSH_LOOPS
4028 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, 4263 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
4029 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, 4264 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4030 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, 4265 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4031 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, 4266 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
4032 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, 4267 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
4033 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, 4268 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
4034# endif 4269# endif
4035# if ENABLE_HUSH_CASE 4270# if ENABLE_HUSH_CASE
4036 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, 4271 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
4037 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, 4272 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
4038# endif 4273# endif
4039 }; 4274};
4275static const struct reserved_combo* match_reserved_word(const char *word)
4276{
4040 const struct reserved_combo *r; 4277 const struct reserved_combo *r;
4041 4278
4042 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { 4279 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
4043 if (strcmp(word->data, r->literal) == 0) 4280 if (strcmp(word, r->literal) == 0)
4044 return r; 4281 return r;
4045 } 4282 }
4046 return NULL; 4283 return NULL;
@@ -4051,16 +4288,36 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4051{ 4288{
4052# if ENABLE_HUSH_CASE 4289# if ENABLE_HUSH_CASE
4053 static const struct reserved_combo reserved_match = { 4290 static const struct reserved_combo reserved_match = {
4054 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC 4291 "", RES_MATCH, NOT_ASSIGNMENT, FLAG_MATCH | FLAG_ESAC
4055 }; 4292 };
4056# endif 4293# endif
4057 const struct reserved_combo *r; 4294 const struct reserved_combo *r;
4058 4295
4059 if (ctx->word.has_quoted_part) 4296# if ENABLE_HUSH_FUNCTION_KEYWORD
4060 return 0; 4297 /* This is ~60 bytes smaller than adding "function" to reserved_list[] */
4061 r = match_reserved_word(&ctx->word); 4298 if (strcmp(ctx->word.data, "function") == 0) {
4299 ctx->command->cmd_type = CMD_FUNCTION_KWORD;
4300 /* Return something harmless !NULL */
4301 return &reserved_list[0];
4302 }
4303# endif
4304
4305 r = match_reserved_word(ctx->word.data);
4062 if (!r) 4306 if (!r)
4063 return r; /* NULL */ 4307 return r; /* NULL */
4308# if ENABLE_HUSH_CASE /* "case" syntax has a curveball */
4309 if (ctx->ctx_res_w == RES_MATCH
4310 && r->res != RES_ESAC
4311 ) {
4312 /* We are at WORD in ";; WORD" or "case .. in WORD".
4313 * Here, only "esac" is a keyword.
4314 * Else WORD is a case pattern, can be keyword-like:
4315 * if) echo got_if;;
4316 * is allowed.
4317 */
4318 return NULL;
4319 }
4320# endif
4064 4321
4065 debug_printf("found reserved word %s, res %d\n", r->literal, r->res); 4322 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
4066# if ENABLE_HUSH_CASE 4323# if ENABLE_HUSH_CASE
@@ -4070,11 +4327,13 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4070 } else 4327 } else
4071# endif 4328# endif
4072 if (r->flag == 0) { /* '!' */ 4329 if (r->flag == 0) { /* '!' */
4073 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ 4330 if (ctx->pipe->num_cmds != 0 /* bash disallows: nice | ! cat */
4074 syntax_error("! ! command"); 4331 /* || ctx->pipe->pi_inverted - bash used to disallow "! ! true" bash 5.2.15 allows it */
4332 ) {
4333 syntax_error_unexpected_ch('!');
4075 ctx->ctx_res_w = RES_SNTX; 4334 ctx->ctx_res_w = RES_SNTX;
4076 } 4335 }
4077 ctx->ctx_inverted = 1; 4336 ctx->pipe->pi_inverted = 1 - ctx->pipe->pi_inverted;
4078 return r; 4337 return r;
4079 } 4338 }
4080 if (r->flag & FLAG_START) { 4339 if (r->flag & FLAG_START) {
@@ -4141,8 +4400,11 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4141} 4400}
4142#endif /* HAS_KEYWORDS */ 4401#endif /* HAS_KEYWORDS */
4143 4402
4144/* Word is complete, look at it and update parsing context. 4403/* Parsing of a word is complete.
4145 * Normal return is 0. Syntax errors return 1. 4404 * Look at it and update current command:
4405 * update current command's argv/cmd_type/etc, fill in redirect name and type,
4406 * check reserved-ness and assignment-ness, etc...
4407 * Normal return is 0. Syntax errors print error message and return 1.
4146 * Note: on return, word is reset, but not o_free'd! 4408 * Note: on return, word is reset, but not o_free'd!
4147 */ 4409 */
4148static int done_word(struct parse_context *ctx) 4410static int done_word(struct parse_context *ctx)
@@ -4150,8 +4412,8 @@ static int done_word(struct parse_context *ctx)
4150 struct command *command = ctx->command; 4412 struct command *command = ctx->command;
4151 4413
4152 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command); 4414 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
4153 if (ctx->word.length == 0 && !ctx->word.has_quoted_part) { 4415 if (IS_NULL_WORD(ctx->word)) {
4154 debug_printf_parse("done_word return 0: true null, ignored\n"); 4416 debug_printf_parse("done_word return 0: no word, ignored\n");
4155 return 0; 4417 return 0;
4156 } 4418 }
4157 4419
@@ -4178,7 +4440,7 @@ static int done_word(struct parse_context *ctx)
4178// as written: 4440// as written:
4179// <<EOF$t 4441// <<EOF$t
4180// <<EOF$((1)) 4442// <<EOF$((1))
4181// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug] 4443// <<EOF`true` [bash 4.3.43 bug: this case also makes heredoc "quoted", a-la <<"EOF". Fixed by 5.2.15]
4182 4444
4183 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data); 4445 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
4184 /* Cater for >\file case: 4446 /* Cater for >\file case:
@@ -4195,124 +4457,124 @@ static int done_word(struct parse_context *ctx)
4195 } 4457 }
4196 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data); 4458 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
4197 ctx->pending_redirect = NULL; 4459 ctx->pending_redirect = NULL;
4198 } else { 4460 goto ret;
4461 }
4462
4199#if HAS_KEYWORDS 4463#if HAS_KEYWORDS
4200# if ENABLE_HUSH_CASE
4201 if (ctx->ctx_dsemicolon
4202 && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4203 ) {
4204 /* already done when ctx_dsemicolon was set to 1: */
4205 /* ctx->ctx_res_w = RES_MATCH; */
4206 ctx->ctx_dsemicolon = 0;
4207 } else
4208# endif
4209# if defined(CMD_TEST2_SINGLEWORD_NOGLOB) 4464# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4210 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB 4465 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4211 && strcmp(ctx->word.data, "]]") == 0 4466 && !ctx->word.has_quoted_part
4212 ) { 4467 && strcmp(ctx->word.data, "]]") == 0
4213 /* allow "[[ ]] >file" etc */ 4468 ) {
4214 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 4469 /* End test2-specific parsing rules */
4215 } else 4470 /* Allow "[[ ]] >file" etc (> is a redirect symbol again) */
4471 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4472 } else
4216# endif 4473# endif
4217 if (!command->argv /* if it's the first word... */ 4474 /* Is it a place where keyword can appear? */
4475//FIXME: this is wrong, it allows invalid syntax: { echo t; } if true; then echo YES; fi
4476 if ((!command->argv /* if it's the first word of command... */
4477# if ENABLE_HUSH_FUNCTIONS
4478 || command->cmd_type == CMD_FUNCDEF /* ^^^ or after FUNC() {} */
4479# endif
4480 )
4481 && !ctx->word.has_quoted_part /* ""WORD never matches any keywords */
4482 && !command->redirects /* no redirects yet... disallows: </dev/null if true; then... */
4218# if ENABLE_HUSH_LOOPS 4483# if ENABLE_HUSH_LOOPS
4219 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ 4484 && ctx->ctx_res_w != RES_FOR /* not after "for" or "in" */
4220 && ctx->ctx_res_w != RES_IN 4485 && ctx->ctx_res_w != RES_IN
4221# endif 4486# endif
4222# if ENABLE_HUSH_CASE 4487# if ENABLE_HUSH_CASE
4223 && ctx->ctx_res_w != RES_CASE 4488 && ctx->ctx_res_w != RES_CASE /* not after "case" */
4224# endif 4489# endif
4225 ) { 4490 ) {
4226 const struct reserved_combo *reserved; 4491 const struct reserved_combo *reserved;
4227 reserved = reserved_word(ctx); 4492 reserved = reserved_word(ctx);
4228 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); 4493 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
4229 if (reserved) { 4494 if (reserved) {
4230# if ENABLE_HUSH_LINENO_VAR 4495# if ENABLE_HUSH_LINENO_VAR
4231/* Case: 4496/* Case:
4232 * "while ...; do 4497 * while ...; do
4233 * cmd ..." 4498 * CMD
4234 * If we don't close the pipe _now_, immediately after "do", lineno logic 4499 * If we don't close the pipe _now_, immediately after "do", lineno logic
4235 * sees "cmd" as starting at "do" - i.e., at the previous line. 4500 * sees CMD as starting at "do" - i.e., at the previous line.
4236 */ 4501 */
4237 if (0 4502 if (0
4238 IF_HUSH_IF(|| reserved->res == RES_THEN) 4503 IF_HUSH_IF(|| reserved->res == RES_THEN)
4239 IF_HUSH_IF(|| reserved->res == RES_ELIF) 4504 IF_HUSH_IF(|| reserved->res == RES_ELIF)
4240 IF_HUSH_IF(|| reserved->res == RES_ELSE) 4505 IF_HUSH_IF(|| reserved->res == RES_ELSE)
4241 IF_HUSH_LOOPS(|| reserved->res == RES_DO) 4506 IF_HUSH_LOOPS(|| reserved->res == RES_DO)
4242 ) { 4507 ) {
4243 done_pipe(ctx, PIPE_SEQ); 4508 done_pipe(ctx, PIPE_SEQ);
4244 }
4245# endif
4246 o_reset_to_empty_unquoted(&ctx->word);
4247 debug_printf_parse("done_word return %d\n",
4248 (ctx->ctx_res_w == RES_SNTX));
4249 return (ctx->ctx_res_w == RES_SNTX);
4250 } 4509 }
4510# endif
4511 o_reset_to_empty_unquoted(&ctx->word);
4512 debug_printf_parse("done_word return %d\n",
4513 (ctx->ctx_res_w == RES_SNTX));
4514 return (ctx->ctx_res_w == RES_SNTX);
4515 }
4251# if defined(CMD_TEST2_SINGLEWORD_NOGLOB) 4516# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4252 if (strcmp(ctx->word.data, "[[") == 0) { 4517 if (strcmp(ctx->word.data, "[[") == 0) {
4253 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; 4518 /* Inside [[ ]], parsing rules are different */
4254 } else 4519 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4520 } else
4255# endif 4521# endif
4256# if defined(CMD_SINGLEWORD_NOGLOB) 4522# if defined(CMD_SINGLEWORD_NOGLOB)
4257 if (0 4523 if (0
4258 /* In bash, local/export/readonly are special, args 4524 /* In bash, local/export/readonly are special, args
4259 * are assignments and therefore expansion of them 4525 * are assignments and therefore expansion of them
4260 * should be "one-word" expansion: 4526 * should be "one-word" expansion:
4261 * $ export i=`echo 'a b'` # one arg: "i=a b" 4527 * $ export i=`echo 'a b'` # one arg: "i=a b"
4262 * compare with: 4528 * compare with:
4263 * $ ls i=`echo 'a b'` # two args: "i=a" and "b" 4529 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
4264 * ls: cannot access i=a: No such file or directory 4530 * ls: cannot access i=a: No such file or directory
4265 * ls: cannot access b: No such file or directory 4531 * ls: cannot access b: No such file or directory
4266 * Note: bash 3.2.33(1) does this only if export word 4532 * Note: bash 3.2.33(1) does this only if export word
4267 * itself is not quoted: 4533 * itself is not quoted:
4268 * $ export i=`echo 'aaa bbb'`; echo "$i" 4534 * $ export i=`echo 'aaa bbb'`; echo "$i"
4269 * aaa bbb 4535 * aaa bbb
4270 * $ "export" i=`echo 'aaa bbb'`; echo "$i" 4536 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
4271 * aaa 4537 * aaa
4272 */ 4538 */
4273 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0) 4539 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
4274 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0) 4540 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
4275 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0) 4541 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
4276 ) { 4542 ) {
4277 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 4543 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4278 } 4544 }
4279# else 4545# else
4280 { /* empty block to pair "if ... else" */ } 4546 { /* empty block to pair "if ... else" */ }
4281# endif 4547# endif
4282 } 4548 }
4283#endif /* HAS_KEYWORDS */ 4549#endif /* HAS_KEYWORDS */
4284 4550
4285 if (command->group) { 4551 if (command->group) {
4286 /* "{ echo foo; } echo bar" - bad */ 4552 /* "{ echo foo; } echo bar" - bad */
4287 syntax_error_at(ctx->word.data); 4553 syntax_error_at(ctx->word.data);
4288 debug_printf_parse("done_word return 1: syntax error, " 4554 debug_printf_parse("done_word return 1: syntax error, "
4289 "groups and arglists don't mix\n"); 4555 "groups and arglists don't mix\n");
4290 return 1; 4556 return 1;
4291 } 4557 }
4292 4558
4293 /* If this word wasn't an assignment, next ones definitely 4559 /* If this word wasn't an assignment, next ones definitely
4294 * can't be assignments. Even if they look like ones. */ 4560 * can't be assignments. Even if they look like ones. */
4295 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT 4561 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT) {
4296 && ctx->is_assignment != WORD_IS_KEYWORD 4562 ctx->is_assignment = NOT_ASSIGNMENT;
4297 ) { 4563 } else {
4298 ctx->is_assignment = NOT_ASSIGNMENT; 4564 /* This was an assignment word, next one maybe will be too */
4299 } else { 4565 command->assignment_cnt++;
4300 if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) { 4566 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4301 command->assignment_cnt++; 4567 ctx->is_assignment = MAYBE_ASSIGNMENT;
4302 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4303 }
4304 debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
4305 ctx->is_assignment = MAYBE_ASSIGNMENT;
4306 }
4307 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4308 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
4309 debug_print_strings("word appended to argv", command->argv);
4310 } 4568 }
4569 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4570
4571 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
4572 debug_print_strings("word appended to argv", command->argv);
4311 4573
4312#if ENABLE_HUSH_LOOPS 4574#if ENABLE_HUSH_LOOPS
4313 if (ctx->ctx_res_w == RES_FOR) { 4575 if (ctx->ctx_res_w == RES_FOR) {
4314 if (ctx->word.has_quoted_part 4576 if (ctx->word.has_quoted_part
4315 || endofname(command->argv[0])[0] != '\0' 4577 || endofname(ctx->word.data)[0] != '\0'
4316 ) { 4578 ) {
4317 /* bash says just "not a valid identifier" */ 4579 /* bash says just "not a valid identifier" */
4318 syntax_error("bad for loop variable"); 4580 syntax_error("bad for loop variable");
@@ -4327,13 +4589,16 @@ static int done_word(struct parse_context *ctx)
4327#endif 4589#endif
4328#if ENABLE_HUSH_CASE 4590#if ENABLE_HUSH_CASE
4329 /* Force CASE to have just one word */ 4591 /* Force CASE to have just one word */
4330 if (ctx->ctx_res_w == RES_CASE) { 4592 if (ctx->ctx_res_w == RES_CASE)
4331 done_pipe(ctx, PIPE_SEQ); 4593 done_pipe(ctx, PIPE_SEQ);
4332 } 4594//TODO syntax check?
4595// if (ctx->ctx_res_w == RES_MATCH)
4596// eat all following spaces and tabs (but not newlines),
4597// check that next char is | or )
4333#endif 4598#endif
4334 4599
4600 ret:
4335 o_reset_to_empty_unquoted(&ctx->word); 4601 o_reset_to_empty_unquoted(&ctx->word);
4336
4337 debug_printf_parse("done_word return 0\n"); 4602 debug_printf_parse("done_word return 0\n");
4338 return 0; 4603 return 0;
4339} 4604}
@@ -4368,7 +4633,7 @@ static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4368 } 4633 }
4369 d = 0; 4634 d = 0;
4370 ok = 0; 4635 ok = 0;
4371 while (ch != EOF && isdigit(ch)) { 4636 while (/*ch != EOF &&*/ isdigit(ch)) {
4372 d = d*10 + (ch-'0'); 4637 d = d*10 + (ch-'0');
4373 ok = 1; 4638 ok = 1;
4374 ch = i_getch(input); 4639 ch = i_getch(input);
@@ -4396,31 +4661,31 @@ static int parse_redirect(struct parse_context *ctx,
4396 int dup_num; 4661 int dup_num;
4397 4662
4398 dup_num = REDIRFD_TO_FILE; 4663 dup_num = REDIRFD_TO_FILE;
4399 if (style != REDIRECT_HEREDOC) { 4664 if (style != REDIRECT_HEREDOC && style != REDIRECT_HERESTRING) {
4400 /* Check for a '>&1' type redirect */ 4665 /* Check for a '>&1' type redirect */
4401 dup_num = parse_redir_right_fd(&ctx->as_string, input); 4666 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4402 if (dup_num == REDIRFD_SYNTAX_ERR) 4667 if (dup_num == REDIRFD_SYNTAX_ERR)
4403 return 1; 4668 return 1;
4404 } else { 4669 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4405 int ch = i_peek_and_eat_bkslash_nl(input); 4670 int ch = i_peek_and_eat_bkslash_nl(input);
4406 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ 4671 if (ch == '|') {
4407 if (dup_num) { /* <<-... */ 4672 /* >|FILE redirect ("clobbering" >).
4408 ch = i_getch(input); 4673 * Since we do not support "set -o noclobber" yet,
4409 nommu_addchr(&ctx->as_string, ch); 4674 * >| and > are the same for now. Just eat |.
4410 ch = i_peek(input); 4675 */
4676 ch = i_getch(input);
4677 nommu_addchr(&ctx->as_string, ch);
4678 }
4411 } 4679 }
4412 } 4680 } else if (style == REDIRECT_HEREDOC) {
4413
4414 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4415 int ch = i_peek_and_eat_bkslash_nl(input); 4681 int ch = i_peek_and_eat_bkslash_nl(input);
4416 if (ch == '|') { 4682 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4417 /* >|FILE redirect ("clobbering" >). 4683 if (dup_num) { /* "<<-HEREDOC"? */
4418 * Since we do not support "set -o noclobber" yet,
4419 * >| and > are the same for now. Just eat |.
4420 */
4421 ch = i_getch(input); 4684 ch = i_getch(input);
4422 nommu_addchr(&ctx->as_string, ch); 4685 nommu_addchr(&ctx->as_string, ch);
4423 } 4686 }
4687 } else { /* REDIRECT_HERESTRING */
4688 dup_num = 0; /* make sure no bits like HEREDOC_QUOTED are set */
4424 } 4689 }
4425 4690
4426 /* Create a new redir_struct and append it to the linked list */ 4691 /* Create a new redir_struct and append it to the linked list */
@@ -4434,11 +4699,14 @@ static int parse_redirect(struct parse_context *ctx,
4434 redir->rd_type = style; 4699 redir->rd_type = style;
4435 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; 4700 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
4436 4701
4437 debug_printf_parse("redirect type %d %s\n", redir->rd_fd, 4702 debug_printf_parse("redirect type %d %.3s\n", redir->rd_fd,
4438 redir_table[style].descrip); 4703 redir_table[style].descrip3);
4439 4704
4440 redir->rd_dup = dup_num; 4705 redir->rd_dup = dup_num;
4441 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) { 4706 if (style != REDIRECT_HEREDOC
4707 && style != REDIRECT_HERESTRING
4708 && dup_num != REDIRFD_TO_FILE
4709 ) {
4442 /* Erik had a check here that the file descriptor in question 4710 /* Erik had a check here that the file descriptor in question
4443 * is legit; I postpone that to "run time" 4711 * is legit; I postpone that to "run time"
4444 * A "-" representation of "close me" shows up as a -3 here */ 4712 * A "-" representation of "close me" shows up as a -3 here */
@@ -4447,7 +4715,7 @@ static int parse_redirect(struct parse_context *ctx,
4447 } else { 4715 } else {
4448#if 0 /* Instead we emit error message at run time */ 4716#if 0 /* Instead we emit error message at run time */
4449 if (ctx->pending_redirect) { 4717 if (ctx->pending_redirect) {
4450 /* For example, "cmd > <file" */ 4718 /* For example, "CMD > <FILE" */
4451 syntax_error("invalid redirect"); 4719 syntax_error("invalid redirect");
4452 } 4720 }
4453#endif 4721#endif
@@ -4462,10 +4730,10 @@ static int parse_redirect(struct parse_context *ctx,
4462 * supposed to tell which file descriptor to redirect. This routine 4730 * supposed to tell which file descriptor to redirect. This routine
4463 * looks for such preceding numbers. In an ideal world this routine 4731 * looks for such preceding numbers. In an ideal world this routine
4464 * needs to handle all the following classes of redirects... 4732 * needs to handle all the following classes of redirects...
4465 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo 4733 * echo 2>FILE # redirects fd 2 to FILE, nothing passed to echo
4466 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo 4734 * echo 49>FILE # redirects fd 49 to FILE, nothing passed to echo
4467 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo 4735 * echo -2>FILE # redirects fd 1 to FILE, "-2" passed to echo
4468 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo 4736 * echo 49x>FILE # redirects fd 1 to FILE, "49x" passed to echo
4469 * 4737 *
4470 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html 4738 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
4471 * "2.7 Redirection 4739 * "2.7 Redirection
@@ -4502,7 +4770,7 @@ static char *fetch_till_str(o_string *as_string,
4502{ 4770{
4503 o_string heredoc = NULL_O_STRING; 4771 o_string heredoc = NULL_O_STRING;
4504 unsigned past_EOL; 4772 unsigned past_EOL;
4505 int prev = 0; /* not \ */ 4773 int prev = 0; /* not '\' */
4506 int ch; 4774 int ch;
4507 4775
4508 /* Starting with "" is necessary for this case: 4776 /* Starting with "" is necessary for this case:
@@ -4538,7 +4806,15 @@ static char *fetch_till_str(o_string *as_string,
4538 past_EOL = heredoc.length; 4806 past_EOL = heredoc.length;
4539 /* Get 1st char of next line, possibly skipping leading tabs */ 4807 /* Get 1st char of next line, possibly skipping leading tabs */
4540 do { 4808 do {
4541 ch = i_getch(input); 4809 if (heredoc_flags & HEREDOC_QUOTED)
4810 ch = i_getch(input);
4811 else { /* see heredoc_bkslash_newline3a.tests:
4812 * cat <<-EOF
4813 * <tab>\
4814 * <tab>EOF
4815 */
4816 ch = i_getch_and_eat_bkslash_nl(input);
4817 }
4542 if (ch != EOF) 4818 if (ch != EOF)
4543 nommu_addchr(as_string, ch); 4819 nommu_addchr(as_string, ch);
4544 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); 4820 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
@@ -4564,7 +4840,7 @@ static char *fetch_till_str(o_string *as_string,
4564 prev = 0; /* not '\' */ 4840 prev = 0; /* not '\' */
4565 continue; 4841 continue;
4566 } 4842 }
4567 } 4843 } /* if (\n or EOF) */
4568 if (ch == EOF) { 4844 if (ch == EOF) {
4569 o_free(&heredoc); 4845 o_free(&heredoc);
4570 return NULL; /* error */ 4846 return NULL; /* error */
@@ -4607,6 +4883,11 @@ static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt,
4607 4883
4608 redir->rd_type = REDIRECT_HEREDOC2; 4884 redir->rd_type = REDIRECT_HEREDOC2;
4609 /* redir->rd_dup is (ab)used to indicate <<- */ 4885 /* redir->rd_dup is (ab)used to indicate <<- */
4886 if (!redir->rd_filename) {
4887 /* examples: "echo <<", "echo <<<", "echo <<>" */
4888 syntax_error("missing here document terminator");
4889 return -1;
4890 }
4610 p = fetch_till_str(as_string, input, 4891 p = fetch_till_str(as_string, input,
4611 redir->rd_filename, redir->rd_dup); 4892 redir->rd_filename, redir->rd_dup);
4612 if (!p) { 4893 if (!p) {
@@ -4643,8 +4924,7 @@ static struct pipe *parse_stream(char **pstring,
4643 struct in_str *input, 4924 struct in_str *input,
4644 int end_trigger); 4925 int end_trigger);
4645 4926
4646/* Returns number of heredocs not yet consumed, 4927/* Returns number of heredocs not yet consumed, or -1 on error.
4647 * or -1 on error.
4648 */ 4928 */
4649static int parse_group(struct parse_context *ctx, 4929static int parse_group(struct parse_context *ctx,
4650 struct in_str *input, int ch) 4930 struct in_str *input, int ch)
@@ -4664,25 +4944,36 @@ static int parse_group(struct parse_context *ctx,
4664 4944
4665 debug_printf_parse("parse_group entered\n"); 4945 debug_printf_parse("parse_group entered\n");
4666#if ENABLE_HUSH_FUNCTIONS 4946#if ENABLE_HUSH_FUNCTIONS
4667 if (ch == '(' && !ctx->word.has_quoted_part) { 4947 if ((ch == '('
4948# if ENABLE_HUSH_FUNCTION_KEYWORD
4949 || command->cmd_type == CMD_FUNCTION_KWORD /* "function WORD" */
4950# endif
4951 )
4952 && !ctx->word.has_quoted_part
4953 ) {
4668 if (ctx->word.length) 4954 if (ctx->word.length)
4669 if (done_word(ctx)) 4955 if (done_word(ctx))
4670 return -1; 4956 return -1;
4671 if (!command->argv) 4957 if (!command->argv)
4672 goto skip; /* (... */ 4958 goto skip; /* (... */
4673 if (command->argv[1]) { /* word word ... (... */ 4959 if (command->argv[1]) { /* word word ... (... */
4674 syntax_error_unexpected_ch('('); 4960 if (ch == '(')
4961 syntax_error_unexpected_ch('(');
4962 else
4963 syntax_error("expected funcdef");
4675 return -1; 4964 return -1;
4676 } 4965 }
4677 /* it is "word(..." or "word (..." */ 4966 if (ch == '(') {
4678 do 4967 /* it is "word(..." or "word (..." */
4679 ch = i_getch(input); 4968 do
4680 while (ch == ' ' || ch == '\t'); 4969 ch = i_getch(input);
4681 if (ch != ')') { 4970 while (ch == ' ' || ch == '\t');
4682 syntax_error_unexpected_ch(ch); 4971 if (ch != ')') {
4683 return -1; 4972 syntax_error_unexpected_ch(ch);
4973 return -1;
4974 }
4975 nommu_addchr(&ctx->as_string, ch);
4684 } 4976 }
4685 nommu_addchr(&ctx->as_string, ch);
4686 do 4977 do
4687 ch = i_getch(input); 4978 ch = i_getch(input);
4688 while (ch == ' ' || ch == '\t' || ch == '\n'); 4979 while (ch == ' ' || ch == '\t' || ch == '\n');
@@ -4703,13 +4994,10 @@ static int parse_group(struct parse_context *ctx,
4703 4994
4704#if 0 /* Prevented by caller */ 4995#if 0 /* Prevented by caller */
4705 if (command->argv /* word [word]{... */ 4996 if (command->argv /* word [word]{... */
4706 || ctx->word.length /* word{... */ 4997 || !IS_NULL_WORD(ctx->word) /* word{... or ""{... */
4707 || ctx->word.has_quoted_part /* ""{... */
4708 ) { 4998 ) {
4709 syntax_error(NULL);
4710 debug_printf_parse("parse_group return -1: " 4999 debug_printf_parse("parse_group return -1: "
4711 "syntax error, groups and arglists don't mix\n"); 5000 "syntax error, groups and arglists don't mix\n");
4712 return -1;
4713 } 5001 }
4714#endif 5002#endif
4715 5003
@@ -4987,16 +5275,16 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4987# if BB_MMU 5275# if BB_MMU
4988#define parse_dollar_squote(as_string, dest, input) \ 5276#define parse_dollar_squote(as_string, dest, input) \
4989 parse_dollar_squote(dest, input) 5277 parse_dollar_squote(dest, input)
4990#define as_string NULL
4991# endif 5278# endif
4992static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input) 5279static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
4993{ 5280{
4994 int start; 5281 int start;
4995 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ 5282 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
4996 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch); 5283
4997 if (ch != '\'') 5284 if (ch != '\'')
4998 return 0; 5285 return 0;
4999 5286
5287 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch);
5000 dest->has_quoted_part = 1; 5288 dest->has_quoted_part = 1;
5001 start = dest->length; 5289 start = dest->length;
5002 5290
@@ -5071,7 +5359,6 @@ static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_st
5071 } 5359 }
5072 5360
5073 return 1; 5361 return 1;
5074# undef as_string
5075} 5362}
5076#else 5363#else
5077# define parse_dollar_squote(as_string, dest, input) 0 5364# define parse_dollar_squote(as_string, dest, input) 0
@@ -5349,7 +5636,6 @@ static int parse_dollar(o_string *as_string,
5349#if BB_MMU 5636#if BB_MMU
5350#define encode_string(as_string, dest, input, dquote_end) \ 5637#define encode_string(as_string, dest, input, dquote_end) \
5351 encode_string(dest, input, dquote_end) 5638 encode_string(dest, input, dquote_end)
5352#define as_string NULL
5353#endif 5639#endif
5354static int encode_string(o_string *as_string, 5640static int encode_string(o_string *as_string,
5355 o_string *dest, 5641 o_string *dest,
@@ -5376,8 +5662,8 @@ static int encode_string(o_string *as_string,
5376 if (ch != '\n') { 5662 if (ch != '\n') {
5377 next = i_peek(input); 5663 next = i_peek(input);
5378 } 5664 }
5379 debug_printf_parse("\" ch=%c (%d) escape=%d\n", 5665 debug_printf_parse("\" ch:%c (%d) globprotect:%d\n",
5380 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 5666 ch, ch, !!(dest->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS));
5381 if (ch == '\\') { 5667 if (ch == '\\') {
5382 if (next == EOF) { 5668 if (next == EOF) {
5383 /* Testcase: in interactive shell a file with 5669 /* Testcase: in interactive shell a file with
@@ -5405,8 +5691,6 @@ static int encode_string(o_string *as_string,
5405 goto again; 5691 goto again;
5406 } 5692 }
5407 if (ch == '$') { 5693 if (ch == '$') {
5408 //if (parse_dollar_squote(as_string, dest, input))
5409 // goto again;
5410 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) { 5694 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
5411 debug_printf_parse("encode_string return 0: " 5695 debug_printf_parse("encode_string return 0: "
5412 "parse_dollar returned 0 (error)\n"); 5696 "parse_dollar returned 0 (error)\n");
@@ -5433,9 +5717,113 @@ static int encode_string(o_string *as_string,
5433 o_addchr(dest, SPECIAL_VAR_SYMBOL); 5717 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5434 } 5718 }
5435 goto again; 5719 goto again;
5436#undef as_string
5437} 5720}
5438 5721
5722#if ENABLE_HUSH_ALIAS
5723static char* end_of_alias_name(const char *name)
5724{
5725 while (*name) {
5726 if (!isalnum(*name)
5727// Uncommented chars are allowed in alias names.
5728// Commented out with // are disallowed in bash: space, "$&'();<=>\`|
5729// Commented out with //bb are allowed in bash, but disallowed in hush: !#*-/?[]{}~
5730// (do you really want alias named '?' to be allowed?)
5731// && *name != ' ' // 20
5732//bb && *name != '!' // 21
5733// && *name != '"' // 22
5734//bb && *name != '#' // 23
5735// && *name != '$' // 24
5736 && *name != '%' // 25
5737// && *name != '&' // 26
5738// && *name != '\'' // 27
5739// && *name != '(' // 28
5740// && *name != ')' // 29
5741//bb && *name != '*' // 2a
5742 && *name != '+' // 2b
5743 && *name != ',' // 2c
5744//bb && *name != '-' // 2d bash _can_ set it: "alias -- -=STR" (and it lists it as "alias -- -='STR'" in "alias" output!)
5745 && *name != '.' // 2e seen Fedora defining alias "l."
5746//bb && *name != '/' // 2f
5747 && *name != ':' // 3a
5748// && *name != ';' // 3b
5749// && *name != '<' // 3c
5750// && *name != '=' // 3d
5751// && *name != '>' // 3e
5752//bb && *name != '?' // 3f
5753 && *name != '@' // 40
5754//bb && *name != '[' // 5b
5755// && *name != '\\' // 5c
5756//bb && *name != ']' // 5d
5757 && *name != '^' // 5e
5758 && *name != '_' // 5f
5759// && *name != '`' // 60
5760//bb && *name != '{' // 7b
5761// && *name != '|' // 7c
5762//bb && *name != '}' // 7d
5763//bb && *name != '~' // 7e
5764 ) {
5765 break; /* disallowed char, stop */
5766 }
5767 name++;
5768 }
5769 return (char*)name;
5770}
5771#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
5772#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
5773
5774static struct alias **find_alias_slot(const char *name, const char *eq)
5775{
5776 unsigned len;
5777 struct alias *alias;
5778 struct alias **aliaspp;
5779
5780 len = eq - name;
5781 aliaspp = &G.top_alias;
5782 while ((alias = *aliaspp) != NULL) {
5783 //bb_error_msg("alias->str'%s' name'%.*s'", alias->str, len, name);
5784 if (strncmp(name, alias->str, len) == 0
5785 && alias->str[len] == '='
5786 ) {
5787 //bb_error_msg("match!");
5788 break;
5789 }
5790 aliaspp = &alias->next;
5791 }
5792 return aliaspp;
5793}
5794
5795static ALWAYS_INLINE const struct alias *find_alias(const char *name)
5796{
5797 //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__);
5798 return *find_alias_slot(name, strchr(name, '\0'));
5799}
5800
5801static const struct alias *word_matches_alias(struct parse_context *ctx)
5802{
5803 if (ctx->ctx_res_w != RES_CASE_BODY
5804/* && !ctx.command->argv - caller checked this */
5805 && !ctx->word.has_quoted_part
5806 && ctx->word.data[0] != '\0' /* optimization */
5807 ) {
5808 const char *word = ctx->word.data;
5809 const char *end = end_of_alias_name(word);
5810 if (*end == '\0') {
5811 struct alias *alias;
5812
5813 //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__);
5814 alias = *find_alias_slot(word, end);
5815 if (alias && !alias->dont_recurse) {
5816 alias->dont_recurse = 1;
5817 o_reset_to_empty_unquoted(&ctx->word);
5818 return alias;
5819 }
5820 }
5821 }
5822 return NULL;
5823}
5824
5825#endif /* ENABLE_HUSH_ALIAS */
5826
5439/* 5827/*
5440 * Scan input until EOF or end_trigger char. 5828 * Scan input until EOF or end_trigger char.
5441 * Return a list of pipes to execute, or NULL on EOF 5829 * Return a list of pipes to execute, or NULL on EOF
@@ -5449,6 +5837,8 @@ static struct pipe *parse_stream(char **pstring,
5449 struct in_str *input, 5837 struct in_str *input,
5450 int end_trigger) 5838 int end_trigger)
5451{ 5839{
5840 IF_HUSH_ALIAS(const struct alias *alias;)
5841 struct pipe *pi;
5452 struct parse_context ctx; 5842 struct parse_context ctx;
5453 int heredoc_cnt; 5843 int heredoc_cnt;
5454 5844
@@ -5459,13 +5849,9 @@ static struct pipe *parse_stream(char **pstring,
5459 end_trigger ? end_trigger : 'X'); 5849 end_trigger ? end_trigger : 'X');
5460 debug_enter(); 5850 debug_enter();
5461 5851
5852 enable_all_aliases();
5462 initialize_context(&ctx); 5853 initialize_context(&ctx);
5463 5854
5464 /* If very first arg is "" or '', ctx.word.data may end up NULL.
5465 * Preventing this:
5466 */
5467 ctx.word.data = xzalloc(1); /* start as "", not as NULL */
5468
5469 /* We used to separate words on $IFS here. This was wrong. 5855 /* We used to separate words on $IFS here. This was wrong.
5470 * $IFS is used only for word splitting when $var is expanded, 5856 * $IFS is used only for word splitting when $var is expanded,
5471 * here we should use blank chars as separators, not $IFS 5857 * here we should use blank chars as separators, not $IFS
@@ -5473,74 +5859,29 @@ static struct pipe *parse_stream(char **pstring,
5473 5859
5474 heredoc_cnt = 0; 5860 heredoc_cnt = 0;
5475 while (1) { 5861 while (1) {
5476 const char *is_blank;
5477 const char *is_special;
5478 int ch; 5862 int ch;
5479 int next; 5863 int next;
5480 int redir_fd; 5864 int redir_fd;
5481 redir_type redir_style; 5865 redir_type redir_style;
5482 5866
5483 ch = i_getch(input); 5867 ch = i_getch(input);
5484 debug_printf_parse(": ch=%c (%d) escape=%d\n", 5868 debug_printf_parse(": ch:%c (%d) globprotect:%d\n",
5485 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 5869 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS));
5486 if (ch == EOF) { 5870# if ENABLE_HUSH_NEED_FOR_SPEED
5487 struct pipe *pi; 5871 if ((ch >= '.' && ch <= ':') /* ASCII "./0123456789:" */
5488 5872 /* can't include preceding "+,-" above: "-" needs glob-escaping (example?) */
5489 if (heredoc_cnt) { 5873 || (ch >= '@' && ch <= 'Z') /* ASCII "@A..Z" */
5490 syntax_error_unterm_str("here document"); 5874 || (ch >= 'a' && ch <= 'z') /* ASCII "a..Z" */
5491 goto parse_error_exitcode1; 5875 /* can't include preceding "^_`" above because of "`". Pity. "_" is relatively common */
5492 } 5876 ) {
5493 if (end_trigger == ')') { 5877 /* These are never special and just go into the current word */
5494 syntax_error_unterm_ch('('); 5878 /* ~5% faster parsing of typical shell scripts */
5495 goto parse_error_exitcode1; 5879 INLINED_o_addchr(&ctx.word, ch);
5496 } 5880 continue;
5497 if (end_trigger == '}') {
5498 syntax_error_unterm_ch('{');
5499 goto parse_error_exitcode1;
5500 }
5501
5502 if (done_word(&ctx)) {
5503 goto parse_error_exitcode1;
5504 }
5505 o_free_and_set_NULL(&ctx.word);
5506 done_pipe(&ctx, PIPE_SEQ);
5507
5508 /* Do we sit inside of any if's, loops or case's? */
5509 if (HAS_KEYWORDS
5510 IF_HAS_KEYWORDS(&& (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0))
5511 ) {
5512 syntax_error_unterm_str("compound statement");
5513 goto parse_error_exitcode1;
5514 }
5515
5516 pi = ctx.list_head;
5517 /* If we got nothing... */
5518 /* (this makes bare "&" cmd a no-op.
5519 * bash says: "syntax error near unexpected token '&'") */
5520 if (pi->num_cmds == 0
5521 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
5522 ) {
5523 free_pipe_list(pi);
5524 pi = NULL;
5525 }
5526#if !BB_MMU
5527 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
5528 if (pstring)
5529 *pstring = ctx.as_string.data;
5530 else
5531 o_free(&ctx.as_string);
5532#endif
5533 // heredoc_cnt must be 0 here anyway
5534 //if (heredoc_cnt_ptr)
5535 // *heredoc_cnt_ptr = heredoc_cnt;
5536 debug_leave();
5537 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
5538 debug_printf_parse("parse_stream return %p: EOF\n", pi);
5539 return pi;
5540 } 5881 }
5541 5882#endif
5542 /* Handle "'" and "\" first, as they won't play nice with 5883 /* Handle "'" and "\" first, as they won't play nice with
5543 * i_peek_and_eat_bkslash_nl() anyway: 5884 * i_peek_and_eat_bkslash_nl():
5544 * echo z\\ 5885 * echo z\\
5545 * and 5886 * and
5546 * echo '\ 5887 * echo '\
@@ -5561,9 +5902,9 @@ static struct pipe *parse_stream(char **pstring,
5561 if (ch == EOF) { 5902 if (ch == EOF) {
5562 /* Testcase: eval 'echo Ok\' */ 5903 /* Testcase: eval 'echo Ok\' */
5563 /* bash-4.3.43 was removing backslash, 5904 /* bash-4.3.43 was removing backslash,
5564 * but 4.4.19 retains it, most other shells too 5905 * but 4.4.19 retains it, most other shells retain too
5565 */ 5906 */
5566 continue; /* get next char */ 5907 break;
5567 } 5908 }
5568 /* Example: echo Hello \2>file 5909 /* Example: echo Hello \2>file
5569 * we need to know that word 2 is quoted 5910 * we need to know that word 2 is quoted
@@ -5573,14 +5914,25 @@ static struct pipe *parse_stream(char **pstring,
5573 o_addchr(&ctx.word, ch); 5914 o_addchr(&ctx.word, ch);
5574 continue; /* get next char */ 5915 continue; /* get next char */
5575 } 5916 }
5917 if (ch == EOF)
5918 break;
5576 nommu_addchr(&ctx.as_string, ch); 5919 nommu_addchr(&ctx.as_string, ch);
5577 if (ch == '\'') { 5920 if (ch == '\'') {
5578 ctx.word.has_quoted_part = 1; 5921 ctx.word.has_quoted_part = 1;
5579 next = i_getch(input); 5922 ch = i_getch(input);
5580 if (next == '\'' && !ctx.pending_redirect) 5923 if (ch == '\'' && !ctx.pending_redirect/*why?*/) {
5581 goto insert_empty_quoted_str_marker; 5924 insert_empty_quoted_str_marker:
5582 5925 nommu_addchr(&ctx.as_string, ch);
5583 ch = next; 5926//Just inserting nothing doesn't work: consider
5927// CMD $EMPTYVAR
5928// CMD ''
5929//At execution time both will expand argv[1] to empty string
5930//and thus the argument will "vanish".
5931//But for second CMD, it should not vanish!
5932 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5933 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5934 continue; /* get next char */
5935 }
5584 while (1) { 5936 while (1) {
5585 if (ch == EOF) { 5937 if (ch == EOF) {
5586 syntax_error_unterm_ch('\''); 5938 syntax_error_unterm_ch('\'');
@@ -5600,135 +5952,173 @@ static struct pipe *parse_stream(char **pstring,
5600 continue; /* get next char */ 5952 continue; /* get next char */
5601 } 5953 }
5602 5954
5603 next = '\0'; 5955 if (ch == ' ' || ch == '\t') {
5604 if (ch != '\n') 5956#if ENABLE_HUSH_ALIAS
5605 next = i_peek_and_eat_bkslash_nl(input); 5957 /* Check for alias expansion (only for first word of command) */
5606 5958 if (G_interactive_fd && !ctx.command->argv) {
5607 is_special = "{}<>&|();#" /* special outside of "str" */ 5959 alias = word_matches_alias(&ctx);
5608 "$\"" IF_HUSH_TICK("`") /* always special */ 5960 if (alias) {
5609 SPECIAL_VAR_SYMBOL_STR; 5961 add_to_albuf_and_get_next_char:
5610#if defined(CMD_TEST2_SINGLEWORD_NOGLOB) 5962 i_prepend_to_alias_buffer(input, strchr(alias->str, '=') + 1, ch);
5611 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { 5963 continue; /* get next char (which will be from albuf) */
5612 /* In [[ ]], {}<>&|() are not special */ 5964 }
5613 is_special += 8;
5614 } else
5615#endif
5616 /* Are { and } special here? */
5617 if (ctx.command->argv /* word [word]{... - non-special */
5618 || ctx.word.length /* word{... - non-special */
5619 || ctx.word.has_quoted_part /* ""{... - non-special */
5620 || (next != ';' /* }; - special */
5621 && next != ')' /* }) - special */
5622 && next != '(' /* {( - special */
5623 && next != '&' /* }& and }&& ... - special */
5624 && next != '|' /* }|| ... - special */
5625 && !strchr(defifs, next) /* {word - non-special */
5626 )
5627 ) {
5628 /* They are not special, skip "{}" */
5629 is_special += 2;
5630 }
5631 is_special = strchr(is_special, ch);
5632 is_blank = strchr(defifs, ch);
5633
5634 if (!is_special && !is_blank) { /* ordinary char */
5635 ordinary_char:
5636 o_addQchr(&ctx.word, ch);
5637 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5638 || ctx.is_assignment == WORD_IS_KEYWORD)
5639 && ch == '='
5640 && endofname(ctx.word.data)[0] == '='
5641 ) {
5642 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5643 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5644 } 5965 }
5645 continue; 5966#endif
5646 }
5647 5967
5648 if (is_blank) {
5649#if ENABLE_HUSH_LINENO_VAR 5968#if ENABLE_HUSH_LINENO_VAR
5650/* Case: 5969/* "while ...; do<whitespace><newline>
5651 * "while ...; do<whitespace><newline> 5970 * CMD"
5652 * cmd ..." 5971 * would think that CMD starts in <whitespace> -
5653 * would think that "cmd" starts in <whitespace> -
5654 * i.e., at the previous line. 5972 * i.e., at the previous line.
5655 * We need to skip all whitespace before newlines. 5973 * Need to skip whitespace up to next newline (and eat it)
5974 * or not-whitespace (and do not eat it).
5656 */ 5975 */
5657 while (ch != '\n') { 5976 for (;;) {
5658 next = i_peek(input); 5977 next = i_peek(input);
5659 if (next != ' ' && next != '\t' && next != '\n') 5978 if (next != ' ' && next != '\t' && next != '\n')
5660 break; /* next char is not ws */ 5979 break; /* next char is not ws */
5661 ch = i_getch(input); 5980 ch = i_getch(input);
5981 if (ch == '\n')
5982 goto ch_is_newline;
5662 } 5983 }
5663 /* ch == last eaten whitespace char */
5664#endif 5984#endif
5665 if (done_word(&ctx)) { 5985 if (done_word(&ctx))
5666 goto parse_error_exitcode1; 5986 goto parse_error_exitcode1;
5987#if ENABLE_HUSH_FUNCTION_KEYWORD
5988 if (ctx.command->cmd_type == CMD_FUNCTION_KWORD
5989 && ctx.command->argv /* "function WORD" */
5990 )
5991 goto parse_group;
5992#endif
5993 continue; /* get next char */
5994 }
5995
5996 if (ch == '\n') {
5997 IF_HUSH_LINENO_VAR(ch_is_newline:)
5998#if ENABLE_HUSH_ALIAS
5999 /* Check for alias expansion (only for first word of command) */
6000 if (G_interactive_fd && !ctx.command->argv) {
6001 alias = word_matches_alias(&ctx);
6002 if (alias)
6003 goto add_to_albuf_and_get_next_char;
5667 } 6004 }
5668 if (ch == '\n') { 6005#endif
5669 /* Is this a case when newline is simply ignored? 6006 if (done_word(&ctx))
5670 * Some examples: 6007 goto parse_error_exitcode1;
5671 * "cmd | <newline> cmd ..." 6008 /* Is this a case when newline is simply ignored?
5672 * "case ... in <newline> word) ..." 6009 * Some examples:
6010 * "CMD | <newline> CMD ..."
6011 * "case ... in <newline> PATTERN) ..."
6012 */
6013 if (IS_NULL_CMD(ctx.command)
6014 && heredoc_cnt == 0
6015 ) {
6016 /* This newline can be ignored. But...
6017 * Without check #1, interactive shell
6018 * ignores even bare <newline>,
6019 * and shows the continuation prompt:
6020 * ps1$ <enter>
6021 * ps2> _ <=== wrong, should be ps1
6022 * Without check #2, "CMD & <newline>"
6023 * is similarly mistreated.
6024 * (BTW, this makes "CMD & CMD"
6025 * and "CMD && CMD" non-orthogonal.
6026 * Really, ask yourself, why
6027 * "CMD && <newline>" doesn't start
6028 * CMD but waits for more input?
6029 * The only reason is that it might be
6030 * a "CMD1 && <nl> CMD2 &" construct:
6031 * CMD1 may need to run in BG).
5673 */ 6032 */
5674 if (IS_NULL_CMD(ctx.command) 6033 pi = ctx.list_head;
5675 && ctx.word.length == 0 6034 if (pi->num_cmds != 0 /* check #1 */
5676 && !ctx.word.has_quoted_part 6035 && pi->followup != PIPE_BG /* check #2 */
5677 && heredoc_cnt == 0
5678 ) { 6036 ) {
5679 /* This newline can be ignored. But... 6037 debug_printf_parse("newline is treated as ws\n");
5680 * Without check #1, interactive shell 6038 continue; /* ignore newline */
5681 * ignores even bare <newline>,
5682 * and shows the continuation prompt:
5683 * ps1_prompt$ <enter>
5684 * ps2> _ <=== wrong, should be ps1
5685 * Without check #2, "cmd & <newline>"
5686 * is similarly mistreated.
5687 * (BTW, this makes "cmd & cmd"
5688 * and "cmd && cmd" non-orthogonal.
5689 * Really, ask yourself, why
5690 * "cmd && <newline>" doesn't start
5691 * cmd but waits for more input?
5692 * The only reason is that it might be
5693 * a "cmd1 && <nl> cmd2 &" construct,
5694 * cmd1 may need to run in BG).
5695 */
5696 struct pipe *pi = ctx.list_head;
5697 if (pi->num_cmds != 0 /* check #1 */
5698 && pi->followup != PIPE_BG /* check #2 */
5699 ) {
5700 continue;
5701 }
5702 } 6039 }
5703 /* Treat newline as a command separator. */ 6040 }
5704 done_pipe(&ctx, PIPE_SEQ); 6041#if ENABLE_HUSH_FUNCTION_KEYWORD
5705 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt); 6042 if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) {
5706 if (heredoc_cnt) { 6043 if (!ctx.command->argv) {
5707 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); 6044 /* Testcase: sh -c $'function\n' */
5708 if (heredoc_cnt != 0) 6045 syntax_error("expected funcdef");
5709 goto parse_error_exitcode1; 6046 goto parse_error_exitcode1;
5710 } 6047 }
5711 ctx.is_assignment = MAYBE_ASSIGNMENT; 6048 /* "function WORD" */
5712 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); 6049 goto parse_group;
5713 ch = ';'; 6050 }
5714 /* note: if (is_blank) continue; 6051#endif
5715 * will still trigger for us */ 6052 /* Treat newline as a command separator */
6053 done_pipe(&ctx, PIPE_SEQ);
6054 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt);
6055 if (heredoc_cnt) {
6056 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
6057 if (heredoc_cnt != 0)
6058 goto parse_error_exitcode1;
6059 }
6060 ctx.is_assignment = MAYBE_ASSIGNMENT;
6061 debug_printf_parse("newline is treated as ';', ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6062 next = '\0';
6063 ch = ';';
6064 } else {
6065 const char *is_special;
6066
6067 next = i_peek_and_eat_bkslash_nl(input);
6068 is_special = "{}<>&|();#" /* special outside of "str" */
6069 "$\"" IF_HUSH_TICK("`") /* always special */
6070 SPECIAL_VAR_SYMBOL_STR;
6071#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
6072 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
6073 /* In [[ ]], {}<>&|() are not special */
6074 is_special += 8;
6075 } else
6076#endif
6077 /* Are { and } special here? */
6078 if ((ctx.command->argv /* WORD [WORD]{... - non-special */
6079#if ENABLE_HUSH_FUNCTIONS
6080 && ctx.command->cmd_type != CMD_FUNCDEF /* ^^^ unless FUNC() {} */
6081#endif
6082 )
6083 || !IS_NULL_WORD(ctx.word) /* WORD{... ""{... - non-special */
6084 || (next != ';' /* }; - special */
6085 && next != ')' /* }) - special */
6086 && next != '(' /* {( - special */
6087 && next != '&' /* }& and }&& ... - special */
6088 && next != '|' /* }|| ... - special */
6089 && !strchr(defifs, next) /* {WORD - non-special */
6090 )
6091 ) {
6092 /* They are not special, skip "{}" */
6093 is_special += 2;
6094 }
6095 if (!strchr(is_special, ch)) { /* ordinary char? */
6096 ordinary_char:
6097 o_addQchr(&ctx.word, ch);
6098 if (ctx.is_assignment == MAYBE_ASSIGNMENT
6099 && ch == '='
6100 && !ctx.word.has_quoted_part /* a''=b a'b'c=d: not assignments */
6101 && endofname(ctx.word.data)[0] == '='
6102 ) {
6103 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
6104 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6105 }
6106 continue; /* get next char */
5716 } 6107 }
5717 } 6108 }
5718 6109
5719 /* "cmd}" or "cmd }..." without semicolon or &: 6110 /* "CMD}" or "CMD }..." without semicolon or &:
5720 * } is an ordinary char in this case, even inside { cmd; } 6111 * } is an ordinary char in this case, even inside { CMD; }
5721 * Pathological example: { ""}; } should exec "}" cmd 6112 * Pathological example: { ""}; } should run "}" command.
5722 */ 6113 */
5723 if (ch == '}') { 6114 if (ch == '}') {
5724 if (ctx.word.length != 0 /* word} */ 6115 if (!IS_NULL_WORD(ctx.word)) {
5725 || ctx.word.has_quoted_part /* ""} */ 6116 /* WORD} or ""} */
5726 ) {
5727 goto ordinary_char; 6117 goto ordinary_char;
5728 } 6118 }
5729 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */ 6119 if (!IS_NULL_CMD(ctx.command)) { /* CMD } */
5730 /* Generally, there should be semicolon: "cmd; }" 6120 /* Generally, there should be semicolon: "CMD; }"
5731 * However, bash allows to omit it if "cmd" is 6121 * However, bash allows to omit it if "CMD" is
5732 * a group. Examples: 6122 * a group. Examples:
5733 * { { echo 1; } } 6123 * { { echo 1; } }
5734 * {(echo 1)} 6124 * {(echo 1)}
@@ -5740,14 +6130,17 @@ static struct pipe *parse_stream(char **pstring,
5740 goto term_group; 6130 goto term_group;
5741 goto ordinary_char; 6131 goto ordinary_char;
5742 } 6132 }
5743 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ 6133 if (!IS_NULL_PIPE(ctx.pipe)) /* CMD | } */
5744 /* Can't be an end of {cmd}, skip the check */ 6134 /* Can't be an end of {CMD}, skip the check */
5745 goto skip_end_trigger; 6135 goto rbrace_skips_end_trigger;
5746 /* else: } does terminate a group */ 6136 /* else: } does terminate a group */
5747 } 6137 }
5748 term_group: 6138 term_group:
5749 if (end_trigger && end_trigger == ch 6139 if (end_trigger && end_trigger == ch
5750 && (ch != ';' || heredoc_cnt == 0) 6140 && (ch != ';'
6141 /* it's ";". Can exit parse_stream() only if have no heredocs to consume, and alias buffer is empty */
6142 || (heredoc_cnt == 0 && !i_has_alias_buffer(input))
6143 )
5751#if ENABLE_HUSH_CASE 6144#if ENABLE_HUSH_CASE
5752 && (ch != ')' 6145 && (ch != ')'
5753 || ctx.ctx_res_w != RES_MATCH 6146 || ctx.ctx_res_w != RES_MATCH
@@ -5755,13 +6148,32 @@ static struct pipe *parse_stream(char **pstring,
5755 ) 6148 )
5756#endif 6149#endif
5757 ) { 6150 ) {
5758 if (done_word(&ctx)) { 6151#if ENABLE_HUSH_ALIAS
6152 /* Check for alias expansion (only for first word of command) */
6153 if (G_interactive_fd && !ctx.command->argv) {
6154 alias = word_matches_alias(&ctx);
6155 if (alias)
6156 goto add_to_albuf_and_get_next_char;
6157 }
6158#endif
6159 if (done_word(&ctx))
6160 goto parse_error_exitcode1;
6161#if ENABLE_HUSH_FUNCTION_KEYWORD
6162 if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) {
6163 /* Testcase: sh -c '(function)' */
6164 syntax_error("expected funcdef");
5759 goto parse_error_exitcode1; 6165 goto parse_error_exitcode1;
5760 } 6166 }
5761 done_pipe(&ctx, PIPE_SEQ); 6167#endif
6168 if (done_pipe(&ctx, PIPE_SEQ)) {
6169 /* Testcase: sh -c 'date|;not_reached' */
6170 syntax_error_unterm_ch('|');
6171 goto parse_error_exitcode1;
6172 };
5762 ctx.is_assignment = MAYBE_ASSIGNMENT; 6173 ctx.is_assignment = MAYBE_ASSIGNMENT;
5763 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); 6174 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5764 /* Do we sit outside of any if's, loops or case's? */ 6175 /* Do we sit outside of any if's, loops or case's? */
6176//TODO? just check ctx.stack != NULL instead?
5765 if (!HAS_KEYWORDS 6177 if (!HAS_KEYWORDS
5766 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) 6178 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
5767 ) { 6179 ) {
@@ -5786,21 +6198,18 @@ static struct pipe *parse_stream(char **pstring,
5786 "end_trigger char found\n", 6198 "end_trigger char found\n",
5787 ctx.list_head); 6199 ctx.list_head);
5788 debug_leave(); 6200 debug_leave();
6201 i_free_alias_buffer(input);
5789 return ctx.list_head; 6202 return ctx.list_head;
5790 } 6203 }
5791 } 6204 }
5792 6205
5793 if (is_blank)
5794 continue;
5795
5796 /* Catch <, > before deciding whether this word is 6206 /* Catch <, > before deciding whether this word is
5797 * an assignment. a=1 2>z b=2: b=2 is still assignment */ 6207 * an assignment. a=1 2>z b=2: b=2 is still assignment */
5798 switch (ch) { 6208 switch (ch) {
5799 case '>': 6209 case '>':
5800 redir_fd = redirect_opt_num(&ctx.word); 6210 redir_fd = redirect_opt_num(&ctx.word);
5801 if (done_word(&ctx)) { 6211 if (done_word(&ctx))
5802 goto parse_error_exitcode1; 6212 goto parse_error_exitcode1;
5803 }
5804 redir_style = REDIRECT_OVERWRITE; 6213 redir_style = REDIRECT_OVERWRITE;
5805 if (next == '>') { 6214 if (next == '>') {
5806 redir_style = REDIRECT_APPEND; 6215 redir_style = REDIRECT_APPEND;
@@ -5818,9 +6227,8 @@ static struct pipe *parse_stream(char **pstring,
5818 continue; /* get next char */ 6227 continue; /* get next char */
5819 case '<': 6228 case '<':
5820 redir_fd = redirect_opt_num(&ctx.word); 6229 redir_fd = redirect_opt_num(&ctx.word);
5821 if (done_word(&ctx)) { 6230 if (done_word(&ctx))
5822 goto parse_error_exitcode1; 6231 goto parse_error_exitcode1;
5823 }
5824 redir_style = REDIRECT_INPUT; 6232 redir_style = REDIRECT_INPUT;
5825 if (next == '<') { 6233 if (next == '<') {
5826 redir_style = REDIRECT_HEREDOC; 6234 redir_style = REDIRECT_HEREDOC;
@@ -5828,6 +6236,14 @@ static struct pipe *parse_stream(char **pstring,
5828 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt); 6236 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt);
5829 ch = i_getch(input); 6237 ch = i_getch(input);
5830 nommu_addchr(&ctx.as_string, ch); 6238 nommu_addchr(&ctx.as_string, ch);
6239 /* Check for here-string (<<<) */
6240 next = i_peek(input);
6241 if (next == '<') {
6242 redir_style = REDIRECT_HERESTRING;
6243 ch = i_getch(input);
6244 nommu_addchr(&ctx.as_string, ch);
6245 heredoc_cnt--; /* here-strings don't use heredoc lines */
6246 }
5831 } else if (next == '>') { 6247 } else if (next == '>') {
5832 redir_style = REDIRECT_IO; 6248 redir_style = REDIRECT_IO;
5833 ch = i_getch(input); 6249 ch = i_getch(input);
@@ -5843,13 +6259,13 @@ static struct pipe *parse_stream(char **pstring,
5843 goto parse_error_exitcode1; 6259 goto parse_error_exitcode1;
5844 continue; /* get next char */ 6260 continue; /* get next char */
5845 case '#': 6261 case '#':
5846 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) { 6262 if (IS_NULL_WORD(ctx.word)) {
5847 /* skip "#comment" */ 6263 /* skip "#COMMENT" */
5848 /* note: we do not add it to &ctx.as_string */ 6264 /* note: we do not add it to &ctx.as_string */
5849/* TODO: in bash: 6265/* TODO: in bash:
5850 * comment inside $() goes to the next \n, even inside quoted string (!): 6266 * comment inside $() goes to the next \n, even inside quoted string (!):
5851 * cmd "$(cmd2 #comment)" - syntax error 6267 * CMD "$(CMD2 #COMMENT)" - syntax error
5852 * cmd "`cmd2 #comment`" - ok 6268 * CMD "`CMD2 #COMMENT`" - ok
5853 * We accept both (comment ends where command subst ends, in both cases). 6269 * We accept both (comment ends where command subst ends, in both cases).
5854 */ 6270 */
5855 while (1) { 6271 while (1) {
@@ -5860,16 +6276,16 @@ static struct pipe *parse_stream(char **pstring,
5860 } 6276 }
5861 ch = i_getch(input); 6277 ch = i_getch(input);
5862 if (ch == EOF) 6278 if (ch == EOF)
5863 break; 6279 goto eof;
5864 } 6280 }
5865 continue; /* get next char */ 6281 continue; /* get next char */
5866 } 6282 }
5867 break; 6283 break;
5868 } 6284 }
5869 skip_end_trigger: 6285 rbrace_skips_end_trigger:
5870 6286
5871 if (ctx.is_assignment == MAYBE_ASSIGNMENT 6287 if (ctx.is_assignment == MAYBE_ASSIGNMENT
5872 /* check that we are not in word in "a=1 2>word b=1": */ 6288 /* check that we are not in WORD in "a=1 2>WORD b=1": */
5873 && !ctx.pending_redirect 6289 && !ctx.pending_redirect
5874 ) { 6290 ) {
5875 /* ch is a special char and thus this word 6291 /* ch is a special char and thus this word
@@ -5892,8 +6308,8 @@ static struct pipe *parse_stream(char **pstring,
5892 o_addchr(&ctx.word, ch); 6308 o_addchr(&ctx.word, ch);
5893 continue; /* get next char */ 6309 continue; /* get next char */
5894 case '$': 6310 case '$':
5895 if (parse_dollar_squote(&ctx.as_string, &ctx.word, input)) 6311 if (next == '\'' && parse_dollar_squote(&ctx.as_string, &ctx.word, input))
5896 continue; /* get next char */ 6312 continue; /* ate $'...', get next char */
5897 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { 6313 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
5898 debug_printf_parse("parse_stream parse error: " 6314 debug_printf_parse("parse_stream parse error: "
5899 "parse_dollar returned 0 (error)\n"); 6315 "parse_dollar returned 0 (error)\n");
@@ -5903,18 +6319,14 @@ static struct pipe *parse_stream(char **pstring,
5903 case '"': 6319 case '"':
5904 ctx.word.has_quoted_part = 1; 6320 ctx.word.has_quoted_part = 1;
5905 if (next == '"' && !ctx.pending_redirect) { 6321 if (next == '"' && !ctx.pending_redirect) {
5906 i_getch(input); /* eat second " */ 6322 ch = i_getch(input); /* eat second " */
5907 insert_empty_quoted_str_marker: 6323 goto insert_empty_quoted_str_marker;
5908 nommu_addchr(&ctx.as_string, next);
5909 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5910 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5911 continue; /* get next char */
5912 } 6324 }
5913 if (ctx.is_assignment == NOT_ASSIGNMENT) 6325 if (ctx.is_assignment == NOT_ASSIGNMENT)
5914 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; 6326 ctx.word.o_expflags |= EXP_FLAG_GLOBPROTECT_CHARS;
5915 if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) 6327 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
5916 goto parse_error_exitcode1; 6328 goto parse_error_exitcode1;
5917 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; 6329 ctx.word.o_expflags &= ~EXP_FLAG_GLOBPROTECT_CHARS;
5918 continue; /* get next char */ 6330 continue; /* get next char */
5919#if ENABLE_HUSH_TICK 6331#if ENABLE_HUSH_TICK
5920 case '`': { 6332 case '`': {
@@ -5935,112 +6347,240 @@ static struct pipe *parse_stream(char **pstring,
5935 } 6347 }
5936#endif 6348#endif
5937 case ';': 6349 case ';':
5938#if ENABLE_HUSH_CASE 6350#if ENABLE_HUSH_ALIAS
5939 case_semi: 6351 /* Check for alias expansion (only for first word of command) */
6352 if (G_interactive_fd && !ctx.command->argv) {
6353 alias = word_matches_alias(&ctx);
6354 if (alias)
6355 goto add_to_albuf_and_get_next_char;
6356 }
5940#endif 6357#endif
5941 if (done_word(&ctx)) { 6358 if (done_word(&ctx))
6359 goto parse_error_exitcode1;
6360#if ENABLE_HUSH_FUNCTION_KEYWORD
6361 if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) {
6362 /* Testcase: sh -c '{ function; }'; sh -c '{ function f; }' */
6363 syntax_error("expected funcdef");
5942 goto parse_error_exitcode1; 6364 goto parse_error_exitcode1;
5943 } 6365 }
6366#endif
5944 done_pipe(&ctx, PIPE_SEQ); 6367 done_pipe(&ctx, PIPE_SEQ);
5945#if ENABLE_HUSH_CASE 6368#if ENABLE_HUSH_CASE
5946 /* Eat multiple semicolons, detect 6369 if (ctx.ctx_res_w == RES_CASE_BODY
5947 * whether it means something special */ 6370 && next == ';' /* and next char is ';' too? */
5948 while (1) { 6371 ) {
5949 ch = i_peek_and_eat_bkslash_nl(input);
5950 if (ch != ';')
5951 break;
5952 ch = i_getch(input); 6372 ch = i_getch(input);
5953 nommu_addchr(&ctx.as_string, ch); 6373 nommu_addchr(&ctx.as_string, ch);
5954 if (ctx.ctx_res_w == RES_CASE_BODY) { 6374 ctx.ctx_res_w = RES_MATCH; /* "we are in PATTERN)" */
5955 ctx.ctx_dsemicolon = 1;
5956 ctx.ctx_res_w = RES_MATCH;
5957 break;
5958 }
5959 } 6375 }
5960#endif 6376#endif
5961 new_cmd: 6377 finished_cmd:
5962 /* We just finished a cmd. New one may start 6378 /* We just finished a cmd. New one may start
5963 * with an assignment */ 6379 * with an assignment */
5964 ctx.is_assignment = MAYBE_ASSIGNMENT; 6380 ctx.is_assignment = MAYBE_ASSIGNMENT;
5965 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); 6381 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5966 continue; /* get next char */ 6382 continue; /* get next char */
5967 case '&': 6383 case '&':
5968 if (done_word(&ctx)) { 6384#if ENABLE_HUSH_ALIAS
6385 /* Check for alias expansion (only for first word of command) */
6386 if (G_interactive_fd && !ctx.command->argv) {
6387 alias = word_matches_alias(&ctx);
6388 if (alias)
6389 goto add_to_albuf_and_get_next_char;
6390 }
6391#endif
6392 if (done_word(&ctx))
6393 goto parse_error_exitcode1;
6394 if (ctx.pipe->num_cmds == 0 && IS_NULL_CMD(ctx.command)) {
6395 /* Testcase: sh -c '&& date' */
6396 /* Testcase: sh -c '&' */
6397 syntax_error_unexpected_str("&&" + (next != '&'));
5969 goto parse_error_exitcode1; 6398 goto parse_error_exitcode1;
5970 } 6399 }
5971 if (next == '&') { 6400 if (next == '&') {
5972 ch = i_getch(input); 6401 ch = i_getch(input);
5973 nommu_addchr(&ctx.as_string, ch); 6402 nommu_addchr(&ctx.as_string, ch);
5974 done_pipe(&ctx, PIPE_AND); 6403 if (done_pipe(&ctx, PIPE_AND)) {
6404 /* Testcase: sh -c 'date | && date' */
6405 syntax_error_unterm_ch('|');
6406 goto parse_error_exitcode1;
6407 }
5975 } else { 6408 } else {
5976 done_pipe(&ctx, PIPE_BG); 6409 if (done_pipe(&ctx, PIPE_BG)) {
6410 /* Testcase: sh -c 'date | &' */
6411 syntax_error_unterm_ch('|');
6412 goto parse_error_exitcode1;
6413 }
5977 } 6414 }
5978 goto new_cmd; 6415 goto finished_cmd;
5979 case '|': 6416 case '|':
5980 if (done_word(&ctx)) {
5981 goto parse_error_exitcode1;
5982 }
5983#if ENABLE_HUSH_CASE 6417#if ENABLE_HUSH_CASE
5984 if (ctx.ctx_res_w == RES_MATCH) 6418 if (ctx.ctx_res_w == RES_MATCH) {
5985 break; /* we are in case's "word | word)" */ 6419// if (IS_NULL_WORD(ctx.word)) {
6420// /* Testcase: sh -c 'case w in |w) nice; esac' */
6421// /* Testcase: sh -c 'case w in v||w) nice; esac' */
6422//also rejects valid: sh -c 'case w in v |w) nice; esac'
6423// syntax_error_unexpected_ch(ch);
6424// goto parse_error_exitcode1;
6425// }
6426 if (done_word(&ctx))
6427 goto parse_error_exitcode1;
6428 continue; /* get next char */
6429 }
5986#endif 6430#endif
6431#if ENABLE_HUSH_ALIAS
6432 /* Check for alias expansion (only for first word of command) */
6433 if (G_interactive_fd && !ctx.command->argv) {
6434 alias = word_matches_alias(&ctx);
6435 if (alias)
6436 goto add_to_albuf_and_get_next_char;
6437 }
6438#endif
6439 if (done_word(&ctx))
6440 goto parse_error_exitcode1;
5987 if (next == '|') { /* || */ 6441 if (next == '|') { /* || */
5988 ch = i_getch(input); 6442 ch = i_getch(input);
5989 nommu_addchr(&ctx.as_string, ch); 6443 nommu_addchr(&ctx.as_string, ch);
5990 done_pipe(&ctx, PIPE_OR); 6444 if (ctx.pipe->num_cmds == 0 && IS_NULL_CMD(ctx.command)) {
6445 /* Testcase: sh -c '|| date' */
6446 syntax_error_unexpected_str("||");
6447 goto parse_error_exitcode1;
6448 }
6449 if (done_pipe(&ctx, PIPE_OR)) {
6450 /* Testcase: sh -c 'date | || date' */
6451 syntax_error_unterm_ch('|');
6452 goto parse_error_exitcode1;
6453 }
5991 } else { 6454 } else {
5992 /* we could pick up a file descriptor choice here 6455 if (IS_NULL_CMD(ctx.command)) {
5993 * with redirect_opt_num(), but bash doesn't do it. 6456 /* Testcase: sh -c '| cat' */
5994 * "echo foo 2| cat" yields "foo 2". */ 6457 /* Testcase: sh -c 'date | | cat' */
6458 syntax_error_unexpected_ch('|');
6459 goto parse_error_exitcode1;
6460 }
5995 done_command(&ctx); 6461 done_command(&ctx);
5996 } 6462 }
5997 goto new_cmd; 6463 goto finished_cmd;
5998 case '(': 6464 case '(':
5999#if ENABLE_HUSH_CASE 6465#if ENABLE_HUSH_CASE
6000 /* "case... in [(]word)..." - skip '(' */ 6466 /* "case... in (PATTERNS)..."? */
6001 if (ctx.ctx_res_w == RES_MATCH 6467 if (ctx.ctx_res_w == RES_MATCH
6002 && ctx.command->argv == NULL /* not (word|(... */ 6468 && ctx.command->argv == NULL /* not WORD (... */
6003 && ctx.word.length == 0 /* not word(... */ 6469 && IS_NULL_WORD(ctx.word) /* not WORD(... or ""(... */
6004 && ctx.word.has_quoted_part == 0 /* not ""(... */
6005 ) { 6470 ) {
6006 continue; /* get next char */ 6471 continue; /* skip '(', get next char */
6007 } 6472 }
6008#endif 6473#endif
6009 /* fall through */ 6474 /* fall through */
6010 case '{': { 6475 case '{': {
6011 int n = parse_group(&ctx, input, ch); 6476 int n;
6012 if (n < 0) { 6477 /* "function WORD" -> */
6478 IF_HUSH_FUNCTION_KEYWORD(parse_group:)
6479 /* Try to parse as { CMDS; } or (CMDS) */
6480 n = parse_group(&ctx, input, ch);
6481 if (n < 0)
6013 goto parse_error_exitcode1; 6482 goto parse_error_exitcode1;
6014 }
6015 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); 6483 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6016 heredoc_cnt += n; 6484 heredoc_cnt += n;
6017 goto new_cmd; 6485 goto finished_cmd;
6018 } 6486 }
6019 case ')': 6487 case ')':
6020#if ENABLE_HUSH_CASE 6488#if ENABLE_HUSH_CASE
6021 if (ctx.ctx_res_w == RES_MATCH) 6489 if (ctx.ctx_res_w == RES_MATCH) {
6022 goto case_semi; 6490// if (IS_NULL_WORD(ctx.word)) {
6491// /* Testcase: sh -c 'case w in w|) nice; esac' */
6492//also rejects valid: sh -c 'case w in w ) nice; esac'
6493// syntax_error_unexpected_ch(ch);
6494// goto parse_error_exitcode1;
6495// }
6496 if (done_word(&ctx))
6497 goto parse_error_exitcode1;
6498//FIXME: accepts "W1 W2) CMD;;" as if it was "W1|W2) CMD;;", should not allow this syntax at all
6499 done_pipe(&ctx, PIPE_SEQ);
6500 goto finished_cmd;
6501 }
6023#endif 6502#endif
6024 case '}': 6503 /* case ')' and !RES_MATCH: falls through to the error: */
6025 /* proper use of this character is caught by end_trigger: 6504 /* Testcase for bad ')' ? */
6505 /* case '}': */
6506 /* Proper use of this character is caught by end_trigger:
6026 * if we see {, we call parse_group(..., end_trigger='}') 6507 * if we see {, we call parse_group(..., end_trigger='}')
6027 * and it will match } earlier (not here). */ 6508 * and it will match } earlier (not here).
6509 */
6510 /* Testcase for bad '}' ? */
6511 default: /* all other chars should not ever reach this... */
6028 G.last_exitcode = 2; 6512 G.last_exitcode = 2;
6029 syntax_error_unexpected_ch(ch); 6513 syntax_error_unexpected_ch(ch);
6030 goto parse_error; 6514 goto parse_error;
6031 default:
6032 if (HUSH_DEBUG)
6033 bb_error_msg_and_die("BUG: unexpected %c", ch);
6034 } 6515 }
6035 } /* while (1) */ 6516 } /* while (1) */
6517 eof:
6518 /* Reached EOF */
6519 if (heredoc_cnt) {
6520 syntax_error_unterm_str("here document");
6521 goto parse_error_exitcode1;
6522 }
6523 if (end_trigger == ')') {
6524 syntax_error_unterm_ch('(');
6525 goto parse_error_exitcode1;
6526 }
6527 if (end_trigger == '}') {
6528 syntax_error_unterm_ch('{');
6529 goto parse_error_exitcode1;
6530 }
6531
6532 if (done_word(&ctx))
6533 goto parse_error_exitcode1;
6534#if ENABLE_HUSH_FUNCTION_KEYWORD
6535 if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) {
6536 /* Testcase: sh -c 'function'; sh -c 'function f' */
6537 syntax_error("expected funcdef");
6538 goto parse_error_exitcode1;
6539 }
6540#endif
6541 o_free_and_set_NULL(&ctx.word);
6542 if (done_pipe(&ctx, PIPE_SEQ)) {
6543 /* Testcase: sh -c 'date |' */
6544 syntax_error_unterm_ch('|');
6545 goto parse_error_exitcode1;
6546 }
6547// TODO: catch 'date &&<whitespace><EOF>' and 'date ||<whitespace><EOF>' too
6548
6549#if HAS_KEYWORDS
6550 /* Do we sit inside of any if's, loops or case's? */
6551 if (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0) {
6552 syntax_error_unterm_str("compound statement");
6553 goto parse_error_exitcode1;
6554 }
6555#endif
6556 pi = ctx.list_head;
6557 /* If we got nothing... */
6558 if (pi->num_cmds == 0
6559 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
6560 ) {
6561 free_pipe_list(pi);
6562 pi = NULL;
6563 }
6564#if !BB_MMU
6565 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
6566 if (pstring)
6567 *pstring = ctx.as_string.data;
6568 else
6569 o_free(&ctx.as_string);
6570#endif
6571 // heredoc_cnt must be 0 here anyway
6572 //if (heredoc_cnt_ptr)
6573 // *heredoc_cnt_ptr = heredoc_cnt;
6574 debug_leave();
6575 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
6576 debug_printf_parse("parse_stream return %p: EOF\n", pi);
6577 i_free_alias_buffer(input);
6578 return pi;
6036 6579
6037 parse_error_exitcode1: 6580 parse_error_exitcode1:
6038 G.last_exitcode = 1; 6581 G.last_exitcode = 1;
6039 parse_error: 6582 parse_error:
6040 { 6583 {
6041 struct parse_context *pctx;
6042 IF_HAS_KEYWORDS(struct parse_context *p2;)
6043
6044 /* Clean up allocated tree. 6584 /* Clean up allocated tree.
6045 * Sample for finding leaks on syntax error recovery path. 6585 * Sample for finding leaks on syntax error recovery path.
6046 * Run it from interactive shell, watch pmap `pidof hush`. 6586 * Run it from interactive shell, watch pmap `pidof hush`.
@@ -6049,44 +6589,39 @@ static struct pipe *parse_stream(char **pstring,
6049 * while if (true | { true;}); then echo ok; fi; do break; done 6589 * while if (true | { true;}); then echo ok; fi; do break; done
6050 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done 6590 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
6051 */ 6591 */
6052 pctx = &ctx; 6592 IF_HAS_KEYWORDS(struct parse_context *stk;)
6593 struct parse_context *pctx = &ctx;
6053 do { 6594 do {
6054 /* Update pipe/command counts, 6595 /* Update pipe/command counts,
6055 * otherwise freeing may miss some */ 6596 * otherwise freeing may miss some */
6056 done_pipe(pctx, PIPE_SEQ); 6597 done_pipe(pctx, PIPE_SEQ);
6057 debug_printf_clean("freeing list %p from ctx %p\n", 6598 debug_printf_clean("freeing list %p from ctx %p\n", pctx->list_head, pctx);
6058 pctx->list_head, pctx);
6059 debug_print_tree(pctx->list_head, 0); 6599 debug_print_tree(pctx->list_head, 0);
6060 free_pipe_list(pctx->list_head); 6600 free_pipe_list(pctx->list_head);
6061 debug_printf_clean("freed list %p\n", pctx->list_head); 6601 debug_printf_clean("freed list %p\n", pctx->list_head);
6062#if !BB_MMU 6602#if !BB_MMU
6063 o_free(&pctx->as_string); 6603 o_free(&pctx->as_string);
6064#endif 6604#endif
6065 IF_HAS_KEYWORDS(p2 = pctx->stack;) 6605 IF_HAS_KEYWORDS(stk = pctx->stack;)
6066 if (pctx != &ctx) { 6606 if (pctx != &ctx)
6067 free(pctx); 6607 free(pctx);
6068 } 6608 IF_HAS_KEYWORDS(pctx = stk;)
6069 IF_HAS_KEYWORDS(pctx = p2;)
6070 } while (HAS_KEYWORDS && pctx); 6609 } while (HAS_KEYWORDS && pctx);
6071 6610 }
6072 o_free(&ctx.word); 6611 o_free(&ctx.word);
6073#if !BB_MMU 6612#if !BB_MMU
6074 if (pstring) 6613 if (pstring)
6075 *pstring = NULL; 6614 *pstring = NULL;
6076#endif 6615#endif
6077 debug_leave(); 6616 debug_leave();
6078 return ERR_PTR; 6617 i_free_alias_buffer(input);
6079 } 6618 return ERR_PTR;
6080} 6619}
6081 6620
6082/* 6621/*
6083 * Execution routines 6622 * Execution routines
6084 */ 6623 */
6085/* Expansion can recurse, need forward decls: */ 6624/* Expansion can recurse, need forward decls: */
6086#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6087#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6088 expand_string_to_string(str)
6089#endif
6090static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); 6625static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6091#if ENABLE_HUSH_TICK 6626#if ENABLE_HUSH_TICK
6092static int process_command_subs(o_string *dest, const char *s); 6627static int process_command_subs(o_string *dest, const char *s);
@@ -6151,7 +6686,7 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
6151 word_len = strcspn(str, G.ifs); 6686 word_len = strcspn(str, G.ifs);
6152 if (word_len) { 6687 if (word_len) {
6153 /* We have WORD_LEN leading non-IFS chars */ 6688 /* We have WORD_LEN leading non-IFS chars */
6154 if (!(output->o_expflags & EXP_FLAG_GLOB)) { 6689 if (!(output->o_expflags & EXP_FLAG_DO_GLOBBING)) {
6155 o_addblock(output, str, word_len); 6690 o_addblock(output, str, word_len);
6156 } else { 6691 } else {
6157 /* Protect backslashes against globbing up :) 6692 /* Protect backslashes against globbing up :)
@@ -6244,7 +6779,7 @@ static char *encode_then_expand_string(const char *str)
6244//TODO: error check (encode_string returns 0 on error)? 6779//TODO: error check (encode_string returns 0 on error)?
6245 //bb_error_msg("'%s' -> '%s'", str, dest.data); 6780 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6246 exp_str = expand_string_to_string(dest.data, 6781 exp_str = expand_string_to_string(dest.data,
6247 EXP_FLAG_ESC_GLOB_CHARS, 6782 EXP_FLAG_GLOBPROTECT_CHARS, /* example: `echo '_\t_\\_\"_'` in heredoc */
6248 /*unbackslash:*/ 1 6783 /*unbackslash:*/ 1
6249 ); 6784 );
6250 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); 6785 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
@@ -6279,15 +6814,11 @@ static const char *first_special_char_in_vararg(const char *cp)
6279 * a dquoted string: "${var#"zz"}"), the difference only comes later 6814 * a dquoted string: "${var#"zz"}"), the difference only comes later
6280 * (word splitting and globbing of the ${var...} result). 6815 * (word splitting and globbing of the ${var...} result).
6281 */ 6816 */
6282#if !BASH_PATTERN_SUBST 6817static char *encode_then_expand_vararg(const char *str,
6283#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \ 6818 int handle_squotes, /* 'str' substrings are parsed as literals (unless inside "")*/
6284 encode_then_expand_vararg(str, handle_squotes) 6819 int protect_vars, /* glob-protect double-quoted $VARS */
6285#endif 6820 int do_unbackslash /* run unbackslash on result before returning it */
6286static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash) 6821) {
6287{
6288#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6289 const int do_unbackslash = 0;
6290#endif
6291 char *exp_str; 6822 char *exp_str;
6292 struct in_str input; 6823 struct in_str input;
6293 o_string dest = NULL_O_STRING; 6824 o_string dest = NULL_O_STRING;
@@ -6322,7 +6853,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6322 goto ret; /* error */ 6853 goto ret; /* error */
6323 } 6854 }
6324 if (ch == '"') { 6855 if (ch == '"') {
6325 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; 6856 dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS;
6326 continue; 6857 continue;
6327 } 6858 }
6328 if (ch == '\\') { 6859 if (ch == '\\') {
@@ -6338,7 +6869,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6338 if (ch == '$') { 6869 if (ch == '$') {
6339 if (parse_dollar_squote(NULL, &dest, &input)) 6870 if (parse_dollar_squote(NULL, &dest, &input))
6340 continue; 6871 continue;
6341 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) { 6872 if (!parse_dollar(NULL, &dest, &input,
6873 /*quote_mask:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) ? 0x80 : 0
6874 )
6875 ) {
6342 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); 6876 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6343 goto ret; 6877 goto ret;
6344 } 6878 }
@@ -6350,7 +6884,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6350 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6884 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6351 o_addchr(&dest, 0x80 | '`'); 6885 o_addchr(&dest, 0x80 | '`');
6352 if (!add_till_backquote(&dest, &input, 6886 if (!add_till_backquote(&dest, &input,
6353 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ 6887 /*in_dquote:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)
6354 ) 6888 )
6355 ) { 6889 ) {
6356 goto ret; /* error */ 6890 goto ret; /* error */
@@ -6363,9 +6897,12 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6363 o_addQchr(&dest, ch); 6897 o_addQchr(&dest, ch);
6364 } /* for (;;) */ 6898 } /* for (;;) */
6365 6899
6366 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data); 6900 debug_printf_parse("encode(do_unbackslash:%d): '%s' -> '%s'\n", do_unbackslash, str, dest.data);
6367 exp_str = expand_string_to_string(dest.data, 6901 exp_str = expand_string_to_string(dest.data,
6368 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, 6902 0
6903 | (do_unbackslash ? EXP_FLAG_GLOBPROTECT_CHARS : 0)
6904 | (protect_vars ? EXP_FLAG_GLOBPROTECT_VARS : 0)
6905 ,
6369 do_unbackslash 6906 do_unbackslash
6370 ); 6907 );
6371 ret: 6908 ret:
@@ -6409,7 +6946,10 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
6409 if (!dest.o_expflags) { 6946 if (!dest.o_expflags) {
6410 if (ch == EOF) 6947 if (ch == EOF)
6411 break; 6948 break;
6412 if (!dquoted && !(output->o_expflags & EXP_FLAG_SINGLEWORD) && strchr(G.ifs, ch)) { 6949 if (!dquoted
6950 && !(output->o_expflags & EXP_FLAG_SINGLEWORD)
6951 && strchr(G.ifs, ch)
6952 ) {
6413 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word. 6953 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
6414 * do not assume we are at the start of the word (PREFIX above). 6954 * do not assume we are at the start of the word (PREFIX above).
6415 */ 6955 */
@@ -6449,7 +6989,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
6449 goto ret; /* error */ 6989 goto ret; /* error */
6450 } 6990 }
6451 if (ch == '"') { 6991 if (ch == '"') {
6452 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; 6992 dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS;
6453 if (dest.o_expflags) { 6993 if (dest.o_expflags) {
6454 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6994 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6455 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6995 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
@@ -6479,7 +7019,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
6479 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 7019 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6480 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`'); 7020 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6481 if (!add_till_backquote(&dest, &input, 7021 if (!add_till_backquote(&dest, &input,
6482 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ 7022 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_GLOBPROTECT_CHARS set */
6483 ) 7023 )
6484 ) { 7024 ) {
6485 goto ret; /* error */ 7025 goto ret; /* error */
@@ -6607,27 +7147,39 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c
6607#endif /* BASH_PATTERN_SUBST */ 7147#endif /* BASH_PATTERN_SUBST */
6608 7148
6609static int append_str_maybe_ifs_split(o_string *output, int n, 7149static int append_str_maybe_ifs_split(o_string *output, int n,
6610 int first_ch, const char *val) 7150 int quoted, const char *val)
6611{ 7151{
6612 if (!(first_ch & 0x80)) { /* unquoted $VAR */ 7152 if (!quoted) { /* unquoted $VAR */
6613 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, 7153 debug_printf_expand("unquoted variable value '%s', o_escape:%d o_varescape:%d, singleword:%d\n", val,
6614 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 7154 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS),
6615 if (val && val[0]) 7155 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS),
7156 !!(output->o_expflags & EXP_FLAG_SINGLEWORD)
7157 );
7158 if (val && val[0]) {
7159 if (output->o_expflags & EXP_FLAG_SINGLEWORD)
7160 goto singleword;
6616 n = expand_on_ifs(output, n, val); 7161 n = expand_on_ifs(output, n, val);
7162 }
6617 } else { /* quoted "$VAR" */ 7163 } else { /* quoted "$VAR" */
6618 output->has_quoted_part = 1; 7164 output->has_quoted_part = 1;
6619 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, 7165 debug_printf_expand("quoted variable value '%s', o_escape:%d o_varescape:%d\n", val,
6620 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 7166 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS),
6621 if (val && val[0]) 7167 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS)
6622 o_addQstr(output, val); 7168 );
7169 if (val && val[0]) {
7170 if (output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS)
7171 o_addqstr(output, val);
7172 else
7173 singleword:
7174 o_addQstr(output, val);
7175 }
6623 } 7176 }
6624 return n; 7177 return n;
6625} 7178}
6626 7179
6627/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. 7180/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
6628 */ 7181 */
6629static NOINLINE int expand_one_var(o_string *output, int n, 7182static NOINLINE int expand_one_var(o_string *output, int n, char *arg, char **pp)
6630 int first_ch, char *arg, char **pp)
6631{ 7183{
6632 const char *val; 7184 const char *val;
6633 char *to_be_freed; 7185 char *to_be_freed;
@@ -6763,7 +7315,11 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6763 if (exp_op == *exp_word) /* ## or %% */ 7315 if (exp_op == *exp_word) /* ## or %% */
6764 exp_word++; 7316 exp_word++;
6765 debug_printf_expand("expand: exp_word:'%s'\n", exp_word); 7317 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
6766 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); 7318 exp_exp_word = encode_then_expand_vararg(exp_word,
7319 /*handle_squotes:*/ 1, /* 'str' are processed (and glob-protected) */
7320 /*globprotect_vars:*/ 1, /* value of "$VAR" is not glob-expanded */
7321 /*unbackslash:*/ 0
7322 );
6767 if (exp_exp_word) 7323 if (exp_exp_word)
6768 exp_word = exp_exp_word; 7324 exp_word = exp_exp_word;
6769 debug_printf_expand("expand: exp_word:'%s'\n", exp_word); 7325 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
@@ -6810,7 +7366,11 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6810 * (note that a*z _pattern_ is never globbed!) 7366 * (note that a*z _pattern_ is never globbed!)
6811 */ 7367 */
6812 char *pattern, *repl, *t; 7368 char *pattern, *repl, *t;
6813 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); 7369 pattern = encode_then_expand_vararg(exp_word,
7370 /*handle_squotes:*/ 1,
7371 /*globprotect_vars:*/ 0,
7372 /*unbackslash:*/ 0
7373 );
6814 if (!pattern) 7374 if (!pattern)
6815 pattern = xstrdup(exp_word); 7375 pattern = xstrdup(exp_word);
6816 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); 7376 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
@@ -6818,7 +7378,11 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6818 exp_word = p; 7378 exp_word = p;
6819 p = strchr(p, SPECIAL_VAR_SYMBOL); 7379 p = strchr(p, SPECIAL_VAR_SYMBOL);
6820 *p = '\0'; 7380 *p = '\0';
6821 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1); 7381 repl = encode_then_expand_vararg(exp_word,
7382 /*handle_squotes:*/ 1,
7383 /*globprotect_vars:*/ 0,
7384 /*unbackslash:*/ 1
7385 );
6822 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); 7386 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
6823 /* HACK ALERT. We depend here on the fact that 7387 /* HACK ALERT. We depend here on the fact that
6824 * G.global_argv and results of utoa and get_local_var_value 7388 * G.global_argv and results of utoa and get_local_var_value
@@ -6971,6 +7535,7 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6971 /* ${var=word} - assign and use default value */ 7535 /* ${var=word} - assign and use default value */
6972 to_be_freed = encode_then_expand_vararg(exp_word, 7536 to_be_freed = encode_then_expand_vararg(exp_word,
6973 /*handle_squotes:*/ !(arg0 & 0x80), 7537 /*handle_squotes:*/ !(arg0 & 0x80),
7538 /*globprotect_vars:*/ 0,
6974 /*unbackslash:*/ 0 7539 /*unbackslash:*/ 0
6975 ); 7540 );
6976 if (to_be_freed) 7541 if (to_be_freed)
@@ -7015,7 +7580,7 @@ static NOINLINE int expand_one_var(o_string *output, int n,
7015 arg[0] = arg0; 7580 arg[0] = arg0;
7016 *pp = p; 7581 *pp = p;
7017 7582
7018 n = append_str_maybe_ifs_split(output, n, first_ch, val); 7583 n = append_str_maybe_ifs_split(output, n, /*quoted:*/ (arg0 & 0x80), val);
7019 7584
7020 free(to_be_freed); 7585 free(to_be_freed);
7021 return n; 7586 return n;
@@ -7143,7 +7708,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7143 G.last_exitcode = process_command_subs(&subst_result, arg); 7708 G.last_exitcode = process_command_subs(&subst_result, arg);
7144 G.expand_exitcode = G.last_exitcode; 7709 G.expand_exitcode = G.last_exitcode;
7145 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); 7710 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7146 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data); 7711 n = append_str_maybe_ifs_split(output, n, /*quoted:*/(first_ch & 0x80), subst_result.data);
7147 o_free(&subst_result); 7712 o_free(&subst_result);
7148 break; 7713 break;
7149 } 7714 }
@@ -7161,7 +7726,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7161 sprintf(arith_buf, ARITH_FMT, res); 7726 sprintf(arith_buf, ARITH_FMT, res);
7162 if (res < 0 7727 if (res < 0
7163 && first_ch == (char)('+'|0x80) 7728 && first_ch == (char)('+'|0x80)
7164 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */ 7729 /* && (output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) */
7165 ) { 7730 ) {
7166 /* Quoted negative ariths, like filename[0"$((-9))"], 7731 /* Quoted negative ariths, like filename[0"$((-9))"],
7167 * should not be interpreted as glob ranges. 7732 * should not be interpreted as glob ranges.
@@ -7176,7 +7741,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7176#endif 7741#endif
7177 default: 7742 default:
7178 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */ 7743 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7179 n = expand_one_var(output, n, first_ch, arg, &p); 7744 n = expand_one_var(output, n, arg, &p);
7180 break; 7745 break;
7181 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ 7746 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7182 7747
@@ -7244,7 +7809,7 @@ static char **expand_variables(char **argv, unsigned expflags)
7244 7809
7245static char **expand_strvec_to_strvec(char **argv) 7810static char **expand_strvec_to_strvec(char **argv)
7246{ 7811{
7247 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); 7812 return expand_variables(argv, EXP_FLAG_DO_GLOBBING | EXP_FLAG_GLOBPROTECT_CHARS);
7248} 7813}
7249 7814
7250#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) 7815#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
@@ -7262,10 +7827,6 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7262 */ 7827 */
7263static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash) 7828static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7264{ 7829{
7265#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7266 const int do_unbackslash = 1;
7267 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7268#endif
7269 char *argv[2], **list; 7830 char *argv[2], **list;
7270 7831
7271 debug_printf_expand("string_to_string<='%s'\n", str); 7832 debug_printf_expand("string_to_string<='%s'\n", str);
@@ -7334,7 +7895,7 @@ static char **expand_assignments(char **argv, int count)
7334 for (i = 0; i < count; i++) { 7895 for (i = 0; i < count; i++) {
7335 p = add_string_to_strings(p, 7896 p = add_string_to_strings(p,
7336 expand_string_to_string(argv[i], 7897 expand_string_to_string(argv[i],
7337 EXP_FLAG_ESC_GLOB_CHARS, 7898 EXP_FLAG_GLOBPROTECT_CHARS,
7338 /*unbackslash:*/ 1 7899 /*unbackslash:*/ 1
7339 ) 7900 )
7340 ); 7901 );
@@ -7366,11 +7927,6 @@ static void switch_off_special_sigs(unsigned mask)
7366} 7927}
7367 7928
7368#if BB_MMU 7929#if BB_MMU
7369/* never called */
7370void re_execute_shell(char ***to_free, const char *s,
7371 char *g_argv0, char **g_argv,
7372 char **builtin_argv) NORETURN;
7373
7374static void reset_traps_to_defaults(void) 7930static void reset_traps_to_defaults(void)
7375{ 7931{
7376 /* This function is always called in a child shell 7932 /* This function is always called in a child shell
@@ -7420,10 +7976,8 @@ static void reset_traps_to_defaults(void)
7420 7976
7421#else /* !BB_MMU */ 7977#else /* !BB_MMU */
7422 7978
7423static void re_execute_shell(char ***to_free, const char *s, 7979static NORETURN void re_execute_shell(
7424 char *g_argv0, char **g_argv, 7980 char * *volatile * to_free, const char *s,
7425 char **builtin_argv) NORETURN;
7426static void re_execute_shell(char ***to_free, const char *s,
7427 char *g_argv0, char **g_argv, 7981 char *g_argv0, char **g_argv,
7428 char **builtin_argv) 7982 char **builtin_argv)
7429{ 7983{
@@ -7653,7 +8207,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7653 pid_t pid; 8207 pid_t pid;
7654 int channel[2]; 8208 int channel[2];
7655# if !BB_MMU 8209# if !BB_MMU
7656 char **to_free = NULL; 8210 /* _volatile_ pointer to "char*".
8211 * Or else compiler can peek from inside re_execute_shell()
8212 * and see that this pointer is a local var (i.e. not globally visible),
8213 * and decide to optimize out the store to it. Yes,
8214 * it was seen in the wild.
8215 */
8216 char * *volatile to_free = NULL;
7657# endif 8217# endif
7658 8218
7659 xpipe(channel); 8219 xpipe(channel);
@@ -7800,16 +8360,29 @@ static void setup_heredoc(struct redir_struct *redir)
7800 const char *heredoc = redir->rd_filename; 8360 const char *heredoc = redir->rd_filename;
7801 char *expanded; 8361 char *expanded;
7802#if !BB_MMU 8362#if !BB_MMU
7803 char **to_free; 8363 char * *volatile to_free;
7804#endif 8364#endif
7805 8365
7806 expanded = NULL; 8366 expanded = NULL;
7807 if (!(redir->rd_dup & HEREDOC_QUOTED)) { 8367 if (redir->rd_type == REDIRECT_HERESTRING) {
8368 heredoc = expanded = expand_string_to_string(heredoc,
8369 EXP_FLAG_GLOBPROTECT_CHARS,
8370 /* ^^^^why? testcases:
8371 * cat <<<`echo '_\t_\\_\"_'` prints _\t_\_\"_<newline>
8372 * cat <<<"`echo '_\t_\\_\"_'`" prints _\t_\_"_<newline>
8373 */
8374 /*unbackslash:*/ 1
8375 /* ^^^^^^^^^^ cat <<<\_ prints _<newline> */
8376 );
8377 } else if (!(redir->rd_dup & HEREDOC_QUOTED)) {
7808 expanded = encode_then_expand_string(heredoc); 8378 expanded = encode_then_expand_string(heredoc);
7809 if (expanded) 8379 if (expanded)
7810 heredoc = expanded; 8380 heredoc = expanded;
7811 } 8381 }
8382
7812 len = strlen(heredoc); 8383 len = strlen(heredoc);
8384 if (redir->rd_type == REDIRECT_HERESTRING)
8385 expanded[len++] = '\n';
7813 8386
7814 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */ 8387 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
7815 xpiped_pair(pair); 8388 xpiped_pair(pair);
@@ -8053,7 +8626,7 @@ static void restore_redirects(struct squirrel *sq)
8053 * Redirect moves ->fd to e.g. 10, 8626 * Redirect moves ->fd to e.g. 10,
8054 * and it is not restored above (we do not restore script fds 8627 * and it is not restored above (we do not restore script fds
8055 * after redirects, we just use new, "moved" fds). 8628 * after redirects, we just use new, "moved" fds).
8056 * However for stdin, get_user_input() -> read_line_input(), 8629 * However for stdin, show_prompt_and_get_stdin() -> read_line_input(),
8057 * and read builtin, depend on fd == STDIN_FILENO. 8630 * and read builtin, depend on fd == STDIN_FILENO.
8058 */ 8631 */
8059 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd); 8632 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
@@ -8105,12 +8678,14 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8105 int newfd; 8678 int newfd;
8106 int closed; 8679 int closed;
8107 8680
8108 if (redir->rd_type == REDIRECT_HEREDOC2) { 8681 if (redir->rd_type == REDIRECT_HEREDOC2
8109 /* "rd_fd<<HERE" case */ 8682 || redir->rd_type == REDIRECT_HERESTRING
8683 ) {
8684 /* "rd_fd<<HEREDOC_EOF" and "rd_fd<<<WORD" cases */
8110 if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0) 8685 if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0)
8111 return 1; 8686 return 1;
8112 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ 8687 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ of the heredoc */
8113 * of the heredoc */ 8688 /* for REDIRECT_HERESTRING, rd_filename holds "WORD" */
8114 debug_printf_redir("set heredoc '%s'\n", 8689 debug_printf_redir("set heredoc '%s'\n",
8115 redir->rd_filename); 8690 redir->rd_filename);
8116 setup_heredoc(redir); 8691 setup_heredoc(redir);
@@ -8132,7 +8707,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8132 } 8707 }
8133 mode = redir_table[redir->rd_type].mode; 8708 mode = redir_table[redir->rd_type].mode;
8134 p = expand_string_to_string(redir->rd_filename, 8709 p = expand_string_to_string(redir->rd_filename,
8135 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); 8710 EXP_FLAG_GLOBPROTECT_CHARS,
8711 /*unbackslash:*/ 1
8712 );
8136 newfd = open_or_warn(p, mode); 8713 newfd = open_or_warn(p, mode);
8137 free(p); 8714 free(p);
8138 if (newfd < 0) { 8715 if (newfd < 0) {
@@ -8275,7 +8852,7 @@ static const struct built_in_command *find_builtin(const char *name)
8275 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8852 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8276} 8853}
8277 8854
8278#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8855#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8279static const char * FAST_FUNC hush_command_name(int i) 8856static const char * FAST_FUNC hush_command_name(int i)
8280{ 8857{
8281 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8858 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8441,10 +9018,8 @@ static void unset_func(const char *name)
8441#define exec_function(to_free, funcp, argv) \ 9018#define exec_function(to_free, funcp, argv) \
8442 exec_function(funcp, argv) 9019 exec_function(funcp, argv)
8443# endif 9020# endif
8444static void exec_function(char ***to_free, 9021static NORETURN void exec_function(
8445 const struct function *funcp, 9022 char * *volatile *to_free,
8446 char **argv) NORETURN;
8447static void exec_function(char ***to_free,
8448 const struct function *funcp, 9023 const struct function *funcp,
8449 char **argv) 9024 char **argv)
8450{ 9025{
@@ -8540,10 +9115,8 @@ static int run_function(const struct function *funcp, char **argv)
8540#define exec_builtin(to_free, x, argv) \ 9115#define exec_builtin(to_free, x, argv) \
8541 exec_builtin(to_free, argv) 9116 exec_builtin(to_free, argv)
8542#endif 9117#endif
8543static void exec_builtin(char ***to_free, 9118static NORETURN void exec_builtin(
8544 const struct built_in_command *x, 9119 char * *volatile *to_free,
8545 char **argv) NORETURN;
8546static void exec_builtin(char ***to_free,
8547 const struct built_in_command *x, 9120 const struct built_in_command *x,
8548 char **argv) 9121 char **argv)
8549{ 9122{
@@ -8566,8 +9139,7 @@ static void exec_builtin(char ***to_free,
8566#endif 9139#endif
8567} 9140}
8568 9141
8569static void execvp_or_die(char **argv) NORETURN; 9142static NORETURN void execvp_or_die(char **argv)
8570static void execvp_or_die(char **argv)
8571{ 9143{
8572 int e; 9144 int e;
8573 debug_printf_exec("execing '%s'\n", argv[0]); 9145 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8688,10 +9260,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp
8688 * The at_exit handlers apparently confuse the calling process, 9260 * The at_exit handlers apparently confuse the calling process,
8689 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 9261 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8690 */ 9262 */
8691static void pseudo_exec_argv(nommu_save_t *nommu_save, 9263static NORETURN NOINLINE void pseudo_exec_argv(
8692 char **argv, int assignment_cnt, 9264 volatile nommu_save_t *nommu_save,
8693 char **argv_expanded) NORETURN;
8694static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8695 char **argv, int assignment_cnt, 9265 char **argv, int assignment_cnt,
8696 char **argv_expanded) 9266 char **argv_expanded)
8697{ 9267{
@@ -8717,7 +9287,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8717#if BB_MMU 9287#if BB_MMU
8718 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 9288 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8719#else 9289#else
8720 G.shadowed_vars_pp = &nommu_save->old_vars; 9290 /* cast away volatility */
9291 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8721 G.var_nest_level++; 9292 G.var_nest_level++;
8722#endif 9293#endif
8723 set_vars_and_save_old(new_env); 9294 set_vars_and_save_old(new_env);
@@ -8844,10 +9415,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8844 9415
8845/* Called after [v]fork() in run_pipe 9416/* Called after [v]fork() in run_pipe
8846 */ 9417 */
8847static void pseudo_exec(nommu_save_t *nommu_save, 9418static NORETURN void pseudo_exec(
8848 struct command *command, 9419 volatile nommu_save_t *nommu_save,
8849 char **argv_expanded) NORETURN;
8850static void pseudo_exec(nommu_save_t *nommu_save,
8851 struct command *command, 9420 struct command *command,
8852 char **argv_expanded) 9421 char **argv_expanded)
8853{ 9422{
@@ -8874,13 +9443,14 @@ static void pseudo_exec(nommu_save_t *nommu_save,
8874 */ 9443 */
8875#if BB_MMU 9444#if BB_MMU
8876 int rcode; 9445 int rcode;
8877 debug_printf_exec("pseudo_exec: run_list\n"); 9446 debug_printf_exec("pseudo_exec: run_list(command->group)\n");
8878 reset_traps_to_defaults(); 9447 reset_traps_to_defaults();
8879 rcode = run_list(command->group); 9448 rcode = run_list(command->group);
8880 /* OK to leak memory by not calling free_pipe_list, 9449 /* OK to leak memory by not calling free_pipe_list,
8881 * since this process is about to exit */ 9450 * since this process is about to exit */
8882 _exit(rcode); 9451 _exit(rcode);
8883#else 9452#else
9453 debug_printf_exec("pseudo_exec: re_exec(command->group_as_string)\n");
8884 re_execute_shell(&nommu_save->argv_from_re_execing, 9454 re_execute_shell(&nommu_save->argv_from_re_execing,
8885 command->group_as_string, 9455 command->group_as_string,
8886 G.global_argv[0], 9456 G.global_argv[0],
@@ -9456,7 +10026,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9456 i = 0; 10026 i = 0;
9457 while (i < command->assignment_cnt) { 10027 while (i < command->assignment_cnt) {
9458 char *p = expand_string_to_string(argv[i], 10028 char *p = expand_string_to_string(argv[i],
9459 EXP_FLAG_ESC_GLOB_CHARS, 10029 EXP_FLAG_GLOBPROTECT_CHARS,
9460 /*unbackslash:*/ 1 10030 /*unbackslash:*/ 1
9461 ); 10031 );
9462#if ENABLE_HUSH_MODE_X 10032#if ENABLE_HUSH_MODE_X
@@ -9678,13 +10248,20 @@ static NOINLINE int run_pipe(struct pipe *pi)
9678#if ENABLE_HUSH_LINENO_VAR 10248#if ENABLE_HUSH_LINENO_VAR
9679 G.execute_lineno = command->lineno; 10249 G.execute_lineno = command->lineno;
9680#endif 10250#endif
9681
9682 command->pid = BB_MMU ? fork() : vfork(); 10251 command->pid = BB_MMU ? fork() : vfork();
9683 if (!command->pid) { /* child */ 10252 if (!command->pid) { /* child */
9684#if ENABLE_HUSH_JOB
9685 disable_restore_tty_pgrp_on_exit(); 10253 disable_restore_tty_pgrp_on_exit();
9686 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
9687 10254
10255#if ENABLE_HUSH_INTERACTIVE
10256 if (BB_MMU && pi->followup == PIPE_BG) {
10257 /* This disables alias expansion in . FILE & */
10258 /* (bash does it only for (. FILE)& */
10259 G.interactive_fd = 0;
10260 }
10261#endif
10262 if (BB_MMU)
10263 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
10264#if ENABLE_HUSH_JOB
9688 /* Every child adds itself to new process group 10265 /* Every child adds itself to new process group
9689 * with pgid == pid_of_first_child_in_pipe */ 10266 * with pgid == pid_of_first_child_in_pipe */
9690 if (G.run_list_level == 1 && G_interactive_fd) { 10267 if (G.run_list_level == 1 && G_interactive_fd) {
@@ -9732,8 +10309,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9732 10309
9733 /* Stores to nommu_save list of env vars putenv'ed 10310 /* Stores to nommu_save list of env vars putenv'ed
9734 * (NOMMU, on MMU we don't need that) */ 10311 * (NOMMU, on MMU we don't need that) */
9735 /* cast away volatility... */ 10312 pseudo_exec(&nommu_save, command, argv_expanded);
9736 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9737 /* pseudo_exec() does not return */ 10313 /* pseudo_exec() does not return */
9738 } 10314 }
9739 10315
@@ -9978,7 +10554,9 @@ static int run_list(struct pipe *pi)
9978 if (rword == RES_CASE) { 10554 if (rword == RES_CASE) {
9979 debug_printf_exec("CASE cond_code:%d\n", cond_code); 10555 debug_printf_exec("CASE cond_code:%d\n", cond_code);
9980 case_word = expand_string_to_string(pi->cmds->argv[0], 10556 case_word = expand_string_to_string(pi->cmds->argv[0],
9981 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); 10557 EXP_FLAG_GLOBPROTECT_CHARS,
10558 /*unbackslash:*/ 1
10559 );
9982 debug_printf_exec("CASE word1:'%s'\n", case_word); 10560 debug_printf_exec("CASE word1:'%s'\n", case_word);
9983 //unbackslash(case_word); 10561 //unbackslash(case_word);
9984 //debug_printf_exec("CASE word2:'%s'\n", case_word); 10562 //debug_printf_exec("CASE word2:'%s'\n", case_word);
@@ -9996,7 +10574,7 @@ static int run_list(struct pipe *pi)
9996 char *pattern; 10574 char *pattern;
9997 debug_printf_exec("expand_string_to_string('%s')\n", *argv); 10575 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
9998 pattern = expand_string_to_string(*argv, 10576 pattern = expand_string_to_string(*argv,
9999 EXP_FLAG_ESC_GLOB_CHARS, 10577 EXP_FLAG_GLOBPROTECT_CHARS,
10000 /*unbackslash:*/ 0 10578 /*unbackslash:*/ 0
10001 ); 10579 );
10002 /* TODO: which FNM_xxx flags to use? */ 10580 /* TODO: which FNM_xxx flags to use? */
@@ -10121,7 +10699,7 @@ static int run_list(struct pipe *pi)
10121 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10699 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10122 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); 10700 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10123 if (G.errexit_depth == 0) 10701 if (G.errexit_depth == 0)
10124 hush_exit(rcode); 10702 save_history_run_exit_trap_and_exit(rcode);
10125 } 10703 }
10126 G.errexit_depth = sv_errexit_depth; 10704 G.errexit_depth = sv_errexit_depth;
10127 10705
@@ -10195,6 +10773,53 @@ static int run_and_free_list(struct pipe *pi)
10195/* 10773/*
10196 * Initialization and main 10774 * Initialization and main
10197 */ 10775 */
10776#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING
10777static void init_line_editing(void)
10778{
10779 G.line_input_state = new_line_input_t(FOR_SHELL);
10780# if ENABLE_FEATURE_TAB_COMPLETION
10781 G.line_input_state->get_exe_name = hush_command_name;
10782# endif
10783# if EDITING_HAS_sh_get_var
10784 G.line_input_state->sh_get_var = get_local_var_value;
10785# endif
10786# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10787 {
10788 const char *hp = get_local_var_value("HISTFILE");
10789 if (!hp) {
10790 hp = get_local_var_value("HOME");
10791 if (hp) {
10792 hp = concat_path_file(hp, ".hush_history");
10793 /* Make HISTFILE set on exit (else history won't be saved) */
10794 set_local_var_from_halves("HISTFILE", hp);
10795 }
10796 } else {
10797 hp = xstrdup(hp);
10798 }
10799 if (hp) {
10800 G.line_input_state->hist_file = hp;
10801 }
10802# if ENABLE_FEATURE_SH_HISTFILESIZE
10803 hp = get_local_var_value("HISTSIZE");
10804 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10805 * users may set HISTFILESIZE=0 in their profile scripts
10806 * to prevent _saving_ of history files, but still want to have
10807 * non-zero history limit for in-memory list.
10808 */
10809// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10810// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10811 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10812// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10813// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10814// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10815# endif
10816 }
10817# endif
10818}
10819#else
10820# define init_line_editing() ((void)0)
10821#endif
10822
10198static void install_sighandlers(unsigned mask) 10823static void install_sighandlers(unsigned mask)
10199{ 10824{
10200 sighandler_t old_handler; 10825 sighandler_t old_handler;
@@ -10333,7 +10958,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10333int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10958int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10334int hush_main(int argc, char **argv) 10959int hush_main(int argc, char **argv)
10335{ 10960{
10336 pid_t cached_getpid;
10337 enum { 10961 enum {
10338 OPT_login = (1 << 0), 10962 OPT_login = (1 << 0),
10339 }; 10963 };
@@ -10346,6 +10970,11 @@ int hush_main(int argc, char **argv)
10346 struct variable *shell_ver; 10970 struct variable *shell_ver;
10347 10971
10348 INIT_G(); 10972 INIT_G();
10973#if ENABLE_HUSH_JOB
10974 die_func = fflush_restore_ttypgrp_and__exit;
10975#else
10976 die_func = fflush_and__exit;
10977#endif
10349 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 10978 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10350 G.last_exitcode = EXIT_SUCCESS; 10979 G.last_exitcode = EXIT_SUCCESS;
10351#if !BB_MMU 10980#if !BB_MMU
@@ -10361,9 +10990,6 @@ int hush_main(int argc, char **argv)
10361 _exit(0); 10990 _exit(0);
10362 } 10991 }
10363 G.argv0_for_re_execing = argv[0]; 10992 G.argv0_for_re_execing = argv[0];
10364 if (G.argv0_for_re_execing[0] == '-')
10365 /* reexeced hush should never be a login shell */
10366 G.argv0_for_re_execing++;
10367#endif 10993#endif
10368#if ENABLE_HUSH_TRAP 10994#if ENABLE_HUSH_TRAP
10369# if ENABLE_HUSH_FUNCTIONS 10995# if ENABLE_HUSH_FUNCTIONS
@@ -10371,14 +10997,11 @@ int hush_main(int argc, char **argv)
10371# endif 10997# endif
10372 G.pre_trap_exitcode = -1; 10998 G.pre_trap_exitcode = -1;
10373#endif 10999#endif
10374
10375#if ENABLE_HUSH_FAST 11000#if ENABLE_HUSH_FAST
10376 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 11001 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10377#endif 11002#endif
10378 11003 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10379 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 11004 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10380 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10381 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10382 11005
10383 /* Deal with HUSH_VERSION */ 11006 /* Deal with HUSH_VERSION */
10384 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 11007 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10427,7 +11050,7 @@ int hush_main(int argc, char **argv)
10427 if (!get_local_var_value("PATH")) 11050 if (!get_local_var_value("PATH"))
10428 set_local_var_from_halves("PATH", bb_default_root_path); 11051 set_local_var_from_halves("PATH", bb_default_root_path);
10429 11052
10430 /* PS1/PS2 are set later, if we determine that we are interactive */ 11053 /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */
10431 11054
10432 /* bash also exports SHLVL and _, 11055 /* bash also exports SHLVL and _,
10433 * and sets (but doesn't export) the following variables: 11056 * and sets (but doesn't export) the following variables:
@@ -10449,7 +11072,6 @@ int hush_main(int argc, char **argv)
10449 * BASH_SOURCE=() 11072 * BASH_SOURCE=()
10450 * DIRSTACK=() 11073 * DIRSTACK=()
10451 * PIPESTATUS=([0]="0") 11074 * PIPESTATUS=([0]="0")
10452 * HISTFILE=/<xxx>/.bash_history
10453 * HISTFILESIZE=500 11075 * HISTFILESIZE=500
10454 * HISTSIZE=500 11076 * HISTSIZE=500
10455 * MAILCHECK=60 11077 * MAILCHECK=60
@@ -10462,27 +11084,24 @@ int hush_main(int argc, char **argv)
10462 * PS4='+ ' 11084 * PS4='+ '
10463 */ 11085 */
10464 11086
11087 /* Shell is non-interactive at first. We need to call
11088 * install_special_sighandlers() if we are going to execute "sh <script>",
11089 * "sh -c <cmds>" or login shell's /etc/profile and friends.
11090 * If we later decide that we are interactive, we run
11091 * install_special_sighandlers() in order to intercept more signals.
11092 */
11093 install_special_sighandlers();
11094
10465#if NUM_SCRIPTS > 0 11095#if NUM_SCRIPTS > 0
10466 if (argc < 0) { 11096 if (argc < 0) {
10467 char *script = get_script_content(-argc - 1); 11097 char *script = get_script_content(-argc - 1);
10468 G.global_argv = argv; 11098 G.global_argv = argv;
10469 G.global_argc = string_array_len(argv); 11099 G.global_argc = string_array_len(argv);
10470 //install_special_sighandlers(); - needed?
10471 parse_and_run_string(script); 11100 parse_and_run_string(script);
10472 goto final_return; 11101 goto final_return;
10473 } 11102 }
10474#endif 11103#endif
10475 11104
10476 /* Initialize some more globals to non-zero values */
10477 die_func = restore_ttypgrp_and__exit;
10478
10479 /* Shell is non-interactive at first. We need to call
10480 * install_special_sighandlers() if we are going to execute "sh <script>",
10481 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10482 * If we later decide that we are interactive, we run install_special_sighandlers()
10483 * in order to intercept (more) signals.
10484 */
10485
10486 /* Parse options */ 11105 /* Parse options */
10487 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 11106 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10488 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 11107 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10546,6 +11165,7 @@ int hush_main(int argc, char **argv)
10546 case '$': { 11165 case '$': {
10547 unsigned long long empty_trap_mask; 11166 unsigned long long empty_trap_mask;
10548 11167
11168 G.reexeced_on_NOMMU = 1;
10549 G.root_pid = bb_strtou(optarg, &optarg, 16); 11169 G.root_pid = bb_strtou(optarg, &optarg, 16);
10550 optarg++; 11170 optarg++;
10551 G.root_ppid = bb_strtou(optarg, &optarg, 16); 11171 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10559,7 +11179,6 @@ int hush_main(int argc, char **argv)
10559 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 11179 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10560 if (empty_trap_mask != 0) { 11180 if (empty_trap_mask != 0) {
10561 IF_HUSH_TRAP(int sig;) 11181 IF_HUSH_TRAP(int sig;)
10562 install_special_sighandlers();
10563# if ENABLE_HUSH_TRAP 11182# if ENABLE_HUSH_TRAP
10564 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 11183 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10565 for (sig = 1; sig < NSIG; sig++) { 11184 for (sig = 1; sig < NSIG; sig++) {
@@ -10625,7 +11244,9 @@ int hush_main(int argc, char **argv)
10625 G.global_argv[0] = argv[0]; 11244 G.global_argv[0] = argv[0];
10626 11245
10627 /* If we are login shell... */ 11246 /* If we are login shell... */
10628 if (flags & OPT_login) { 11247 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
11248 && (flags & OPT_login)
11249 ) {
10629 const char *hp = NULL; 11250 const char *hp = NULL;
10630 HFILE *input; 11251 HFILE *input;
10631 11252
@@ -10633,7 +11254,6 @@ int hush_main(int argc, char **argv)
10633 input = hfopen("/etc/profile"); 11254 input = hfopen("/etc/profile");
10634 run_profile: 11255 run_profile:
10635 if (input != NULL) { 11256 if (input != NULL) {
10636 install_special_sighandlers();
10637 parse_and_run_file(input); 11257 parse_and_run_file(input);
10638 hfclose(input); 11258 hfclose(input);
10639 } 11259 }
@@ -10655,6 +11275,10 @@ int hush_main(int argc, char **argv)
10655 } 11275 }
10656 } 11276 }
10657 } 11277 }
11278 /* "hush -l" doesn't die in startup scripts, but after -l is processed,
11279 * it can die if scripts. Set the flag to achieve this behavior.
11280 */
11281 G.flag_startup_done = 1;
10658 11282
10659 /* -c takes effect *after* -l */ 11283 /* -c takes effect *after* -l */
10660 if (G.opt_c) { 11284 if (G.opt_c) {
@@ -10670,8 +11294,6 @@ int hush_main(int argc, char **argv)
10670 */ 11294 */
10671 char *script; 11295 char *script;
10672 11296
10673 install_special_sighandlers();
10674
10675 G.global_argc--; 11297 G.global_argc--;
10676 G.global_argv++; 11298 G.global_argv++;
10677#if !BB_MMU 11299#if !BB_MMU
@@ -10722,7 +11344,6 @@ int hush_main(int argc, char **argv)
10722 bb_simple_perror_msg_and_die(G.global_argv[0]); 11344 bb_simple_perror_msg_and_die(G.global_argv[0]);
10723 } 11345 }
10724 xfunc_error_retval = 1; 11346 xfunc_error_retval = 1;
10725 install_special_sighandlers();
10726 parse_and_run_file(input); 11347 parse_and_run_file(input);
10727#if ENABLE_FEATURE_CLEAN_UP 11348#if ENABLE_FEATURE_CLEAN_UP
10728 hfclose(input); 11349 hfclose(input);
@@ -10738,126 +11359,86 @@ int hush_main(int argc, char **argv)
10738 11359
10739 /* A shell is interactive if the '-i' flag was given, 11360 /* A shell is interactive if the '-i' flag was given,
10740 * or if all of the following conditions are met: 11361 * or if all of the following conditions are met:
10741 * no -c command 11362 * not -c 'CMD'
10742 * no arguments remaining or the -s flag given 11363 * not running a script (no arguments remaining, or -s flag given)
10743 * standard input is a terminal 11364 * standard input is a terminal
10744 * standard output is a terminal 11365 * standard output is a terminal
10745 * Refer to Posix.2, the description of the 'sh' utility. 11366 * Refer to Posix.2, the description of the 'sh' utility.
10746 */ 11367 */
10747#if ENABLE_HUSH_JOB 11368#if ENABLE_HUSH_INTERACTIVE
10748 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 11369 if (!G_reexeced_on_NOMMU
10749 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 11370 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10750 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 11371 ) {
10751 if (G_saved_tty_pgrp < 0) 11372 /* Try to dup stdin to high fd#, >= 255 */
10752 G_saved_tty_pgrp = 0;
10753
10754 /* try to dup stdin to high fd#, >= 255 */
10755 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 11373 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10756 if (G_interactive_fd < 0) { 11374 if (G_interactive_fd < 0) {
10757 /* try to dup to any fd */ 11375 /* Try to dup to any fd */
10758 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 11376 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10759 if (G_interactive_fd < 0) { 11377 if (G_interactive_fd < 0)
10760 /* give up */ 11378 /* Give up */
10761 G_interactive_fd = 0; 11379 G_interactive_fd = 0;
10762 G_saved_tty_pgrp = 0;
10763 }
10764 } 11380 }
10765 } 11381 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10766 debug_printf("interactive_fd:%d\n", G_interactive_fd); 11382 if (G_interactive_fd) {
10767 if (G_interactive_fd) { 11383// TODO? bash:
10768 if (G_saved_tty_pgrp) { 11384// if interactive but not a login shell, sources ~/.bashrc
10769 /* If we were run as 'hush &', sleep until we are 11385// (--norc turns this off, --rcfile <file> overrides)
10770 * in the foreground (tty pgrp == our pgrp). 11386# if ENABLE_HUSH_JOB
10771 * If we get started under a job aware app (like bash), 11387 /* Can we do job control? */
10772 * make sure we are now in charge so we don't fight over 11388 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10773 * who gets the foreground */ 11389 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10774 while (1) { 11390 if (G_saved_tty_pgrp < 0)
10775 pid_t shell_pgrp = getpgrp(); 11391 G_saved_tty_pgrp = 0; /* no */
10776 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 11392 if (G_saved_tty_pgrp) {
10777 if (G_saved_tty_pgrp == shell_pgrp) 11393 /* If we were run as 'hush &', sleep until we are
10778 break; 11394 * in the foreground (tty pgrp == our pgrp).
10779 /* send TTIN to ourself (should stop us) */ 11395 * If we get started under a job aware app (like bash),
10780 kill(- shell_pgrp, SIGTTIN); 11396 * make sure we are now in charge so we don't fight over
11397 * who gets the foreground */
11398 while (1) {
11399 pid_t shell_pgrp = getpgrp();
11400 if (G_saved_tty_pgrp == shell_pgrp) {
11401/* Often both pgrps here are set to our pid - but not always!
11402 * Example: sh -c 'echo $$; hush; echo FIN'
11403 * Here, the parent shell is not interactive, so it does NOT set up
11404 * a separate process group for its children, and we (hush) initially
11405 * run in parent's process group (until we set up our own a few lines down).
11406 */
11407 //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp);
11408 break;
11409 }
11410 /* Send TTIN to ourself (should stop us) */
11411 kill(- shell_pgrp, SIGTTIN);
11412 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
11413 }
10781 } 11414 }
10782 }
10783
10784 /* Install more signal handlers */
10785 install_special_sighandlers();
10786
10787 if (G_saved_tty_pgrp) {
10788 /* Set other signals to restore saved_tty_pgrp */
10789 install_fatal_sighandlers();
10790 /* Put ourselves in our own process group
10791 * (bash, too, does this only if ctty is available) */
10792 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10793 /* Grab control of the terminal */
10794 tcsetpgrp(G_interactive_fd, cached_getpid);
10795 }
10796 enable_restore_tty_pgrp_on_exit();
10797
10798# if ENABLE_FEATURE_EDITING
10799 G.line_input_state = new_line_input_t(FOR_SHELL);
10800# if ENABLE_FEATURE_TAB_COMPLETION
10801 G.line_input_state->get_exe_name = hush_command_name;
10802# endif
10803# if EDITING_HAS_sh_get_var
10804 G.line_input_state->sh_get_var = get_local_var_value;
10805# endif
10806# endif 11415# endif
10807# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 11416 /* Install more signal handlers */
10808 { 11417 install_special_sighandlers();
10809 const char *hp = get_local_var_value("HISTFILE"); 11418# if ENABLE_HUSH_JOB
10810 if (!hp) { 11419 if (G_saved_tty_pgrp) {
10811 hp = get_local_var_value("HOME"); 11420 /* Set fatal signals to restore saved_tty_pgrp */
10812 if (hp) 11421 install_fatal_sighandlers();
10813 hp = concat_path_file(hp, ".hush_history"); 11422 /* (The if() is an optimization: can avoid two redundant syscalls) */
10814 } else { 11423 if (G_saved_tty_pgrp != G.root_pid) {
10815 hp = xstrdup(hp); 11424 /* Put ourselves in our own process group
10816 } 11425 * (bash, too, does this only if ctty is available) */
10817 if (hp) { 11426 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10818 G.line_input_state->hist_file = hp; 11427 /* Grab control of the terminal */
10819 //set_local_var(xasprintf("HISTFILE=%s", ...)); 11428 tcsetpgrp(G_interactive_fd, G.root_pid);
11429 }
10820 } 11430 }
10821# if ENABLE_FEATURE_SH_HISTFILESIZE
10822 hp = get_local_var_value("HISTFILESIZE");
10823 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10824# endif
10825 }
10826# endif 11431# endif
10827 } else { 11432# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10828 install_special_sighandlers(); 11433 /* Set (but not export) PS1/2 unless already set */
10829 } 11434 if (!get_local_var_value("PS1"))
10830#elif ENABLE_HUSH_INTERACTIVE 11435 set_local_var_from_halves("PS1", "\\w \\$ ");
10831 /* No job control compiled in, only prompt/line editing */ 11436 if (!get_local_var_value("PS2"))
10832 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 11437 set_local_var_from_halves("PS2", "> ");
10833 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 11438# endif
10834 if (G_interactive_fd < 0) { 11439 init_line_editing();
10835 /* try to dup to any fd */
10836 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10837 if (G_interactive_fd < 0)
10838 /* give up */
10839 G_interactive_fd = 0;
10840 }
10841 }
10842 install_special_sighandlers();
10843#else
10844 /* We have interactiveness code disabled */
10845 install_special_sighandlers();
10846#endif
10847 /* bash:
10848 * if interactive but not a login shell, sources ~/.bashrc
10849 * (--norc turns this off, --rcfile <file> overrides)
10850 */
10851 11440
10852 if (G_interactive_fd) { 11441# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10853#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10854 /* Set (but not export) PS1/2 unless already set */
10855 if (!get_local_var_value("PS1"))
10856 set_local_var_from_halves("PS1", "\\w \\$ ");
10857 if (!get_local_var_value("PS2"))
10858 set_local_var_from_halves("PS2", "> ");
10859#endif
10860 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10861 /* note: ash and hush share this string */ 11442 /* note: ash and hush share this string */
10862 printf("\n\n%s %s\n" 11443 printf("\n\n%s %s\n"
10863 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 11444 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
@@ -10865,13 +11446,15 @@ int hush_main(int argc, char **argv)
10865 bb_banner, 11446 bb_banner,
10866 "hush - the humble shell" 11447 "hush - the humble shell"
10867 ); 11448 );
10868 } 11449# endif
10869 } 11450 } /* if become interactive */
11451 } /* if on tty */
11452#endif /* if INTERACTIVE is allowed by build config */
10870 11453
10871 parse_and_run_file(hfopen(NULL)); /* stdin */ 11454 parse_and_run_file(hfopen(NULL)); /* stdin */
10872 11455
10873 final_return: 11456 final_return:
10874 hush_exit(G.last_exitcode); 11457 save_history_run_exit_trap_and_exit(G.last_exitcode);
10875} 11458}
10876 11459
10877/* 11460/*
@@ -11057,19 +11640,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11057 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" 11640 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11058 */ 11641 */
11059 11642
11060 /* note: EXIT trap is run by hush_exit */ 11643 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11061 argv = skip_dash_dash(argv); 11644 argv = skip_dash_dash(argv);
11062 if (argv[0] == NULL) { 11645 if (argv[0] == NULL) {
11063#if ENABLE_HUSH_TRAP 11646#if ENABLE_HUSH_TRAP
11064 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ 11647 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11065 hush_exit(G.pre_trap_exitcode); 11648 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11066#endif 11649#endif
11067 hush_exit(G.last_exitcode); 11650 save_history_run_exit_trap_and_exit(G.last_exitcode);
11068 } 11651 }
11069 /* mimic bash: exit 123abc == exit 255 + error msg */ 11652 /* mimic bash: exit 123abc == exit 255 + error msg */
11070 xfunc_error_retval = 255; 11653 xfunc_error_retval = 255;
11071 /* bash: exit -2 == exit 254, no error msg */ 11654 /* bash: exit -2 == exit 254, no error msg */
11072 hush_exit(xatoi(argv[0]) & 0xff); 11655 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11073} 11656}
11074 11657
11075#if ENABLE_HUSH_TYPE 11658#if ENABLE_HUSH_TYPE
@@ -11083,12 +11666,14 @@ static int FAST_FUNC builtin_type(char **argv)
11083 char *path = NULL; 11666 char *path = NULL;
11084 11667
11085 if (0) {} /* make conditional compile easier below */ 11668 if (0) {} /* make conditional compile easier below */
11086 /*else if (find_alias(*argv))
11087 type = "an alias";*/
11088# if ENABLE_HUSH_FUNCTIONS 11669# if ENABLE_HUSH_FUNCTIONS
11089 else if (find_function(*argv)) 11670 else if (find_function(*argv))
11090 type = "a function"; 11671 type = "a function";
11091# endif 11672# endif
11673# if ENABLE_HUSH_ALIAS
11674 else if (find_alias(*argv))
11675 type = "an alias";
11676# endif
11092 else if (find_builtin(*argv)) 11677 else if (find_builtin(*argv))
11093 type = "a shell builtin"; 11678 type = "a shell builtin";
11094 else { 11679 else {
@@ -11175,6 +11760,11 @@ static int FAST_FUNC builtin_read(char **argv)
11175 goto again; 11760 goto again;
11176 } 11761 }
11177 11762
11763 if ((uintptr_t)r == 2) /* -t SEC timeout? */
11764 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
11765 /* The actual value observed with bash 5.2.15: */
11766 return 128 + SIGALRM;
11767
11178 if ((uintptr_t)r > 1) { 11768 if ((uintptr_t)r > 1) {
11179 bb_simple_error_msg(r); 11769 bb_simple_error_msg(r);
11180 r = (char*)(uintptr_t)1; 11770 r = (char*)(uintptr_t)1;
@@ -11223,7 +11813,7 @@ static int FAST_FUNC builtin_umask(char **argv)
11223} 11813}
11224#endif 11814#endif
11225 11815
11226#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY || ENABLE_HUSH_SET || ENABLE_HUSH_TRAP 11816#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY || ENABLE_HUSH_SET || ENABLE_HUSH_TRAP || ENABLE_HUSH_ALIAS
11227static void print_escaped(const char *s) 11817static void print_escaped(const char *s)
11228{ 11818{
11229//TODO? bash "set" does not quote variables which contain only alnums and "%+,-./:=@_~", 11819//TODO? bash "set" does not quote variables which contain only alnums and "%+,-./:=@_~",
@@ -11248,6 +11838,18 @@ static void print_escaped(const char *s)
11248 putchar('"'); 11838 putchar('"');
11249 } while (*s); 11839 } while (*s);
11250} 11840}
11841static void print_pfx_escaped_nl(const char *pfx, const char *s)
11842{
11843 const char *p = strchr(s, '=');
11844 if (p) {
11845 if (pfx)
11846 printf("%s %.*s", pfx, (int)(p - s) + 1, s);
11847 else
11848 printf("%.*s", (int)(p - s) + 1, s);
11849 print_escaped(p + 1);
11850 putchar('\n');
11851 }
11852}
11251#endif 11853#endif
11252 11854
11253#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY 11855#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
@@ -11358,14 +11960,7 @@ static int FAST_FUNC builtin_export(char **argv)
11358 * bash: declare -x VAR="VAL" 11960 * bash: declare -x VAR="VAL"
11359 * we follow ash example */ 11961 * we follow ash example */
11360 const char *s = *e++; 11962 const char *s = *e++;
11361 const char *p = strchr(s, '='); 11963 print_pfx_escaped_nl("export", s);
11362
11363 if (!p) /* wtf? take next variable */
11364 continue;
11365 /* "export VAR=" */
11366 printf("%s %.*s", "export", (int)(p - s) + 1, s);
11367 print_escaped(p + 1);
11368 putchar('\n');
11369# endif 11964# endif
11370 } 11965 }
11371 /*fflush_all(); - done after each builtin anyway */ 11966 /*fflush_all(); - done after each builtin anyway */
@@ -11407,15 +12002,7 @@ static int FAST_FUNC builtin_readonly(char **argv)
11407 struct variable *e; 12002 struct variable *e;
11408 for (e = G.top_var; e; e = e->next) { 12003 for (e = G.top_var; e; e = e->next) {
11409 if (e->flg_read_only) { 12004 if (e->flg_read_only) {
11410 const char *s = e->varstr; 12005 print_pfx_escaped_nl("readonly", e->varstr);
11411 const char *p = strchr(s, '=');
11412
11413 if (!p) /* wtf? take next variable */
11414 continue;
11415 /* "readonly VAR=" */
11416 printf("%s %.*s", "readonly", (int)(p - s) + 1, s);
11417 print_escaped(p + 1);
11418 putchar('\n');
11419 } 12006 }
11420 } 12007 }
11421 return EXIT_SUCCESS; 12008 return EXIT_SUCCESS;
@@ -11494,15 +12081,7 @@ static int FAST_FUNC builtin_set(char **argv)
11494 if (arg == NULL) { 12081 if (arg == NULL) {
11495 struct variable *e; 12082 struct variable *e;
11496 for (e = G.top_var; e; e = e->next) { 12083 for (e = G.top_var; e; e = e->next) {
11497 const char *s = e->varstr; 12084 print_pfx_escaped_nl(NULL, e->varstr);
11498 const char *p = strchr(s, '=');
11499
11500 if (!p) /* wtf? take next variable */
11501 continue;
11502 /* var= */
11503 printf("%.*s", (int)(p - s) + 1, s);
11504 print_escaped(p + 1);
11505 putchar('\n');
11506 } 12085 }
11507 return EXIT_SUCCESS; 12086 return EXIT_SUCCESS;
11508 } 12087 }
@@ -12062,8 +12641,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
12062 * Or else we may race with SIGCHLD, lose it, 12641 * Or else we may race with SIGCHLD, lose it,
12063 * and get stuck in sigsuspend... 12642 * and get stuck in sigsuspend...
12064 */ 12643 */
12065 sigfillset(&oldset); /* block all signals, remember old set */ 12644 sigblockall(&oldset); /* block all signals, remember old set */
12066 sigprocmask2(SIG_SETMASK, &oldset);
12067 12645
12068 if (!sigisemptyset(&G.pending_set)) { 12646 if (!sigisemptyset(&G.pending_set)) {
12069 /* Crap! we raced with some signal! */ 12647 /* Crap! we raced with some signal! */
@@ -12080,7 +12658,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
12080 debug_printf_exec("job_exited_or_stopped:%d\n", rcode); 12658 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
12081 if (rcode >= 0) { 12659 if (rcode >= 0) {
12082 ret = rcode; 12660 ret = rcode;
12083 sigprocmask(SIG_SETMASK, &oldset, NULL); 12661 sigprocmask2(SIG_SETMASK, &oldset);
12084 break; 12662 break;
12085 } 12663 }
12086 } 12664 }
@@ -12098,7 +12676,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
12098 ret = 127; 12676 ret = 127;
12099 } 12677 }
12100# endif 12678# endif
12101 sigprocmask(SIG_SETMASK, &oldset, NULL); 12679 sigprocmask2(SIG_SETMASK, &oldset);
12102 break; 12680 break;
12103 } 12681 }
12104 /* Wait for SIGCHLD or any other signal */ 12682 /* Wait for SIGCHLD or any other signal */
@@ -12116,7 +12694,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
12116 * making "wait" commands in SCRIPT block forever. 12694 * making "wait" commands in SCRIPT block forever.
12117 */ 12695 */
12118 restore: 12696 restore:
12119 sigprocmask(SIG_SETMASK, &oldset, NULL); 12697 sigprocmask2(SIG_SETMASK, &oldset);
12120 check_sig: 12698 check_sig:
12121 /* So, did we get a signal? */ 12699 /* So, did we get a signal? */
12122 sig = check_and_run_traps(); 12700 sig = check_and_run_traps();
@@ -12378,3 +12956,103 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
12378 return l; 12956 return l;
12379} 12957}
12380#endif 12958#endif
12959
12960#if ENABLE_HUSH_ALIAS
12961static void new_alias(char *str, char *eq)
12962{
12963 struct alias *alias, **aliaspp;
12964
12965 //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__);
12966 aliaspp = find_alias_slot(str, eq);
12967 str = xstrdup(str);
12968 alias = *aliaspp;
12969 if (alias) {
12970 /* Replace existing alias */
12971 free(alias->str);
12972 alias->str = str;
12973 } else {
12974 /* Create new alias */
12975 alias = xzalloc(sizeof(*alias));
12976 alias->str = str;
12977 *aliaspp = alias;
12978 }
12979}
12980static int FAST_FUNC builtin_alias(char **argv)
12981{
12982 const struct alias *alias;
12983
12984 if (!argv[1]) {
12985 alias = G.top_alias;
12986 while (alias) {
12987 print_pfx_escaped_nl("alias", alias->str);
12988 alias = alias->next;
12989 }
12990 return 0;
12991 }
12992
12993 while (*++argv) {
12994 char *eq = end_of_alias_name(*argv);
12995 if (*eq == '=' && eq != *argv) {
12996 /* alias NAME=VALUE */
12997 new_alias(*argv, eq);
12998 } else {
12999 eq = strchrnul(eq, '=');
13000 if (*eq == '=') {
13001 /* alias 'NA&ME=VALUE' (invalid chars in name) */
13002 bb_error_msg("alias: '%.*s': invalid alias name", (int)(eq - *argv), *argv);
13003 continue; /* continue processing argv (bash compat) */
13004 }
13005 /* alias SOMETHING_WITHOUT_EQUAL_SIGN */
13006 //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__);
13007 alias = *find_alias_slot(*argv, eq);
13008 if (alias) {
13009 print_pfx_escaped_nl("alias", alias->str);
13010 } else {
13011 bb_error_msg("unalias: '%s': not found" + 2, *argv);
13012 /* return 1; - no, continue processing argv (bash compat) */
13013 }
13014 }
13015 }
13016 return 0;
13017}
13018
13019static void unset_alias(struct alias **aliaspp)
13020{
13021 struct alias *alias = *aliaspp;
13022
13023 *aliaspp = alias->next;
13024 free(alias->str);
13025 free(alias);
13026}
13027static int FAST_FUNC builtin_unalias(char **argv)
13028{
13029 int ret = 0;
13030
13031 if (!argv[1]) {
13032 bb_simple_error_msg("usage: unalias -a | NAME...");
13033 return EXIT_FAILURE;
13034 }
13035
13036 while (*++argv) {
13037 struct alias **aliaspp;
13038
13039 if (strcmp(*argv, "-a") == 0) {
13040 /* Remove all aliases */
13041 while (G.top_alias) {
13042 unset_alias(&G.top_alias);
13043 }
13044 /* bash: "unalias -a NO_SUCH_ALIAS" says nothing (won't try to unalias NO_SUCH_ALIAS): */
13045 break;
13046 }
13047 //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__);
13048 aliaspp = find_alias_slot(*argv, strchr(*argv, '\0')); /* think of "unalias a=b" case! */
13049 if (*aliaspp) {
13050 unset_alias(aliaspp);
13051 } else {
13052 bb_error_msg("unalias: '%s': not found", *argv);
13053 ret = EXIT_FAILURE;
13054 }
13055 }
13056 return ret;
13057}
13058#endif
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh
index ca35ec144..3edd3df61 100755
--- a/shell/hush_leaktool.sh
+++ b/shell/hush_leaktool.sh
@@ -7,7 +7,7 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
7 7
8grep -v free "$output" >"$output.leaked" 8grep -v free "$output" >"$output.leaked"
9 9
10i=8 10i=16
11list= 11list=
12for freed in $freelist; do 12for freed in $freelist; do
13 list="$list -e $freed" 13 list="$list -e $freed"
@@ -15,7 +15,7 @@ for freed in $freelist; do
15 echo Dropping $list 15 echo Dropping $list
16 grep -F -v $list <"$output.leaked" >"$output.temp" 16 grep -F -v $list <"$output.leaked" >"$output.temp"
17 mv "$output.temp" "$output.leaked" 17 mv "$output.temp" "$output.leaked"
18 i=8 18 i=16
19 list= 19 list=
20done 20done
21if test "$list"; then 21if test "$list"; then
@@ -23,3 +23,17 @@ if test "$list"; then
23 grep -F -v $list <"$output.leaked" >"$output.temp" 23 grep -F -v $list <"$output.leaked" >"$output.temp"
24 mv "$output.temp" "$output.leaked" 24 mv "$output.temp" "$output.leaked"
25fi 25fi
26
27# All remaining allocations are on addresses which were never freed.
28# * Sort them by line, grouping together allocations which allocated the same address.
29# A leaky allocation will give many different addresses (because it's never freed,
30# the address can not be reused).
31# * Remove the address (field #4).
32# * Count the allocations per every unique source line and alloc type.
33# * Show largest counts on top.
34cat output.leaked \
35 | sort -u \
36 | cut -d' ' -f1-3 \
37 | uniq -c \
38 | sort -rn \
39>output.leaked.counted_uniq_alloc_address
diff --git a/shell/hush_test/hush-misc/tickquote1.right b/shell/hush_test/hush-bugs/tickquote1.right
index 56f8515b7..56f8515b7 100644
--- a/shell/hush_test/hush-misc/tickquote1.right
+++ b/shell/hush_test/hush-bugs/tickquote1.right
diff --git a/shell/hush_test/hush-misc/tickquote1.tests b/shell/hush_test/hush-bugs/tickquote1.tests
index 02e3904f1..02e3904f1 100755
--- a/shell/hush_test/hush-misc/tickquote1.tests
+++ b/shell/hush_test/hush-bugs/tickquote1.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right
new file mode 100644
index 000000000..3d79316d7
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right
@@ -0,0 +1 @@
Ok1
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests
new file mode 100755
index 000000000..eb2223031
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests
@@ -0,0 +1,4 @@
1cat <<-EOF
2 Ok1
3 EO\
4F
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right
new file mode 100644
index 000000000..3d79316d7
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right
@@ -0,0 +1 @@
Ok1
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests
new file mode 100755
index 000000000..de21132d1
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests
@@ -0,0 +1,4 @@
1cat <<EOF
2Ok1
3\
4EOF
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right
new file mode 100644
index 000000000..3d79316d7
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right
@@ -0,0 +1 @@
Ok1
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests
new file mode 100755
index 000000000..da3860804
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests
@@ -0,0 +1,4 @@
1cat <<-EOF
2Ok1
3 \
4 EOF
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
new file mode 100755
index 000000000..33ccde26b
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
@@ -0,0 +1,2 @@
1echo <<
2echo Done:
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
new file mode 100755
index 000000000..fcda11045
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
@@ -0,0 +1,2 @@
1echo << >
2echo Done:
diff --git a/shell/hush_test/hush-heredoc/herestring1.right b/shell/hush_test/hush-heredoc/herestring1.right
new file mode 100644
index 000000000..555937daa
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/herestring1.right
@@ -0,0 +1,33 @@
1one
2_two
3\_three
4\_four
5\_two
6\_three
7\\_four
8two_newlines
9
10/bin/c*
11star_*
12star_*
13star_*
14star_\*
15star_*
16star_\*
17line1
18line2
19 line3
20line1
21line2
22 line3
23512
24256
25128
2664
2732
2816
298
304
312
32v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
33v-$a-\t-\-"-\x-`-\--\z-\*-\?-
diff --git a/shell/hush_test/hush-heredoc/herestring1.tests b/shell/hush_test/hush-heredoc/herestring1.tests
new file mode 100755
index 000000000..fb7bd0dda
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/herestring1.tests
@@ -0,0 +1,35 @@
1cat <<<one
2cat <<<\_two
3cat <<<"\_three"
4cat <<<'\_four'
5cat <<<\\_two
6cat <<<"\\_three"
7cat <<<'\\_four'
8
9cat <<<$'two_newlines\n'
10
11cat <<</bin/c*
12
13cat <<<star_*
14cat <<<star_\*
15cat <<<"star_*"
16cat <<<"star_\*"
17cat <<<'star_*'
18cat <<<'star_\*'
19
20var=$'line1
21line2
22 line3'
23
24cat <<<$var
25
26cat <<<"$var"
27
28i=10
29until test $((--i)) = 0; do
30 cat <<<$((2**i))
31done
32
33a=qwerty
34cat <<<`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
35cat <<<"`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`"
diff --git a/shell/hush_test/hush-misc/assignment2.right b/shell/hush_test/hush-misc/assignment2.right
index 9d1860be7..5e54b408b 100644
--- a/shell/hush_test/hush-misc/assignment2.right
+++ b/shell/hush_test/hush-misc/assignment2.right
@@ -1,2 +1,4 @@
1hush: can't execute 'a=b': No such file or directory 1hush: can't execute 'a=b': No such file or directory
2127 2127
3hush: can't execute 'ab=c': No such file or directory
4127
diff --git a/shell/hush_test/hush-misc/assignment2.tests b/shell/hush_test/hush-misc/assignment2.tests
index f6938434c..a93a48d09 100755
--- a/shell/hush_test/hush-misc/assignment2.tests
+++ b/shell/hush_test/hush-misc/assignment2.tests
@@ -1,3 +1,5 @@
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 $?
4a'b'=c true
5echo $?
diff --git a/shell/hush_test/hush-misc/case2.right b/shell/hush_test/hush-misc/case2.right
new file mode 100644
index 000000000..4780199de
--- /dev/null
+++ b/shell/hush_test/hush-misc/case2.right
@@ -0,0 +1 @@
hush: syntax error: unexpected )
diff --git a/shell/hush_test/hush-misc/case2.tests b/shell/hush_test/hush-misc/case2.tests
new file mode 100755
index 000000000..b9475392e
--- /dev/null
+++ b/shell/hush_test/hush-misc/case2.tests
@@ -0,0 +1,6 @@
1# had a parser bug which incorrectly detected ";;"
2case w in
3a);
4b);;
5esac
6echo Should not be reached
diff --git a/shell/hush_test/hush-misc/case3.right b/shell/hush_test/hush-misc/case3.right
new file mode 100644
index 000000000..4780199de
--- /dev/null
+++ b/shell/hush_test/hush-misc/case3.right
@@ -0,0 +1 @@
hush: syntax error: unexpected )
diff --git a/shell/hush_test/hush-misc/case3.tests b/shell/hush_test/hush-misc/case3.tests
new file mode 100755
index 000000000..7ef6a18a9
--- /dev/null
+++ b/shell/hush_test/hush-misc/case3.tests
@@ -0,0 +1,7 @@
1# had a parser bug which incorrectly detected ";;"
2case w in
3a) true
4;
5b);;
6esac
7echo Should not be reached
diff --git a/shell/hush_test/hush-misc/case4.right b/shell/hush_test/hush-misc/case4.right
new file mode 100644
index 000000000..02a29ccbc
--- /dev/null
+++ b/shell/hush_test/hush-misc/case4.right
@@ -0,0 +1,2 @@
1Keyword-like word may be in pattern
2Ok:0
diff --git a/shell/hush_test/hush-misc/case4.tests b/shell/hush_test/hush-misc/case4.tests
new file mode 100755
index 000000000..63b9dfd99
--- /dev/null
+++ b/shell/hush_test/hush-misc/case4.tests
@@ -0,0 +1,8 @@
1if=if
2case if in
3if) echo "Keyword-like word may be in pattern";;
4fi) echo "Not reached";;
5# esac) echo "Not reached";; # not accepted by bash 5.2.15
6do) echo "Not reached"
7esac
8echo Ok:$?
diff --git a/shell/hush_test/hush-misc/func6.right b/shell/hush_test/hush-misc/func6.right
new file mode 100644
index 000000000..01e79c32a
--- /dev/null
+++ b/shell/hush_test/hush-misc/func6.right
@@ -0,0 +1,3 @@
11
22
33
diff --git a/shell/hush_test/hush-misc/func6.tests b/shell/hush_test/hush-misc/func6.tests
new file mode 100755
index 000000000..5f1699c42
--- /dev/null
+++ b/shell/hush_test/hush-misc/func6.tests
@@ -0,0 +1,8 @@
1{ f() { echo $1; } }
2f 1
3
4{ f() ( echo $1; )}
5f 2
6
7{ f()(echo $1)}
8f 3
diff --git a/shell/hush_test/hush-misc/func7.right b/shell/hush_test/hush-misc/func7.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/hush_test/hush-misc/func7.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/hush_test/hush-misc/func7.tests b/shell/hush_test/hush-misc/func7.tests
new file mode 100755
index 000000000..f5e03b6e1
--- /dev/null
+++ b/shell/hush_test/hush-misc/func7.tests
@@ -0,0 +1 @@
if f() { echo Ok:$?; } then f; fi
diff --git a/shell/hush_test/hush-misc/sig_exitcode.tests b/shell/hush_test/hush-misc/sig_exitcode.tests
index 7879dc854..67b4500f4 100755
--- a/shell/hush_test/hush-misc/sig_exitcode.tests
+++ b/shell/hush_test/hush-misc/sig_exitcode.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1exec 2>&1 4exec 2>&1
2 5
3$THIS_SH -c 'kill -9 $$' 6$THIS_SH -c 'kill -9 $$'
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.right b/shell/hush_test/hush-misc/syntax_err_negate.right
index 8c7010629..39db799ea 100644
--- a/shell/hush_test/hush-misc/syntax_err_negate.right
+++ b/shell/hush_test/hush-misc/syntax_err_negate.right
@@ -1,2 +1,2 @@
1bash 3.2 fails this 10:0
2hush: syntax error: ! ! command 21:1
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.tests b/shell/hush_test/hush-misc/syntax_err_negate.tests
index d61b1b09f..64f0f8a75 100755
--- a/shell/hush_test/hush-misc/syntax_err_negate.tests
+++ b/shell/hush_test/hush-misc/syntax_err_negate.tests
@@ -1,2 +1,3 @@
1echo bash 3.2 fails this 1# bash 3.2 fails this, 5.2.15 allows
2! ! true 2! ! true; echo 0:$?
3! ! ! true; echo 1:$?
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
index f9cf6d48c..54120319b 100755
--- a/shell/hush_test/hush-misc/wait1.tests
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & sleep 1 & wait $! 4sleep 2 & sleep 1 & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
index be20f95a5..60f382c9f 100755
--- a/shell/hush_test/hush-misc/wait2.tests
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 3 & sleep 2 & sleep 1 4sleep 3 & sleep 2 & sleep 1
2wait $! 5wait $!
3echo $? 6echo $?
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
index ac541c3fc..aceed1126 100755
--- a/shell/hush_test/hush-misc/wait3.tests
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & (sleep 1;exit 3) & wait $! 4sleep 2 & (sleep 1;exit 3) & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests
index cc34059ac..c979a38b6 100755
--- a/shell/hush_test/hush-misc/wait4.tests
+++ b/shell/hush_test/hush-misc/wait4.tests
@@ -1,2 +1,5 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 1 | (sleep 1;exit 3) & wait %1 4sleep 1 | (sleep 1;exit 3) & wait %1
2echo Three:$? 5echo Three:$?
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests
index 1b4762d89..e0ac8c251 100755
--- a/shell/hush_test/hush-misc/wait5.tests
+++ b/shell/hush_test/hush-misc/wait5.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 0 | (sleep 0;exit 3) & 4sleep 0 | (sleep 0;exit 3) &
2sleep 1 5sleep 1
3echo Zero:$? 6echo Zero:$?
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
index c23713199..c09002ab0 100755
--- a/shell/hush_test/hush-misc/wait6.tests
+++ b/shell/hush_test/hush-misc/wait6.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited 4# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3: 5# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1 6(sleep 0; exit 3) & sleep 1
diff --git a/shell/hush_test/hush-parsing/bad_cmd1.right b/shell/hush_test/hush-parsing/bad_cmd1.right
new file mode 100644
index 000000000..ad7f80d96
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_cmd1.right
@@ -0,0 +1 @@
hush: syntax error: unexpected ||
diff --git a/shell/hush_test/hush-parsing/bad_cmd1.tests b/shell/hush_test/hush-parsing/bad_cmd1.tests
new file mode 100755
index 000000000..b4bf53a75
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_cmd1.tests
@@ -0,0 +1 @@
||date
diff --git a/shell/hush_test/hush-parsing/bad_cmd2.right b/shell/hush_test/hush-parsing/bad_cmd2.right
new file mode 100644
index 000000000..9c3ed0a65
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_cmd2.right
@@ -0,0 +1 @@
hush: syntax error: unexpected &&
diff --git a/shell/hush_test/hush-parsing/bad_cmd2.tests b/shell/hush_test/hush-parsing/bad_cmd2.tests
new file mode 100755
index 000000000..13580e14a
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_cmd2.tests
@@ -0,0 +1 @@
&&date
diff --git a/shell/hush_test/hush-parsing/bad_cmd3.right b/shell/hush_test/hush-parsing/bad_cmd3.right
new file mode 100644
index 000000000..05618a2d9
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_cmd3.right
@@ -0,0 +1 @@
hush: syntax error: unexpected &
diff --git a/shell/hush_test/hush-parsing/bad_cmd3.tests b/shell/hush_test/hush-parsing/bad_cmd3.tests
new file mode 100755
index 000000000..bec7e5180
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_cmd3.tests
@@ -0,0 +1 @@
& date
diff --git a/shell/hush_test/hush-parsing/bad_pipe1.right b/shell/hush_test/hush-parsing/bad_pipe1.right
new file mode 100644
index 000000000..c8ce26235
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe1.right
@@ -0,0 +1 @@
hush: syntax error: unexpected |
diff --git a/shell/hush_test/hush-parsing/bad_pipe1.tests b/shell/hush_test/hush-parsing/bad_pipe1.tests
new file mode 100755
index 000000000..ca5f2ec48
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe1.tests
@@ -0,0 +1 @@
|date
diff --git a/shell/hush_test/hush-parsing/bad_pipe2.right b/shell/hush_test/hush-parsing/bad_pipe2.right
new file mode 100644
index 000000000..6d63482b8
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe2.right
@@ -0,0 +1 @@
hush: syntax error: unterminated |
diff --git a/shell/hush_test/hush-parsing/bad_pipe2.tests b/shell/hush_test/hush-parsing/bad_pipe2.tests
new file mode 100755
index 000000000..d0aa5c73c
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe2.tests
@@ -0,0 +1 @@
date|
diff --git a/shell/hush_test/hush-parsing/bad_pipe3.right b/shell/hush_test/hush-parsing/bad_pipe3.right
new file mode 100644
index 000000000..6d63482b8
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe3.right
@@ -0,0 +1 @@
hush: syntax error: unterminated |
diff --git a/shell/hush_test/hush-parsing/bad_pipe3.tests b/shell/hush_test/hush-parsing/bad_pipe3.tests
new file mode 100755
index 000000000..ab312d139
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe3.tests
@@ -0,0 +1 @@
date|;
diff --git a/shell/hush_test/hush-parsing/bad_pipe4.right b/shell/hush_test/hush-parsing/bad_pipe4.right
new file mode 100644
index 000000000..6d63482b8
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe4.right
@@ -0,0 +1 @@
hush: syntax error: unterminated |
diff --git a/shell/hush_test/hush-parsing/bad_pipe4.tests b/shell/hush_test/hush-parsing/bad_pipe4.tests
new file mode 100755
index 000000000..c39098c8b
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe4.tests
@@ -0,0 +1 @@
date| &
diff --git a/shell/hush_test/hush-parsing/bad_pipe5.right b/shell/hush_test/hush-parsing/bad_pipe5.right
new file mode 100644
index 000000000..6d63482b8
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe5.right
@@ -0,0 +1 @@
hush: syntax error: unterminated |
diff --git a/shell/hush_test/hush-parsing/bad_pipe5.tests b/shell/hush_test/hush-parsing/bad_pipe5.tests
new file mode 100755
index 000000000..d5866ce79
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe5.tests
@@ -0,0 +1 @@
date| && date
diff --git a/shell/hush_test/hush-parsing/bad_pipe6.right b/shell/hush_test/hush-parsing/bad_pipe6.right
new file mode 100644
index 000000000..6d63482b8
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe6.right
@@ -0,0 +1 @@
hush: syntax error: unterminated |
diff --git a/shell/hush_test/hush-parsing/bad_pipe6.tests b/shell/hush_test/hush-parsing/bad_pipe6.tests
new file mode 100755
index 000000000..1a9476b92
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe6.tests
@@ -0,0 +1 @@
date| || date
diff --git a/shell/hush_test/hush-parsing/bad_pipe7.right b/shell/hush_test/hush-parsing/bad_pipe7.right
new file mode 100644
index 000000000..c8ce26235
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe7.right
@@ -0,0 +1 @@
hush: syntax error: unexpected |
diff --git a/shell/hush_test/hush-parsing/bad_pipe7.tests b/shell/hush_test/hush-parsing/bad_pipe7.tests
new file mode 100755
index 000000000..b048affb0
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bad_pipe7.tests
@@ -0,0 +1 @@
date| |date
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/hush_test/hush-read/read_t.right
+++ b/shell/hush_test/hush-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/hush_test/hush-read/read_t.tests
+++ b/shell/hush_test/hush-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/hush_test/hush-redir/redir_EINTR1.right b/shell/hush_test/hush-redir/redir_EINTR1.right
new file mode 100644
index 000000000..287d91f67
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_EINTR1.right
@@ -0,0 +1,2 @@
1Hello
2Done:0
diff --git a/shell/hush_test/hush-redir/redir_EINTR1.tests b/shell/hush_test/hush-redir/redir_EINTR1.tests
new file mode 100755
index 000000000..cede4d4d8
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_EINTR1.tests
@@ -0,0 +1,13 @@
1rm -f test.fifo
2mkfifo test.fifo
3
4(sleep 1; kill -chld $$) &
5(sleep 2; echo Hello >test.fifo) &
6
7# We get open("test.fifo") interrupted by SIGCHLD from the first subshell.
8# The shell MUST retry the open (no printing of error messages).
9# Then, the second subshell opens fifo for writing and open unblocks and succeeds.
10cat <test.fifo
11
12echo "Done:$?"
13rm -f test.fifo
diff --git a/shell/hush_test/hush-redir/redir_EINTR2.right b/shell/hush_test/hush-redir/redir_EINTR2.right
new file mode 100644
index 000000000..287d91f67
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_EINTR2.right
@@ -0,0 +1,2 @@
1Hello
2Done:0
diff --git a/shell/hush_test/hush-redir/redir_EINTR2.tests b/shell/hush_test/hush-redir/redir_EINTR2.tests
new file mode 100755
index 000000000..3d343c7ea
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_EINTR2.tests
@@ -0,0 +1,14 @@
1rm -f test.fifo
2mkfifo test.fifo
3
4(sleep 1; kill -chld $$) &
5(sleep 2; echo Hello >test.fifo) &
6
7# We get open("test.fifo") interrupted by SIGCHLD from the first subshell.
8# The shell MUST retry the open (no printing of error messages).
9# Then, the second subshell opens fifo for writing and open unblocks and succeeds.
10read HELLO <test.fifo
11echo "$HELLO"
12
13echo "Done:$?"
14rm -f test.fifo
diff --git a/shell/hush_test/hush-redir/redir_and_constructs1.right b/shell/hush_test/hush-redir/redir_and_constructs1.right
new file mode 100644
index 000000000..232cd8734
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_and_constructs1.right
@@ -0,0 +1,2 @@
1hush: can't execute '!': No such file or directory
2127:127
diff --git a/shell/hush_test/hush-redir/redir_and_constructs1.tests b/shell/hush_test/hush-redir/redir_and_constructs1.tests
new file mode 100755
index 000000000..a92731e04
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_and_constructs1.tests
@@ -0,0 +1,2 @@
1# Reserved words are not recognized after redirects
2</dev/null ! true; echo 127:$?
diff --git a/shell/hush_test/hush-signals/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d17e..4b1a08e8f 100755
--- a/shell/hush_test/hush-signals/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("User defined signal 2" text not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# avoid ugly warnings about signals not being caught 4# avoid ugly warnings about signals not being caught
2trap ":" USR1 USR2 5trap ":" USR1 USR2
3 6
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests
index 61943467a..c83fa1254 100755
--- a/shell/hush_test/hush-signals/signal1.tests
+++ b/shell/hush_test/hush-signals/signal1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1trap "echo got signal" USR1 4trap "echo got signal" USR1
2 5
3for try in 1 2 3 4 5; do 6for try in 1 2 3 4 5; do
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
index 731af7477..cd5790164 100755
--- a/shell/hush_test/hush-signals/signal8.tests
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1"$THIS_SH" -c ' 4"$THIS_SH" -c '
2exit_func() { 5exit_func() {
3 echo "Removing traps" 6 echo "Removing traps"
diff --git a/shell/hush_test/hush-signals/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b5b..11accd5ab 100755
--- a/shell/hush_test/hush-signals/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("Hangup" not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1$THIS_SH -c ' 4$THIS_SH -c '
2(sleep 1; kill -HUP $$) & 5(sleep 1; kill -HUP $$) &
3while true; do 6while true; do
diff --git a/shell/hush_test/hush-signals/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b82..856c922d3 100755
--- a/shell/hush_test/hush-signals/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
@@ -1,5 +1,8 @@
1# Non-empty traps should be reset in subshell 1# Non-empty traps should be reset in subshell
2 2
3# If job control is disabled, skip the test ("Terminated" text not emitted)
4test "`type jobs`" = "jobs is a shell builtin" || exit 77
5
3# HUP is special in interactive shells 6# HUP is special in interactive shells
4trap '' HUP 7trap '' HUP
5# QUIT is always special 8# QUIT is always special
diff --git a/shell/hush_test/hush-vars/var_backslash1.right b/shell/hush_test/hush-vars/var_backslash1.right
new file mode 100644
index 000000000..3f2c21289
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_backslash1.right
@@ -0,0 +1,35 @@
1a is '\*bc'
2b is '\'
3c is '*'
4${a##?*} removes everything: || - matches one char, then all
5${a##?"*"} removes \*: |bc| - matches one char, then *
6${a##\*} removes nothing: |\*bc| - first char is not *
7${a##\\*} removes everything: || - matches \, then all
8${a##\\\*} removes \*: |bc| - matches \, then *
9${a##?$c} removes everything: || - matches one char, then all
10${a##?"$c"} removes \*: |bc| - matches one char, then *
11${a##\\$c} removes everything: || - matches \, then all
12${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $
13${a##\\"$c"} removes \*: |bc| - matches \, then *
14${a##$b} removes \: |*bc| - matches \
15${a##"$b"} removes \: |*bc| - matches \
16
17Single quote tests:
18${a##?'*'} removes \*: |bc| - matches one char, then *
19${a##'\'*} removes everything: || - matches \, then all
20${a##'\'\*} removes \*: |bc| - matches \, then *
21${a##'\*'} removes \*: |bc| - matches \, then *
22${a##'\'$c} removes everything: || - matches \, then all
23${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $
24${a##'\'"$c"} removes \*: |bc| - matches \, then *
25
26${a##"$b"?} removes \*: |bc| - matches \, then one char
27${a##"$b"*} removes everything: || - matches \, then all
28${a##"$b""?"} removes nothing: |\*bc| - second char is not ?
29${a##"$b""*"} removes \*: |bc| - matches \, then *
30${a##"$b"\*} removes \*: |bc| - matches \, then *
31${a##"$b"$c} removes everything:|| - matches \, then all
32${a##"$b""$c"} removes \*: |bc| - matches \, then *
33${a##"$b?"} removes nothing: |\*bc| - second char is not ?
34${a##"$b*"} removes \*: |bc| - matches \, then *
35${a##"$b$c"} removes \*: |bc| - matches \, then *
diff --git a/shell/hush_test/hush-vars/var_backslash1.tests b/shell/hush_test/hush-vars/var_backslash1.tests
new file mode 100755
index 000000000..015a6107b
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_backslash1.tests
@@ -0,0 +1,48 @@
1a='\*bc'
2b='\'
3c='*'
4echo "a is '$a'"
5echo "b is '$b'"
6echo "c is '$c'"
7echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all'
8echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *'
9echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *'
10echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all'
11echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *'
12echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all'
13echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *'
14echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all'
15echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $'
16echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *'
17echo '${a##$b} removes \: '"|${a##$b}|"' - matches \'
18echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \'
19echo
20sq="'"
21echo 'Single quote tests:'
22echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *'
23echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all'
24echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *'
25echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *'
26echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all'
27echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $'
28echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *'
29echo
30## In bash, this isn't working as expected
31#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc|
32#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc|
33#echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc|
34#echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc|
35## the cause seems to be that $b emits backslash that "glues" onto next character if there is one:
36## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *)
37## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc|
38#echo
39echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char'
40echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all'
41echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc|
42echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *'
43echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *'
44echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all'
45echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *'
46echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc|
47echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints ||
48echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *'
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 7345fee43..ff29ca0b1 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -67,9 +67,12 @@ do_test()
67# echo Running test: "$x" 67# echo Running test: "$x"
68 echo -n "$1/$x:" 68 echo -n "$1/$x:"
69 ( 69 (
70 "$THIS_SH" "./$x" 2>&1 | \ 70 "$THIS_SH" "./$x" >"$name.xx" 2>&1
71 grep -va "^hush: using fallback suid method$" >"$name.xx"
72 r=$? 71 r=$?
72 # filter !FEATURE_SUID_CONFIG_QUIET message
73 sed -i \
74 -e "/^hush: using fallback suid method$/d" \
75 "$name.xx"
73 # filter C library differences 76 # filter C library differences
74 sed -i \ 77 sed -i \
75 -e "/: invalid option /s:'::g" \ 78 -e "/: invalid option /s:'::g" \
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 7fb5f8c58..657f0df8f 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -55,7 +55,7 @@ const char* FAST_FUNC
55shell_builtin_read(struct builtin_read_params *params) 55shell_builtin_read(struct builtin_read_params *params)
56{ 56{
57 struct pollfd pfd[1]; 57 struct pollfd pfd[1];
58#define fd (pfd[0].fd) /* -u FD */ 58#define fd (pfd->fd) /* -u FD */
59 unsigned err; 59 unsigned err;
60 unsigned end_ms; /* -t TIMEOUT */ 60 unsigned end_ms; /* -t TIMEOUT */
61 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
@@ -144,7 +144,7 @@ shell_builtin_read(struct builtin_read_params *params)
144 * bash seems to ignore -p PROMPT for this use case. 144 * bash seems to ignore -p PROMPT for this use case.
145 */ 145 */
146 int r; 146 int r;
147 pfd[0].events = POLLIN; 147 pfd->events = POLLIN;
148 r = poll(pfd, 1, /*timeout:*/ 0); 148 r = poll(pfd, 1, /*timeout:*/ 0);
149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
150 return (const char *)(uintptr_t)(r <= 0); 150 return (const char *)(uintptr_t)(r <= 0);
@@ -209,8 +209,8 @@ shell_builtin_read(struct builtin_read_params *params)
209 * 32-bit unix time wrapped (year 2038+). 209 * 32-bit unix time wrapped (year 2038+).
210 */ 210 */
211 if (timeout <= 0) { /* already late? */ 211 if (timeout <= 0) { /* already late? */
212 retval = (const char *)(uintptr_t)1; 212 retval = (const char *)(uintptr_t)2;
213 goto ret; 213 break;
214 } 214 }
215 } 215 }
216 216
@@ -219,15 +219,23 @@ shell_builtin_read(struct builtin_read_params *params)
219 * regardless of SA_RESTART-ness of that signal! 219 * regardless of SA_RESTART-ness of that signal!
220 */ 220 */
221 errno = 0; 221 errno = 0;
222 pfd[0].events = POLLIN; 222 pfd->events = POLLIN;
223//TODO race with a signal arriving just before the poll! 223
224#if ENABLE_PLATFORM_MINGW32 224#if ENABLE_PLATFORM_MINGW32
225 /* Don't poll if timeout is -1, it hurts performance. */ 225 /* Don't poll if timeout is -1, it hurts performance. The
226 * caution above about interrupts isn't relevant on Windows
227 * where Ctrl-C causes an event, not a signal.
228 */
226 if (timeout >= 0) 229 if (timeout >= 0)
227#endif 230#endif
228 if (poll(pfd, 1, timeout) <= 0) { 231 /* test bb_got_signal, then poll(), atomically wrt signals */
229 /* timed out, or EINTR */ 232 if (check_got_signal_and_poll(pfd, timeout) <= 0) {
233 /* timed out, or some error */
230 err = errno; 234 err = errno;
235 if (!err) { /* timed out */
236 retval = (const char *)(uintptr_t)2;
237 break;
238 }
231 retval = (const char *)(uintptr_t)1; 239 retval = (const char *)(uintptr_t)1;
232 goto ret; 240 goto ret;
233 } 241 }
@@ -238,15 +246,18 @@ shell_builtin_read(struct builtin_read_params *params)
238 key = windows_read_key(fd, NULL, timeout); 246 key = windows_read_key(fd, NULL, timeout);
239 if (key == 0x03) { 247 if (key == 0x03) {
240 /* ^C pressed */ 248 /* ^C pressed */
241 retval = (const char *)(uintptr_t)2; 249 retval = (const char *)(uintptr_t)3;
242 goto ret; 250 goto ret;
243 } 251 }
244 else if (key == -1 || (key == 0x1a && bufpos == 0)) { 252 else if (key == -1) {
245 /* timeout or ^Z at start of buffer */ 253 /* timeout */
254 retval = (const char *)(uintptr_t)2;
255 break;
256 } else if (key == 0x1a && bufpos == 0) {
257 /* ^Z at start of buffer */
246 retval = (const char *)(uintptr_t)1; 258 retval = (const char *)(uintptr_t)1;
247 goto ret; 259 break;
248 } 260 } else if (key == '\b') {
249 else if (key == '\b') {
250 if (bufpos > 0) { 261 if (bufpos > 0) {
251 --bufpos; 262 --bufpos;
252 ++nchars; 263 ++nchars;
@@ -278,7 +289,7 @@ shell_builtin_read(struct builtin_read_params *params)
278 * and exit BS context. 289 * and exit BS context.
279 * - CR LF not in BS context: replace CR with LF */ 290 * - CR LF not in BS context: replace CR with LF */
280 buffer[--bufpos] = c; 291 buffer[--bufpos] = c;
281 ++nchars; 292 nchars += 1 + (backslash == 2);
282 } 293 }
283 } else if (backslash == 2) { 294 } else if (backslash == 2) {
284 /* We saw BS CR ??, keep escaped CR, exit BS context, 295 /* We saw BS CR ??, keep escaped CR, exit BS context,
@@ -298,6 +309,9 @@ shell_builtin_read(struct builtin_read_params *params)
298 backslash = 0; 309 backslash = 0;
299 if (c != '\n') 310 if (c != '\n')
300 goto put; 311 goto put;
312#if ENABLE_PLATFORM_MINGW32
313 ++nchars;
314#endif
301 continue; 315 continue;
302 } 316 }
303 if (c == '\\') { 317 if (c == '\\') {
@@ -338,7 +352,7 @@ shell_builtin_read(struct builtin_read_params *params)
338 } 352 }
339 put: 353 put:
340 bufpos++; 354 bufpos++;
341 } while (--nchars); 355 } while (IF_PLATFORM_MINGW32(backslash ||) --nchars);
342 356
343 if (argv[0]) { 357 if (argv[0]) {
344 /* Remove trailing space $IFS chars */ 358 /* Remove trailing space $IFS chars */
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 7558051f0..2cbb22b6d 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -1045,7 +1045,7 @@ static int NOINLINE syslogd_init(char **argv)
1045#endif 1045#endif
1046 /* If they have not specified remote logging, then log locally */ 1046 /* If they have not specified remote logging, then log locally */
1047 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R 1047 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R
1048 option_mask32 |= OPT_locallog; 1048 option_mask32 = (opts |= OPT_locallog);
1049#if ENABLE_FEATURE_SYSLOGD_CFG 1049#if ENABLE_FEATURE_SYSLOGD_CFG
1050 parse_syslogdcfg(opt_f); 1050 parse_syslogdcfg(opt_f);
1051#endif 1051#endif
diff --git a/testsuite/cpio.tests b/testsuite/cpio.tests
index 85e746589..a4462c53e 100755
--- a/testsuite/cpio.tests
+++ b/testsuite/cpio.tests
@@ -154,6 +154,29 @@ testing "cpio -R with extract" \
154" "" "" 154" "" ""
155SKIP= 155SKIP=
156 156
157# Create an archive containing a file with "../dont_write" filename.
158# See that it will not be allowed to unpack.
159# NB: GNU cpio 2.15 DOES NOT do such checks.
160optional FEATURE_PATH_TRAVERSAL_PROTECTION
161rm -rf cpio.testdir
162mkdir -p cpio.testdir/prepare/inner
163echo "file outside of destination was written" > cpio.testdir/prepare/dont_write
164echo "data" > cpio.testdir/prepare/inner/to_extract
165mkdir -p cpio.testdir/extract
166testing "cpio extract file outside of destination" "\
167(cd cpio.testdir/prepare/inner && echo -e '../dont_write\nto_extract' | cpio -o -H newc) | (cd cpio.testdir/extract && cpio -vi 2>&1)
168echo \$?
169ls cpio.testdir/dont_write 2>&1" \
170"\
171cpio: removing leading '../' from member names
172../dont_write
173to_extract
1741 blocks
1750
176ls: cpio.testdir/dont_write: No such file or directory
177" "" ""
178SKIP=
179
157# Clean up 180# Clean up
158rm -rf cpio.testdir cpio.testdir2 2>/dev/null 181rm -rf cpio.testdir cpio.testdir2 2>/dev/null
159 182
diff --git a/testsuite/cryptpw.tests b/testsuite/cryptpw.tests
index 0dd91fe15..83bfde521 100755
--- a/testsuite/cryptpw.tests
+++ b/testsuite/cryptpw.tests
@@ -22,21 +22,93 @@ testing "cryptpw des zz" \
22#SKIP= 22#SKIP=
23 23
24optional USE_BB_CRYPT_SHA 24optional USE_BB_CRYPT_SHA
25testing "cryptpw sha256" \ 25# Note: mkpasswd-5.6.2 won't accept "-m sha256", wants "-m sha256crypt"
26 "cryptpw -m sha256 QWErty '123456789012345678901234567890'" \ 26testing 'cryptpw sha256' \
27 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' "" "" 27 'cryptpw -m sha256 QWErty 1234567890123456' \
28 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
29 '' ''
30# mkpasswd-5.6.2 does not allow overlong salts, we truncate (at 16 chars for sha256)
31testing 'cryptpw sha256 overlong' \
32 'cryptpw -m sha256 QWErty 123456789012345678901234567890' \
33 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
34 '' ''
35testing 'cryptpw sha256 implicit' \
36 'cryptpw QWErty \$5\$1234567890123456' \
37 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
38 '' ''
39testing 'cryptpw sha256 rounds=99999' \
40 'cryptpw -m sha256 QWErty rounds=99999\$123456789012345678901234567890' \
41 '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \
42 '' ''
43testing 'cryptpw sha256 rounds=99999 implicit' \
44 'cryptpw QWErty \$5\$rounds=99999\$123456789012345678901234567890' \
45 '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \
46 '' ''
28 47
29testing "cryptpw sha256 rounds=99999" \ 48testing 'cryptpw sha512' \
30 "cryptpw -m sha256 QWErty 'rounds=99999\$123456789012345678901234567890'" \ 49 'cryptpw -m sha512 QWErty 123456789012345678901234567890' \
31 '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' "" "" 50 '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' \
32 51 '' ''
33testing "cryptpw sha512" \ 52testing 'cryptpw sha512crypt' \
34 "cryptpw -m sha512 QWErty '123456789012345678901234567890'" \ 53 'cryptpw -m sha512crypt QWErty 123456789012345678901234567890' \
35 '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' "" "" 54 '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' \
55 '' ''
56testing 'cryptpw sha512 rounds=99999' \
57 'cryptpw -m sha512 QWErty rounds=99999\$123456789012345678901234567890' \
58 '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' \
59 '' ''
60SKIP=
36 61
37testing "cryptpw sha512 rounds=99999" \ 62optional USE_BB_CRYPT_YES
38 "cryptpw -m sha512 QWErty 'rounds=99999\$123456789012345678901234567890'" \ 63testing 'cryptpw yescrypt' \
39 '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' "" "" 64 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123456789012345678901234' \
65 '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
66 '' ''
67testing 'cryptpw yescrypt with non-standard N=2048 instead of 4096 (j8T instead of j9T)' \
68 'cryptpw -m yescrypt qweRTY123@-+ j8T\$123456789012345678901234' \
69 '$y$j8T$123456789012345678901234$JQUUfopCxlfZNE8f.THJwbOkhy.XtB3GIjo9HUVioWB\n' \
70 '' ''
71# mkpasswd-5.6.2 allows short salts for yescrypt
72# ...but there is a catch. Not all of them.
73# The "partial" (not fitting in whole bytes) ascii64-encoded salt
74# is a special case. For example, "$zzz" would not even work in upstream.
75testing 'cryptpw yescrypt with empty salt' \
76 'cryptpw -m yescrypt qweRTY123@-+ j9T\$' \
77 '$y$j9T$$hpeksL94GXNRwnA00L3c8WFy0khFAUbCpBSak.N3Bp.\n' \
78 '' ''
79testing 'cryptpw yescrypt with 3-char salt' \
80 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123' \
81 '$y$j9T$123$A34DMIGUbUIo3bjx66Wtk2IFoREMIw6d49it25KQh2D\n' \
82 '' ''
83# "." is not allowed in mkpasswd-5.6.2
84# ....................................
85# ".." is decoded into one zero byte (not two)
86testing 'cryptpw yescrypt with 2-char salt ".."' \
87 'cryptpw -m yescrypt qweRTY123@-+ j9T\$..' \
88 '$y$j9T$..$yVHeOayxOGg6cHL3.dg10u7T.qSgySfLN3uhSVSLNn/\n' \
89 '' ''
90# "..." is decoded into two zero bytes (not three, not one)
91testing 'cryptpw yescrypt with 3-char salt "..."' \
92 'cryptpw -m yescrypt qweRTY123@-+ j9T\$...' \
93 '$y$j9T$...$xHvJ5USZ7hFyXYbOijtEOMfZRS23cWIxu2eIBXRymA5\n' \
94 '' ''
95# "...." is decoded into three zero bytes (no surprises here)
96testing 'cryptpw yescrypt with 4-char salt "...."' \
97 'cryptpw -m yescrypt qweRTY123@-+ j9T\$....' \
98 '$y$j9T$....$wOnauYL2/NEtr6YQi9pi8AtV7L57sEbVOAnWJIcP9q2\n' \
99 '' ''
100# 84 chars = 21 4-char blocks which decode into 21*3 = 63 bytes.
101# The last byte of the maximum allowed salt size has to come from an incomplete
102# char block. E.g. "z/" encodes byte 0x7f. "z1" is 0xff.
103# Anything larger (e.g. "z2") is an error (it encodes 0x13f).
104testing 'cryptpw yescrypt with 86-char salt (max size)' \
105 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123456789012345678901234567890123456789012345678901234567890123456789012345678901234z/' \
106 '$y$j9T$123456789012345678901234567890123456789012345678901234567890123456789012345678901234z/$Exxe8IoPXiddFsqj7iqCanRf8FyquAoB0/uceLmLjG.\n' \
107 '' ''
108testing 'cryptpw yescrypt implicit' \
109 'cryptpw qweRTY123@-+ \$y\$j9T\$123456789012345678901234' \
110 '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
111 '' ''
40SKIP= 112SKIP=
41 113
42exit $FAILCOUNT 114exit $FAILCOUNT
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index 517ec508b..d2c0a5dc8 100755
--- a/testsuite/hexdump.tests
+++ b/testsuite/hexdump.tests
@@ -5,6 +5,17 @@
5 5
6. ./testing.sh 6. ./testing.sh
7 7
8input=\
9"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
10"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
11"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
12"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
13"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
14
15little_endian=false
16{ printf '\0\1' | hexdump -d | grep -q 256; } && little_endian=true
17readonly little_endian
18
8# testing "description" "command" "result" "infile" "stdin" 19# testing "description" "command" "result" "infile" "stdin"
9testing 'hexdump -C with four NULs' \ 20testing 'hexdump -C with four NULs' \
10 'hexdump -C' \ 21 'hexdump -C' \
@@ -43,12 +54,17 @@ testing "hexdump -e %3_u" \
43 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 54 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
44 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 55 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
45" \ 56" \
46 "" \ 57 "" "$input"
47"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\ 58
48"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\ 59testing "hexdump -e %3_c" \
49"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\ 60 "hexdump -e '16/1 \" %3_c\" \"\n\"'" \
50"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ 61' \\0 001 002 003 004 005 006 \\a \\b \\t \\n \\v \\f \\r 016 017
51"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ 62 020 021 022 023 024 025 026 027 030 031 032 033 034 035 036 037
63 p q r s t u v w x y z { | } ~ 177
64 200 201 202 203 204 205 206 207 210 211 212 213 214 215 216 217
65 360 361 362 363 364 365 366 367 370 371 372 373 374 375 376 377
66' \
67 "" "$input"
52 68
53testing "hexdump -e /1 %d" \ 69testing "hexdump -e /1 %d" \
54 "hexdump -e '16/1 \" %4d\" \"\n\"'" \ 70 "hexdump -e '16/1 \" %4d\" \"\n\"'" \
@@ -59,33 +75,74 @@ testing "hexdump -e /1 %d" \
59 -128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113 75 -128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113
60 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 76 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
61" \ 77" \
62 "" \ 78 "" "$input"
63"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
64"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
65"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
66"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
67"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\
68 79
69testing "hexdump -e /2 %d" \ 80$little_endian || SKIP=1
70 "hexdump -e '8/2 \" %6d\" \"\n\"'" \ 81testing "hexdump -e /2 %d (little endian)" \
71 "\ 82 "hexdump -e '8/2 \" %6d\" \"\n\"'" \
83 "\
72 256 770 1284 1798 2312 2826 3340 3854 84 256 770 1284 1798 2312 2826 3340 3854
73 4368 4882 5396 5910 6424 6938 7452 7966 85 4368 4882 5396 5910 6424 6938 7452 7966
74 29040 29554 30068 30582 31096 31610 32124 32638 86 29040 29554 30068 30582 31096 31610 32124 32638
75 -32384 -31870 -31356 -30842 -30328 -29814 -29300 -28786 87 -32384 -31870 -31356 -30842 -30328 -29814 -29300 -28786
76 -3600 -3086 -2572 -2058 -1544 -1030 -516 -2 88 -3600 -3086 -2572 -2058 -1544 -1030 -516 -2
77" \ 89" \
78 "" \ 90 "" "$input"
79"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\ 91SKIP=
80"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\ 92
81"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\ 93$little_endian && SKIP=1
82"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ 94testing "hexdump -e /2 %d (big endian)" \
83"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ 95 "hexdump -e '8/2 \" %6d\" \"\n\"'" \
96 "\
97 1 515 1029 1543 2057 2571 3085 3599
98 4113 4627 5141 5655 6169 6683 7197 7711
99 28785 29299 29813 30327 30841 31355 31869 32383
100 -32639 -32125 -31611 -31097 -30583 -30069 -29555 -29041
101 -3855 -3341 -2827 -2313 -1799 -1285 -771 -257
102" \
103 "" "$input"
104SKIP=
84 105
85testing "hexdump -n4 -e '\"%u\"'" \ 106$little_endian || SKIP=1
107testing "hexdump -e /2 %x (little endian)" \
108 "hexdump -e '8/2 \" %6x\" \"\n\"'" \
109 "\
110 100 302 504 706 908 b0a d0c f0e
111 1110 1312 1514 1716 1918 1b1a 1d1c 1f1e
112 7170 7372 7574 7776 7978 7b7a 7d7c 7f7e
113 8180 8382 8584 8786 8988 8b8a 8d8c 8f8e
114 f1f0 f3f2 f5f4 f7f6 f9f8 fbfa fdfc fffe
115" \
116 "" "$input"
117SKIP=
118
119$little_endian && SKIP=1
120testing "hexdump -e /2 %x (big endian)" \
121 "hexdump -e '8/2 \" %6x\" \"\n\"'" \
122 "\
123 1 203 405 607 809 a0b c0d e0f
124 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f
125 7071 7273 7475 7677 7879 7a7b 7c7d 7e7f
126 8081 8283 8485 8687 8889 8a8b 8c8d 8e8f
127 f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff
128" \
129 "" "$input"
130SKIP=
131
132$little_endian || SKIP=1
133testing "hexdump -n4 -e '\"%u\"' (little endian)" \
86 "hexdump -n4 -e '\"%u\"'" \ 134 "hexdump -n4 -e '\"%u\"'" \
87 "12345678" \ 135 "12345678" \
88 "" \ 136 "" \
89 "\x4e\x61\xbc\x00AAAA" 137 "\x4e\x61\xbc\x00AAAA"
138SKIP=
139
140$little_endian && SKIP=1
141testing "hexdump -n4 -e '\"%u\"' (big endian)" \
142 "hexdump -n4 -e '\"%u\"'" \
143 "1315027968" \
144 "" \
145 "\x4e\x61\xbc\x00AAAA"
146SKIP=
90 147
91exit $FAILCOUNT 148exit $FAILCOUNT
diff --git a/testsuite/ls.tests b/testsuite/ls.tests
index 9309d366b..a95911034 100755
--- a/testsuite/ls.tests
+++ b/testsuite/ls.tests
@@ -19,7 +19,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
19&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \ 19&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \
20&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ 20&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
21&& testing "ls unicode test with codepoints limited to 767" \ 21&& testing "ls unicode test with codepoints limited to 767" \
22"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ 22"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
23'0001_1__Some_correct_UTF-8_text___________________________________________| 23'0001_1__Some_correct_UTF-8_text___________________________________________|
240002_2__Boundary_condition_test_cases_____________________________________| 240002_2__Boundary_condition_test_cases_____________________________________|
250003_2.1__First_possible_sequence_of_a_certain_length_____________________| 250003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -138,7 +138,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
138&& test x"$CONFIG_SUBST_WCHAR" = x"63" \ 138&& test x"$CONFIG_SUBST_WCHAR" = x"63" \
139&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \ 139&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \
140&& testing "ls unicode test with unlimited codepoints" \ 140&& testing "ls unicode test with unlimited codepoints" \
141"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ 141"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
142'0001_1__Some_correct_UTF-8_text___________________________________________| 142'0001_1__Some_correct_UTF-8_text___________________________________________|
1430002_2__Boundary_condition_test_cases_____________________________________| 1430002_2__Boundary_condition_test_cases_____________________________________|
1440003_2.1__First_possible_sequence_of_a_certain_length_____________________| 1440003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -262,6 +262,28 @@ test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
262"A\nB\nA\nB\nA\nB\n" \ 262"A\nB\nA\nB\nA\nB\n" \
263"" "" 263"" ""
264 264
265rm -rf ls.testdir 2>/dev/null
266mkdir ls.testdir || exit 1
267touch "`printf "ls.testdir/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f_\x7f\x80\xfe\xff_\x22_\x27_\x5c"`"
268
269sq="'"
270
271# testing "test name" "command" "expected result" "file input" "stdin"
272testing "ls -q" \
273'ls -q ls.testdir' \
274'???????????????????????????????_????_"_'$sq'_\\''\n' \
275"" ""
276
277testing "ls -Q" \
278'ls -Q ls.testdir' \
279'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
280"" ""
281
282testing "ls -qQ" \
283'ls -qQ ls.testdir' \
284'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
285"" ""
286
265# Clean up 287# Clean up
266rm -rf ls.testdir 2>/dev/null 288rm -rf ls.testdir 2>/dev/null
267 289
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests
index cca26dc64..a6d2b7ffb 100755
--- a/testsuite/md5sum.tests
+++ b/testsuite/md5sum.tests
@@ -9,6 +9,7 @@
9# efe30c482e0b687e0cca0612f42ca29b 9# efe30c482e0b687e0cca0612f42ca29b
10# d41337e834377140ae7f98460d71d908598ef04f 10# d41337e834377140ae7f98460d71d908598ef04f
11# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3 11# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3
12# c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485
12# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f 13# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f
13 14
14if ! test "$1"; then 15if ! test "$1"; then
@@ -31,9 +32,9 @@ text=`yes "$text" | head -c 9999`
31result=`( 32result=`(
32n=0 33n=0
33while test $n -le 999; do 34while test $n -le 999; do
34 echo "$text" | head -c $n | "$sum" 35 echo "$text" | head -c $n | $sum
35 n=$(($n+1)) 36 n=$(($n+1))
36done | "$sum" 37done | $sum
37)` 38)`
38if test x"$result" != x"$expected -"; then 39if test x"$result" != x"$expected -"; then
39 echo "FAIL: $sum (r:$result exp:$expected)" 40 echo "FAIL: $sum (r:$result exp:$expected)"
@@ -44,7 +45,7 @@ fi
44 45
45# GNU compat: -c EMPTY must fail (exitcode 1)! 46# GNU compat: -c EMPTY must fail (exitcode 1)!
46>EMPTY 47>EMPTY
47if "$sum" -c EMPTY 2>/dev/null; then 48if $sum -c EMPTY 2>/dev/null; then
48 echo "FAIL: $sum -c EMPTY" 49 echo "FAIL: $sum -c EMPTY"
49 : $((FAILCOUNT++)) 50 : $((FAILCOUNT++))
50else 51else
diff --git a/testsuite/od.tests b/testsuite/od.tests
index 4f245a7e8..c863bf2e8 100755
--- a/testsuite/od.tests
+++ b/testsuite/od.tests
@@ -61,7 +61,8 @@ testing "od -a (DESKTOP)" \
61"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" 61"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
62SKIP= 62SKIP=
63 63
64testing "od -B" \ 64$little_endian || SKIP=1
65testing "od -B (little-endian)" \
65 "od -B" \ 66 "od -B" \
66"\ 67"\
670000000 001001 005003 041101 177103 680000000 001001 005003 041101 177103
@@ -70,6 +71,16 @@ testing "od -B" \
70 "" "$input" 71 "" "$input"
71SKIP= 72SKIP=
72 73
74$little_endian && SKIP=1
75testing "od -B (big-endian)" \
76 "od -B" \
77"\
780000000 000402 001412 040502 041776
790000010
80" \
81 "" "$input"
82SKIP=
83
73$little_endian || SKIP=1 84$little_endian || SKIP=1
74testing "od -o (little-endian)" \ 85testing "od -o (little-endian)" \
75 "od -o" \ 86 "od -o" \
diff --git a/testsuite/sha384sum.tests b/testsuite/sha384sum.tests
new file mode 100755
index 000000000..f1449d195
--- /dev/null
+++ b/testsuite/sha384sum.tests
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3. ./md5sum.tests sha384sum c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests
index 2cd8e3bf2..631141735 100755
--- a/testsuite/sha3sum.tests
+++ b/testsuite/sha3sum.tests
@@ -1,3 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9 3 (. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \
4&& (. ./md5sum.tests "sha3sum -a224" 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \
5&& (. ./md5sum.tests "sha3sum -a256" 6f69c8d36a9a579a943d878dab38c179d2a9dde12b244aa8840002c0f3d5bb73) \
6&& (. ./md5sum.tests "sha3sum -a384" 303913449042257996a869e0378323193b4f58d90eea801b12186a3d65640bd3403d3404c63527424ec43dff842c0cd0) \
7&& (. ./md5sum.tests "sha3sum -a512" e14814dccc2fef967af74eb6710885b35dfe660a362c0609b642404987d24a13dac66ad037e6affa5c42631110231655fcf4c972b1457ac49fb83af8113fc51f)
diff --git a/testsuite/tr.tests b/testsuite/tr.tests
index 5cca299ac..96f88ccf3 100755
--- a/testsuite/tr.tests
+++ b/testsuite/tr.tests
@@ -15,6 +15,10 @@ testing "tr understands 0-9A-F" \
15 "tr -cd '[0-9A-F]'" \ 15 "tr -cd '[0-9A-F]'" \
16 "19AF" "" "19AFH\n" 16 "19AF" "" "19AFH\n"
17 17
18testing "tr does not treat [p\\-r] as a range" \
19 "tr '[p\-r]' '+'" \
20 "o+q+s+" "" "opqrs-"
21
18optional FEATURE_TR_CLASSES 22optional FEATURE_TR_CLASSES
19testing "tr understands [:xdigit:]" \ 23testing "tr understands [:xdigit:]" \
20 "tr -cd '[:xdigit:]'" \ 24 "tr -cd '[:xdigit:]'" \
diff --git a/testsuite/wget/wget-handles-https b/testsuite/wget/wget-handles-https
new file mode 100644
index 000000000..11915f42f
--- /dev/null
+++ b/testsuite/wget/wget-handles-https
@@ -0,0 +1,4 @@
1test x"$SKIP_INTERNET_TESTS" != x"" && exit
2
3busybox wget -q -O foo https://www.google.com/
4test -s foo
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index b38b46be3..25be23a01 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -47,7 +47,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
47 int pci_class = 0, pci_vid = 0, pci_did = 0; 47 int pci_class = 0, pci_vid = 0, pci_did = 0;
48 int pci_subsys_vid = 0, pci_subsys_did = 0; 48 int pci_subsys_vid = 0, pci_subsys_did = 0;
49 49
50 char *uevent_filename = concat_path_file(fileName, "/uevent"); 50 char *uevent_filename = concat_path_file(fileName, "uevent");
51 parser = config_open2(uevent_filename, fopen_for_read); 51 parser = config_open2(uevent_filename, fopen_for_read);
52 free(uevent_filename); 52 free(uevent_filename);
53 53
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index 0a9e505f4..f7d0de32d 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -50,7 +50,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
50 char *tokens[4]; 50 char *tokens[4];
51 char *busnum = NULL, *devnum = NULL; 51 char *busnum = NULL, *devnum = NULL;
52 int product_vid = 0, product_did = 0; 52 int product_vid = 0, product_did = 0;
53 char *uevent_filename = concat_path_file(fileName, "/uevent"); 53 char *uevent_filename = concat_path_file(fileName, "uevent");
54 54
55 parser = config_open2(uevent_filename, fopen_for_read); 55 parser = config_open2(uevent_filename, fopen_for_read);
56 free(uevent_filename); 56 free(uevent_filename);
diff --git a/win32/dirent.c b/win32/dirent.c
index 795fc779c..f0e8deae2 100644
--- a/win32/dirent.c
+++ b/win32/dirent.c
@@ -3,7 +3,9 @@
3struct DIR { 3struct DIR {
4 struct dirent dd_dir; 4 struct dirent dd_dir;
5 HANDLE dd_handle; /* FindFirstFile handle */ 5 HANDLE dd_handle; /* FindFirstFile handle */
6 int dd_stat; /* 0-based index */ 6 int not_first;
7 int got_dot;
8 int got_dotdot;
7}; 9};
8 10
9static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata) 11static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
@@ -59,9 +61,11 @@ DIR *opendir(const char *name)
59 } 61 }
60 62
61 /* initialize DIR structure and copy first dir entry */ 63 /* initialize DIR structure and copy first dir entry */
62 dir = xmalloc(sizeof(DIR)); 64 dir = xzalloc(sizeof(DIR));
63 dir->dd_handle = h; 65 dir->dd_handle = h;
64 dir->dd_stat = 0; 66 /* dir->not_first = 0; */
67 /* dir->got_dot = 0; */
68 /* dir->got_dotdot = 0; */
65 finddata2dirent(&dir->dd_dir, &fdata); 69 finddata2dirent(&dir->dd_dir, &fdata);
66 return dir; 70 return dir;
67} 71}
@@ -74,11 +78,17 @@ struct dirent *readdir(DIR *dir)
74 } 78 }
75 79
76 /* if first entry, dirent has already been set up by opendir */ 80 /* if first entry, dirent has already been set up by opendir */
77 if (dir->dd_stat) { 81 if (dir->not_first) {
78 /* get next entry and convert from WIN32_FIND_DATA to dirent */ 82 /* get next entry and convert from WIN32_FIND_DATA to dirent */
79 WIN32_FIND_DATAA fdata; 83 WIN32_FIND_DATAA fdata;
80 if (FindNextFileA(dir->dd_handle, &fdata)) { 84 if (FindNextFileA(dir->dd_handle, &fdata)) {
81 finddata2dirent(&dir->dd_dir, &fdata); 85 finddata2dirent(&dir->dd_dir, &fdata);
86 } else if (!dir->got_dot) {
87 strcpy(dir->dd_dir.d_name, ".");
88 dir->dd_dir.d_type = DT_DIR;
89 } else if (!dir->got_dotdot) {
90 strcpy(dir->dd_dir.d_name, "..");
91 dir->dd_dir.d_type = DT_DIR;
82 } else { 92 } else {
83 DWORD lasterr = GetLastError(); 93 DWORD lasterr = GetLastError();
84 /* POSIX says you shouldn't set errno when readdir can't 94 /* POSIX says you shouldn't set errno when readdir can't
@@ -89,7 +99,15 @@ struct dirent *readdir(DIR *dir)
89 } 99 }
90 } 100 }
91 101
92 ++dir->dd_stat; 102 /* Have we seen '.' or '..'? */
103 if (dir->dd_dir.d_name[0] == '.') {
104 if (dir->dd_dir.d_name[1] == '\0')
105 dir->got_dot = TRUE;
106 else if (dir->dd_dir.d_name[1] == '.' && dir->dd_dir.d_name[2] == '\0')
107 dir->got_dotdot = TRUE;
108 }
109
110 dir->not_first = TRUE;
93 return &dir->dd_dir; 111 return &dir->dd_dir;
94} 112}
95 113
diff --git a/win32/glob.c b/win32/glob.c
index 1cc6483e7..35a1e9a65 100644
--- a/win32/glob.c
+++ b/win32/glob.c
@@ -298,7 +298,7 @@ int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, i
298 if (append(&tail, pat, strlen(pat), 0)) 298 if (append(&tail, pat, strlen(pat), 0))
299 return GLOB_NOSPACE; 299 return GLOB_NOSPACE;
300 cnt++; 300 cnt++;
301 } else 301 } else if (!error)
302 return GLOB_NOMATCH; 302 return GLOB_NOMATCH;
303 } 303 }
304 304
diff --git a/win32/inet_pton.c b/win32/inet_pton.c
index f229a9355..eec8bd2fe 100644
--- a/win32/inet_pton.c
+++ b/win32/inet_pton.c
@@ -78,6 +78,7 @@ int inet_pton(int af, const char *restrict s, void *restrict a0)
78 if (s[j]!='.' || (i<6 && brk<0)) return 0; 78 if (s[j]!='.' || (i<6 && brk<0)) return 0;
79 need_v4=1; 79 need_v4=1;
80 i++; 80 i++;
81 ip[i&7]=0;
81 break; 82 break;
82 } 83 }
83 s += j+1; 84 s += j+1;
diff --git a/win32/ioctl.c b/win32/ioctl.c
index 93f9f504d..d0ed68d61 100644
--- a/win32/ioctl.c
+++ b/win32/ioctl.c
@@ -1,5 +1,7 @@
1#include "libbb.h" 1#include "libbb.h"
2#include "lazyload.h"
2 3
4#if ENABLE_STTY || ENABLE_TTYSIZE
3static int mingw_get_terminal_width_height(struct winsize *win) 5static int mingw_get_terminal_width_height(struct winsize *win)
4{ 6{
5 int fd; 7 int fd;
@@ -21,6 +23,45 @@ static int mingw_get_terminal_width_height(struct winsize *win)
21 23
22 return -1; 24 return -1;
23} 25}
26#endif
27
28#if ENABLE_STTY
29static int mingw_set_terminal_width_height(struct winsize *win)
30{
31 BOOL ret;
32 DECLARE_PROC_ADDR(BOOL, GetConsoleScreenBufferInfoEx, HANDLE,
33 PCONSOLE_SCREEN_BUFFER_INFOEX);
34 DECLARE_PROC_ADDR(BOOL, SetConsoleScreenBufferInfoEx, HANDLE,
35 PCONSOLE_SCREEN_BUFFER_INFOEX);
36
37 if (!INIT_PROC_ADDR(kernel32.dll, GetConsoleScreenBufferInfoEx))
38 return -1;
39 if (!INIT_PROC_ADDR(kernel32.dll, SetConsoleScreenBufferInfoEx))
40 return -1;
41
42 for (int fd = STDOUT_FILENO; fd <= STDERR_FILENO; ++fd) {
43 CONSOLE_SCREEN_BUFFER_INFOEX sbi;
44 HANDLE handle = (HANDLE)_get_osfhandle(fd);
45
46 sbi.cbSize = sizeof(sbi);
47 if (handle != INVALID_HANDLE_VALUE &&
48 (ret=GetConsoleScreenBufferInfoEx(handle, &sbi)) != 0) {
49 if (sbi.dwSize.X != win->ws_col) {
50 sbi.dwSize.X = win->ws_col;
51 }
52 if (sbi.dwSize.Y < win->ws_row) {
53 sbi.dwSize.Y = win->ws_row;
54 }
55 sbi.srWindow.Bottom = sbi.srWindow.Top + win->ws_row;
56 sbi.srWindow.Right = sbi.srWindow.Left + win->ws_col;
57 ret = SetConsoleScreenBufferInfoEx(handle, &sbi);
58 break;
59 }
60 }
61
62 return ret ? 0 : -1;
63}
64#endif
24 65
25int ioctl(int fd UNUSED_PARAM, int code, ...) 66int ioctl(int fd UNUSED_PARAM, int code, ...)
26{ 67{
@@ -31,10 +72,18 @@ int ioctl(int fd UNUSED_PARAM, int code, ...)
31 va_start(ap, code); 72 va_start(ap, code);
32 73
33 switch (code) { 74 switch (code) {
75#if ENABLE_STTY || ENABLE_TTYSIZE
34 case TIOCGWINSZ: 76 case TIOCGWINSZ:
35 arg = va_arg(ap, void *); 77 arg = va_arg(ap, void *);
36 ret = mingw_get_terminal_width_height((struct winsize *)arg); 78 ret = mingw_get_terminal_width_height((struct winsize *)arg);
37 break; 79 break;
80#endif
81#if ENABLE_STTY
82 case TIOCSWINSZ:
83 arg = va_arg(ap, void *);
84 ret = mingw_set_terminal_width_height((struct winsize *)arg);
85 break;
86#endif
38 default: 87 default:
39 ret = -1; 88 ret = -1;
40 errno = EINVAL; 89 errno = EINVAL;
diff --git a/win32/mingw.c b/win32/mingw.c
index cb1f84f30..061e7bac6 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -2560,3 +2560,12 @@ int mingw_shell_execute(SHELLEXECUTEINFO *info)
2560 free(lpath); 2560 free(lpath);
2561 return ret; 2561 return ret;
2562} 2562}
2563
2564#if ENABLE_FEATURE_USE_CNG_API
2565void mingw_die_if_error(NTSTATUS status, const char *function_name) {
2566 if (!NT_SUCCESS(status)) {
2567 bb_error_msg_and_die("call to %s failed: 0x%08lX",
2568 function_name, (unsigned long)status);
2569 }
2570}
2571#endif
diff --git a/win32/paths.h b/win32/paths.h
index e69de29bb..dc81f1b7a 100644
--- a/win32/paths.h
+++ b/win32/paths.h
@@ -0,0 +1 @@
#define _PATH_DEVNULL "/dev/null"
diff --git a/win32/process.c b/win32/process.c
index 33f45ee42..0d120936e 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -795,7 +795,7 @@ UNUSED_PARAM
795 return sp; 795 return sp;
796} 796}
797 797
798void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 798int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
799{ 799{
800 const char *str, *cmdline; 800 const char *str, *cmdline;
801 801
@@ -807,6 +807,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
807 else 807 else
808 cmdline = comm; 808 cmdline = comm;
809 safe_strncpy(buf, cmdline, col); 809 safe_strncpy(buf, cmdline, col);
810 return 0;
810} 811}
811 812
812/** 813/**
diff --git a/win32/select.c b/win32/select.c
index 2be221ac8..46a051cfc 100644
--- a/win32/select.c
+++ b/win32/select.c
@@ -273,8 +273,11 @@ mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
273 int i, fd, rc; 273 int i, fd, rc;
274 clock_t tend = 0; 274 clock_t tend = 0;
275 275
276 if (nfds > FD_SETSIZE) 276 if (nfds < 0 || nfds > FD_SETSIZE)
277 nfds = FD_SETSIZE; 277 {
278 errno = EINVAL;
279 return -1;
280 }
278 281
279 if (!timeout) 282 if (!timeout)
280 wait_timeout = INFINITE; 283 wait_timeout = INFINITE;
diff --git a/win32/strptime.c b/win32/strptime.c
index 3205b95a2..c12e96202 100644
--- a/win32/strptime.c
+++ b/win32/strptime.c
@@ -1,23 +1,23 @@
1/* Copyright (C) 2002, 2004-2005, 2007, 2009-2020 Free Software Foundation, 1/* Copyright (C) 2002, 2004-2005, 2007, 2009-2024 Free Software Foundation,
2 Inc. 2 Inc.
3 This file is part of the GNU C Library. 3 This file is part of the GNU C Library.
4 4
5 This program is free software; you can redistribute it and/or modify 5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by 6 it under the terms of the GNU Lesser General Public License as
7 the Free Software Foundation; either version 2, or (at your option) 7 published by the Free Software Foundation; either version 2.1 of the
8 any later version. 8 License, or (at your option) any later version.
9 9
10 This program is distributed in the hope that it will be useful, 10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details. 13 GNU Lesser General Public License for more details.
14 14
15 You should have received a copy of the GNU General Public License along 15 You should have received a copy of the GNU Lesser General Public License
16 with this program; if not, see <https://www.gnu.org/licenses/>. */ 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 17
18/* 18/*
19 * File from gnulib (https://www.gnu.org/software/gnulib/), processed with 19 * File from gnulib (https://www.gnu.org/software/gnulib/), processed with
20 * coan source -U_LIBC -U_NL_CURRENT -UHAVE_TM_GMTOFF strptime.c 20 * coan source -U_LIBC -U_NL_CURRENT -UHAVE_STRUCT_TM_TM_GMTOFF strptime.c
21 * and lightly edited. 21 * and lightly edited.
22 * 22 *
23 * A form of support for tm_gmtoff was later restored. 23 * A form of support for tm_gmtoff was later restored.
@@ -30,7 +30,7 @@
30#include <ctype.h> 30#include <ctype.h>
31#include <limits.h> 31#include <limits.h>
32#include <string.h> 32#include <string.h>
33#include <stdbool.h> 33#include <strings.h>
34 34
35 35
36enum ptime_locale_status { not, loc, raw }; 36enum ptime_locale_status { not, loc, raw };
@@ -543,23 +543,23 @@ __strptime_internal (const char *rp, const char *fmt, struct tm *tm,
543 543
544 if ((have_uweek || have_wweek) && have_wday) 544 if ((have_uweek || have_wweek) && have_wday)
545 { 545 {
546 int save_wday = tm->tm_wday; 546 int saved_wday = tm->tm_wday;
547 int save_mday = tm->tm_mday; 547 int saved_mday = tm->tm_mday;
548 int save_mon = tm->tm_mon; 548 int saved_mon = tm->tm_mon;
549 int w_offset = have_uweek ? 0 : 1; 549 int w_offset = have_uweek ? 0 : 1;
550 550
551 tm->tm_mday = 1; 551 tm->tm_mday = 1;
552 tm->tm_mon = 0; 552 tm->tm_mon = 0;
553 day_of_the_week (tm); 553 day_of_the_week (tm);
554 if (have_mday) 554 if (have_mday)
555 tm->tm_mday = save_mday; 555 tm->tm_mday = saved_mday;
556 if (have_mon) 556 if (have_mon)
557 tm->tm_mon = save_mon; 557 tm->tm_mon = saved_mon;
558 558
559 if (!have_yday) 559 if (!have_yday)
560 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 560 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
561 + (week_no - 1) *7 561 + (week_no - 1) *7
562 + save_wday - w_offset); 562 + saved_wday - w_offset);
563 563
564 if (!have_mday || !have_mon) 564 if (!have_mday || !have_mon)
565 { 565 {
@@ -575,7 +575,7 @@ __strptime_internal (const char *rp, const char *fmt, struct tm *tm,
575 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); 575 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
576 } 576 }
577 577
578 tm->tm_wday = save_wday; 578 tm->tm_wday = saved_wday;
579 } 579 }
580 580
581 return (char *) rp; 581 return (char *) rp;
diff --git a/win32/termios.c b/win32/termios.c
index f18ff7c3b..b94f68f59 100644
--- a/win32/termios.c
+++ b/win32/termios.c
@@ -2,12 +2,10 @@
2 2
3int tcsetattr(int fd, int mode UNUSED_PARAM, const struct termios *t) 3int tcsetattr(int fd, int mode UNUSED_PARAM, const struct termios *t)
4{ 4{
5 if (terminal_mode(FALSE) & VT_INPUT) { 5 HANDLE h = (HANDLE)_get_osfhandle(fd);
6 HANDLE h = (HANDLE)_get_osfhandle(fd); 6 if (!SetConsoleMode(h, t->w_mode)) {
7 if (!SetConsoleMode(h, t->imode)) { 7 errno = err_win_to_posix();
8 errno = err_win_to_posix(); 8 return -1;
9 return -1;
10 }
11 } 9 }
12 10
13 return 0; 11 return 0;
@@ -15,16 +13,20 @@ int tcsetattr(int fd, int mode UNUSED_PARAM, const struct termios *t)
15 13
16int tcgetattr(int fd, struct termios *t) 14int tcgetattr(int fd, struct termios *t)
17{ 15{
18 if (terminal_mode(FALSE) & VT_INPUT) { 16 HANDLE h = (HANDLE)_get_osfhandle(fd);
19 HANDLE h = (HANDLE)_get_osfhandle(fd); 17 if (!GetConsoleMode(h, &t->w_mode)) {
20 if (!GetConsoleMode(h, &t->imode)) { 18 errno = err_win_to_posix();
21 errno = err_win_to_posix(); 19 return -1;
22 return -1;
23 }
24 } 20 }
21
25 t->c_cc[VINTR] = 3; // ctrl-c 22 t->c_cc[VINTR] = 3; // ctrl-c
26 t->c_cc[VEOF] = 4; // ctrl-d 23 t->c_cc[VEOF] = 4; // ctrl-d
27 24
25 if (t->w_mode & ENABLE_ECHO_INPUT)
26 t->c_lflag |= ECHO;
27 else
28 t->c_lflag &= ~ECHO;
29
28 return 0; 30 return 0;
29} 31}
30 32
diff --git a/win32/termios.h b/win32/termios.h
index 8408aa3e3..60c51119b 100644
--- a/win32/termios.h
+++ b/win32/termios.h
@@ -1,23 +1,32 @@
1#ifndef TERMIOS_H 1#ifndef TERMIOS_H
2#define TERMIOS_H 2#define TERMIOS_H
3 3
4#define VINTR 0 4#define ECHO 0x0004
5#define VEOF 1
6 5
7#define TCIFLUSH 0 6#define VINTR 0
8#define TCSAFLUSH 1 7#define VEOF 1
9#define TCSANOW 2 8
10#define TCSADRAIN 3 9#define TCIFLUSH 0
11#define TCSADFLUSH 4 10#define TCSAFLUSH 1
11#define TCSANOW 2
12#define TCSADRAIN 3
13#define TCSADFLUSH 4
14
15#define CSIZE 0
12 16
13typedef unsigned char cc_t; 17typedef unsigned char cc_t;
18typedef unsigned int tcflag_t;
14typedef unsigned int speed_t; 19typedef unsigned int speed_t;
15 20
16#define NCCS 2 21#define NCCS 18
17struct termios { 22struct termios {
23 tcflag_t c_iflag;
24 tcflag_t c_oflag;
25 tcflag_t c_cflag;
26 tcflag_t c_lflag;
27 char c_line;
18 cc_t c_cc[NCCS]; 28 cc_t c_cc[NCCS];
19 unsigned long imode; 29 unsigned long w_mode;
20 unsigned long omode;
21}; 30};
22 31
23struct winsize { 32struct winsize {
diff --git a/win32/winansi.c b/win32/winansi.c
index c7529c453..427c71f11 100644
--- a/win32/winansi.c
+++ b/win32/winansi.c
@@ -160,7 +160,7 @@ int FAST_FUNC terminal_mode(int reset)
160 mode |= VT_INPUT; 160 mode |= VT_INPUT;
161 } 161 }
162 162
163 if (newmode != oldmode) { 163 if (reset && newmode != oldmode) {
164 if (!SetConsoleMode(h, newmode)) { 164 if (!SetConsoleMode(h, newmode)) {
165 if (mode >= 4) 165 if (mode >= 4)
166 mode &= ~VT_INPUT; 166 mode &= ~VT_INPUT;
@@ -1182,7 +1182,7 @@ char *winansi_fgets(char *s, int size, FILE *stream)
1182/* Ensure that isatty(fd) returns 0 for the NUL device */ 1182/* Ensure that isatty(fd) returns 0 for the NUL device */
1183int mingw_isatty(int fd) 1183int mingw_isatty(int fd)
1184{ 1184{
1185 int result = _isatty(fd); 1185 int result = _isatty(fd) != 0;
1186 1186
1187 if (result) { 1187 if (result) {
1188 HANDLE handle = (HANDLE) _get_osfhandle(fd); 1188 HANDLE handle = (HANDLE) _get_osfhandle(fd);