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--coreutils/cut.c455
-rw-r--r--coreutils/date.c2
-rw-r--r--coreutils/df.c33
-rw-r--r--coreutils/expr.c4
-rw-r--r--coreutils/ls.c246
-rw-r--r--coreutils/md5_sha1_sum.c58
-rw-r--r--coreutils/stty.c148
-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--findutils/xargs.c6
-rw-r--r--include/libbb.h294
-rw-r--r--include/mingw.h3
-rw-r--r--include/platform.h2
-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/appletlib.c2
-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.c71
-rw-r--r--libbb/getopt32.c30
-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.c112
-rw-r--r--libbb/poll_with_signals.c48
-rw-r--r--libbb/procps.c174
-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/u_signal_names.c4
-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--loginutils/suw32.c18
-rw-r--r--miscutils/crond.c12
-rw-r--r--miscutils/fbsplash.c2
-rw-r--r--miscutils/less.c2
-rw-r--r--miscutils/make.c316
-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.c769
-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.c126
-rw-r--r--procps/top.c351
-rw-r--r--runit/chpst.c6
-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.c54
-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/printenv.c4
-rw-r--r--shell/ash_test/recho.c2
-rw-r--r--shell/hush.c483
-rwxr-xr-xshell/hush_leaktool.sh18
-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
-rwxr-xr-xshell/hush_test/hush-misc/sig_exitcode.tests3
-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-read/read_t.right8
-rwxr-xr-xshell/hush_test/hush-read/read_t.tests18
-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
-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/cut.tests140
-rwxr-xr-xtestsuite/hexdump.tests103
-rwxr-xr-xtestsuite/ls.tests26
-rwxr-xr-xtestsuite/make.tests40
-rwxr-xr-xtestsuite/md5sum.tests7
-rwxr-xr-xtestsuite/od.tests13
-rwxr-xr-xtestsuite/sha384sum.tests3
-rwxr-xr-xtestsuite/sha3sum.tests6
-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.c31
-rw-r--r--win32/process.c47
-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.c2
185 files changed, 7486 insertions, 2328 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/coreutils/cut.c b/coreutils/cut.c
index f7b501a46..d81f36bcd 100644
--- a/coreutils/cut.c
+++ b/coreutils/cut.c
@@ -27,21 +27,34 @@
27//kbuild:lib-$(CONFIG_CUT) += cut.o 27//kbuild:lib-$(CONFIG_CUT) += cut.o
28 28
29//usage:#define cut_trivial_usage 29//usage:#define cut_trivial_usage
30//usage: "[OPTIONS] [FILE]..." 30//usage: "{-b|c LIST | -f"IF_FEATURE_CUT_REGEX("|F")" LIST [-d SEP] [-s]} [-D] [-O SEP] [FILE]..."
31// --output-delimiter SEP is too long to fit into 80 char-wide help ----------------^^^^^^^^
31//usage:#define cut_full_usage "\n\n" 32//usage:#define cut_full_usage "\n\n"
32//usage: "Print selected fields from FILEs to stdout\n" 33//usage: "Print selected fields from FILEs to stdout\n"
33//usage: "\n -b LIST Output only bytes from LIST" 34//usage: "\n -b LIST Output only bytes from LIST"
34//usage: "\n -c LIST Output only characters from LIST" 35//usage: "\n -c LIST Output only characters from LIST"
35//usage: "\n -d SEP Field delimiter for input (default -f TAB, -F run of whitespace)" 36//usage: IF_FEATURE_CUT_REGEX(
36//usage: "\n -O SEP Field delimeter for output (default = -d for -f, one space for -F)" 37//usage: "\n -d SEP Input field delimiter (default -f TAB, -F run of whitespace)"
37//usage: "\n -D Don't sort/collate sections or match -fF lines without delimeter" 38//usage: ) IF_NOT_FEATURE_CUT_REGEX(
39//usage: "\n -d SEP Input field delimiter (default TAB)"
40//usage: )
38//usage: "\n -f LIST Print only these fields (-d is single char)" 41//usage: "\n -f LIST Print only these fields (-d is single char)"
39//usage: IF_FEATURE_CUT_REGEX( 42//usage: IF_FEATURE_CUT_REGEX(
40//usage: "\n -F LIST Print only these fields (-d is regex)" 43//usage: "\n -F LIST Print only these fields (-d is regex)"
41//usage: ) 44//usage: )
42//usage: "\n -s Output only lines containing delimiter" 45//usage: "\n -s Drop lines with no delimiter (else print them in full)"
46//usage: "\n -D Don't sort ranges; line without delimiters has one field"
47//usage: IF_LONG_OPTS(
48//usage: "\n --output-delimiter SEP Output field delimeter"
49//usage: ) IF_NOT_LONG_OPTS(
50//usage: IF_FEATURE_CUT_REGEX(
51//usage: "\n -O SEP Output field delimeter (default = -d for -f, one space for -F)"
52//usage: ) IF_NOT_FEATURE_CUT_REGEX(
53//usage: "\n -O SEP Output field delimeter (default = -d)"
54//usage: )
55//usage: )
43//usage: "\n -n Ignored" 56//usage: "\n -n Ignored"
44//(manpage:-n with -b: don't split multibyte characters) 57//(manpage:-n with -b: don't split multibyte characters)
45//usage: 58//usage:
46//usage:#define cut_example_usage 59//usage:#define cut_example_usage
47//usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" 60//usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n"
@@ -53,11 +66,6 @@
53 66
54#if ENABLE_FEATURE_CUT_REGEX 67#if ENABLE_FEATURE_CUT_REGEX
55#include "xregex.h" 68#include "xregex.h"
56#else
57#define regex_t int
58typedef struct { int rm_eo, rm_so; } regmatch_t;
59#define xregcomp(x, ...) *(x) = 0
60#define regexec(...) 0
61#endif 69#endif
62 70
63/* This is a NOEXEC applet. Be very careful! */ 71/* This is a NOEXEC applet. Be very careful! */
@@ -65,265 +73,346 @@ typedef struct { int rm_eo, rm_so; } regmatch_t;
65 73
66/* option vars */ 74/* option vars */
67#define OPT_STR "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" 75#define OPT_STR "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n"
68#define CUT_OPT_BYTE_FLGS (1 << 0) 76#define OPT_BYTE (1 << 0)
69#define CUT_OPT_CHAR_FLGS (1 << 1) 77#define OPT_CHAR (1 << 1)
70#define CUT_OPT_FIELDS_FLGS (1 << 2) 78#define OPT_FIELDS (1 << 2)
71#define CUT_OPT_DELIM_FLGS (1 << 3) 79#define OPT_DELIM (1 << 3)
72#define CUT_OPT_ODELIM_FLGS (1 << 4) 80#define OPT_ODELIM (1 << 4)
73#define CUT_OPT_SUPPRESS_FLGS (1 << 5) 81#define OPT_SUPPRESS (1 << 5)
74#define CUT_OPT_NOSORT_FLGS (1 << 6) 82#define OPT_NOSORT (1 << 6)
75#define CUT_OPT_REGEX_FLGS ((1 << 7) * ENABLE_FEATURE_CUT_REGEX) 83#define OPT_REGEX ((1 << 7) * ENABLE_FEATURE_CUT_REGEX)
76 84
77struct cut_list { 85#define opt_REGEX (option_mask32 & OPT_REGEX)
78 int startpos; 86
79 int endpos; 87struct cut_range {
88 unsigned startpos;
89 unsigned endpos;
80}; 90};
81 91
82static int cmpfunc(const void *a, const void *b) 92static int cmpfunc(const void *a, const void *b)
83{ 93{
84 return (((struct cut_list *) a)->startpos - 94 const struct cut_range *aa = a;
85 ((struct cut_list *) b)->startpos); 95 const struct cut_range *bb = b;
96 return aa->startpos - bb->startpos;
86} 97}
87 98
99#define END_OF_LIST(list_elem) ((list_elem).startpos == UINT_MAX)
100#define NOT_END_OF_LIST(list_elem) ((list_elem).startpos != UINT_MAX)
101
88static void cut_file(FILE *file, const char *delim, const char *odelim, 102static void cut_file(FILE *file, const char *delim, const char *odelim,
89 const struct cut_list *cut_lists, unsigned nlists) 103 const struct cut_range *cut_list)
90{ 104{
91 char *line; 105 char *line;
92 unsigned linenum = 0; /* keep these zero-based to be consistent */ 106 unsigned linenum = 0; /* keep these zero-based to be consistent */
93 regex_t reg; 107 int first_print = 1;
94 int spos, shoe = option_mask32 & CUT_OPT_REGEX_FLGS;
95
96 if (shoe) xregcomp(&reg, delim, REG_EXTENDED);
97 108
98 /* go through every line in the file */ 109 /* go through every line in the file */
99 while ((line = xmalloc_fgetline(file)) != NULL) { 110 while ((line = xmalloc_fgetline(file)) != NULL) {
100 111
101 /* set up a list so we can keep track of what's been printed */ 112 /* set up a list so we can keep track of what's been printed */
102 int linelen = strlen(line); 113 unsigned linelen = strlen(line);
103 char *printed = xzalloc(linelen + 1);
104 char *orig_line = line;
105 unsigned cl_pos = 0; 114 unsigned cl_pos = 0;
106 115
107 /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ 116 /* Cut based on chars/bytes XXX: only works when sizeof(char) == byte */
108 if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { 117 if (option_mask32 & (OPT_CHAR | OPT_BYTE)) {
118 char *printed = xzalloc(linelen + 1);
119 int need_odelim = 0;
120
109 /* print the chars specified in each cut list */ 121 /* print the chars specified in each cut list */
110 for (; cl_pos < nlists; cl_pos++) { 122 for (; NOT_END_OF_LIST(cut_list[cl_pos]); cl_pos++) {
111 for (spos = cut_lists[cl_pos].startpos; spos < linelen;) { 123 unsigned spos = cut_list[cl_pos].startpos;
124 while (spos < linelen) {
112 if (!printed[spos]) { 125 if (!printed[spos]) {
113 printed[spos] = 'X'; 126 printed[spos] = 'X';
127 if (need_odelim && spos != 0 && !printed[spos-1]) {
128 need_odelim = 0;
129 fputs_stdout(odelim);
130 }
114 putchar(line[spos]); 131 putchar(line[spos]);
115 } 132 }
116 if (++spos > cut_lists[cl_pos].endpos) { 133 spos++;
134 if (spos > cut_list[cl_pos].endpos) {
135 /* will print OSEP (if not empty) */
136 need_odelim = (odelim && odelim[0]);
117 break; 137 break;
118 } 138 }
119 } 139 }
120 } 140 }
121 } else if (*delim == '\n') { /* cut by lines */ 141 free(printed);
122 spos = cut_lists[cl_pos].startpos; 142 /* Cut by lines */
143 } else if (!opt_REGEX && *delim == '\n') {
144 unsigned spos = cut_list[cl_pos].startpos;
123 145
124 /* get out if we have no more lists to process or if the lines 146 linenum++;
147 /* get out if we have no more ranges to process or if the lines
125 * are lower than what we're interested in */ 148 * are lower than what we're interested in */
126 if (((int)linenum < spos) || (cl_pos >= nlists)) 149 if (linenum <= spos || END_OF_LIST(cut_list[cl_pos]))
127 goto next_line; 150 goto next_line;
128 151
129 /* if the line we're looking for is lower than the one we were 152 /* if the line we're looking for is lower than the one we were
130 * passed, it means we displayed it already, so move on */ 153 * passed, it means we displayed it already, so move on */
131 while (spos < (int)linenum) { 154 while (++spos < linenum) {
132 spos++;
133 /* go to the next list if we're at the end of this one */ 155 /* go to the next list if we're at the end of this one */
134 if (spos > cut_lists[cl_pos].endpos) { 156 if (spos > cut_list[cl_pos].endpos) {
135 cl_pos++; 157 cl_pos++;
136 /* get out if there's no more lists to process */ 158 /* get out if there's no more ranges to process */
137 if (cl_pos >= nlists) 159 if (END_OF_LIST(cut_list[cl_pos]))
138 goto next_line; 160 goto next_line;
139 spos = cut_lists[cl_pos].startpos; 161 spos = cut_list[cl_pos].startpos;
140 /* get out if the current line is lower than the one 162 /* get out if the current line is lower than the one
141 * we just became interested in */ 163 * we just became interested in */
142 if ((int)linenum < spos) 164 if (linenum <= spos)
143 goto next_line; 165 goto next_line;
144 } 166 }
145 } 167 }
146 168
147 /* If we made it here, it means we've found the line we're 169 /* If we made it here, it means we've found the line we're
148 * looking for, so print it */ 170 * looking for, so print it */
149 puts(line); 171 if (first_print) {
172 first_print = 0;
173 fputs_stdout(line);
174 } else
175 printf("%s%s", odelim, line);
150 goto next_line; 176 goto next_line;
151 } else { /* cut by fields */ 177 /* Cut by fields */
152 unsigned uu = 0, start = 0, end = 0, out = 0; 178 } else {
153 int dcount = 0; 179 unsigned next = 0, start = 0, end = 0;
180 unsigned dcount = 0; /* we saw Nth delimiter (0 - didn't see any yet) */
181
182 /* Blank line? Check -s (later check for -s does not catch empty lines) */
183 if (linelen == 0) {
184 if (option_mask32 & OPT_SUPPRESS)
185 goto next_line;
186 }
187
188 if (!odelim)
189 odelim = "\t";
190 first_print = 1;
154 191
155#if ENABLE_PLATFORM_MINGW32
156 /* An empty line can't contain a delimiter */
157 if (linelen == 0 && (option_mask32 & CUT_OPT_SUPPRESS_FLGS))
158 goto next_line;
159#endif
160 /* Loop through bytes, finding next delimiter */ 192 /* Loop through bytes, finding next delimiter */
161 for (;;) { 193 for (;;) {
162 /* End of current range? */ 194 /* End of current range? */
163 if (end == linelen || dcount > cut_lists[cl_pos].endpos) { 195 if (end == linelen || dcount > cut_list[cl_pos].endpos) {
164 if (++cl_pos >= nlists) break; 196 end_of_range:
165 if (option_mask32 & CUT_OPT_NOSORT_FLGS) 197 cl_pos++;
166 start = dcount = uu = 0; 198 if (END_OF_LIST(cut_list[cl_pos]))
167 end = 0; 199 break;
200 if (option_mask32 & OPT_NOSORT)
201 start = dcount = next = 0;
202 end = 0; /* (why?) */
203 //bb_error_msg("End of current range");
168 } 204 }
169 /* End of current line? */ 205 /* End of current line? */
170 if (uu == linelen) { 206 if (next == linelen) {
171 /* If we've seen no delimiters, check -s */ 207 end = linelen; /* print up to end */
172 if (!cl_pos && !dcount && !shoe) { 208 /* If we've seen no delimiters, and no -D, check -s */
173 if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) 209 if (!(option_mask32 & OPT_NOSORT) && cl_pos == 0 && dcount == 0) {
210 if (option_mask32 & OPT_SUPPRESS)
174 goto next_line; 211 goto next_line;
175 } else if (dcount < cut_lists[cl_pos].startpos) 212 /* else: will print entire line */
176 start = linelen; 213 } else if (dcount < cut_list[cl_pos].startpos) {
177 end = linelen; 214 /* echo 1.2 | cut -d. -f1,3: prints "1", not "1." */
215 //break;
216 /* ^^^ this fails a case with -D:
217 * echo 1 2 | cut -DF 1,3,2:
218 * do not end line processing when didn't find field#3
219 */
220 //if (option_mask32 & OPT_NOSORT) - no, just do it always
221 goto end_of_range;
222 }
223 //bb_error_msg("End of current line: s:%d e:%d", start, end);
178 } else { 224 } else {
179 /* Find next delimiter */ 225 /* Find next delimiter */
180 if (shoe) { 226#if ENABLE_FEATURE_CUT_REGEX
181 regmatch_t rr = {-1, -1}; 227 if (opt_REGEX) {
182 228 regmatch_t rr;
183 if (!regexec(&reg, line+uu, 1, &rr, REG_NOTBOL|REG_NOTEOL)) { 229 regex_t *reg = (void*) delim;
184 end = uu + rr.rm_so; 230
185 uu += rr.rm_eo; 231 if (regexec(reg, line + next, 1, &rr, REG_NOTBOL|REG_NOTEOL) != 0) {
186 } else { 232 /* not found, go to "end of line" logic */
187 uu = linelen; 233 next = linelen;
188 continue; 234 continue;
189 } 235 }
190 } else if (line[end = uu++] != *delim) 236 end = next + rr.rm_so;
191 continue; 237 next += (rr.rm_eo ? rr.rm_eo : 1);
192 238 /* ^^^ advancing by at least 1 prevents infinite loops */
193 /* Got delimiter. Loop if not yet within range. */ 239 /* testcase: echo "no at sign" | cut -d'@*' -F 1- */
194 if (dcount++ < cut_lists[cl_pos].startpos) { 240 } else
195 start = uu; 241#endif
242 {
243 end = next++;
244 if (line[end] != *delim)
245 continue;
246 }
247 /* Got delimiter */
248 dcount++;
249 if (dcount <= cut_list[cl_pos].startpos) {
250 /* Not yet within range - loop */
251 start = next;
196 continue; 252 continue;
197 } 253 }
254 /* -F N-M preserves intermediate delimiters: */
255 //printf "1 2 3 4 5 6 7\n" | toybox cut -O: -F2,4-6,7
256 //2:4 5 6:7
257 if (opt_REGEX && dcount <= cut_list[cl_pos].endpos)
258 continue;
259// NB: toybox does the above for -f too, but it's a compatibility bug:
260//printf "1 2 3 4 5 6 7 8\n" | toybox cut -d' ' -O: -f2,4-6,7
261//2:4 5 6:7 // WRONG!
262//printf "1 2 3 4 5 6 7 8\n" | cut -d' ' --output-delimiter=: -f2,4-6,7
263//2:4:5:6:7 // GNU coreutils 9.1
198 } 264 }
199 if (end != start || !shoe) 265#if ENABLE_FEATURE_CUT_REGEX
200 printf("%s%.*s", out++ ? odelim : "", end-start, line + start); 266 if (end != start || !opt_REGEX)
201 start = uu; 267#endif
202 if (!dcount) 268 {
203 break; 269 if (first_print) {
204 } 270 first_print = 0;
271 printf("%.*s", end - start, line + start);
272 } else
273 printf("%s%.*s", odelim, end - start, line + start);
274 }
275 start = next;
276 //if (dcount == 0)
277 // break; - why?
278 } /* byte loop */
205 } 279 }
206 /* if we printed anything, finish with newline */ 280 /* if we printed anything, finish with newline */
207 putchar('\n'); 281 putchar('\n');
208 next_line: 282 next_line:
209 linenum++; 283 free(line);
210 free(printed); 284 } /* while (got line) */
211 free(orig_line); 285
212 } 286 /* For -d$'\n' --output-delimiter=^, the overall output is still terminated with \n, not ^ */
287 if (!opt_REGEX && *delim == '\n' && !first_print)
288 putchar('\n');
213} 289}
214 290
215int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 291int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
216int cut_main(int argc UNUSED_PARAM, char **argv) 292int cut_main(int argc UNUSED_PARAM, char **argv)
217{ 293{
218 /* growable array holding a series of lists */ 294 /* growable array holding a series of ranges */
219 struct cut_list *cut_lists = NULL; 295 struct cut_range *cut_list = NULL;
220 unsigned nlists = 0; /* number of elements in above list */ 296 unsigned nranges = 0; /* number of elements in above list */
221 char *sopt, *ltok; 297 char *LIST, *ltok;
222 const char *delim = NULL; 298 const char *delim = NULL;
223 const char *odelim = NULL; 299 const char *odelim = NULL;
224 unsigned opt; 300 unsigned opt;
301#if ENABLE_FEATURE_CUT_REGEX
302 regex_t reg;
303#endif
304#if ENABLE_LONG_OPTS
305 static const char cut_longopts[] ALIGN1 =
306 "output-delimiter\0" Required_argument "O"
307 ;
308#endif
225 309
226#define ARG "bcf"IF_FEATURE_CUT_REGEX("F") 310#define ARG "bcf"IF_FEATURE_CUT_REGEX("F")
227 opt = getopt32(argv, "^" 311#if ENABLE_LONG_OPTS
312 opt = getopt32long
313#else
314 opt = getopt32
315#endif
316 (argv, "^"
228 OPT_STR // = "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n" 317 OPT_STR // = "b:c:f:d:O:sD"IF_FEATURE_CUT_REGEX("F:")"n"
229 "\0" "b--"ARG":c--"ARG":f--"ARG IF_FEATURE_CUT_REGEX("F--"ARG), 318 "\0" "b:c:f:" IF_FEATURE_CUT_REGEX("F:") /* one of -bcfF is required */
230 &sopt, &sopt, &sopt, &delim, &odelim IF_FEATURE_CUT_REGEX(, &sopt) 319 "b--"ARG":c--"ARG":f--"ARG IF_FEATURE_CUT_REGEX(":F--"ARG), /* they are mutually exclusive */
231 ); 320 IF_LONG_OPTS(cut_longopts,)
232 if (!delim || !*delim) 321 &LIST, &LIST, &LIST, &delim, &odelim IF_FEATURE_CUT_REGEX(, &LIST)
233 delim = (opt & CUT_OPT_REGEX_FLGS) ? "[[:space:]]+" : "\t"; 322 );
234 if (!odelim) odelim = (opt & CUT_OPT_REGEX_FLGS) ? " " : delim; 323 if (!odelim)
324 odelim = (opt & OPT_REGEX) ? " " : delim;
325 if (!delim)
326 delim = (opt & OPT_REGEX) ? "[[:space:]]+" : "\t";
235 327
236// argc -= optind; 328// argc -= optind;
237 argv += optind; 329 argv += optind;
238 if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS | CUT_OPT_REGEX_FLGS))) 330 //if (!(opt & (OPT_BYTE | OPT_CHAR | OPT_FIELDS | OPT_REGEX)))
239 bb_simple_error_msg_and_die("expected a list of bytes, characters, or fields"); 331 // bb_simple_error_msg_and_die("expected a list of bytes, characters, or fields");
240 332 //^^^ handled by getopt32
241 /* non-field (char or byte) cutting has some special handling */ 333
242 if (!(opt & (CUT_OPT_FIELDS_FLGS|CUT_OPT_REGEX_FLGS))) { 334 /* non-field (char or byte) cutting has some special handling */
243 static const char _op_on_field[] ALIGN1 = " only when operating on fields"; 335 if (!(opt & (OPT_FIELDS|OPT_REGEX))) {
244 336 static const char requires_f[] ALIGN1 = " requires -f"
245 if (opt & CUT_OPT_SUPPRESS_FLGS) { 337 IF_FEATURE_CUT_REGEX(" or -F");
246 bb_error_msg_and_die 338 if (opt & OPT_SUPPRESS)
247 ("suppressing non-delimited lines makes sense%s", _op_on_field); 339 bb_error_msg_and_die("-s%s", requires_f);
248 } 340 if (opt & OPT_DELIM)
249 if (opt & CUT_OPT_DELIM_FLGS) { 341 bb_error_msg_and_die("-d DELIM%s", requires_f);
250 bb_error_msg_and_die
251 ("a delimiter may be specified%s", _op_on_field);
252 }
253 } 342 }
254 343
255 /* 344 /*
256 * parse list and put values into startpos and endpos. 345 * parse list and put values into startpos and endpos.
257 * valid list formats: N, N-, N-M, -M 346 * valid range formats: N, N-, N-M, -M
258 * more than one list can be separated by commas 347 * more than one range can be separated by commas
259 */ 348 */
260 { 349 /* take apart the ranges, one by one (separated with commas) */
350 while ((ltok = strsep(&LIST, ",")) != NULL) {
261 char *ntok; 351 char *ntok;
262 int s = 0, e = 0; 352 int s, e;
263 353
264 /* take apart the lists, one by one (they are separated with commas) */ 354 /* it's actually legal to pass an empty list */
265 while ((ltok = strsep(&sopt, ",")) != NULL) { 355 //if (!ltok[0])
356 // continue;
357 //^^^ testcase?
358
359 /* get the start pos */
360 ntok = strsep(&ltok, "-");
361 if (!ntok[0]) {
362 if (!ltok) /* testcase: -f '' */
363 bb_show_usage();
364 if (!ltok[0]) /* testcase: -f - */
365 bb_show_usage();
366 s = 0; /* "-M" means "1-M" */
367 } else {
368 /* "N" or "N-[M]" */
369 /* arrays are zero based, while the user expects
370 * the first field/char on the line to be char #1 */
371 s = xatoi_positive(ntok) - 1;
372 }
266 373
267 /* it's actually legal to pass an empty list */ 374 /* get the end pos */
268 if (!ltok[0]) 375 if (!ltok) {
269 continue; 376 e = s; /* "N" means "N-N" */
377 } else if (!ltok[0]) {
378 /* "N-" means "until the end of the line" */
379 e = INT_MAX;
380 } else {
381 /* again, arrays are zero based, fields are 1 based */
382 e = xatoi_positive(ltok) - 1;
383 }
270 384
271 /* get the start pos */ 385 if (s < 0 || e < s)
272 ntok = strsep(&ltok, "-"); 386 bb_error_msg_and_die("invalid range %s-%s", ntok, ltok ?: ntok);
273 if (!ntok[0]) {
274 s = 0;
275 } else {
276 s = xatoi_positive(ntok);
277 /* account for the fact that arrays are zero based, while
278 * the user expects the first char on the line to be char #1 */
279#if !ENABLE_PLATFORM_MINGW32
280 if (s != 0)
281 s--;
282#else
283 s--;
284#endif
285 }
286 387
287 /* get the end pos */ 388 /* add the new range */
288 if (ltok == NULL) { 389 cut_list = xrealloc_vector(cut_list, 4, nranges);
289 e = s; 390 /* NB: s is always >= 0 */
290 } else if (!ltok[0]) { 391 cut_list[nranges].startpos = s;
291 e = INT_MAX; 392 cut_list[nranges].endpos = e;
292 } else { 393 nranges++;
293 e = xatoi_positive(ltok); 394 }
294 /* if the user specified no end position, 395 cut_list[nranges].startpos = UINT_MAX; /* end indicator */
295 * that means "til the end of the line" */
296#if !ENABLE_PLATFORM_MINGW32
297 if (!*ltok)
298 e = INT_MAX;
299 else if (e < s)
300 bb_error_msg_and_die("%d<%d", e, s);
301#endif
302 e--; /* again, arrays are zero based, lines are 1 based */
303 }
304#if ENABLE_PLATFORM_MINGW32
305 if (s < 0 || e < s)
306 bb_error_msg_and_die("invalid range %s-%s", ntok, ltok ?: ntok);
307#endif
308 396
309 /* add the new list */ 397 /* make sure we got some cut positions out of all that */
310 cut_lists = xrealloc_vector(cut_lists, 4, nlists); 398 //if (nranges == 0)
311 /* NB: startpos is always >= 0 */ 399 // bb_simple_error_msg_and_die("missing list of positions");
312 cut_lists[nlists].startpos = s; 400 //^^^ this is impossible since one of -bcfF is required,
313 cut_lists[nlists].endpos = e; 401 // they populate LIST with non-NULL string and when it is parsed,
314 nlists++; 402 // cut_list[] gets at least one element.
315 }
316 403
317 /* make sure we got some cut positions out of all that */ 404 /* now that the lists are parsed, we need to sort them to make life
318 if (nlists == 0) 405 * easier on us when it comes time to print the chars / fields / lines
319 bb_simple_error_msg_and_die("missing list of positions"); 406 */
407 if (!(opt & OPT_NOSORT))
408 qsort(cut_list, nranges, sizeof(cut_list[0]), cmpfunc);
320 409
321 /* now that the lists are parsed, we need to sort them to make life 410#if ENABLE_FEATURE_CUT_REGEX
322 * easier on us when it comes time to print the chars / fields / lines 411 if (opt & OPT_REGEX) {
323 */ 412 xregcomp(&reg, delim, REG_EXTENDED);
324 if (!(opt & CUT_OPT_NOSORT_FLGS)) 413 delim = (void*) &reg;
325 qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc);
326 } 414 }
415#endif
327 416
328 { 417 {
329 exitcode_t retval = EXIT_SUCCESS; 418 exitcode_t retval = EXIT_SUCCESS;
@@ -337,12 +426,12 @@ int cut_main(int argc UNUSED_PARAM, char **argv)
337 retval = EXIT_FAILURE; 426 retval = EXIT_FAILURE;
338 continue; 427 continue;
339 } 428 }
340 cut_file(file, delim, odelim, cut_lists, nlists); 429 cut_file(file, delim, odelim, cut_list);
341 fclose_if_not_stdin(file); 430 fclose_if_not_stdin(file);
342 } while (*++argv); 431 } while (*++argv);
343 432
344 if (ENABLE_FEATURE_CLEAN_UP) 433 if (ENABLE_FEATURE_CLEAN_UP)
345 free(cut_lists); 434 free(cut_list);
346 fflush_stdout_and_exit(retval); 435 fflush_stdout_and_exit(retval);
347 } 436 }
348} 437}
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..2153554e8 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -129,6 +129,8 @@
129//usage: "\n -F Append indicator (one of */=@|) to names" 129//usage: "\n -F Append indicator (one of */=@|) to names"
130//usage: ) 130//usage: )
131//usage: "\n -l Long format" 131//usage: "\n -l Long format"
132////usage: "\n -g Long format without group column"
133////TODO: support -G too ("suppress owner column", GNUism)
132//usage: "\n -i List inode numbers" 134//usage: "\n -i List inode numbers"
133//usage: "\n -n List numeric UIDs and GIDs instead of names" 135//usage: "\n -n List numeric UIDs and GIDs instead of names"
134//usage: "\n -s List allocated blocks" 136//usage: "\n -s List allocated blocks"
@@ -162,6 +164,8 @@
162//usage: IF_FEATURE_LS_WIDTH( 164//usage: IF_FEATURE_LS_WIDTH(
163//usage: "\n -w N Format N columns wide" 165//usage: "\n -w N Format N columns wide"
164//usage: ) 166//usage: )
167////usage: "\n -Q Double-quote names"
168////usage: "\n -q Replace unprintable chars with '?'"
165//usage: IF_FEATURE_LS_COLOR( 169//usage: IF_FEATURE_LS_COLOR(
166//usage: "\n --color[={always,never,auto}]" 170//usage: "\n --color[={always,never,auto}]"
167//usage: ) 171//usage: )
@@ -199,27 +203,47 @@ SPLIT_SUBDIR = 2,
199 203
200/* -Cadi1l Std options, busybox always supports */ 204/* -Cadi1l Std options, busybox always supports */
201/* -gnsxA Std options, busybox always supports */ 205/* -gnsxA Std options, busybox always supports */
202/* -Q GNU option, busybox always supports */ 206/* -Q GNU option, busybox always supports: */
203/* -k Std option, busybox always supports (by ignoring) */ 207/* -Q, --quote-name */
204/* It means "for -s, show sizes in kbytes" */ 208/* 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 */ 209/* -LHRctur Std options, busybox optionally supports */
208/* -Fp Std options, busybox optionally supports */ 210/* -Fp Std options, busybox optionally supports */
209/* -SXvhTw GNU options, busybox optionally supports */ 211/* -SXvhTw GNU options, busybox optionally supports */
210/* -T WIDTH Ignored (we don't use tabs on output) */ 212/* -T WIDTH Ignored (we don't use tabs on output) */
211/* -Z SELinux mandated option, busybox optionally supports */ 213/* -Z SELinux mandated option, busybox optionally supports */
214/* -q Std option, busybox always supports: */
215/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
216/* Force each instance of non-printable filename characters and */
217/* <tab> characters to be written as the <question-mark> ('?') */
218/* character. Implementations may provide this option by default */
219/* if the output is to a terminal device. */
220/* -k Std option, busybox always supports (by ignoring) */
221/* It means "for -s, show sizes in kbytes" */
222/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
223/* since otherwise -s shows kbytes anyway */
212#define ls_options \ 224#define ls_options \
213 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ 225 "Cadi1lgnsxA" /* 11 opts, total 11 */ \
214 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ 226 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
215 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ 227 IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
216 IF_SELINUX("Z") /* 1, 16 */ \ 228 IF_SELINUX("Z") /* 1, 15 */ \
217 "Q" /* 1, 17 */ \ 229 "Q" /* 1, 16 */ \
218 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ 230 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
219 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ 231 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
220 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ 232 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
221 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ 233 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
222 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 234 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
235 IF_LONG_OPTS("\xff") /* 1, 29 */ \
236 IF_LONG_OPTS("\xfe") /* 1, 30 */ \
237 IF_LONG_OPTS("\xfd") /* 1, 31 */ \
238 "qk" /* 2, 33 */
239
240#if ENABLE_LONG_OPTS
241static const char ls_longopts[] ALIGN1 =
242 "full-time\0" No_argument "\xff"
243 "group-directories-first\0" No_argument "\xfe"
244 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
245;
246#endif
223 247
224enum { 248enum {
225 OPT_C = (1 << 0), 249 OPT_C = (1 << 0),
@@ -233,29 +257,31 @@ enum {
233 OPT_s = (1 << 8), 257 OPT_s = (1 << 8),
234 OPT_x = (1 << 9), 258 OPT_x = (1 << 9),
235 OPT_A = (1 << 10), 259 OPT_A = (1 << 10),
236 //OPT_k = (1 << 11),
237 260
238 OPTBIT_F = 12, 261 OPTBIT_F = 11,
239 OPTBIT_p, /* 13 */ 262 OPTBIT_p, /* 12 */
240 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 263 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
241 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 264 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
242 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, 265 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
243 OPTBIT_c, /* 17 */ 266 OPTBIT_c, /* 16 */
244 OPTBIT_t, /* 18 */ 267 OPTBIT_t, /* 17 */
245 OPTBIT_u, /* 19 */ 268 OPTBIT_u, /* 18 */
246 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, 269 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
247 OPTBIT_X, /* 21 */ 270 OPTBIT_X, /* 20 */
248 OPTBIT_r, /* 22 */ 271 OPTBIT_r, /* 21 */
249 OPTBIT_v, /* 23 */ 272 OPTBIT_v, /* 22 */
250 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 273 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
251 OPTBIT_H, /* 25 */ 274 OPTBIT_H, /* 24 */
252 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 275 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
253 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 276 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
254 OPTBIT_w, /* 28 */ 277 OPTBIT_w, /* 27 */
255 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, 278 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
256 OPTBIT_dirs_first, 279 OPTBIT_dirs_first,
257 OPTBIT_color, /* 31 */ 280 OPTBIT_color, /* 30 */
258 /* with long opts, we use all 32 bits */ 281 OPTBIT_q = OPTBIT_color + 1, /* 31 */
282 OPTBIT_k = OPTBIT_q + 1, /* 32 */
283 /* with all options enabled, we use all 32 bits and even one extra bit! */
284 /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
259 285
260 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 286 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
261 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 287 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
@@ -277,6 +303,8 @@ enum {
277 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, 303 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
278 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, 304 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
279 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, 305 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
306 OPT_q = (1 << OPTBIT_q),
307 //-k is ignored: OPT_k = (1 << OPTBIT_k),
280}; 308};
281 309
282/* 310/*
@@ -333,6 +361,7 @@ struct globals {
333#endif 361#endif
334 smallint exit_code; 362 smallint exit_code;
335 smallint show_dirname; 363 smallint show_dirname;
364 smallint tty_out;
336#if ENABLE_FEATURE_LS_WIDTH 365#if ENABLE_FEATURE_LS_WIDTH
337 unsigned terminal_width; 366 unsigned terminal_width;
338# define G_terminal_width (G.terminal_width) 367# define G_terminal_width (G.terminal_width)
@@ -353,16 +382,21 @@ struct globals {
353 setup_common_bufsiz(); \ 382 setup_common_bufsiz(); \
354 /* we have to zero it out because of NOEXEC */ \ 383 /* we have to zero it out because of NOEXEC */ \
355 memset(&G, 0, sizeof(G)); \ 384 memset(&G, 0, sizeof(G)); \
356 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 385 IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
357 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 386 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
358} while (0) 387} while (0)
359 388
360#define ESC "\033" 389#define ESC "\033"
361 390
391static int G_isatty(void)
392{
393 if (!G.tty_out) /* not known yet? */
394 G.tty_out = isatty(STDOUT_FILENO) + 1;
395 return (G.tty_out == 2);
396}
362 397
363/*** Output code ***/ 398/*** Output code ***/
364 399
365
366/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 400/* 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 401 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
368 * 3/7:multiplexed char/block device) 402 * 3/7:multiplexed char/block device)
@@ -425,56 +459,93 @@ static char append_char(mode_t mode)
425} 459}
426#endif 460#endif
427 461
462/* Return the number of used columns.
463 * Note that only columnar output uses return value.
464 * -l and -1 modes don't care.
465 * coreutils 7.2 also supports:
466 * ls -b (--escape) = octal escapes (although it doesn't look like working)
467 * ls -N (--literal) = not escape at all
468 */
428static unsigned calc_name_len(const char *name) 469static unsigned calc_name_len(const char *name)
429{ 470{
430 unsigned len; 471 unsigned len;
431 uni_stat_t uni_stat; 472 uni_stat_t uni_stat;
432 473
433 // TODO: quote tab as \t, etc, if -Q 474 if (!(option_mask32 & (OPT_q|OPT_Q)))
434 name = printable_string2(&uni_stat, name); 475 return strlen(name);
435 476
436 if (!(option_mask32 & OPT_Q)) { 477 if (!(option_mask32 & OPT_Q)) {
478 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
479 printable_string2(&uni_stat, name);
437 return uni_stat.unicode_width; 480 return uni_stat.unicode_width;
438 } 481 }
439 482
440 len = 2 + uni_stat.unicode_width; 483 len = 2 + strlen(name);
441 while (*name) { 484 while (*name) {
485 unsigned char ch = (unsigned char)*name;
486 if (ch < ' ' || ch > 0x7e) {
487 ch -= 7;
488 if (ch <= 6) {
489 /* quote chars 7..13 as \a,b,t,n,v,f,r */
490 goto two;
491 }
492 /* other chars <32 or >126 as \ooo octal */
493 len += 3;
494 goto next;
495 }
442 if (*name == '"' || *name == '\\') { 496 if (*name == '"' || *name == '\\') {
497 two:
443 len++; 498 len++;
444 } 499 }
500 next:
445 name++; 501 name++;
446 } 502 }
447 return len; 503 return len;
448} 504}
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) 505static unsigned print_name(const char *name)
458{ 506{
459 unsigned len; 507 unsigned len;
460 uni_stat_t uni_stat; 508 uni_stat_t uni_stat;
461 509
462 // TODO: quote tab as \t, etc, if -Q 510 if (!(option_mask32 & (OPT_q|OPT_Q))) {
463 name = printable_string2(&uni_stat, name); 511 fputs_stdout(name);
512 return strlen(name);
513 }
464 514
465 if (!(option_mask32 & OPT_Q)) { 515 if (!(option_mask32 & OPT_Q)) {
516 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
517 name = printable_string2(&uni_stat, name);
466 fputs_stdout(name); 518 fputs_stdout(name);
467 return uni_stat.unicode_width; 519 return uni_stat.unicode_width;
468 } 520 }
469 521
470 len = 2 + uni_stat.unicode_width; 522 len = 2 + strlen(name);
471 putchar('"'); 523 putchar('"');
472 while (*name) { 524 while (*name) {
473 if (*name == '"' || *name == '\\') { 525 unsigned char ch = (unsigned char)*name;
526 if (ch < ' ' || ch > 0x7e) {
474 putchar('\\'); 527 putchar('\\');
528 ch -= 7;
529 if (ch <= 6) {
530 /* quote chars 7..13 as \a,b,t,n,v,f,r */
531 ch = c_escape_conv_str07[1 + 3 * ch];
532 goto two;
533 }
534 /* other chars <32 or >126 as \ooo octal */
535 ch = (unsigned char)*name;
536 putchar('0' + (ch>>6));
537 putchar('0' + ((ch>>3) & 7));
538 ch = '0' + (ch & 7);
539 len += 3;
540 goto put_ch;
541 }
542 if (ch == '"' || ch == '\\') {
543 putchar('\\');
544 two:
475 len++; 545 len++;
476 } 546 }
477 putchar(*name); 547 put_ch:
548 putchar(ch);
478 name++; 549 name++;
479 } 550 }
480 putchar('"'); 551 putchar('"');
@@ -660,7 +731,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
660 unsigned i, ncols, nrows, row, nc; 731 unsigned i, ncols, nrows, row, nc;
661 unsigned column; 732 unsigned column;
662 unsigned nexttab; 733 unsigned nexttab;
663 unsigned column_width = 0; /* used only by coulmnal output */ 734 unsigned column_width = 0; /* used only by columnar output */
664 735
665 if (option_mask32 & (OPT_l|OPT_1)) { 736 if (option_mask32 & (OPT_l|OPT_1)) {
666 ncols = 1; 737 ncols = 1;
@@ -709,6 +780,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
709 } 780 }
710 nexttab = column + column_width; 781 nexttab = column + column_width;
711 column += display_single(dn[i]); 782 column += display_single(dn[i]);
783 } else {
784 /* if -w999999999, ncols can be very large */
785 //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
786 /* without "break", we loop millions of times here */
787 break;
712 } 788 }
713 } 789 }
714 putchar('\n'); 790 putchar('\n');
@@ -1155,25 +1231,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1155 /* need to initialize since --color has _an optional_ argument */ 1231 /* need to initialize since --color has _an optional_ argument */
1156 const char *color_opt = color_str; /* "always" */ 1232 const char *color_opt = color_str; /* "always" */
1157#endif 1233#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 1234
1166 INIT_G(); 1235 INIT_G();
1167 1236
1168 init_unicode(); 1237 init_unicode();
1169 1238
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 */ 1239 /* process options */
1178 opt = getopt32long(argv, "^" 1240 opt = getopt32long(argv, "^"
1179 ls_options 1241 ls_options
@@ -1211,6 +1273,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1211 exit(0); 1273 exit(0);
1212#endif 1274#endif
1213 1275
1276 /* ftpd secret backdoor? */
1277 if (ENABLE_FTPD && applet_name[0] == 'f') {
1278 /* dirs first are much nicer */
1279 opt = option_mask32 |= OPT_dirs_first;
1280 /* don't show SEcontext */
1281 IF_SELINUX(opt = option_mask32 &= ~OPT_Z;)
1282 /* do not query stdout about size and tty-ness */
1283 IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;)
1284 G.tty_out = 1; /* not a tty */
1285 goto skip_if_ftpd;
1286 }
1287
1288#if ENABLE_FEATURE_LS_WIDTH
1289 if ((int)G_terminal_width < 0) {
1290 /* obtain the terminal width */
1291 G_terminal_width = get_terminal_width(STDIN_FILENO);
1292 /* go one less... */
1293 G_terminal_width--;
1294 }
1295 if (G_terminal_width == 0) /* -w0 */
1296 G_terminal_width = INT_MAX; /* "infinite" */
1297#endif
1298
1214#if ENABLE_SELINUX 1299#if ENABLE_SELINUX
1215 if (opt & OPT_Z) { 1300 if (opt & OPT_Z) {
1216 if (!is_selinux_enabled()) 1301 if (!is_selinux_enabled())
@@ -1229,7 +1314,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1229# endif 1314# endif
1230 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1315 /* LS_COLORS is unset, or (not empty && not "none") ? */
1231 if (!p || (p[0] && strcmp(p, "none") != 0)) { 1316 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1232 if (isatty(STDOUT_FILENO)) { 1317 if (G_isatty()) {
1233 /* check isatty() last because it's expensive (syscall) */ 1318 /* check isatty() last because it's expensive (syscall) */
1234 G_show_color = 1; 1319 G_show_color = 1;
1235 } 1320 }
@@ -1238,23 +1323,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1238 if (opt & OPT_color) { 1323 if (opt & OPT_color) {
1239 if (color_opt[0] == 'n') 1324 if (color_opt[0] == 'n')
1240 G_show_color = 0; 1325 G_show_color = 0;
1241 else switch (index_in_substrings(color_str, color_opt)) { 1326 else if (!G_show_color) {
1242 case 3: 1327 /* if() is not needed, but avoids extra isatty() if G_show_color is already set */
1243 case 4: 1328 /* Check --color=COLOR_OPT and maybe set show_color=1 */
1244 case 5: 1329 switch (index_in_substrings(color_str, color_opt)) {
1245 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { 1330 case 3: // auto
1246 case 0: 1331 case 4: // tty
1247 case 1: 1332 case 5: // if-tty
1248 case 2: 1333 if (!is_TERM_dumb() && G_isatty()) {
1249 G_show_color = 1; 1334 case 0: // always
1335 case 1: // yes
1336 case 2: // force
1337 G_show_color = 1;
1338 }
1250 } 1339 }
1251 } 1340 }
1252 } 1341 }
1253#endif 1342#endif
1343 skip_if_ftpd:
1254 1344
1255 /* sort out which command line options take precedence */ 1345 /* sort out which command line options take precedence */
1256 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) 1346 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1257 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ 1347 opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
1258 if (!(opt & OPT_l)) { /* not -l? */ 1348 if (!(opt & OPT_l)) { /* not -l? */
1259 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1349 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1260 /* when to sort by time? -t[cu] sorts by time even with -l */ 1350 /* when to sort by time? -t[cu] sorts by time even with -l */
@@ -1262,19 +1352,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1262 /* without -l, bare -c or -u enable sort too */ 1352 /* without -l, bare -c or -u enable sort too */
1263 /* (with -l, bare -c or -u just select which time to show) */ 1353 /* (with -l, bare -c or -u just select which time to show) */
1264 if (opt & (OPT_c|OPT_u)) { 1354 if (opt & (OPT_c|OPT_u)) {
1265 option_mask32 |= OPT_t; 1355 opt = option_mask32 |= OPT_t;
1266 } 1356 }
1267 } 1357 }
1268 } 1358 }
1269 1359
1270 /* choose a display format if one was not already specified by an option */ 1360 /* 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))) 1361 if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
1272 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); 1362 opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
1273 1363
1274 if (ENABLE_FTPD && applet_name[0] == 'f') { 1364 if (!(opt & OPT_q) && G_isatty())
1275 /* ftpd secret backdoor. dirs first are much nicer */ 1365 opt = option_mask32 |= OPT_q;
1276 option_mask32 |= OPT_dirs_first;
1277 }
1278 1366
1279#if ENABLE_FEATURE_EXTRA_FILE_DATA 1367#if ENABLE_FEATURE_EXTRA_FILE_DATA
1280 /* Enable accurate link counts for directories */ 1368 /* 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/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/findutils/xargs.c b/findutils/xargs.c
index f0abf1a23..83af75521 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -846,11 +846,11 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
846 * arguments, but not if the command is a NOFORK applet. If the rules 846 * arguments, but not if the command is a NOFORK applet. If the rules
847 * to detect this situation change xargs_exec() above will also need 847 * to detect this situation change xargs_exec() above will also need
848 * to be updated. */ 848 * to be updated. */
849# if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 849# if ENABLE_FEATURE_PREFER_APPLETS && (NUM_APPLETS > 1)
850# if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
850 if (G.max_procs == 1) 851 if (G.max_procs == 1)
851# endif 852# endif
852 { 853 {
853# if ENABLE_FEATURE_PREFER_APPLETS && (NUM_APPLETS > 1)
854 int applet = find_applet_by_name(argv[0]); 854 int applet = find_applet_by_name(argv[0]);
855 if (applet >= 0 && APPLET_IS_NOFORK(applet)) { 855 if (applet >= 0 && APPLET_IS_NOFORK(applet)) {
856 quote = FALSE; 856 quote = FALSE;
diff --git a/include/libbb.h b/include/libbb.h
index bc1453e12..60037ed3d 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 \
@@ -899,7 +919,36 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC;
899// Also mount.c and inetd.c are using gethostbyname(), 919// Also mount.c and inetd.c are using gethostbyname(),
900// + inet_common.c has additional IPv4-only stuff 920// + inet_common.c has additional IPv4-only stuff
901 921
922#if defined CONFIG_FEATURE_TLS_SCHANNEL
923typedef struct tls_state {
924 int ofd;
925 int ifd;
926
927 // handles
928 CredHandle cred_handle;
929 CtxtHandle ctx_handle;
930
931 // buffers
932 char in_buffer[16384 + 256]; // input buffer (to read from server)
933 unsigned long in_buffer_size; // amount of data currently in input buffer
934
935 char *out_buffer; // output buffer (for decrypted data), this is essentially the same as input buffer as data is decrypted in place
936 unsigned long out_buffer_size; // amount of data currently in output buffer
937 unsigned long out_buffer_used; // amount of extra data currently in output buffer
902 938
939 // data
940 char *hostname;
941 SecPkgContext_StreamSizes stream_sizes;
942
943 // booleans
944
945 // context initialized
946 int initialized;
947
948 // closed by remote peer
949 int closed;
950} tls_state_t;
951#else
903struct tls_aes { 952struct tls_aes {
904 uint32_t key[60]; 953 uint32_t key[60];
905 unsigned rounds; 954 unsigned rounds;
@@ -956,12 +1005,14 @@ typedef struct tls_state {
956 struct tls_aes aes_decrypt; 1005 struct tls_aes aes_decrypt;
957 uint8_t H[16]; //used by AES_GCM 1006 uint8_t H[16]; //used by AES_GCM
958} tls_state_t; 1007} tls_state_t;
1008#endif
959 1009
960static inline tls_state_t *new_tls_state(void) 1010static inline tls_state_t *new_tls_state(void)
961{ 1011{
962 tls_state_t *tls = xzalloc(sizeof(*tls)); 1012 tls_state_t *tls = xzalloc(sizeof(*tls));
963 return tls; 1013 return tls;
964} 1014}
1015
965void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC; 1016void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC;
966#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) 1017#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0)
967void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; 1018void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC;
@@ -1071,13 +1122,13 @@ unsigned bb_clk_tck(void) FAST_FUNC;
1071 1122
1072#if SEAMLESS_COMPRESSION 1123#if SEAMLESS_COMPRESSION
1073/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ 1124/* 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; 1125int setup_unzip_on_fd(int fd, int die_if_not_compressed) FAST_FUNC;
1075/* Autodetects .gz etc */ 1126/* Autodetects .gz etc */
1076extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC; 1127extern 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; 1128extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
1078#else 1129#else
1079# define setup_unzip_on_fd(...) (0) 1130# define setup_unzip_on_fd(...) (0)
1080# define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY); 1131# 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)) 1132# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p))
1082#endif 1133#endif
1083/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */ 1134/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */
@@ -1173,6 +1224,32 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
1173/* Reverse */ 1224/* Reverse */
1174char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; 1225char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
1175 1226
1227/* Returns strlen as a bonus */
1228//size_t replace_char(char *s, char what, char with) FAST_FUNC;
1229static inline size_t replace_char(char *str, char from, char to)
1230{
1231 char *p = str;
1232 while (*p) {
1233 if (*p == from)
1234 *p = to;
1235 p++;
1236 }
1237 return p - str;
1238}
1239
1240extern const char c_escape_conv_str00[];
1241#define c_escape_conv_str07 (c_escape_conv_str00+3)
1242
1243void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count);
1244void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count);
1245void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask);
1246void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2);
1247#if BB_UNALIGNED_MEMACCESS_OK
1248# define xorbuf16(buf,mask) xorbuf16_aligned_long(buf,mask)
1249#else
1250void FAST_FUNC xorbuf16(void* buf, const void* mask);
1251#endif
1252
1176/* Generate a UUID */ 1253/* Generate a UUID */
1177void generate_uuid(uint8_t *buf) FAST_FUNC; 1254void generate_uuid(uint8_t *buf) FAST_FUNC;
1178 1255
@@ -1887,18 +1964,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; 1964extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
1888/* 1965/*
1889 * rnd is additional random input. New one is returned. 1966 * rnd is additional random input. New one is returned.
1890 * Useful if you call crypt_make_salt many times in a row: 1967 * Useful if you call crypt_make_rand64encoded many times in a row:
1891 * rnd = crypt_make_salt(buf1, 4, 0); 1968 * rnd = crypt_make_rand64encoded(buf1, 4, 0);
1892 * rnd = crypt_make_salt(buf2, 4, rnd); 1969 * rnd = crypt_make_rand64encoded(buf2, 4, rnd);
1893 * rnd = crypt_make_salt(buf3, 4, rnd); 1970 * rnd = crypt_make_rand64encoded(buf3, 4, rnd);
1894 * (otherwise we risk having same salt generated) 1971 * (otherwise we risk having same salt generated)
1895 */ 1972 */
1896extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; 1973extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC;
1897/* "$N$" + sha_salt_16_bytes + NUL */ 1974/* Size of char salt[] to hold randomly-generated salt string
1898#define MAX_PW_SALT_LEN (3 + 16 + 1) 1975 * sha256/512:
1976 * "$5$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1977 * "$6$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1978 * #define MAX_PW_SALT_LEN (3 + sizeof("rounds=999999999$")-1 + 16 + 1)
1979 * yescrypt:
1980 * "$y$" <up to 8 params of up to 6 chars each> "$" <up to 86 chars salt><NUL>
1981 * (86 chars are ascii64-encoded 64 binary bytes)
1982 */
1983#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; 1984extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
1900 1985
1901
1902/* Returns number of lines changed, or -1 on error */ 1986/* Returns number of lines changed, or -1 on error */
1903#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) 1987#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
1904#define update_passwd(filename, username, data, member) \ 1988#define update_passwd(filename, username, data, member) \
@@ -2041,6 +2125,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; 2125int64_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; 2126void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC;
2043 2127
2128int check_got_signal_and_poll(struct pollfd pfd[1], int timeout) FAST_FUNC;
2129#if ENABLE_PLATFORM_MINGW32
2130# define check_got_signal_and_poll(p, t) poll(p, 1, t)
2131#endif
2044 2132
2045#if ENABLE_FEATURE_EDITING 2133#if ENABLE_FEATURE_EDITING
2046/* It's NOT just ENABLEd or disabled. It's a number: */ 2134/* It's NOT just ENABLEd or disabled. It's a number: */
@@ -2087,7 +2175,7 @@ typedef struct line_input_t {
2087# if MAX_HISTORY 2175# if MAX_HISTORY
2088 int cnt_history; 2176 int cnt_history;
2089 int cur_history; 2177 int cur_history;
2090 int max_history; /* must never be <= 0 */ 2178 int max_history; /* must never be < 0 */
2091# if ENABLE_FEATURE_EDITING_SAVEHISTORY 2179# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2092 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: 2180 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT:
2093 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are 2181 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are
@@ -2153,33 +2241,6 @@ enum { COMM_LEN = 16 };
2153# endif 2241# endif
2154#endif 2242#endif
2155 2243
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 { 2244typedef struct procps_status_t {
2184#if !ENABLE_PLATFORM_MINGW32 2245#if !ENABLE_PLATFORM_MINGW32
2185 DIR *dir; 2246 DIR *dir;
@@ -2215,7 +2276,13 @@ typedef struct procps_status_t {
2215#endif 2276#endif
2216 unsigned tty_major,tty_minor; 2277 unsigned tty_major,tty_minor;
2217#if ENABLE_FEATURE_TOPMEM 2278#if ENABLE_FEATURE_TOPMEM
2218 struct smaprec smaps; 2279 unsigned long mapped_rw;
2280 unsigned long mapped_ro;
2281 unsigned long shared_clean;
2282 unsigned long shared_dirty;
2283 unsigned long private_clean;
2284 unsigned long private_dirty;
2285 unsigned long stack;
2219#endif 2286#endif
2220 char state[4]; 2287 char state[4];
2221 /* basename of executable in exec(2), read from /proc/N/stat 2288 /* basename of executable in exec(2), read from /proc/N/stat
@@ -2264,11 +2331,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC;
2264procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; 2331procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
2265/* Format cmdline (up to col chars) into char buf[size] */ 2332/* Format cmdline (up to col chars) into char buf[size] */
2266/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ 2333/* 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; 2334int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC;
2268pid_t *find_pid_by_name(const char* procName) FAST_FUNC; 2335pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
2269pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; 2336pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
2270int starts_with_cpu(const char *str) FAST_FUNC; 2337int starts_with_cpu(const char *str) FAST_FUNC;
2271unsigned get_cpu_count(void) FAST_FUNC; 2338unsigned get_cpu_count(void) FAST_FUNC;
2339/* Some internals reused by pmap: */
2340unsigned long FAST_FUNC fast_strtoul_10(char **endptr);
2341unsigned long long FAST_FUNC fast_strtoull_16(char **endptr);
2342char* FAST_FUNC skip_fields(char *str, int count);
2272 2343
2273 2344
2274/* Use strict=1 if you process input from untrusted source: 2345/* Use strict=1 if you process input from untrusted source:
@@ -2294,6 +2365,56 @@ char *decode_base64(char *dst, const char **pp_src) FAST_FUNC;
2294char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; 2365char *decode_base32(char *dst, const char **pp_src) FAST_FUNC;
2295void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; 2366void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC;
2296 2367
2368int FAST_FUNC i2a64(int i);
2369int FAST_FUNC a2i64(char c);
2370char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n);
2371
2372enum {
2373 /* how many bytes XYZ_end() fills */
2374 MD5_OUTSIZE = 16,
2375 SHA1_OUTSIZE = 20,
2376 SHA256_OUTSIZE = 32,
2377 SHA384_OUTSIZE = 48,
2378 SHA512_OUTSIZE = 64,
2379 //SHA3-224_OUTSIZE = 28,
2380 /* size of input block */
2381 SHA2_INSIZE = 64,
2382};
2383
2384#if defined CONFIG_FEATURE_USE_CNG_API
2385struct bcrypt_hash_ctx_t {
2386 void *handle;
2387 void *hash_obj;
2388 unsigned int output_size;
2389};
2390typedef struct bcrypt_hash_ctx_t md5_ctx_t;
2391typedef struct bcrypt_hash_ctx_t sha1_ctx_t;
2392typedef struct bcrypt_hash_ctx_t sha256_ctx_t;
2393typedef struct bcrypt_hash_ctx_t sha384_ctx_t;
2394typedef struct bcrypt_hash_ctx_t sha512_ctx_t;
2395typedef struct sha3_ctx_t {
2396 uint64_t state[25];
2397 unsigned bytes_queued;
2398 unsigned input_block_bytes;
2399} sha3_ctx_t;
2400void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2401void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2402void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2403void sha384_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2404void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2405void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2406unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC;
2407# define md5_hash generic_hash
2408# define sha1_hash generic_hash
2409# define sha256_hash generic_hash
2410# define sha384_hash generic_hash
2411# define sha512_hash generic_hash
2412# define md5_end generic_end
2413# define sha1_end generic_end
2414# define sha256_end generic_end
2415# define sha384_end generic_end
2416# define sha512_end generic_end
2417#else
2297typedef struct md5_ctx_t { 2418typedef struct md5_ctx_t {
2298 uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ 2419 uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */
2299 void (*process_block)(struct md5_ctx_t*) FAST_FUNC; 2420 void (*process_block)(struct md5_ctx_t*) FAST_FUNC;
@@ -2307,6 +2428,7 @@ typedef struct sha512_ctx_t {
2307 uint64_t hash[8]; 2428 uint64_t hash[8];
2308 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ 2429 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
2309} sha512_ctx_t; 2430} sha512_ctx_t;
2431typedef struct sha512_ctx_t sha384_ctx_t;
2310typedef struct sha3_ctx_t { 2432typedef struct sha3_ctx_t {
2311 uint64_t state[25]; 2433 uint64_t state[25];
2312 unsigned bytes_queued; 2434 unsigned bytes_queued;
@@ -2324,20 +2446,69 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
2324void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; 2446void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
2325void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2447void 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; 2448unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
2449void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC;
2450#define sha384_hash sha512_hash
2451unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC;
2452#endif
2327void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; 2453void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
2328void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2454void 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; 2455unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC;
2456void 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 */ 2457/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */
2458#if defined CONFIG_FEATURE_USE_CNG_API
2459typedef struct bcrypt_hash_ctx_t md5sha_ctx_t;
2460#define md5sha_hash generic_hash
2461#define sha_end generic_end
2462#else
2331typedef struct md5_ctx_t md5sha_ctx_t; 2463typedef struct md5_ctx_t md5sha_ctx_t;
2332#define md5sha_hash md5_hash 2464#define md5sha_hash md5_hash
2333#define sha_end sha1_end 2465#define sha_end sha1_end
2334enum { 2466#endif
2335 MD5_OUTSIZE = 16, 2467
2336 SHA1_OUTSIZE = 20, 2468/* RFC 2104 HMAC (hash-based message authentication code) */
2337 SHA256_OUTSIZE = 32, 2469#if !ENABLE_FEATURE_USE_CNG_API
2338 SHA512_OUTSIZE = 64, 2470typedef struct hmac_ctx {
2339 SHA3_OUTSIZE = 28, 2471 md5sha_ctx_t hashed_key_xor_ipad;
2340}; 2472 md5sha_ctx_t hashed_key_xor_opad;
2473} hmac_ctx_t;
2474#else
2475typedef struct bcrypt_hash_ctx_t hmac_ctx_t;
2476#endif
2477#define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1)
2478typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
2479#if !ENABLE_FEATURE_USE_CNG_API
2480#if HMAC_ONLY_SHA256
2481#define hmac_begin(ctx,key,key_size,begin) \
2482 hmac_begin(ctx,key,key_size)
2483#endif
2484void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin);
2485static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len)
2486{
2487 md5sha_hash(&ctx->hashed_key_xor_ipad, in, len);
2488}
2489#else
2490# if HMAC_ONLY_SHA256
2491# define hmac_begin(pre,key,key_size,begin) \
2492 _hmac_begin(pre, key, key_size, sha256_begin_hmac)
2493# else
2494# define hmac_begin _hmac_begin
2495# endif
2496void _hmac_begin(hmac_ctx_t *pre, uint8_t *key, unsigned key_size,
2497 BCRYPT_ALG_HANDLE alg_handle);
2498void hmac_uninit(hmac_ctx_t *pre);
2499#endif
2500unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out);
2501#if HMAC_ONLY_SHA256
2502#define hmac_block(key,key_size,begin,in,sz,out) \
2503 hmac_block(key,key_size,in,sz,out)
2504#endif
2505unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size,
2506 md5sha_begin_func *begin,
2507 const void *in, unsigned sz,
2508 uint8_t *out);
2509/* HMAC helpers for TLS: */
2510void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va);
2511unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...);
2341 2512
2342extern uint32_t *global_crc32_table; 2513extern uint32_t *global_crc32_table;
2343uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; 2514uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
@@ -2473,31 +2644,10 @@ extern struct globals *BB_GLOBAL_CONST ptr_to_globals;
2473#define barrier() asm volatile ("":::"memory") 2644#define barrier() asm volatile ("":::"memory")
2474 2645
2475#if defined(__clang_major__) && __clang_major__ >= 9 2646#if defined(__clang_major__) && __clang_major__ >= 9
2476/* Clang/llvm drops assignment to "constant" storage. Silently. 2647/* {ASSIGN,XZALLOC}_CONST_PTR() are out-of-line functions
2477 * Needs serious convincing to not eliminate the store. 2648 * 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 */ 2649 */
2650void ASSIGN_CONST_PTR(const void *pptr, void *v) FAST_FUNC;
2501void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC; 2651void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC;
2502#else 2652#else
2503# define ASSIGN_CONST_PTR(pptr, v) do { \ 2653# define ASSIGN_CONST_PTR(pptr, v) do { \
diff --git a/include/mingw.h b/include/mingw.h
index c41c0f91e..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
@@ -669,3 +670,5 @@ enum {
669}; 670};
670int elevation_state(void); 671int elevation_state(void);
671void set_interp(int i) FAST_FUNC; 672void set_interp(int i) FAST_FUNC;
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..0b88f990b 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
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/appletlib.c b/libbb/appletlib.c
index d6e042775..b1064d10a 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -933,7 +933,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
933 full_write1_str(" multi-call binary.\n"); /* reuse */ 933 full_write1_str(" multi-call binary.\n"); /* reuse */
934#endif 934#endif
935 full_write1_str( 935 full_write1_str(
936 "BusyBox is copyrighted by many authors between 1998-2024.\n" 936 "BusyBox is copyrighted by many authors between 1998-2025.\n"
937 "Licensed under GPLv2. See source distribution for detailed\n" 937 "Licensed under GPLv2. See source distribution for detailed\n"
938 "copyright notices.\n" 938 "copyright notices.\n"
939 "\n" 939 "\n"
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 ffc46f6a7..3dc53d55f 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -204,9 +204,11 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
204 if (!e) 204 if (!e)
205 goto DO_BAD_CONV_CHAR; 205 goto DO_BAD_CONV_CHAR;
206 pr->flags = F_INT; 206 pr->flags = F_INT;
207 if (e > int_convs + 1) /* not d or i? */
208 pr->flags = F_UINT;
209 byte_count_str = "\010\004\002\001"; 207 byte_count_str = "\010\004\002\001";
208 if (e > int_convs + 1) { /* not d or i? */
209 pr->flags = F_UINT;
210 byte_count_str++;
211 }
210 goto DO_BYTE_COUNT; 212 goto DO_BYTE_COUNT;
211 } else 213 } else
212 if (strchr(int_convs, *p1)) { /* %d etc */ 214 if (strchr(int_convs, *p1)) { /* %d etc */
@@ -512,37 +514,52 @@ static void bpad(PR *pr)
512 continue; 514 continue;
513} 515}
514 516
515static const char conv_str[] ALIGN1 =
516 "\0" "\\""0""\0"
517 "\007""\\""a""\0"
518 "\b" "\\""b""\0"
519 "\f" "\\""f""\0"
520 "\n" "\\""n""\0"
521 "\r" "\\""r""\0"
522 "\t" "\\""t""\0"
523 "\v" "\\""v""\0"
524 ;
525
526static void conv_c(PR *pr, unsigned char *p) 517static void conv_c(PR *pr, unsigned char *p)
527{ 518{
528 const char *str = conv_str; 519 const char *str;
529 520 unsigned char ch;
530 do { 521
531 if (*p == *str) { 522 ch = *p;
532 ++str; 523 if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) {
533 goto strpr; /* map e.g. '\n' to "\\n" */ 524 /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */
534 } 525 str = c_escape_conv_str00 + 3 * ch;
535 str += 4; 526 goto strpr;
536 } while (*str); 527 }
537 528
538 if (isprint_asciionly(*p)) { 529 if (isprint_asciionly(*p)) {
539 *pr->cchar = 'c'; 530 *pr->cchar = 'c';
540 printf(pr->fmt, *p); 531 printf(pr->fmt, *p);
541 } 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
542 char buf[4]; 550 char buf[4];
543 /* gcc-8.0.1 needs lots of casts to shut up */ 551 /* gcc-8.0.1 needs lots of casts to shut up */
544 sprintf(buf, "%03o", (unsigned)(uint8_t)*p); 552 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
545 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
546 strpr: 563 strpr:
547 *pr->cchar = 's'; 564 *pr->cchar = 's';
548 printf(pr->fmt, str); 565 printf(pr->fmt, str);
@@ -701,15 +718,21 @@ static NOINLINE void display(priv_dumper_t* dumper)
701 conv_u(pr, bp); 718 conv_u(pr, bp);
702 break; 719 break;
703 case F_UINT: { 720 case F_UINT: {
721 union {
722 uint16_t uval16;
723 uint32_t uval32;
724 } u;
704 unsigned value = (unsigned char)*bp; 725 unsigned value = (unsigned char)*bp;
705 switch (pr->bcnt) { 726 switch (pr->bcnt) {
706 case 1: 727 case 1:
707 break; 728 break;
708 case 2: 729 case 2:
709 move_from_unaligned16(value, bp); 730 move_from_unaligned16(u.uval16, bp);
731 value = u.uval16;
710 break; 732 break;
711 case 4: 733 case 4:
712 move_from_unaligned32(value, bp); 734 move_from_unaligned32(u.uval32, bp);
735 value = u.uval32;
713 break; 736 break;
714 /* case 8: no users yet */ 737 /* case 8: no users yet */
715 } 738 }
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 56040e150..9247588d9 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -93,7 +93,7 @@ getopt32(char **argv, const char *applet_opts, ...)
93 93
94 "!" If the first character in the applet_opts string is a '!', 94 "!" If the first character in the applet_opts string is a '!',
95 report bad options, missing required options, 95 report bad options, missing required options,
96 inconsistent options with all-ones return value (instead of abort. 96 inconsistent options with all-ones return value instead of aborting.
97 97
98 "+" If the first character in the applet_opts string is a plus, 98 "+" If the first character in the applet_opts string is a plus,
99 then option processing will stop as soon as a non-option is 99 then option processing will stop as soon as a non-option is
@@ -265,7 +265,7 @@ Special characters:
265 for "long options only" cases, such as tar --exclude=PATTERN, 265 for "long options only" cases, such as tar --exclude=PATTERN,
266 wget --header=HDR cases. 266 wget --header=HDR cases.
267 267
268 "a?b" A "?" between an option and a group of options means that 268 "a?bc" A "?" between an option and a group of options means that
269 at least one of them is required to occur if the first option 269 at least one of them is required to occur if the first option
270 occurs in preceding command line arguments. 270 occurs in preceding command line arguments.
271 271
@@ -348,9 +348,6 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
348 unsigned trigger; 348 unsigned trigger;
349 int min_arg = 0; 349 int min_arg = 0;
350 int max_arg = -1; 350 int max_arg = -1;
351 int spec_flgs = 0;
352
353#define SHOW_USAGE_IF_ERROR 1
354 351
355 on_off = complementary; 352 on_off = complementary;
356 memset(on_off, 0, sizeof(complementary)); 353 memset(on_off, 0, sizeof(complementary));
@@ -449,9 +446,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
449 continue; 446 continue;
450 c = s[1]; 447 c = s[1];
451 if (*s == '?') { 448 if (*s == '?') {
452 if (c < '0' || c > '9') { 449 if (c >= '0' && c <= '9') {
453 spec_flgs |= SHOW_USAGE_IF_ERROR;
454 } else {
455 max_arg = c - '0'; 450 max_arg = c - '0';
456 s++; 451 s++;
457 } 452 }
@@ -465,8 +460,10 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
465 continue; 460 continue;
466 } 461 }
467 if (*s == '=') { 462 if (*s == '=') {
468 min_arg = max_arg = c - '0'; 463 if (c >= '0' && c <= '9') {
469 s++; 464 min_arg = max_arg = c - '0';
465 s++;
466 }
470 continue; 467 continue;
471 } 468 }
472 for (on_off = complementary; on_off->opt_char; on_off++) 469 for (on_off = complementary; on_off->opt_char; on_off++)
@@ -533,6 +530,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
533 * "fake" short options, like this one: 530 * "fake" short options, like this one:
534 * wget $'-\203' "Test: test" http://kernel.org/ 531 * wget $'-\203' "Test: test" http://kernel.org/
535 * (supposed to act as --header, but doesn't) */ 532 * (supposed to act as --header, but doesn't) */
533 next_opt:
536#if ENABLE_LONG_OPTS 534#if ENABLE_LONG_OPTS
537 while ((c = getopt_long(argc, argv, applet_opts, 535 while ((c = getopt_long(argc, argv, applet_opts,
538 long_options, NULL)) != -1) { 536 long_options, NULL)) != -1) {
@@ -547,8 +545,16 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
547 * but we construct long opts so that flag 545 * but we construct long opts so that flag
548 * is always NULL (see above) */ 546 * is always NULL (see above) */
549 if (on_off->opt_char == '\0' /* && c != '\0' */) { 547 if (on_off->opt_char == '\0' /* && c != '\0' */) {
550 /* c is probably '?' - "bad option" */ 548 /* We reached the end of complementary[] and did not find -c */
551 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;
552 } 558 }
553 } 559 }
554 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..77207a427 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;
@@ -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..c751100bc 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];
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,53 @@ 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
216 if (buf[0] == 'S' || buf[0] == 'P') {
208#define SCAN(S, X) \ 217#define SCAN(S, X) \
209 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ 218 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
210 total->X += currec.X = fast_strtoul_10(&tp); \ 219 tp = skip_whitespace(buf + sizeof(S)-1); \
211 continue; \ 220 sp->X += fast_strtoul_10(&tp); \
212 } 221 continue; \
213 if (cb) { 222 }
214 SCAN("Pss:" , smap_pss ); 223 SCAN("Private_Dirty:", private_dirty)
215 SCAN("Swap:" , smap_swap ); 224 SCAN("Private_Clean:", private_clean)
216 } 225 SCAN("Shared_Dirty:" , shared_dirty )
217 SCAN("Private_Dirty:", private_dirty); 226 SCAN("Shared_Clean:" , shared_clean )
218 SCAN("Private_Clean:", private_clean);
219 SCAN("Shared_Dirty:" , shared_dirty );
220 SCAN("Shared_Clean:" , shared_clean );
221#undef SCAN 227#undef SCAN
228 }
222 tp = strchr(buf, '-'); 229 tp = strchr(buf, '-');
223 if (tp) { 230 if (tp) {
224 // We reached next mapping - the line of this form: 231 // We reached next mapping - the line of this form:
225 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 232 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
226 233
227 if (cb) { 234 char *rwx;
228 /* If we have a previous record, there's nothing more 235 unsigned 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 236
237 *tp = ' '; 237 *tp = ' ';
238 tp = buf; 238 tp = buf;
239 currec.smap_start = fast_strtoull_16(&tp); 239 sz = fast_strtoull_16(&tp); // start
240 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; 240 sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start
241 241 // tp -> "rw-s" string
242 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); 242 rwx = tp;
243
244 // skipping "rw-s FILEOFS M:m INODE " 243 // skipping "rw-s FILEOFS M:m INODE "
245 tp = skip_whitespace(skip_fields(tp, 4)); 244 tp = skip_whitespace(skip_fields(tp, 4));
246 // filter out /dev/something (something != zero) 245 // if not a device memory mapped...
247 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { 246 if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something"
248 if (currec.smap_mode[1] == 'w') { 247 || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device)
249 currec.mapped_rw = currec.smap_size; 248 ) {
250 total->mapped_rw += currec.smap_size; 249 if (rwx[1] == 'w')
251 } else if (currec.smap_mode[1] == '-') { 250 sp->mapped_rw += sz;
252 currec.mapped_ro = currec.smap_size; 251 else if (rwx[0] == 'r' || rwx[2] == 'x')
253 total->mapped_ro += currec.smap_size; 252 sp->mapped_ro += sz;
254 } 253 // else: seen "---p" mappings (mmap guard gaps?),
254 // do NOT account these as VSZ, they aren't really
255 } 255 }
256
257 if (strcmp(tp, "[stack]\n") == 0) 256 if (strcmp(tp, "[stack]\n") == 0)
258 total->stack += currec.smap_size; 257 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 } 258 }
270 } 259 }
271 fclose(file); 260 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} 261}
281#endif 262#endif
282 263
@@ -371,7 +352,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
371 char *cp, *comm1; 352 char *cp, *comm1;
372 int tty; 353 int tty;
373#if !ENABLE_FEATURE_FAST_TOP 354#if !ENABLE_FEATURE_FAST_TOP
374 unsigned long vsz, rss; 355 unsigned long long vsz;
356 unsigned long rss;
375#endif 357#endif
376 /* see proc(5) for some details on this */ 358 /* see proc(5) for some details on this */
377 strcpy(filename_tail, "stat"); 359 strcpy(filename_tail, "stat");
@@ -397,7 +379,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
397 "%ld " /* nice */ 379 "%ld " /* nice */
398 "%*s %*s " /* timeout, it_real_value */ 380 "%*s %*s " /* timeout, it_real_value */
399 "%lu " /* start_time */ 381 "%lu " /* start_time */
400 "%lu " /* vsize */ 382 "%llu " /* vsize - can be very large */
401 "%lu " /* rss */ 383 "%lu " /* rss */
402# if ENABLE_FEATURE_TOP_SMP_PROCESS 384# 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 */ 385 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -450,7 +432,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
450 cp = skip_fields(cp, 2); /* timeout, it_real_value */ 432 cp = skip_fields(cp, 2); /* timeout, it_real_value */
451 sp->start_time = fast_strtoul_10(&cp); 433 sp->start_time = fast_strtoul_10(&cp);
452 /* vsz is in bytes and we want kb */ 434 /* vsz is in bytes and we want kb */
453 sp->vsz = fast_strtoul_10(&cp) >> 10; 435 sp->vsz = fast_strtoull_10(&cp) >> 10;
454 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 436 /* 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; 437 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
456# if ENABLE_FEATURE_TOP_SMP_PROCESS 438# if ENABLE_FEATURE_TOP_SMP_PROCESS
@@ -484,7 +466,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
484 466
485#if ENABLE_FEATURE_TOPMEM 467#if ENABLE_FEATURE_TOPMEM
486 if (flags & PSSCAN_SMAPS) 468 if (flags & PSSCAN_SMAPS)
487 procps_read_smaps(pid, &sp->smaps, NULL, NULL); 469 procps_read_smaps(pid, sp);
488#endif /* TOPMEM */ 470#endif /* TOPMEM */
489#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 471#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
490 if (flags & PSSCAN_RUIDGID) { 472 if (flags & PSSCAN_RUIDGID) {
@@ -567,36 +549,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
567 return sp; 549 return sp;
568} 550}
569 551
570void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 552int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
571{ 553{
572 int sz; 554 int sz;
573 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; 555 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
574 556
575 sprintf(filename, "/proc/%u/cmdline", pid); 557 sprintf(filename, "/proc/%u/cmdline", pid);
576 sz = open_read_close(filename, buf, col - 1); 558 sz = open_read_close(filename, buf, col - 1);
559 if (sz < 0)
560 return sz;
577 if (sz > 0) { 561 if (sz > 0) {
578 const char *base; 562 const char *program_basename;
579 int comm_len; 563 int comm_len;
580 564
581 buf[sz] = '\0'; 565 buf[sz] = '\0';
582 while (--sz >= 0 && buf[sz] == '\0') 566 while (--sz >= 0 && buf[sz] == '\0')
583 continue; 567 continue;
584 /* Prevent basename("process foo/bar") = "bar" */ 568
585 strchrnul(buf, ' ')[0] = '\0'; 569 /* Find "program" in "[-][/PATH/TO/]program" */
586 base = bb_basename(buf); /* before we replace argv0's NUL with space */ 570 strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */
571 program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf);
572 /* ^^^ note: must do it *before* replacing argv0's NUL with space */
573
574 /* Prevent stuff like this:
575 * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c
576 * messing up top and ps output (or worse).
577 * This also replaces NULs with spaces, converting
578 * list of NUL-strings into one string.
579 */
587 while (sz >= 0) { 580 while (sz >= 0) {
588 if ((unsigned char)(buf[sz]) < ' ') 581 if ((unsigned char)(buf[sz]) < ' ')
589 buf[sz] = ' '; 582 buf[sz] = ' ';
590 sz--; 583 sz--;
591 } 584 }
592 if (base[0] == '-') /* "-sh" (login shell)? */
593 base++;
594 585
595 /* If comm differs from argv0, prepend "{comm} ". 586 /* If comm differs from argv0, prepend "{comm} ".
596 * It allows to see thread names set by prctl(PR_SET_NAME). 587 * It allows to see thread names set by prctl(PR_SET_NAME).
597 */ 588 */
598 if (!comm) 589 if (!comm)
599 return; 590 return 0;
600 comm_len = strlen(comm); 591 comm_len = strlen(comm);
601 /* Why compare up to comm_len, not COMM_LEN-1? 592 /* Why compare up to comm_len, not COMM_LEN-1?
602 * Well, some processes rewrite argv, and use _spaces_ there 593 * Well, some processes rewrite argv, and use _spaces_ there
@@ -604,19 +595,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" 595 * I prefer to still treat argv0 "process foo bar"
605 * as 'equal' to comm "process". 596 * as 'equal' to comm "process".
606 */ 597 */
607 if (strncmp(base, comm, comm_len) != 0) { 598 if (strncmp(program_basename, comm, comm_len) != 0) {
608 comm_len += 3; 599 comm_len += 3;
609 if (col > comm_len) 600 if (col > comm_len)
610 memmove(buf + comm_len, buf, col - comm_len); 601 memmove(buf + comm_len, buf, col - comm_len);
611 snprintf(buf, col, "{%s}", comm); 602 snprintf(buf, col, "{%s}", comm);
612 if (col <= comm_len) 603 if (col <= comm_len)
613 return; 604 return 0;
614 buf[comm_len - 1] = ' '; 605 buf[comm_len - 1] = ' ';
615 buf[col - 1] = '\0'; 606 buf[col - 1] = '\0';
616 } 607 }
617 } else { 608 } else {
618 snprintf(buf, col, "[%s]", comm ? comm : "?"); 609 snprintf(buf, col, "[%s]", comm ? comm : "?");
619 } 610 }
611 return 0;
620} 612}
621 613
622#endif /* ENABLE_PLATFORM_MINGW32 */ 614#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/u_signal_names.c b/libbb/u_signal_names.c
index ef2b6f891..e233849c5 100644
--- a/libbb/u_signal_names.c
+++ b/libbb/u_signal_names.c
@@ -27,10 +27,6 @@
27 27
28#include "libbb.h" 28#include "libbb.h"
29 29
30#if ENABLE_PLATFORM_MINGW32
31# undef SIGPIPE
32#endif
33
34#if ENABLE_PLATFORM_POSIX || defined(SIGSTKFLT) || defined(SIGVTALRM) 30#if ENABLE_PLATFORM_POSIX || defined(SIGSTKFLT) || defined(SIGVTALRM)
35# define SIGLEN 7 31# define SIGLEN 7
36#elif defined(SIGWINCH) || (ENABLE_FEATURE_RTMINMAX && \ 32#elif defined(SIGWINCH) || (ENABLE_FEATURE_RTMINMAX && \
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/loginutils/suw32.c b/loginutils/suw32.c
index b3976dcfd..77c038582 100644
--- a/loginutils/suw32.c
+++ b/loginutils/suw32.c
@@ -48,8 +48,8 @@ int suw32_main(int argc UNUSED_PARAM, char **argv)
48 char *opt_command = NULL; 48 char *opt_command = NULL;
49 char *opt_shell = NULL; 49 char *opt_shell = NULL;
50 SHELLEXECUTEINFO info; 50 SHELLEXECUTEINFO info;
51 char *bb_path, *cwd, *realcwd, *q, *args; 51 const char *bb_path;
52 DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); 52 char *cwd, *realcwd, *q, *args;
53 53
54 opt = getopt32(argv, "^c:s:tNW" "\0" "s--N:N--s", &opt_command, &opt_shell); 54 opt = getopt32(argv, "^c:s:tNW" "\0" "s--N:N--s", &opt_command, &opt_shell);
55 argv += optind; 55 argv += optind;
@@ -70,19 +70,17 @@ int suw32_main(int argc UNUSED_PARAM, char **argv)
70 } 70 }
71#endif 71#endif
72 72
73 /* ShellExecuteEx() needs backslash as separator in UNC paths. */
74 if (opt_shell) { 73 if (opt_shell) {
75 bb_path = file_is_win32_exe(opt_shell); 74 bb_path = file_is_win32_exe(opt_shell);
76 if (!bb_path) 75 if (!bb_path)
77 bb_error_msg_and_die("%s: Not found", opt_shell); 76 bb_error_msg_and_die("%s: Not found", opt_shell);
78 args = NULL; 77 args = NULL;
79 } else { 78 } else {
80 bb_path = xstrdup(bb_busybox_exec_path); 79 bb_path = bb_busybox_exec_path;
81 args = xstrdup("--busybox ash"); 80 args = xstrdup("--busybox ash");
82 if (!test_mode) 81 if (!test_mode)
83 args = xappendword(args, "-t \"BusyBox ash (Admin)\""); 82 args = xappendword(args, "-t \"BusyBox ash (Admin)\"");
84 } 83 }
85 slash_to_bs(bb_path);
86 84
87 memset(&info, 0, sizeof(SHELLEXECUTEINFO)); 85 memset(&info, 0, sizeof(SHELLEXECUTEINFO));
88 info.cbSize = sizeof(SHELLEXECUTEINFO); 86 info.cbSize = sizeof(SHELLEXECUTEINFO);
@@ -137,12 +135,7 @@ int suw32_main(int argc UNUSED_PARAM, char **argv)
137 /* info.lpDirectory = NULL; */ 135 /* info.lpDirectory = NULL; */
138 info.nShow = SW_SHOWNORMAL; 136 info.nShow = SW_SHOWNORMAL;
139 137
140 if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) { 138 if (!mingw_shell_execute(&info)) {
141 ret = -1;
142 goto end;
143 }
144
145 if (!ShellExecuteExA(&info)) {
146 ret = 1; 139 ret = 1;
147 goto end; 140 goto end;
148 } 141 }
@@ -159,7 +152,8 @@ int suw32_main(int argc UNUSED_PARAM, char **argv)
159 } 152 }
160 end: 153 end:
161 if (ENABLE_FEATURE_CLEAN_UP) { 154 if (ENABLE_FEATURE_CLEAN_UP) {
162 free(bb_path); 155 if (bb_path != bb_busybox_exec_path)
156 free((void *)bb_path);
163 free(cwd); 157 free(cwd);
164 free(realcwd); 158 free(realcwd);
165 free(args); 159 free(args);
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 7316408bf..a165274aa 100644
--- a/miscutils/make.c
+++ b/miscutils/make.c
@@ -257,6 +257,11 @@ enum {
257#define MAKE_FAILURE 0x01 257#define MAKE_FAILURE 0x01
258#define MAKE_DIDSOMETHING 0x02 258#define MAKE_DIDSOMETHING 0x02
259 259
260// Return TRUE if c is allowed in a POSIX 2017 macro or target name
261#define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_')
262// Return TRUE if c is in the POSIX 'portable filename character set'
263#define isfname(c) (ispname(c) || c == '-')
264
260#define HTABSIZE 39 265#define HTABSIZE 39
261 266
262struct globals { 267struct globals {
@@ -320,11 +325,8 @@ struct globals {
320#endif 325#endif
321 326
322static int make(struct name *np, int level); 327static int make(struct name *np, int level);
323 328static struct name *dyndep(struct name *np, struct rule *infrule,
324// Return TRUE if c is allowed in a POSIX 2017 macro or target name 329 const char **ptsuff);
325#define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_')
326// Return TRUE if c is in the POSIX 'portable filename character set'
327#define isfname(c) (ispname(c) || c == '-')
328 330
329/* 331/*
330 * Utility functions. 332 * Utility functions.
@@ -469,8 +471,7 @@ newcmd(struct cmd **cphead, char *str)
469 /*(*cphead)->c_next = NULL; - xzalloc did it */ 471 /*(*cphead)->c_next = NULL; - xzalloc did it */
470 (*cphead)->c_cmd = xstrdup(str); 472 (*cphead)->c_cmd = xstrdup(str);
471 /*(*cphead)->c_refcnt = 0; */ 473 /*(*cphead)->c_refcnt = 0; */
472 if (makefile) 474 (*cphead)->c_makefile = xstrdup(makefile);
473 (*cphead)->c_makefile = xstrdup(makefile);
474 (*cphead)->c_dispno = dispno; 475 (*cphead)->c_dispno = dispno;
475} 476}
476 477
@@ -993,38 +994,27 @@ suffix(const char *name)
993} 994}
994 995
995/* 996/*
996 * Dynamic dependency. This routine applies the suffix rules 997 * Search for an inference rule to convert some suffix ('psuff')
997 * to try and find a source and a set of rules for a missing 998 * to the target suffix 'tsuff'. The basename of the prerequisite
998 * target. NULL is returned on failure. On success the name of 999 * is 'base'.
999 * the implicit prerequisite is returned and the details are
1000 * placed in the imprule structure provided by the caller.
1001 */ 1000 */
1002static struct name * 1001static struct name *
1003dyndep(struct name *np, struct rule *imprule) 1002dyndep0(char *base, const char *tsuff, struct rule *infrule)
1004{ 1003{
1005 char *suff, *newsuff; 1004 char *psuff;
1006 char *base, *name, *member;
1007 struct name *xp; // Suffixes 1005 struct name *xp; // Suffixes
1008 struct name *sp; // Suffix rule 1006 struct name *sp; // Suffix rule
1009 struct name *pp = NULL; // Implicit prerequisite
1010 struct rule *rp; 1007 struct rule *rp;
1011 struct depend *dp; 1008 struct depend *dp;
1012 bool chain = FALSE; 1009 bool chain = FALSE;
1013 1010
1014 member = NULL;
1015 name = splitlib(np->n_name, &member);
1016
1017 suff = xstrdup(suffix(name));
1018 base = member ? member : name;
1019 *suffix(base) = '\0';
1020
1021 xp = newname(".SUFFIXES"); 1011 xp = newname(".SUFFIXES");
1022 retry: 1012 retry:
1023 for (rp = xp->n_rule; rp; rp = rp->r_next) { 1013 for (rp = xp->n_rule; rp; rp = rp->r_next) {
1024 for (dp = rp->r_dep; dp; dp = dp->d_next) { 1014 for (dp = rp->r_dep; dp; dp = dp->d_next) {
1025 // Generate new suffix rule to try 1015 // Generate new suffix rule to try
1026 newsuff = dp->d_name->n_name; 1016 psuff = dp->d_name->n_name;
1027 sp = findname(auto_concat(newsuff, suff)); 1017 sp = findname(auto_concat(psuff, tsuff));
1028 if (sp && sp->n_rule) { 1018 if (sp && sp->n_rule) {
1029 struct name *ip; 1019 struct name *ip;
1030 int got_ip; 1020 int got_ip;
@@ -1032,9 +1022,8 @@ dyndep(struct name *np, struct rule *imprule)
1032 // Has rule already been used in this chain? 1022 // Has rule already been used in this chain?
1033 if ((sp->n_flag & N_MARK)) 1023 if ((sp->n_flag & N_MARK))
1034 continue; 1024 continue;
1035
1036 // Generate a name for an implicit prerequisite 1025 // Generate a name for an implicit prerequisite
1037 ip = newname(auto_concat(base, newsuff)); 1026 ip = newname(auto_concat(base, psuff));
1038 if ((ip->n_flag & N_DOING)) 1027 if ((ip->n_flag & N_DOING))
1039 continue; 1028 continue;
1040 1029
@@ -1045,20 +1034,19 @@ dyndep(struct name *np, struct rule *imprule)
1045 got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); 1034 got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET);
1046 } else { 1035 } else {
1047 sp->n_flag |= N_MARK; 1036 sp->n_flag |= N_MARK;
1048 got_ip = dyndep(ip, NULL) != NULL; 1037 got_ip = dyndep(ip, NULL, NULL) != NULL;
1049 sp->n_flag &= ~N_MARK; 1038 sp->n_flag &= ~N_MARK;
1050 } 1039 }
1051 1040
1052 if (got_ip) { 1041 if (got_ip) {
1053 // Prerequisite exists or we know how to make it 1042 // Prerequisite exists or we know how to make it
1054 if (imprule) { 1043 if (infrule) {
1055 dp = NULL; 1044 dp = NULL;
1056 newdep(&dp, ip); 1045 newdep(&dp, ip);
1057 imprule->r_dep = dp; 1046 infrule->r_dep = dp;
1058 imprule->r_cmd = sp->n_rule->r_cmd; 1047 infrule->r_cmd = sp->n_rule->r_cmd;
1059 } 1048 }
1060 pp = ip; 1049 return ip;
1061 goto finish;
1062 } 1050 }
1063 } 1051 }
1064 } 1052 }
@@ -1069,9 +1057,87 @@ dyndep(struct name *np, struct rule *imprule)
1069 chain = TRUE; 1057 chain = TRUE;
1070 goto retry; 1058 goto retry;
1071 } 1059 }
1072 finish: 1060 return NULL;
1073 free(suff); 1061}
1062
1063/*
1064 * If 'name' ends with 'suffix' return an allocated string containing
1065 * the name with the suffix removed, else return NULL.
1066 */
1067static char *
1068has_suffix(const char *name, const char *suffix)
1069{
1070 ssize_t delta = strlen(name) - strlen(suffix);
1071 char *base = NULL;
1072
1073 if (delta > 0 && strcmp(name + delta, suffix) == 0) {
1074 base = xstrdup(name);
1075 base[delta] = '\0';
1076 }
1077
1078 return base;
1079}
1080
1081/*
1082 * Dynamic dependency. This routine applies the suffix rules
1083 * to try and find a source and a set of rules for a missing
1084 * target. NULL is returned on failure. On success the name of
1085 * the implicit prerequisite is returned and the rule used is
1086 * placed in the infrule structure provided by the caller.
1087 */
1088static struct name *
1089dyndep(struct name *np, struct rule *infrule, const char **ptsuff)
1090{
1091 const char *tsuff;
1092 char *base, *name, *member;
1093 struct name *pp = NULL; // Implicit prerequisite
1094
1095 member = NULL;
1096 name = splitlib(np->n_name, &member);
1097
1098 // POSIX only allows inference rules with one or two periods.
1099 // As an extension this restriction is lifted, but not for
1100 // targets of the form lib.a(member.o).
1101 if (!posix && member == NULL) {
1102 struct name *xp = newname(".SUFFIXES");
1103 int found_suffix = FALSE;
1104
1105 for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) {
1106 for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) {
1107 tsuff = dp->d_name->n_name;
1108 base = has_suffix(name, tsuff);
1109 if (base) {
1110 found_suffix = TRUE;
1111 pp = dyndep0(base, tsuff, infrule);
1112 free(base);
1113 if (pp) {
1114 goto done;
1115 }
1116 }
1117 }
1118 }
1119
1120 if (!found_suffix) {
1121 // The name didn't have a known suffix. Try single-suffix rule.
1122 tsuff = "";
1123 pp = dyndep0(name, tsuff, infrule);
1124 if (pp) {
1125 done:
1126 if (ptsuff) {
1127 *ptsuff = tsuff;
1128 }
1129 }
1130 }
1131 } else {
1132 tsuff = xstrdup(suffix(name));
1133 base = member ? member : name;
1134 *suffix(base) = '\0';
1135
1136 pp = dyndep0(base, tsuff, infrule);
1137 free((void *)tsuff);
1138 }
1074 free(name); 1139 free(name);
1140
1075 return pp; 1141 return pp;
1076} 1142}
1077 1143
@@ -1789,9 +1855,10 @@ readline(FILE *fd, int want_command)
1789} 1855}
1790 1856
1791/* 1857/*
1792 * Return TRUE if the argument is a known suffix. 1858 * Return a pointer to the suffix name if the argument is a known suffix
1859 * or NULL if it isn't.
1793 */ 1860 */
1794static int 1861static const char *
1795is_suffix(const char *s) 1862is_suffix(const char *s)
1796{ 1863{
1797 struct name *np; 1864 struct name *np;
@@ -1802,7 +1869,39 @@ is_suffix(const char *s)
1802 for (rp = np->n_rule; rp; rp = rp->r_next) { 1869 for (rp = np->n_rule; rp; rp = rp->r_next) {
1803 for (dp = rp->r_dep; dp; dp = dp->d_next) { 1870 for (dp = rp->r_dep; dp; dp = dp->d_next) {
1804 if (strcmp(s, dp->d_name->n_name) == 0) { 1871 if (strcmp(s, dp->d_name->n_name) == 0) {
1805 return TRUE; 1872 return dp->d_name->n_name;
1873 }
1874 }
1875 }
1876 return NULL;
1877}
1878
1879/*
1880 * Return TRUE if the argument is formed by concatenating two
1881 * known suffixes.
1882 */
1883static int
1884is_inference_target(const char *s)
1885{
1886 struct name *np;
1887 struct rule *rp1, *rp2;
1888 struct depend *dp1, *dp2;
1889
1890 np = newname(".SUFFIXES");
1891 for (rp1 = np->n_rule; rp1; rp1 = rp1->r_next) {
1892 for (dp1 = rp1->r_dep; dp1; dp1 = dp1->d_next) {
1893 const char *suff1 = dp1->d_name->n_name;
1894 size_t len = strlen(suff1);
1895
1896 if (strncmp(s, suff1, len) == 0) {
1897 for (rp2 = np->n_rule; rp2; rp2 = rp2->r_next) {
1898 for (dp2 = rp2->r_dep; dp2; dp2 = dp2->d_next) {
1899 const char *suff2 = dp2->d_name->n_name;
1900 if (strcmp(s + len, suff2) == 0) {
1901 return TRUE;
1902 }
1903 }
1904 }
1806 } 1905 }
1807 } 1906 }
1808 } 1907 }
@@ -1824,7 +1923,6 @@ enum {
1824static int 1923static int
1825target_type(char *s) 1924target_type(char *s)
1826{ 1925{
1827 char *sfx;
1828 int ret; 1926 int ret;
1829 static const char *s_name = 1927 static const char *s_name =
1830 ".DEFAULT\0" 1928 ".DEFAULT\0"
@@ -1854,9 +1952,6 @@ target_type(char *s)
1854 T_SPECIAL, 1952 T_SPECIAL,
1855 }; 1953 };
1856 1954
1857 if (*s != '.')
1858 return T_NORMAL;
1859
1860 // Check for one of the known special targets 1955 // Check for one of the known special targets
1861 ret = index_in_strings(s_name, s); 1956 ret = index_in_strings(s_name, s);
1862 if (ret >= 0) 1957 if (ret >= 0)
@@ -1864,16 +1959,23 @@ target_type(char *s)
1864 1959
1865 // Check for an inference rule 1960 // Check for an inference rule
1866 ret = T_NORMAL; 1961 ret = T_NORMAL;
1867 sfx = suffix(s); 1962 if (!posix) {
1868 if (is_suffix(sfx)) { 1963 if (is_suffix(s) || is_inference_target(s)) {
1869 if (s == sfx) { // Single suffix rule
1870 ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; 1964 ret = T_INFERENCE | T_NOPREREQ | T_COMMAND;
1871 } else { 1965 }
1872 // Suffix is valid, check that prefix is too 1966 } else {
1873 *sfx = '\0'; 1967 // In POSIX inference rule targets must contain one or two dots
1874 if (is_suffix(s)) 1968 char *sfx = suffix(s);
1969 if (*s == '.' && is_suffix(sfx)) {
1970 if (s == sfx) { // Single suffix rule
1875 ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; 1971 ret = T_INFERENCE | T_NOPREREQ | T_COMMAND;
1876 *sfx = '.'; 1972 } else {
1973 // Suffix is valid, check that prefix is too
1974 *sfx = '\0';
1975 if (is_suffix(s))
1976 ret = T_INFERENCE | T_NOPREREQ | T_COMMAND;
1977 *sfx = '.';
1978 }
1877 } 1979 }
1878 } 1980 }
1879 return ret; 1981 return ret;
@@ -2069,6 +2171,20 @@ pragmas_from_env(void)
2069#endif 2171#endif
2070 2172
2071/* 2173/*
2174 * Determine if a line is a target rule with an inline command.
2175 * Return a pointer to the semicolon separator if it is, else NULL.
2176 */
2177static char *
2178inline_command(char *line)
2179{
2180 char *p = find_char(line, ':');
2181
2182 if (p)
2183 p = strchr(p, ';');
2184 return p;
2185}
2186
2187/*
2072 * Parse input from the makefile and construct a tree structure of it. 2188 * Parse input from the makefile and construct a tree structure of it.
2073 */ 2189 */
2074static void 2190static void
@@ -2287,9 +2403,9 @@ input(FILE *fd, int ilevel)
2287 cp = NULL; 2403 cp = NULL;
2288 s = strchr(q, ';'); 2404 s = strchr(q, ';');
2289 if (s) { 2405 if (s) {
2290 // Retrieve command from expanded copy of line 2406 // Retrieve command from original or expanded copy of line
2291 char *copy3 = expand_macros(copy, FALSE); 2407 char *copy3 = expand_macros(copy, FALSE);
2292 if ((p = find_colon(copy3)) && (p = strchr(p, ';'))) 2408 if ((p = inline_command(copy)) || (p = inline_command(copy3)))
2293 newcmd(&cp, process_command(p + 1)); 2409 newcmd(&cp, process_command(p + 1));
2294 free(copy3); 2410 free(copy3);
2295 *s = '\0'; 2411 *s = '\0';
@@ -2585,9 +2701,34 @@ docmds(struct name *np, struct cmd *cp)
2585 return estat; 2701 return estat;
2586} 2702}
2587 2703
2704/*
2705 * Remove the suffix from a name, either the one provided in 'tsuff'
2706 * or, if 'tsuff' is NULL, one of the known suffixes.
2707 */
2708static char *
2709remove_suffix(const char *name, const char *tsuff)
2710{
2711 char *base = NULL;
2712
2713 if (tsuff != NULL) {
2714 base = has_suffix(name, tsuff);
2715 } else {
2716 struct name *xp = newname(".SUFFIXES");
2717 for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) {
2718 for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) {
2719 base = has_suffix(name, dp->d_name->n_name);
2720 if (base) {
2721 return base;
2722 }
2723 }
2724 }
2725 }
2726 return base;
2727}
2728
2588static int 2729static int
2589make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, 2730make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc,
2590 char *dedup, struct name *implicit) 2731 char *dedup, struct name *implicit, const char *tsuff)
2591{ 2732{
2592 char *name, *member = NULL, *base = NULL, *prereq = NULL; 2733 char *name, *member = NULL, *base = NULL, *prereq = NULL;
2593 2734
@@ -2603,7 +2744,7 @@ make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc,
2603 char *s; 2744 char *s;
2604 2745
2605 // As an extension, if we're not dealing with an implicit 2746 // As an extension, if we're not dealing with an implicit
2606 // rule set $< to the first out-of-date prerequisite. 2747 // prerequisite set $< to the first out-of-date prerequisite.
2607 if (implicit == NULL) { 2748 if (implicit == NULL) {
2608 if (oodate) { 2749 if (oodate) {
2609 s = strchr(oodate, ' '); 2750 s = strchr(oodate, ' ');
@@ -2614,15 +2755,26 @@ make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc,
2614 } else 2755 } else
2615 prereq = implicit->n_name; 2756 prereq = implicit->n_name;
2616 2757
2617 base = member ? member : name; 2758 if (!posix && member == NULL) {
2618 s = suffix(base); 2759 // As an extension remove a suffix that doesn't necessarily
2619 // As an extension, if we're not dealing with an implicit 2760 // start with a period from a target, but not for targets
2620 // rule and the target ends with a known suffix, remove it 2761 // of the form lib.a(member.o).
2621 // and set $* to the stem, else to an empty string. 2762 base = remove_suffix(name, tsuff);
2622 if (implicit == NULL && !is_suffix(s)) 2763 if (base) {
2623 base = NULL; 2764 free(name);
2624 else 2765 name = base;
2625 *s = '\0'; 2766 }
2767 } else {
2768 base = member ? member : name;
2769 s = suffix(base);
2770 // As an extension, if we're not dealing with an implicit
2771 // prerequisite and the target ends with a known suffix,
2772 // remove it and set $* to the stem, else to an empty string.
2773 if (implicit == NULL && !is_suffix(s))
2774 base = NULL;
2775 else
2776 *s = '\0';
2777 }
2626 } 2778 }
2627 setmacro("<", prereq, 0 | M_VALID); 2779 setmacro("<", prereq, 0 | M_VALID);
2628 setmacro("*", base, 0 | M_VALID); 2780 setmacro("*", base, 0 | M_VALID);
@@ -2667,11 +2819,12 @@ make(struct name *np, int level)
2667 struct depend *dp; 2819 struct depend *dp;
2668 struct rule *rp; 2820 struct rule *rp;
2669 struct name *impdep = NULL; // implicit prerequisite 2821 struct name *impdep = NULL; // implicit prerequisite
2670 struct rule imprule; 2822 struct rule infrule;
2671 struct cmd *sc_cmd = NULL; // commands for single-colon rule 2823 struct cmd *sc_cmd = NULL; // commands for single-colon rule
2672 char *oodate = NULL; 2824 char *oodate = NULL;
2673 char *allsrc = NULL; 2825 char *allsrc = NULL;
2674 char *dedup = NULL; 2826 char *dedup = NULL;
2827 const char *tsuff = NULL;
2675 struct timespec dtim = {1, 0}; 2828 struct timespec dtim = {1, 0};
2676 int estat = 0; 2829 int estat = 0;
2677 2830
@@ -2690,10 +2843,10 @@ make(struct name *np, int level)
2690 // as an extension, not for phony targets) 2843 // as an extension, not for phony targets)
2691 sc_cmd = getcmd(np); 2844 sc_cmd = getcmd(np);
2692 if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { 2845 if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) {
2693 impdep = dyndep(np, &imprule); 2846 impdep = dyndep(np, &infrule, &tsuff);
2694 if (impdep) { 2847 if (impdep) {
2695 sc_cmd = imprule.r_cmd; 2848 sc_cmd = infrule.r_cmd;
2696 addrule(np, imprule.r_dep, NULL, FALSE); 2849 addrule(np, infrule.r_dep, NULL, FALSE);
2697 } 2850 }
2698 } 2851 }
2699 2852
@@ -2708,14 +2861,15 @@ make(struct name *np, int level)
2708 } 2861 }
2709 impdep = np; 2862 impdep = np;
2710 } 2863 }
2711 } 2864 } else {
2712 else {
2713 // If any double-colon rule has no commands we need 2865 // If any double-colon rule has no commands we need
2714 // an inference rule (but, as an extension, not for phony targets) 2866 // an inference rule.
2715 for (rp = np->n_rule; rp; rp = rp->r_next) { 2867 for (rp = np->n_rule; rp; rp = rp->r_next) {
2716 if (!rp->r_cmd) { 2868 if (!rp->r_cmd) {
2717 if (posix || !(np->n_flag & N_PHONY)) 2869 // Phony targets don't need an inference rule.
2718 impdep = dyndep(np, &imprule); 2870 if (!posix && (np->n_flag & N_PHONY))
2871 continue;
2872 impdep = dyndep(np, &infrule, &tsuff);
2719 if (!impdep) { 2873 if (!impdep) {
2720 if (doinclude) 2874 if (doinclude)
2721 return 1; 2875 return 1;
@@ -2741,11 +2895,14 @@ make(struct name *np, int level)
2741 // Each double-colon rule is handled separately. 2895 // Each double-colon rule is handled separately.
2742 if ((np->n_flag & N_DOUBLE)) { 2896 if ((np->n_flag & N_DOUBLE)) {
2743 // If the rule has no commands use the inference rule. 2897 // If the rule has no commands use the inference rule.
2898 // Unless there isn't one, as allowed for phony targets.
2744 if (!rp->r_cmd) { 2899 if (!rp->r_cmd) {
2745 locdep = impdep; 2900 if (impdep) {
2746 imprule.r_dep->d_next = rp->r_dep; 2901 locdep = impdep;
2747 rp->r_dep = imprule.r_dep; 2902 infrule.r_dep->d_next = rp->r_dep;
2748 rp->r_cmd = imprule.r_cmd; 2903 rp->r_dep = infrule.r_dep;
2904 rp->r_cmd = infrule.r_cmd;
2905 }
2749 } 2906 }
2750 // A rule with no prerequisities is executed unconditionally. 2907 // A rule with no prerequisities is executed unconditionally.
2751 if (!rp->r_dep) 2908 if (!rp->r_dep)
@@ -2776,7 +2933,7 @@ make(struct name *np, int level)
2776 if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { 2933 if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) {
2777 if (!(estat & MAKE_FAILURE)) { 2934 if (!(estat & MAKE_FAILURE)) {
2778 estat |= make1(np, rp->r_cmd, oodate, allsrc, 2935 estat |= make1(np, rp->r_cmd, oodate, allsrc,
2779 dedup, locdep); 2936 dedup, locdep, tsuff);
2780 dtim = (struct timespec){1, 0}; 2937 dtim = (struct timespec){1, 0};
2781 } 2938 }
2782 free(oodate); 2939 free(oodate);
@@ -2792,7 +2949,7 @@ make(struct name *np, int level)
2792 } 2949 }
2793 } 2950 }
2794 if ((np->n_flag & N_DOUBLE) && impdep) 2951 if ((np->n_flag & N_DOUBLE) && impdep)
2795 free(imprule.r_dep); 2952 free(infrule.r_dep);
2796 2953
2797 np->n_flag |= N_DONE; 2954 np->n_flag |= N_DONE;
2798 np->n_flag &= ~N_DOING; 2955 np->n_flag &= ~N_DOING;
@@ -2801,7 +2958,8 @@ make(struct name *np, int level)
2801 ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { 2958 ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) {
2802 if (!(estat & MAKE_FAILURE)) { 2959 if (!(estat & MAKE_FAILURE)) {
2803 if (sc_cmd) 2960 if (sc_cmd)
2804 estat |= make1(np, sc_cmd, oodate, allsrc, dedup, impdep); 2961 estat |= make1(np, sc_cmd, oodate, allsrc, dedup,
2962 impdep, tsuff);
2805 else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) 2963 else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING))
2806 warning("nothing to be done for %s", np->n_name); 2964 warning("nothing to be done for %s", np->n_name);
2807 } else if (!doinclude && !quest) { 2965 } else if (!doinclude && !quest) {
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..9c05364ea 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,549 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2481 } 2362 }
2482 } 2363 }
2483} 2364}
2365#else
2366
2367#if ENABLE_FEATURE_TLS_SCHANNEL_1_3
2368#include <subauth.h>
2369#endif
2370
2371#define SCHANNEL_USE_BLACKLISTS
2372
2373#include <security.h>
2374#include <schannel.h>
2375
2376
2377#define BB_SCHANNEL_ISC_FLAGS \
2378 (ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT | \
2379 ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM | ISC_REQ_USE_SUPPLIED_CREDS)
2380
2381static char *hresult_to_error_string(HRESULT result) {
2382 char *output = NULL;
2383
2384 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
2385 | FORMAT_MESSAGE_IGNORE_INSERTS |
2386 FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, result,
2387 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2388 (char *) &output, 0, NULL);
2389 return output;
2390}
2391
2392static ssize_t tls_read(struct tls_state *state, char *buf, ssize_t len) {
2393 ssize_t amount_read = 0;
2394
2395 if (state->closed) {
2396 return 0;
2397 }
2398
2399 while (len > 0) {
2400 if (state->out_buffer && (state->out_buffer_size > 0)) {
2401 unsigned long copy_amount =
2402 min(len, (ssize_t) state->out_buffer_size);
2403 memcpy(buf, state->out_buffer, copy_amount);
2404
2405 amount_read += copy_amount;
2406 buf += copy_amount;
2407 len -= copy_amount;
2408
2409 if (copy_amount == state->out_buffer_size) {
2410 // We've used all the decrypted data
2411 // Move extra data to the front
2412 memmove(state->in_buffer,
2413 state->in_buffer + state->out_buffer_used,
2414 state->in_buffer_size - state->out_buffer_used);
2415 state->in_buffer_size -= state->out_buffer_used;
2416
2417 state->out_buffer = NULL;
2418 state->out_buffer_used = 0;
2419 state->out_buffer_size = 0;
2420 } else {
2421 state->out_buffer_size -= copy_amount;
2422 state->out_buffer += copy_amount;
2423 }
2424 } else {
2425 SECURITY_STATUS status;
2426
2427 int received;
2428
2429 SecBuffer buffers[4];
2430
2431 SecBufferDesc desc;
2432
2433 buffers[0].BufferType = SECBUFFER_DATA;
2434 buffers[0].pvBuffer = state->in_buffer;
2435 buffers[0].cbBuffer = state->in_buffer_size;
2436
2437 buffers[1].BufferType = SECBUFFER_EMPTY;
2438 buffers[1].pvBuffer = NULL;
2439 buffers[1].cbBuffer = 0;
2440
2441 buffers[2].BufferType = SECBUFFER_EMPTY;
2442 buffers[2].pvBuffer = NULL;
2443 buffers[2].cbBuffer = 0;
2444
2445 buffers[3].BufferType = SECBUFFER_EMPTY;
2446 buffers[3].pvBuffer = NULL;
2447 buffers[3].cbBuffer = 0;
2448
2449 desc.ulVersion = SECBUFFER_VERSION;
2450 desc.pBuffers = buffers;
2451 desc.cBuffers = _countof(buffers);
2452
2453 status = DecryptMessage(&state->ctx_handle, &desc, 0, NULL);
2454
2455 switch (status) {
2456 case SEC_E_OK:{
2457 state->out_buffer = buffers[1].pvBuffer;
2458 state->out_buffer_size = buffers[1].cbBuffer;
2459
2460 state->out_buffer_used = state->in_buffer_size;
2461 if (buffers[3].BufferType == SECBUFFER_EXTRA) {
2462 state->out_buffer_used -= buffers[3].cbBuffer;
2463 }
2464
2465 continue;
2466 }
2467 case SEC_I_CONTEXT_EXPIRED:{
2468 state->closed = 1;
2469 goto Success;
2470 }
2471 case SEC_I_RENEGOTIATE:{
2472 // Renegotiate the TLS connection.
2473 // Microsoft repurposed this flag
2474 // for TLS 1.3 support.
2475 int i;
2476
2477 DWORD flags;
2478
2479 SecBuffer in_buffers[2];
2480
2481 SecBuffer out_buffers[2];
2482
2483 SecBufferDesc in_desc;
2484
2485 SecBufferDesc out_desc;
2486
2487
2488 for (i = 0; i < 4; i++) {
2489 if (buffers[i].BufferType == SECBUFFER_EXTRA)
2490 break;
2491 }
2492
2493 flags = BB_SCHANNEL_ISC_FLAGS;
2494
2495 in_buffers[0].BufferType = SECBUFFER_TOKEN;
2496 in_buffers[0].pvBuffer = buffers[i].pvBuffer;
2497 in_buffers[0].cbBuffer = buffers[i].cbBuffer;
2498
2499 in_buffers[1].BufferType = SECBUFFER_EMPTY;
2500 in_buffers[1].pvBuffer = NULL;
2501 in_buffers[1].cbBuffer = 0;
2502
2503 out_buffers[0].BufferType = SECBUFFER_TOKEN;
2504 out_buffers[0].pvBuffer = NULL;
2505 out_buffers[0].cbBuffer = 0;
2506
2507 out_buffers[1].BufferType = SECBUFFER_ALERT;
2508 out_buffers[1].pvBuffer = NULL;
2509 out_buffers[1].cbBuffer = 0;
2510
2511 in_desc.ulVersion = SECBUFFER_VERSION;
2512 in_desc.pBuffers = in_buffers;
2513 in_desc.cBuffers = _countof(in_buffers);
2514
2515 out_desc.ulVersion = SECBUFFER_VERSION;
2516 out_desc.pBuffers = out_buffers;
2517 out_desc.cBuffers = _countof(out_buffers);
2518
2519 status = InitializeSecurityContext(&state->cred_handle,
2520 state->initialized ?
2521 &state->ctx_handle : NULL,
2522 state->initialized ? NULL :
2523 state->hostname, flags, 0,
2524 0,
2525 state->initialized ?
2526 &in_desc : NULL, 0,
2527 state->initialized ? NULL :
2528 &state->ctx_handle,
2529 &out_desc, &flags, 0);
2530
2531 if (status != SEC_E_OK) {
2532 bb_error_msg_and_die("schannel: renegotiate failed: (0x%08lx): %s",
2533 status, hresult_to_error_string(status));
2534 }
2535
2536 if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
2537 memmove(state->in_buffer,
2538 state->in_buffer + (state->in_buffer_size -
2539 in_buffers[1].cbBuffer),
2540 in_buffers[1].cbBuffer);
2541 }
2542
2543 state->out_buffer_used =
2544 state->in_buffer_size - in_buffers[1].cbBuffer;
2545 state->in_buffer_size = in_buffers[1].cbBuffer;
2546
2547 continue;
2548 }
2549 case SEC_E_INCOMPLETE_MESSAGE:{
2550 break;
2551 }
2552 default:{
2553 bb_error_msg_and_die("schannel: DecryptMessage failed: (0x%08lx): %s", status,
2554 hresult_to_error_string(status));
2555 }
2556 }
2557
2558 received =
2559 safe_read(state->ifd,
2560 state->in_buffer + state->in_buffer_size,
2561 sizeof(state->in_buffer) - state->in_buffer_size);
2562 if (received == 0) {
2563 state->closed = 1;
2564 goto Success;
2565 } else if (received < 0) {
2566 bb_error_msg_and_die("schannel: read() failed");
2567 }
2568
2569 state->in_buffer_size += received;
2570 }
2571 }
2572
2573 Success:
2574 return amount_read;
2575}
2576
2577static void tls_write(struct tls_state *state, char *buf, size_t len) {
2578 if (state->closed) {
2579 bb_error_msg_and_die("schannel: attempted to write to a closed connection");
2580 }
2581
2582 while (len > 0) {
2583 unsigned long copy_amount =
2584 min(len, (size_t) state->stream_sizes.cbMaximumMessage);
2585 char *write_buffer = _alloca(sizeof(state->in_buffer));
2586
2587 SECURITY_STATUS status;
2588
2589 SecBuffer buffers[4];
2590
2591 SecBufferDesc desc;
2592
2593 buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
2594 buffers[0].pvBuffer = write_buffer;
2595 buffers[0].cbBuffer = state->stream_sizes.cbHeader;
2596
2597 buffers[1].BufferType = SECBUFFER_DATA;
2598 buffers[1].pvBuffer = write_buffer + state->stream_sizes.cbHeader;
2599 buffers[1].cbBuffer = copy_amount;
2600
2601 buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
2602 buffers[2].pvBuffer =
2603 write_buffer + state->stream_sizes.cbHeader + copy_amount;
2604 buffers[2].cbBuffer = state->stream_sizes.cbTrailer;
2605
2606 buffers[3].BufferType = SECBUFFER_EMPTY;
2607 buffers[3].pvBuffer = NULL;
2608 buffers[3].cbBuffer = 0;
2609
2610 memcpy(buffers[1].pvBuffer, buf, copy_amount);
2611
2612 desc.ulVersion = SECBUFFER_VERSION;
2613 desc.pBuffers = buffers;
2614 desc.cBuffers = _countof(buffers);
2615
2616 status = EncryptMessage(&state->ctx_handle, 0, &desc, 0);
2617 if (status != SEC_E_OK) {
2618 bb_error_msg_and_die("schannel: EncryptMessage failed: (0x%08lx): %s", status,
2619 hresult_to_error_string(status));
2620 }
2621
2622 xwrite(state->ofd, write_buffer,
2623 buffers[0].cbBuffer + buffers[1].cbBuffer +
2624 buffers[2].cbBuffer);
2625
2626 len -= copy_amount;
2627 }
2628}
2629
2630static void tls_disconnect(tls_state_t * state) {
2631 SECURITY_STATUS status;
2632 DWORD token = SCHANNEL_SHUTDOWN;
2633 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2634
2635 SecBuffer buf_token;
2636
2637 SecBufferDesc buf_token_desc;
2638
2639 SecBuffer in_buffers[2];
2640 SecBuffer out_buffers[2];
2641
2642 SecBufferDesc in_desc;
2643 SecBufferDesc out_desc;
2644
2645 buf_token.BufferType = SECBUFFER_TOKEN;
2646 buf_token.pvBuffer = &token;
2647 buf_token.cbBuffer = sizeof(token);
2648
2649 buf_token_desc.ulVersion = SECBUFFER_VERSION;
2650 buf_token_desc.pBuffers = &buf_token;
2651 buf_token_desc.cBuffers = 1;
2652
2653 ApplyControlToken(&state->ctx_handle, &buf_token_desc);
2654
2655 // attempt to send any final data
2656
2657 in_buffers[0].BufferType = SECBUFFER_TOKEN;
2658 in_buffers[0].pvBuffer = state->in_buffer;
2659 in_buffers[0].cbBuffer = state->in_buffer_size;
2660
2661 in_buffers[1].BufferType = SECBUFFER_EMPTY;
2662 in_buffers[1].pvBuffer = NULL;
2663 in_buffers[1].cbBuffer = 0;
2664
2665 out_buffers[0].BufferType = SECBUFFER_TOKEN;
2666 out_buffers[0].pvBuffer = NULL;
2667 out_buffers[0].cbBuffer = 0;
2668
2669 out_buffers[1].BufferType = SECBUFFER_ALERT;
2670 out_buffers[1].pvBuffer = NULL;
2671 out_buffers[1].cbBuffer = 0;
2672
2673 in_desc.ulVersion = SECBUFFER_VERSION;
2674 in_desc.pBuffers = in_buffers;
2675 in_desc.cBuffers = _countof(in_buffers);
2676
2677 out_desc.ulVersion = SECBUFFER_VERSION;
2678 out_desc.pBuffers = out_buffers;
2679 out_desc.cBuffers = _countof(out_buffers);
2680
2681 status = InitializeSecurityContext(&state->cred_handle,
2682 state->
2683 initialized ? &state->ctx_handle :
2684 NULL,
2685 state->
2686 initialized ? NULL : state->hostname,
2687 flags, 0, 0,
2688 state->initialized ? &in_desc : NULL,
2689 0,
2690 state->
2691 initialized ? NULL :
2692 &state->ctx_handle, &out_desc, &flags,
2693 0);
2694
2695 if (status == SEC_E_OK) {
2696 // attempt to write any extra data
2697 write(state->ofd, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
2698 }
2699
2700 DeleteSecurityContext(&state->ctx_handle);
2701 FreeCredentialsHandle(&state->cred_handle);
2702 free(state->hostname);
2703}
2704
2705
2706void FAST_FUNC tls_handshake(tls_state_t * state, const char *hostname) {
2707 SECURITY_STATUS status;
2708 int received;
2709
2710#if ENABLE_FEATURE_TLS_SCHANNEL_1_3
2711 SCH_CREDENTIALS credential = {.dwVersion = SCH_CREDENTIALS_VERSION,
2712 .dwCredFormat = 0,
2713 .cCreds = 0,
2714 .paCred = NULL,
2715 .hRootStore = NULL,
2716 .cMappers = 0,
2717 .aphMappers = NULL,
2718 .dwSessionLifespan = 0,
2719 .dwFlags =
2720 SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
2721 SCH_USE_STRONG_CRYPTO,
2722 .cTlsParameters = 0,
2723 .pTlsParameters = NULL
2724 };
2725#else
2726 SCHANNEL_CRED credential = {.dwVersion = SCHANNEL_CRED_VERSION,
2727 .cCreds = 0,
2728 .paCred = NULL,
2729 .hRootStore = NULL,
2730 .cMappers = 0,
2731 .aphMappers = NULL,
2732 .cSupportedAlgs = 0,
2733 .palgSupportedAlgs = NULL,
2734 .grbitEnabledProtocols =
2735 SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT |
2736 SP_PROT_TLS1_2_CLIENT,
2737 .dwMinimumCipherStrength = 0,
2738 .dwMaximumCipherStrength = 0,
2739 .dwSessionLifespan = 0,
2740 .dwFlags =
2741 SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
2742 SCH_USE_STRONG_CRYPTO,
2743 .dwCredFormat = 0
2744 };
2745#endif
2746
2747 if ((status = AcquireCredentialsHandleA(NULL, (SEC_CHAR *) UNISP_NAME_A,
2748 SECPKG_CRED_OUTBOUND, NULL,
2749 &credential,
2750 NULL, NULL, &state->cred_handle,
2751 NULL)) != SEC_E_OK) {
2752 bb_error_msg_and_die("schannel: AcquireCredentialsHandleA failed: (0x%08lx): %s",
2753 status, hresult_to_error_string(status));
2754 }
2755
2756 state->in_buffer_size = 0;
2757 state->out_buffer_size = 0;
2758 state->out_buffer_used = 0;
2759
2760 state->out_buffer = NULL;
2761
2762 state->hostname = strdup(hostname);
2763
2764 state->initialized = 0;
2765 state->closed = 0;
2766
2767 while (1) {
2768 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2769
2770 SecBuffer in_buffers[2];
2771 SecBuffer out_buffers[2];
2772
2773 SecBufferDesc in_desc;
2774 SecBufferDesc out_desc;
2775
2776 in_buffers[0].BufferType = SECBUFFER_TOKEN;
2777 in_buffers[0].pvBuffer = state->in_buffer;
2778 in_buffers[0].cbBuffer = state->in_buffer_size;
2779
2780 in_buffers[1].BufferType = SECBUFFER_EMPTY;
2781 in_buffers[1].pvBuffer = NULL;
2782 in_buffers[1].cbBuffer = 0;
2783
2784 out_buffers[0].BufferType = SECBUFFER_TOKEN;
2785 out_buffers[0].pvBuffer = NULL;
2786 out_buffers[0].cbBuffer = 0;
2787
2788 out_buffers[1].BufferType = SECBUFFER_ALERT;
2789 out_buffers[1].pvBuffer = NULL;
2790 out_buffers[1].cbBuffer = 0;
2791
2792 in_desc.ulVersion = SECBUFFER_VERSION;
2793 in_desc.pBuffers = in_buffers;
2794 in_desc.cBuffers = _countof(in_buffers);
2795
2796 out_desc.ulVersion = SECBUFFER_VERSION;
2797 out_desc.pBuffers = out_buffers;
2798 out_desc.cBuffers = _countof(out_buffers);
2799
2800 status = InitializeSecurityContext(&state->cred_handle,
2801 state->
2802 initialized ? &state->ctx_handle :
2803 NULL,
2804 state->
2805 initialized ? NULL :
2806 state->hostname, flags, 0, 0,
2807 state->initialized ? &in_desc :
2808 NULL, 0,
2809 state->initialized ? NULL :
2810 &state->ctx_handle, &out_desc,
2811 &flags, 0);
2812
2813 state->initialized = 1;
2814
2815 if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
2816 memmove(state->in_buffer,
2817 state->in_buffer + (state->in_buffer_size -
2818 in_buffers[1].cbBuffer),
2819 in_buffers[1].cbBuffer);
2820 state->in_buffer_size = in_buffers[1].cbBuffer;
2821 } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
2822 state->in_buffer_size = 0;
2823 }
2824
2825 switch (status) {
2826 case SEC_E_OK:{
2827 if (out_buffers[0].cbBuffer > 0) {
2828 xwrite(state->ofd, out_buffers[0].pvBuffer,
2829 out_buffers[0].cbBuffer);
2830 FreeContextBuffer(out_buffers[0].pvBuffer);
2831 }
2832 goto Success;
2833 }
2834 case SEC_I_CONTINUE_NEEDED:{
2835 xwrite(state->ofd, out_buffers[0].pvBuffer,
2836 out_buffers[0].cbBuffer);
2837 FreeContextBuffer(out_buffers[0].pvBuffer);
2838 break;
2839 }
2840 case SEC_I_INCOMPLETE_CREDENTIALS:{
2841 // we don't support this
2842 bb_error_msg_and_die("schannel: client certificates not supported");
2843 }
2844 case SEC_E_INCOMPLETE_MESSAGE:{
2845 break;
2846 }
2847 default:{
2848 bb_error_msg_and_die("schannel: handshake failed: (0x%08lx): %s",
2849 status, hresult_to_error_string(status));
2850 }
2851 }
2852
2853 received =
2854 safe_read(state->ifd, state->in_buffer + state->in_buffer_size,
2855 sizeof(state->in_buffer) - state->in_buffer_size);
2856 if (received <= 0) {
2857 bb_error_msg_and_die("schannel: handshake read() failed");
2858 }
2859 state->in_buffer_size += received;
2860 }
2861
2862 Success:
2863 QueryContextAttributes(&state->ctx_handle, SECPKG_ATTR_STREAM_SIZES,
2864 &state->stream_sizes);
2865
2866 //SecPkgContext_ConnectionInfo info;
2867 //QueryContextAttributes(&state->ctx_handle, SECPKG_ATTR_CONNECTION_INFO,
2868 // &info);
2869 //
2870 //fprintf(stderr, "TLS 1.%d\n", (((uint32_t)(8 * sizeof(unsigned long long) - __builtin_clzll((info.dwProtocol)) - 1)) - 7)/2);
2871}
2872
2873void FAST_FUNC tls_run_copy_loop(tls_state_t * tls, unsigned flags) {
2874 char buffer[65536];
2875
2876 struct pollfd pfds[2];
2877
2878 pfds[0].fd = STDIN_FILENO;
2879 pfds[0].events = POLLIN;
2880 pfds[1].fd = tls->ifd;
2881 pfds[1].events = POLLIN;
2882
2883 for (;;) {
2884 int nread;
2885
2886 if (safe_poll(pfds, 2, -1) < 0)
2887 bb_simple_perror_msg_and_die("poll");
2888
2889 if (pfds[0].revents) {
2890 nread = safe_read(STDIN_FILENO, buffer, sizeof(buffer));
2891 if (nread < 1) {
2892 pfds[0].fd = -1;
2893 tls_disconnect(tls);
2894 if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF)
2895 break;
2896 } else {
2897 tls_write(tls, buffer, nread);
2898 }
2899 }
2900 if (pfds[1].revents) {
2901 nread = tls_read(tls, buffer, sizeof(buffer));
2902 if (nread < 1) {
2903 tls_disconnect(tls);
2904 break;
2905 }
2906 xwrite(STDOUT_FILENO, buffer, nread);
2907 }
2908 }
2909}
2910#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..3069856a4 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,145 @@ 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);
59 81
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); 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 if (buf[0] == 'S' || buf[0] == 'P') {
113#define SCAN(S, X) \
114 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
115 tp = skip_whitespace(buf + sizeof(S)-1); \
116 total->X += currec.X = fast_strtoul_10(&tp); \
117 continue; \
118 }
119 SCAN("Pss:" , smap_pss );
120 SCAN("Swap:" , smap_swap );
121 SCAN("Private_Dirty:", private_dirty);
122#undef SCAN
123 }
124 tp = strchr(buf, '-');
125 if (tp) {
126 // We reached next mapping - the line of this form:
127 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
128
129 // If we have a previous record, there's nothing more
130 // for it, print and clear currec
131 if (currec.smap_size)
132 print_smaprec(&currec);
133 free(currec.smap_name);
134 memset(&currec, 0, sizeof(currec));
135
136 *tp = ' ';
137 tp = buf;
138 currec.smap_start = fast_strtoull_16(&tp);
139 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
140 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
141
142 // skipping "rw-s FILEOFS M:m INODE "
143 tp = skip_fields(tp, 4);
144 tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
145 p = strchrnul(tp, '\n');
146 if (p != tp) {
147 currec.smap_name = xstrndup(tp, p - tp);
148 }
149 total->smap_size += currec.smap_size;
150 }
151 } // while (got line)
152 fclose(file);
153
154 if (currec.smap_size)
155 print_smaprec(&currec);
156 free(currec.smap_name);
157
158 return 0;
61} 159}
62 160
63static int procps_get_maps(pid_t pid, unsigned opt) 161static int procps_get_maps(pid_t pid, unsigned opt)
64{ 162{
65 struct smaprec total; 163 struct smaprec total;
66 int ret; 164 int ret;
67 char buf[256]; 165 char buf[PMAP_BUFSZ];
166
167 ret = read_cmdline(buf, sizeof(buf), pid, NULL);
168 if (ret < 0)
169 return ret;
68 170
69 read_cmdline(buf, sizeof(buf), pid, NULL);
70 printf("%u: %s\n", (int)pid, buf); 171 printf("%u: %s\n", (int)pid, buf);
71 172
72 if (!(opt & OPT_q) && (opt & OPT_x)) 173 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); 174 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74 175
75 memset(&total, 0, sizeof(total)); 176 memset(&total, 0, sizeof(total));
76 177 ret = read_smaps(pid, buf, &total);
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
78 if (ret) 178 if (ret)
79 return ret; 179 return ret;
80 180
81 if (!(opt & OPT_q)) { 181 if (!(opt & OPT_q)) {
82 if (opt & OPT_x) 182 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n" 183 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n", 184 "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); 185 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else 186 else
87 printf("mapped: %luK\n", total.smap_size); 187 printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
88 } 188 }
89 189
90 return 0; 190 return 0;
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..96b3e2d4e 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 not_first_line;
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,38 @@ 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.not_first_line) {
424 G.not_first_line = 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.not_first_line) {
436// printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf);
437// G.not_first_line = 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 G.not_first_line = 0; /* next print will be "first line" (will clear the screen) */
447}
448
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 449#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411/* formats 7 char string (8 with terminating NUL) */ 450/* formats 7 char string (8 with terminating NUL) */
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) 451static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
@@ -433,7 +472,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
433#endif 472#endif
434 473
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 474#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) 475static void display_cpus(void)
437{ 476{
438 /* 477 /*
439 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% 478 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
@@ -469,8 +508,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
469# else 508# else
470 /* Loop thru CPU(s) */ 509 /* Loop thru CPU(s) */
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1; 510 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p) 511 if (n_cpu_lines > G.lines_remaining)
473 n_cpu_lines = *lines_rem_p; 512 n_cpu_lines = G.lines_remaining;
474 513
475 for (i = 0; i < n_cpu_lines; i++) { 514 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i]; 515 p_jif = &cpu_jif[i];
@@ -488,7 +527,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
488 CALC_STAT(softirq); 527 CALC_STAT(softirq);
489 /*CALC_STAT(steal);*/ 528 /*CALC_STAT(steal);*/
490 529
491 snprintf(scrbuf, scr_width, 530 sprintf(G.line_buf,
492 /* Barely fits in 79 chars when in "decimals" mode. */ 531 /* Barely fits in 79 chars when in "decimals" mode. */
493# if ENABLE_FEATURE_TOP_SMP_CPU 532# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", 533 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
@@ -501,16 +540,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
501 /*, SHOW_STAT(steal) - what is this 'steal' thing? */ 540 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
502 /* I doubt anyone wants to know it */ 541 /* I doubt anyone wants to know it */
503 ); 542 );
504 puts(scrbuf); 543 print_line_buf();
505 } 544 }
506 } 545 }
507# undef SHOW_STAT 546# undef SHOW_STAT
508# undef CALC_STAT 547# undef CALC_STAT
509# undef FMT 548# undef FMT
510 *lines_rem_p -= i;
511} 549}
512#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ 550#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) 551# define display_cpus() ((void)0)
514#endif 552#endif
515 553
516enum { 554enum {
@@ -564,52 +602,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX])
564 fclose(f); 602 fclose(f);
565} 603}
566 604
567static unsigned long display_header(int scr_width, int *lines_rem_p) 605static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm)
606{
607 int width = G.scr_width - offset;
608 if (width > 1) /* wider than to fit just the NUL? */
609 read_cmdline(G.line_buf + offset, width, pid, comm);
610//TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e
611 print_line_buf();
612}
613
614static unsigned long display_header(void)
568{ 615{
569 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
570 char *buf; 616 char *buf;
571 unsigned long meminfo[MI_MAX]; 617 unsigned long meminfo[MI_MAX];
572 618
573 parse_meminfo(meminfo); 619 parse_meminfo(meminfo);
574 620
575 /* Output memory info */ 621 /* Output memory info */
576 if (scr_width > (int)sizeof(scrbuf)) 622 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", 623 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], 624 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE], 625 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], 626 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS], 627 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]); 628 meminfo[MI_CACHED]);
585 /* Go to top & clear to the end of screen */ 629 print_line_buf();
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588 630
589 /* Display CPU time split as percentage of total time. 631 /* Display CPU time split as percentage of total time.
590 * This displays either a cumulative line or one line per CPU. 632 * This displays either a cumulative line or one line per CPU.
591 */ 633 */
592 display_cpus(scr_width, scrbuf, lines_rem_p); 634 display_cpus();
593 635
594 /* Read load average as a string */ 636 /* Read load average as a string */
595 buf = stpcpy(scrbuf, "Load average: "); 637 buf = stpcpy(G.line_buf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); 638 open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0'; 639 G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */
598 strchrnul(buf, '\n')[0] = '\0'; 640 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf); 641 print_line_buf();
600 (*lines_rem_p)--;
601 642
602 return meminfo[MI_MEMTOTAL]; 643 return meminfo[MI_MEMTOTAL];
603} 644}
604 645
605static NOINLINE void display_process_list(int lines_rem, int scr_width) 646static NOINLINE void display_process_list(void)
606{ 647{
607 enum { 648 enum {
608 BITS_PER_INT = sizeof(int) * 8 649 BITS_PER_INT = sizeof(int) * 8
609 }; 650 };
610 651
611 top_status_t *s; 652 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ 653 unsigned long total_memory = display_header();
613 /* xxx_shift and xxx_scale variables allow us to replace 654 /* xxx_shift and xxx_scale variables allow us to replace
614 * expensive divides with multiply and shift */ 655 * expensive divides with multiply and shift */
615 unsigned pmem_shift, pmem_scale, pmem_half; 656 unsigned pmem_shift, pmem_scale, pmem_half;
@@ -621,7 +662,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
621 662
622#if ENABLE_FEATURE_TOP_DECIMALS 663#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000 664# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t; 665 typedef struct { unsigned quot, rem; } bb_div_t;
625/* Used to have "div_t name = div((val), 10)" here 666/* Used to have "div_t name = div((val), 10)" here
626 * (IOW: intended to use libc-compatible way to divide and use 667 * (IOW: intended to use libc-compatible way to divide and use
627 * both result and remainder, but musl does not inline div()...) 668 * both result and remainder, but musl does not inline div()...)
@@ -629,28 +670,34 @@ typedef struct { unsigned quot, rem; } bb_div_t;
629 */ 670 */
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } 671# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem 672# define SHOW_STAT(name) name.quot, '0'+name.rem
673# define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0')
632# define FMT "%3u.%c" 674# define FMT "%3u.%c"
633#else 675#else
634# define UPSCALE 100 676# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val) 677# define CALC_STAT(name, val) unsigned name = (val)
678# define SANITIZE(name) if (name > 99) name = 99
636# define SHOW_STAT(name) name 679# define SHOW_STAT(name) name
637# define FMT "%4u%%" 680# define FMT "%4u%%"
638#endif 681#endif
639 682
640 /* what info of the processes is shown */ 683 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") 684 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") 685 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND"); 686 " COMMAND");
646 lines_rem--; 687 print_line_bold();
647 688
648 /* 689 /* %RSS = s->memsize / MemTotal * 100%
649 * %VSZ = s->vsz/MemTotal 690 * Calculate this with multiply and shift. Example:
691 * shift = 12
692 * scale = 100 * 0x1000 / total_memory
693 * percent_mem = (size_mem * scale) >> shift
694 * ~= (size_mem >> shift) * scale
695 * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory
696 * ~= size_mem * 100 / total_memory
650 */ 697 */
651 pmem_shift = BITS_PER_INT-11; 698 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; 699 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 */ 700 /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */
654 while (pmem_scale >= 512) { 701 while (pmem_scale >= 512) {
655 pmem_scale /= 4; 702 pmem_scale /= 4;
656 pmem_shift -= 2; 703 pmem_shift -= 2;
@@ -689,25 +736,29 @@ typedef struct { unsigned quot, rem; } bb_div_t;
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); 736 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 737 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
691#endif 738#endif
739 if (G.lines_remaining > ntop - G_scroll_ofs)
740 G.lines_remaining = ntop - G_scroll_ofs;
692 741
693 /* Ok, all preliminary data is ready, go through the list */ 742 /* 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; 743 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) { 744 while (G.lines_remaining > 0) {
699 int n; 745 int n;
700 char *ppu; 746 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12]; 747 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8]; 748 char memsize_str_buf[8];
703 unsigned col; 749 unsigned col;
704 750
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 751 CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 752#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); 753 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif 754#endif
755 /* VSZ can be much larger than total memory
756 * (seen values close to 2Tbyte), don't try to display
757 * "uses 12345.6% of MemTotal" (won't fit the column)
758 */
759 SANITIZE(pmem);
709 760
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); 761 smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy");
711 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ 762 /* 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)); 763 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf; 764 ppu = ppubuf;
@@ -736,27 +787,23 @@ typedef struct { unsigned quot, rem; } bb_div_t;
736 ppu[6+6+8] = '\0'; /* truncate USER */ 787 ppu[6+6+8] = '\0'; /* truncate USER */
737 } 788 }
738 shortened: 789 shortened:
739 col = snprintf(line_buf, scr_width, 790 col = sprintf(G.line_buf,
740 "\n" "%s %s %.5s" FMT 791 "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d") 792 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) 793 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ", 794 " ",
744 ppu, 795 ppu,
745 s->state, vsz_str_buf, 796 s->state, memsize_str_buf,
746 SHOW_STAT(pmem) 797 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) 798 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) 799 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 ); 800 );
750 if ((int)(scr_width - col) > 1) 801 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, 802 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
754 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ 803 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
755 s++; 804 s++;
756 } 805 }
757 /* printf(" %d", hist_iterations); */ 806 /* printf(" %d", hist_iterations); */
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760} 807}
761#undef UPSCALE 808#undef UPSCALE
762#undef SHOW_STAT 809#undef SHOW_STAT
@@ -828,36 +875,34 @@ static int topmem_sort(char *a, char *b)
828} 875}
829 876
830/* display header info (meminfo / loadavg) */ 877/* display header info (meminfo / loadavg) */
831static void display_topmem_header(int scr_width, int *lines_rem_p) 878static void display_topmem_header(void)
832{ 879{
833 unsigned long meminfo[MI_MAX]; 880 unsigned long meminfo[MI_MAX];
834 881
835 parse_meminfo(meminfo); 882 parse_meminfo(meminfo);
836 883
837 snprintf(line_buf, LINE_BUF_SIZE, 884 sprintf(G.line_buf,
838 "Mem total:%lu anon:%lu map:%lu free:%lu", 885 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL], 886 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES], 887 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED], 888 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]); 889 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); 890 print_line_buf();
844 891
845 snprintf(line_buf, LINE_BUF_SIZE, 892 sprintf(G.line_buf,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", 893 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB], 894 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS], 895 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED], 896 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY], 897 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]); 898 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf); 899 print_line_buf();
853 900
854 snprintf(line_buf, LINE_BUF_SIZE, 901 sprintf(G.line_buf,
855 "Swap total:%lu free:%lu", // TODO: % used? 902 "Swap total:%lu free:%lu", // TODO: % used?
856 meminfo[MI_SWAPTOTAL], 903 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]); 904 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf); 905 print_line_buf();
859
860 (*lines_rem_p) -= 3;
861} 906}
862 907
863/* see http://en.wikipedia.org/wiki/Tera */ 908/* see http://en.wikipedia.org/wiki/Tera */
@@ -870,75 +915,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5])
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; 915 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871} 916}
872 917
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) 918static NOINLINE void display_topmem_process_list(void)
874{ 919{
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; 920 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch; 921 char *cp, ch;
879 922
880 display_topmem_header(scr_width, &lines_rem); 923 display_topmem_header();
881 924
882 strcpy(line_buf, HDR_STR " COMMAND"); 925 strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND");
883 /* Mark the ^FIELD^ we sort by */ 926 /* Mark the ^FIELD^ we sort by */
884 cp = &line_buf[5 + sort_field * 6]; 927 cp = &G.line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted]; 928 ch = "^_"[inverted];
886 cp[6] = ch; 929 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' '); 930 do *cp++ = ch; while (*cp == ' ');
931 print_line_bold();
888 932
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); 933 if (G.lines_remaining > ntop - G_scroll_ofs)
890 lines_rem--; 934 G.lines_remaining = ntop - G_scroll_ofs;
891 935 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 */ 936 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
896 int n = sprintf(line_buf, "%5u ", s->pid); 937 int n = sprintf(G.line_buf, "%5u ", s->pid);
897 if (n > 7) { 938 if (n > 7) {
898 /* PID is 7 chars long (up to 4194304) */ 939 /* PID is 7 chars long (up to 4194304) */
899 ulltoa4_and_space(s->vsz , &line_buf[8]); 940 ulltoa4_and_space(s->vsz , &G.line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]); 941 ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]);
901 /* the next field (RSS) starts at 8+10 = 3*6 */ 942 /* the next field (RSS) starts at 8+10 = 3*6 */
902 } else { 943 } else {
903 if (n == 7) /* PID is 6 chars long */ 944 if (n == 7) /* PID is 6 chars long */
904 ulltoa4_and_space(s->vsz, &line_buf[7]); 945 ulltoa4_and_space(s->vsz, &G.line_buf[7]);
905 /* the next field (VSZRW) starts at 7+5 = 2*6 */ 946 /* the next field (VSZRW) starts at 7+5 = 2*6 */
906 else /* PID is 5 chars or less */ 947 else /* PID is 5 chars or less */
907 ulltoa5_and_space(s->vsz, &line_buf[6]); 948 ulltoa5_and_space(s->vsz, &G.line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]); 949 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 } 950 }
919 printf("\n""%.*s", scr_width, line_buf); 951 ulltoa5_and_space(s->rss , &G.line_buf[3*6]);
952 ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]);
953 ulltoa5_and_space(s->dirty , &G.line_buf[5*6]);
954 ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]);
955 ulltoa5_and_space(s->stack , &G.line_buf[7*6]);
956 G.line_buf[8*6] = '\0';
957 cmdline_to_line_buf_and_print(8*6, s->pid, s->comm);
920 s++; 958 s++;
921 } 959 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926} 960}
927 961
928#else 962#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 963
937enum { 964enum {
938 TOP_MASK = 0 965 TOP_MASK = 0
939 | PSSCAN_PID 966 | PSSCAN_PID
940 | PSSCAN_PPID 967 | PSSCAN_PPID
941 | PSSCAN_VSZ 968 | PSSCAN_RSS
942 | PSSCAN_STIME 969 | PSSCAN_STIME
943 | PSSCAN_UTIME 970 | PSSCAN_UTIME
944 | PSSCAN_STATE 971 | PSSCAN_STATE
@@ -950,7 +977,7 @@ enum {
950 | PSSCAN_SMAPS 977 | PSSCAN_SMAPS
951 | PSSCAN_COMM, 978 | PSSCAN_COMM,
952 EXIT_MASK = 0, 979 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1, 980 ONLY_REDRAW = (unsigned)-1,
954}; 981};
955 982
956#if ENABLE_FEATURE_TOP_INTERACTIVE 983#if ENABLE_FEATURE_TOP_INTERACTIVE
@@ -963,15 +990,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
963 } 990 }
964 991
965 while (1) { 992 while (1) {
966 int32_t c; 993 int32_t c, cc;
967 994
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); 995 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) { 996 if (c == -1) {
970 /* error/EOF */ 997 if (errno != EAGAIN)
971 option_mask32 |= OPT_EOF; 998 /* error/EOF */
999 option_mask32 |= OPT_EOF;
1000 /* else: timeout - rescan and refresh */
972 break; 1001 break;
973 } 1002 }
974 interval = 0; 1003 interval = 0;
1004 /* "continue" statements below return to do one additional
1005 * quick attempt to read a key. This prevents
1006 * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn"
1007 * to cause lots of rescans.
1008 */
975 1009
976 if (c == initial_settings.c_cc[VINTR]) 1010 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK; 1011 return EXIT_MASK;
@@ -1005,9 +1039,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1005 G_scroll_ofs = ntop - 1; 1039 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0) 1040 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0; 1041 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK; 1042 return ONLY_REDRAW;
1009 } 1043 }
1010 1044
1045 cc = c;
1011 c |= 0x20; /* lowercase */ 1046 c |= 0x20; /* lowercase */
1012 if (c == 'q') 1047 if (c == 'q')
1013 return EXIT_MASK; 1048 return EXIT_MASK;
@@ -1055,9 +1090,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1055 continue; 1090 continue;
1056 } 1091 }
1057# if ENABLE_FEATURE_TOPMEM 1092# if ENABLE_FEATURE_TOPMEM
1093 if (cc == 'S') {
1094 if (--sort_field < 0)
1095 sort_field = NUM_SORT_FIELD - 1;
1096 if (--sort_field < 0)
1097 sort_field = NUM_SORT_FIELD - 1;
1098 }
1058 if (c == 's') { 1099 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD; 1100 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1101 if (scan_mask == TOPMEM_MASK)
1102 return ONLY_REDRAW;
1103 scan_mask = TOPMEM_MASK;
1061 free(prev_hist); 1104 free(prev_hist);
1062 prev_hist = NULL; 1105 prev_hist = NULL;
1063 prev_hist_count = 0; 1106 prev_hist_count = 0;
@@ -1066,7 +1109,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1066# endif 1109# endif
1067 if (c == 'r') { 1110 if (c == 'r') {
1068 inverted ^= 1; 1111 inverted ^= 1;
1069 continue; 1112 return ONLY_REDRAW;
1070 } 1113 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU 1114# if ENABLE_FEATURE_TOP_SMP_CPU
1072 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ 1115 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
@@ -1088,8 +1131,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1088 } 1131 }
1089# endif 1132# endif
1090# endif 1133# endif
1091 break; /* unknown key -> force refresh */ 1134 /* Unknown key. Eat remaining buffered input (if any) */
1092 } 1135 } /* while (1) */
1093 1136
1094 return scan_mask; 1137 return scan_mask;
1095} 1138}
@@ -1155,7 +1198,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1155{ 1198{
1156 duration_t interval; 1199 duration_t interval;
1157 int iterations; 1200 int iterations;
1158 unsigned col; 1201 unsigned opt;
1159 char *str_interval, *str_iterations; 1202 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK; 1203 unsigned scan_mask = TOP_MASK;
1161 1204
@@ -1172,13 +1215,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1172 1215
1173 /* all args are options; -n NUM */ 1216 /* all args are options; -n NUM */
1174 make_all_argv_opts(argv); /* options can be specified w/o dash */ 1217 make_all_argv_opts(argv); /* options can be specified w/o dash */
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); 1218 opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176 /* NB: -m and -H are accepted even if not configured */ 1219 /* NB: -m and -H are accepted even if not configured */
1177#if ENABLE_FEATURE_TOPMEM 1220#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m) /* -m (busybox specific) */ 1221 if (opt & OPT_m) /* -m (busybox specific) */
1179 scan_mask = TOPMEM_MASK; 1222 scan_mask = TOPMEM_MASK;
1180#endif 1223#endif
1181 if (col & OPT_d) { 1224 if (opt & OPT_d) {
1182 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1225 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1183 if (str_interval[0] == '-') 1226 if (str_interval[0] == '-')
1184 str_interval++; 1227 str_interval++;
@@ -1187,18 +1230,17 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1187 if (interval > INT_MAX / 1000) 1230 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000; 1231 interval = INT_MAX / 1000;
1189 } 1232 }
1190 if (col & OPT_n) { 1233 if (opt & OPT_n) {
1191 if (str_iterations[0] == '-') 1234 if (str_iterations[0] == '-')
1192 str_iterations++; 1235 str_iterations++;
1193 iterations = xatou(str_iterations); 1236 iterations = xatou(str_iterations);
1194 } 1237 }
1195#if ENABLE_FEATURE_SHOW_THREADS 1238#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) { 1239 if (opt & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS; 1240 scan_mask |= PSSCAN_TASKS;
1198 } 1241 }
1199#endif 1242#endif
1200 1243
1201 /* change to /proc */
1202 xchdir("/proc"); 1244 xchdir("/proc");
1203 1245
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1246#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -1226,23 +1268,22 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1226#endif 1268#endif
1227 1269
1228 while (scan_mask != EXIT_MASK) { 1270 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) 1271 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;)
1230 procps_status_t *p = NULL; 1272 procps_status_t *p = NULL;
1231 1273
1232 if (OPT_BATCH_MODE) { 1274 G.lines = INT_MAX;
1233 G.lines = INT_MAX; 1275 G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1234 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ 1276 if (!OPT_BATCH_MODE) {
1235 } else {
1236 G.lines = 24; /* default */ 1277 G.lines = 24; /* default */
1237 col = 79; 1278 G.scr_width = 80;
1238 /* We output to stdout, we need size of stdout (not stdin)! */ 1279 /* We output to stdout, we need size of stdout (not stdin)! */
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1280 get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines);
1240 if (G.lines < 5 || col < 10) { 1281 if (G.lines < 5 || G.scr_width < 10) {
1241 sleep_for_duration(interval); 1282 sleep_for_duration(interval);
1242 continue; 1283 continue;
1243 } 1284 }
1244 if (col > LINE_BUF_SIZE - 2) 1285 if (G.scr_width > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2; 1286 G.scr_width = LINE_BUF_SIZE - 2;
1246 } 1287 }
1247 1288
1248 /* read process IDs & status for all the processes */ 1289 /* read process IDs & status for all the processes */
@@ -1255,7 +1296,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1255 top = xrealloc_vector(top, 6, ntop++); 1296 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid; 1297 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid; 1298 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz; 1299 top[n].memsize = p->rss;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1300#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime; 1301 top[n].ticks = p->stime + p->utime;
1261#endif 1302#endif
@@ -1268,20 +1309,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1268 } 1309 }
1269#if ENABLE_FEATURE_TOPMEM 1310#if ENABLE_FEATURE_TOPMEM
1270 else { /* TOPMEM */ 1311 else { /* TOPMEM */
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) 1312 if (!(p->mapped_ro | p->mapped_rw))
1272 continue; /* kernel threads are ignored */ 1313 continue; /* kernel threads are ignored */
1273 n = ntop; 1314 n = ntop;
1274 /* No bug here - top and topmem are the same */ 1315 /* No bug here - top and topmem are the same */
1275 top = xrealloc_vector(topmem, 6, ntop++); 1316 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm); 1317 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid; 1318 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; 1319 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw; 1320 topmem[n].vszrw = p->mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; 1321 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; 1322 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; 1323 topmem[n].dirty = p->private_dirty + p->shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty; 1324 topmem[n].dirty_sh = p->shared_dirty;
1284 topmem[n].stack = p->smaps.stack; 1325 topmem[n].stack = p->stack;
1285 } 1326 }
1286#endif 1327#endif
1287 } /* end of "while we read /proc" */ 1328 } /* end of "while we read /proc" */
@@ -1310,30 +1351,40 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1351 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 } 1352 }
1312#endif 1353#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:) 1354 IF_FEATURE_TOP_INTERACTIVE(redraw:)
1355 G.lines_remaining = G.lines;
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { 1356 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col); 1357 display_process_list();
1316 } 1358 }
1317#if ENABLE_FEATURE_TOPMEM 1359#if ENABLE_FEATURE_TOPMEM
1318 else { /* TOPMEM */ 1360 else { /* TOPMEM */
1319 display_topmem_process_list(G.lines, col); 1361 display_topmem_process_list();
1320 } 1362 }
1321#endif 1363#endif
1364 print_end();
1365 fflush_all();
1322 if (iterations >= 0 && !--iterations) 1366 if (iterations >= 0 && !--iterations)
1323 break; 1367 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE 1368#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems(); 1369 clearmems();
1326 sleep_for_duration(interval); 1370 sleep_for_duration(interval);
1327#else 1371#else
1328 new_mask = handle_input(scan_mask, interval); 1372 new_mask = handle_input(scan_mask,
1329 if (new_mask == NO_RESCAN_MASK) 1373 /* After "redraw with no rescan", have one
1330 goto display; 1374 * key timeout shorter that normal
1375 * (IOW: rescan sooner):
1376 */
1377 (new_mask == ONLY_REDRAW ? 1 : interval)
1378 );
1379 if (new_mask == ONLY_REDRAW)
1380 goto redraw;
1331 scan_mask = new_mask; 1381 scan_mask = new_mask;
1332 clearmems(); 1382 clearmems();
1333#endif 1383#endif
1334 } /* end of "while (not Q)" */ 1384 } /* end of "while (not Q)" */
1335 1385
1336 bb_putchar('\n'); 1386 if (!OPT_BATCH_MODE)
1387 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE 1388#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term(); 1389 reset_term();
1339#endif 1390#endif
diff --git a/runit/chpst.c b/runit/chpst.c
index 2be1a5775..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)"
@@ -466,7 +467,8 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
466 /* nice should be done before xsetuid */ 467 /* nice should be done before xsetuid */
467 if (opt & OPT_n) { 468 if (opt & OPT_n) {
468 errno = 0; 469 errno = 0;
469 if (nice(xatoi(nicestr)) == -1) 470 nice(xatoi(nicestr));
471 if (errno)
470 bb_simple_perror_msg_and_die("nice"); 472 bb_simple_perror_msg_and_die("nice");
471 } 473 }
472 474
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..605215e41 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -461,7 +461,7 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
461 461
462/* ============ Shell options */ 462/* ============ Shell options */
463 463
464/* If you add/change options hare, update --help text too */ 464/* If you add/change options here, update --help text too */
465static const char *const optletters_optnames[] ALIGN_PTR = { 465static const char *const optletters_optnames[] ALIGN_PTR = {
466 "e" "errexit", 466 "e" "errexit",
467 "f" "noglob", 467 "f" "noglob",
@@ -929,6 +929,7 @@ raise_interrupt(void)
929 raise(SIGINT); 929 raise(SIGINT);
930#else 930#else
931 fflush_all(); 931 fflush_all();
932 kill(-getpid(), SIGINT);
932 _exit(SIGINT << 24); 933 _exit(SIGINT << 24);
933#endif 934#endif
934 } 935 }
@@ -1821,7 +1822,6 @@ struct stackmark {
1821 size_t stacknleft; 1822 size_t stacknleft;
1822}; 1823};
1823 1824
1824
1825struct globals_memstack { 1825struct globals_memstack {
1826 struct stack_block *g_stackp; // = &stackbase; 1826 struct stack_block *g_stackp; // = &stackbase;
1827 char *g_stacknxt; // = stackbase.space; 1827 char *g_stacknxt; // = stackbase.space;
@@ -1844,7 +1844,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1844 sstrend = stackbase.space + MINSIZE; \ 1844 sstrend = stackbase.space + MINSIZE; \
1845} while (0) 1845} while (0)
1846 1846
1847
1848#define stackblock() ((void *)g_stacknxt) 1847#define stackblock() ((void *)g_stacknxt)
1849#define stackblocksize() g_stacknleft 1848#define stackblocksize() g_stacknleft
1850 1849
@@ -2361,7 +2360,6 @@ struct localvar {
2361# define VIMPORT 0x400 /* variable was imported from environment */ 2360# define VIMPORT 0x400 /* variable was imported from environment */
2362#endif 2361#endif
2363 2362
2364
2365/* Need to be before varinit_data[] */ 2363/* Need to be before varinit_data[] */
2366#if ENABLE_LOCALE_SUPPORT 2364#if ENABLE_LOCALE_SUPPORT
2367static void FAST_FUNC 2365static void FAST_FUNC
@@ -3581,7 +3579,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3581 3579
3582/* ============ ... */ 3580/* ============ ... */
3583 3581
3584
3585#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3582#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3586 3583
3587/* Syntax classes */ 3584/* Syntax classes */
@@ -3988,13 +3985,11 @@ struct alias {
3988 int flag; 3985 int flag;
3989}; 3986};
3990 3987
3991
3992static struct alias **atab; // [ATABSIZE]; 3988static struct alias **atab; // [ATABSIZE];
3993#define INIT_G_alias() do { \ 3989#define INIT_G_alias() do { \
3994 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3990 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3995} while (0) 3991} while (0)
3996 3992
3997
3998static struct alias ** 3993static struct alias **
3999__lookupalias(const char *name) 3994__lookupalias(const char *name)
4000{ 3995{
@@ -4176,7 +4171,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
4176 4171
4177#endif /* ASH_ALIAS */ 4172#endif /* ASH_ALIAS */
4178 4173
4179
4180/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4174/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
4181#define FORK_FG 0 4175#define FORK_FG 0
4182#define FORK_BG 1 4176#define FORK_BG 1
@@ -4274,7 +4268,7 @@ signal_handler(int signo)
4274 return; 4268 return;
4275 } 4269 }
4276#if ENABLE_FEATURE_EDITING 4270#if ENABLE_FEATURE_EDITING
4277 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 4271 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
4278#endif 4272#endif
4279 gotsig[signo - 1] = 1; 4273 gotsig[signo - 1] = 1;
4280 pending_sig = signo; 4274 pending_sig = signo;
@@ -6193,7 +6187,6 @@ stoppedjobs(void)
6193} 6187}
6194#endif 6188#endif
6195 6189
6196
6197/* 6190/*
6198 * Code for dealing with input/output redirection. 6191 * Code for dealing with input/output redirection.
6199 */ 6192 */
@@ -9903,7 +9896,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9903} 9896}
9904#endif 9897#endif
9905 9898
9906
9907/*static int funcblocksize; // size of structures in function */ 9899/*static int funcblocksize; // size of structures in function */
9908/*static int funcstringsize; // size of strings in node */ 9900/*static int funcstringsize; // size of strings in node */
9909static void *funcblock; /* block to allocate function from */ 9901static void *funcblock; /* block to allocate function from */
@@ -11919,7 +11911,6 @@ goodname(const char *p)
11919 return endofname(p)[0] == '\0'; 11911 return endofname(p)[0] == '\0';
11920} 11912}
11921 11913
11922
11923/* 11914/*
11924 * Search for a command. This is called before we fork so that the 11915 * 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 11916 * location of the command will be available in the parent as well as
@@ -14772,7 +14763,6 @@ parseheredoc(void)
14772 } 14763 }
14773} 14764}
14774 14765
14775
14776static const char * 14766static const char *
14777expandstr(const char *ps, int syntax_type) 14767expandstr(const char *ps, int syntax_type)
14778{ 14768{
@@ -15402,7 +15392,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15402 entry->u = cmdp->param; 15392 entry->u = cmdp->param;
15403} 15393}
15404 15394
15405
15406/* 15395/*
15407 * The trap builtin. 15396 * The trap builtin.
15408 */ 15397 */
@@ -15812,6 +15801,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15812 r = shell_builtin_read(&params); 15801 r = shell_builtin_read(&params);
15813 INT_ON; 15802 INT_ON;
15814 15803
15804#if !ENABLE_PLATFORM_MINGW32
15815 if ((uintptr_t)r == 1 && errno == EINTR) { 15805 if ((uintptr_t)r == 1 && errno == EINTR) {
15816 /* To get SIGCHLD: sleep 1 & read x; echo $x 15806 /* To get SIGCHLD: sleep 1 & read x; echo $x
15817 * Correct behavior is to not exit "read" 15807 * Correct behavior is to not exit "read"
@@ -15820,8 +15810,15 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15820 goto again; 15810 goto again;
15821 } 15811 }
15822 15812
15823#if ENABLE_PLATFORM_MINGW32 15813 if ((uintptr_t)r == 2) /* -t SEC timeout? */
15814 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
15815 /* The actual value observed with bash 5.2.15: */
15816 return 128 + SIGALRM;
15817#else /* ENABLE_PLATFORM_MINGW32 */
15824 if ((uintptr_t)r == 2) { 15818 if ((uintptr_t)r == 2) {
15819 /* Timeout, return 128 + SIGALRM */
15820 return 142;
15821 } else if ((uintptr_t)r == 3) {
15825 /* ^C pressed, propagate event */ 15822 /* ^C pressed, propagate event */
15826 if (trap[SIGINT]) { 15823 if (trap[SIGINT]) {
15827 write(STDOUT_FILENO, "^C", 2); 15824 write(STDOUT_FILENO, "^C", 2);
@@ -15969,8 +15966,25 @@ exitshell(void)
15969 char *p; 15966 char *p;
15970 15967
15971#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 15968#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
15972 save_history(line_input_state); /* may be NULL */ 15969 if (line_input_state) {
15970 const char *hp;
15971# if ENABLE_FEATURE_SH_HISTFILESIZE
15972// in bash:
15973// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
15974// "When this variable is assigned a value, the history file is truncated, if necessary"
15975// but we do it only at exit, not on assignment:
15976 /* Use HISTFILESIZE to limit file size */
15977 hp = lookupvar("HISTFILESIZE");
15978 if (hp)
15979 line_input_state->max_history = size_from_HISTFILESIZE(hp);
15980# endif
15981 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
15982 hp = lookupvar("HISTFILE");
15983 line_input_state->hist_file = hp;
15984 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
15985 }
15973#endif 15986#endif
15987
15974 savestatus = exitstatus; 15988 savestatus = exitstatus;
15975 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 15989 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
15976 if (setjmp(loc.loc)) 15990 if (setjmp(loc.loc))
@@ -16144,7 +16158,6 @@ init(void)
16144 } 16158 }
16145} 16159}
16146 16160
16147
16148//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
16149//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16162//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
16150//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
@@ -16474,7 +16487,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16474 if (hp) 16487 if (hp)
16475 line_input_state->hist_file = xstrdup(hp); 16488 line_input_state->hist_file = xstrdup(hp);
16476# if ENABLE_FEATURE_SH_HISTFILESIZE 16489# if ENABLE_FEATURE_SH_HISTFILESIZE
16477 hp = lookupvar("HISTFILESIZE"); 16490 hp = lookupvar("HISTSIZE");
16491 /* Using HISTFILESIZE above to limit max_history would be WRONG:
16492 * users may set HISTFILESIZE=0 in their profile scripts
16493 * to prevent _saving_ of history files, but still want to have
16494 * non-zero history limit for in-memory list.
16495 */
16478 line_input_state->max_history = size_from_HISTFILESIZE(hp); 16496 line_input_state->max_history = size_from_HISTFILESIZE(hp);
16479# endif 16497# endif
16480 } 16498 }
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/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 04dda0734..09ab6ebc0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -392,6 +392,8 @@
392 392
393/* Build knobs */ 393/* Build knobs */
394#define LEAK_HUNTING 0 394#define LEAK_HUNTING 0
395#define LEAK_PRINTF(...) fdprintf(__VA_ARGS__)
396//#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0)
395#define BUILD_AS_NOMMU 0 397#define BUILD_AS_NOMMU 0
396/* Enable/disable sanity checks. Ok to enable in production, 398/* 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. 399 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
@@ -930,6 +932,12 @@ struct globals {
930# define G_flag_return_in_progress 0 932# define G_flag_return_in_progress 0
931#endif 933#endif
932 smallint exiting; /* used to prevent EXIT trap recursion */ 934 smallint exiting; /* used to prevent EXIT trap recursion */
935#if !BB_MMU
936 smallint reexeced_on_NOMMU;
937# define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU)
938#else
939# define G_reexeced_on_NOMMU 0
940#endif
933 /* These support $? */ 941 /* These support $? */
934 smalluint last_exitcode; 942 smalluint last_exitcode;
935 smalluint expand_exitcode; 943 smalluint expand_exitcode;
@@ -1352,30 +1360,67 @@ static void debug_print_strings(const char *prefix, char **vv)
1352static void *xxmalloc(int lineno, size_t size) 1360static void *xxmalloc(int lineno, size_t size)
1353{ 1361{
1354 void *ptr = xmalloc((size + 0xff) & ~0xff); 1362 void *ptr = xmalloc((size + 0xff) & ~0xff);
1355 fdprintf(2, "line %d: malloc %p\n", lineno, ptr); 1363 LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr);
1364 return ptr;
1365}
1366static void *xxzalloc(int lineno, size_t size)
1367{
1368 void *ptr = xzalloc((size + 0xff) & ~0xff);
1369 LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr);
1356 return ptr; 1370 return ptr;
1357} 1371}
1358static void *xxrealloc(int lineno, void *ptr, size_t size) 1372static void *xxrealloc(int lineno, void *ptr, size_t size)
1359{ 1373{
1374 char *p = ptr;
1360 ptr = xrealloc(ptr, (size + 0xff) & ~0xff); 1375 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1361 fdprintf(2, "line %d: realloc %p\n", lineno, ptr); 1376 if (p != ptr)
1377 LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr);
1378 return ptr;
1379}
1380static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr)
1381{
1382 char *p = ptr;
1383 ptr = xrealloc_getcwd_or_warn(ptr);
1384 if (p != ptr)
1385 LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr);
1362 return ptr; 1386 return ptr;
1363} 1387}
1364static char *xxstrdup(int lineno, const char *str) 1388static char *xxstrdup(int lineno, const char *str)
1365{ 1389{
1366 char *ptr = xstrdup(str); 1390 char *ptr = xstrdup(str);
1367 fdprintf(2, "line %d: strdup %p\n", lineno, ptr); 1391 LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr);
1392 return ptr;
1393}
1394static char *xxstrndup(int lineno, const char *str, size_t n)
1395{
1396 char *ptr = xstrndup(str, n);
1397 LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr);
1398 return ptr;
1399}
1400static char *xxasprintf(int lineno, const char *f, ...)
1401{
1402 char *ptr;
1403 va_list args;
1404 va_start(args, f);
1405 if (vasprintf(&ptr, f, args) < 0)
1406 bb_die_memory_exhausted();
1407 va_end(args);
1408 LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr);
1368 return ptr; 1409 return ptr;
1369} 1410}
1370static void xxfree(void *ptr) 1411static void xxfree(void *ptr)
1371{ 1412{
1372 fdprintf(2, "free %p\n", ptr); 1413 LEAK_PRINTF(2, "free %p\n", ptr);
1373 free(ptr); 1414 free(ptr);
1374} 1415}
1375# define xmalloc(s) xxmalloc(__LINE__, s) 1416# define xmalloc(s) xxmalloc(__LINE__, s)
1376# define xrealloc(p, s) xxrealloc(__LINE__, p, s) 1417# define xzalloc(s) xxzalloc(__LINE__, s)
1377# define xstrdup(s) xxstrdup(__LINE__, s) 1418# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1378# define free(p) xxfree(p) 1419# define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p)
1420# define xstrdup(s) xxstrdup(__LINE__, s)
1421# define xstrndup(s, n) xxstrndup(__LINE__, s, n)
1422# define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__)
1423# define free(p) xxfree(p)
1379#endif 1424#endif
1380 1425
1381/* 1426/*
@@ -1929,7 +1974,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1929 * "trap - SIGxxx": 1974 * "trap - SIGxxx":
1930 * if sig is in special_sig_mask, set handler back to: 1975 * 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 1976 * 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. 1977 * 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 1978 * else: set handler back to SIG_DFL
1934 * "trap 'cmd' SIGxxx": 1979 * "trap 'cmd' SIGxxx":
1935 * set handler to record_pending_signo. 1980 * set handler to record_pending_signo.
@@ -1973,7 +2018,7 @@ static void record_pending_signo(int sig)
1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) 2018 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ 2019 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
1975 ) { 2020 ) {
1976 bb_got_signal = sig; /* for read_line_input: "we got a signal" */ 2021 bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */
1977 } 2022 }
1978#endif 2023#endif
1979#if ENABLE_HUSH_FAST 2024#if ENABLE_HUSH_FAST
@@ -2002,19 +2047,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
2002 return old_sa.sa_handler; 2047 return old_sa.sa_handler;
2003} 2048}
2004 2049
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: 2050/* Needed only on some libc:
2019 * It was observed that on exit(), fgetc'ed buffered data 2051 * It was observed that on exit(), fgetc'ed buffered data
2020 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 2052 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -2028,26 +2060,20 @@ static void restore_ttypgrp_and__exit(void)
2028 * and in `cmd` handling. 2060 * and in `cmd` handling.
2029 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 2061 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2030 */ 2062 */
2031static void fflush_and__exit(void) NORETURN; 2063static NORETURN void fflush_and__exit(void)
2032static void fflush_and__exit(void)
2033{ 2064{
2034 fflush_all(); 2065 fflush_all();
2035 _exit(xfunc_error_retval); 2066 _exit(xfunc_error_retval);
2036} 2067}
2037 2068
2038/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 2069#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. 2070/* Restores tty foreground process group, and exits.
2044 * May be called as signal handler for fatal signal 2071 * May be called as signal handler for fatal signal
2045 * (will resend signal to itself, producing correct exit state) 2072 * (will resend signal to itself, producing correct exit state)
2046 * or called directly with -EXITCODE. 2073 * or called directly with -EXITCODE.
2047 * We also call it if xfunc is exiting. 2074 * We also call it if xfunc is exiting.
2048 */ 2075 */
2049static void sigexit(int sig) NORETURN; 2076static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig)
2050static void sigexit(int sig)
2051{ 2077{
2052 /* Careful: we can end up here after [v]fork. Do not restore 2078 /* Careful: we can end up here after [v]fork. Do not restore
2053 * tty pgrp then, only top-level shell process does that */ 2079 * tty pgrp then, only top-level shell process does that */
@@ -2065,6 +2091,19 @@ static void sigexit(int sig)
2065 2091
2066 kill_myself_with_sig(sig); /* does not return */ 2092 kill_myself_with_sig(sig); /* does not return */
2067} 2093}
2094
2095static NORETURN void fflush_restore_ttypgrp_and__exit(void)
2096{
2097 /* xfunc has failed! die die die */
2098 fflush_all();
2099 restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval);
2100}
2101
2102/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2103# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2104/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2105# define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit)
2106
2068#else 2107#else
2069 2108
2070# define disable_restore_tty_pgrp_on_exit() ((void)0) 2109# define disable_restore_tty_pgrp_on_exit() ((void)0)
@@ -2081,7 +2120,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2081#if ENABLE_HUSH_JOB 2120#if ENABLE_HUSH_JOB
2082 /* is sig fatal? */ 2121 /* is sig fatal? */
2083 if (G_fatal_sig_mask & sigmask) 2122 if (G_fatal_sig_mask & sigmask)
2084 handler = sigexit; 2123 handler = restore_ttypgrp_and_killsig_or__exit;
2085 else 2124 else
2086#endif 2125#endif
2087 /* sig has special handling? */ 2126 /* sig has special handling? */
@@ -2099,11 +2138,33 @@ static sighandler_t pick_sighandler(unsigned sig)
2099 return handler; 2138 return handler;
2100} 2139}
2101 2140
2102/* Restores tty foreground process group, and exits. */ 2141static const char* FAST_FUNC get_local_var_value(const char *name);
2103static void hush_exit(int exitcode) 2142
2143/* Self-explanatory.
2144 * Restores tty foreground process group too.
2145 */
2146static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2104{ 2147{
2105#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2148#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2106 save_history(G.line_input_state); /* may be NULL */ 2149 if (G.line_input_state
2150 && getpid() == G.root_pid /* exits in subshells do not save history */
2151 ) {
2152 const char *hp;
2153# if ENABLE_FEATURE_SH_HISTFILESIZE
2154// in bash:
2155// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
2156// "When this variable is assigned a value, the history file is truncated, if necessary"
2157// but we do it only at exit, not on every assignment:
2158 /* Use HISTFILESIZE to limit file size */
2159 hp = get_local_var_value("HISTFILESIZE");
2160 if (hp)
2161 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
2162# endif
2163 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
2164 hp = get_local_var_value("HISTFILE");
2165 G.line_input_state->hist_file = hp;
2166 save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */
2167 }
2107#endif 2168#endif
2108 2169
2109 fflush_all(); 2170 fflush_all();
@@ -2137,7 +2198,7 @@ static void hush_exit(int exitcode)
2137 2198
2138 fflush_all(); 2199 fflush_all();
2139#if ENABLE_HUSH_JOB 2200#if ENABLE_HUSH_JOB
2140 sigexit(- (exitcode & 0xff)); 2201 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2141#else 2202#else
2142 _exit(exitcode); 2203 _exit(exitcode);
2143#endif 2204#endif
@@ -2222,7 +2283,7 @@ static int check_and_run_traps(void)
2222 } 2283 }
2223 } 2284 }
2224 /* this restores tty pgrp, then kills us with SIGHUP */ 2285 /* this restores tty pgrp, then kills us with SIGHUP */
2225 sigexit(SIGHUP); 2286 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2226 } 2287 }
2227#endif 2288#endif
2228#if ENABLE_HUSH_FAST 2289#if ENABLE_HUSH_FAST
@@ -4607,6 +4668,11 @@ static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt,
4607 4668
4608 redir->rd_type = REDIRECT_HEREDOC2; 4669 redir->rd_type = REDIRECT_HEREDOC2;
4609 /* redir->rd_dup is (ab)used to indicate <<- */ 4670 /* redir->rd_dup is (ab)used to indicate <<- */
4671 if (!redir->rd_filename) {
4672 /* examples: "echo <<", "echo <<<", "echo <<>" */
4673 syntax_error("missing here document terminator");
4674 return -1;
4675 }
4610 p = fetch_till_str(as_string, input, 4676 p = fetch_till_str(as_string, input,
4611 redir->rd_filename, redir->rd_dup); 4677 redir->rd_filename, redir->rd_dup);
4612 if (!p) { 4678 if (!p) {
@@ -7366,11 +7432,6 @@ static void switch_off_special_sigs(unsigned mask)
7366} 7432}
7367 7433
7368#if BB_MMU 7434#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) 7435static void reset_traps_to_defaults(void)
7375{ 7436{
7376 /* This function is always called in a child shell 7437 /* This function is always called in a child shell
@@ -7420,10 +7481,8 @@ static void reset_traps_to_defaults(void)
7420 7481
7421#else /* !BB_MMU */ 7482#else /* !BB_MMU */
7422 7483
7423static void re_execute_shell(char ***to_free, const char *s, 7484static NORETURN void re_execute_shell(
7424 char *g_argv0, char **g_argv, 7485 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, 7486 char *g_argv0, char **g_argv,
7428 char **builtin_argv) 7487 char **builtin_argv)
7429{ 7488{
@@ -7653,7 +7712,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7653 pid_t pid; 7712 pid_t pid;
7654 int channel[2]; 7713 int channel[2];
7655# if !BB_MMU 7714# if !BB_MMU
7656 char **to_free = NULL; 7715 /* _volatile_ pointer to "char*".
7716 * Or else compiler can peek from inside re_execute_shell()
7717 * and see that this pointer is a local var (i.e. not globally visible),
7718 * and decide to optimize out the store to it. Yes,
7719 * it was seen in the wild.
7720 */
7721 char * *volatile to_free = NULL;
7657# endif 7722# endif
7658 7723
7659 xpipe(channel); 7724 xpipe(channel);
@@ -7800,7 +7865,7 @@ static void setup_heredoc(struct redir_struct *redir)
7800 const char *heredoc = redir->rd_filename; 7865 const char *heredoc = redir->rd_filename;
7801 char *expanded; 7866 char *expanded;
7802#if !BB_MMU 7867#if !BB_MMU
7803 char **to_free; 7868 char * *volatile to_free;
7804#endif 7869#endif
7805 7870
7806 expanded = NULL; 7871 expanded = NULL;
@@ -8275,7 +8340,7 @@ static const struct built_in_command *find_builtin(const char *name)
8275 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8340 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8276} 8341}
8277 8342
8278#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8343#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8279static const char * FAST_FUNC hush_command_name(int i) 8344static const char * FAST_FUNC hush_command_name(int i)
8280{ 8345{
8281 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8346 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8441,10 +8506,8 @@ static void unset_func(const char *name)
8441#define exec_function(to_free, funcp, argv) \ 8506#define exec_function(to_free, funcp, argv) \
8442 exec_function(funcp, argv) 8507 exec_function(funcp, argv)
8443# endif 8508# endif
8444static void exec_function(char ***to_free, 8509static NORETURN void exec_function(
8445 const struct function *funcp, 8510 char * *volatile *to_free,
8446 char **argv) NORETURN;
8447static void exec_function(char ***to_free,
8448 const struct function *funcp, 8511 const struct function *funcp,
8449 char **argv) 8512 char **argv)
8450{ 8513{
@@ -8540,10 +8603,8 @@ static int run_function(const struct function *funcp, char **argv)
8540#define exec_builtin(to_free, x, argv) \ 8603#define exec_builtin(to_free, x, argv) \
8541 exec_builtin(to_free, argv) 8604 exec_builtin(to_free, argv)
8542#endif 8605#endif
8543static void exec_builtin(char ***to_free, 8606static NORETURN void exec_builtin(
8544 const struct built_in_command *x, 8607 char * *volatile *to_free,
8545 char **argv) NORETURN;
8546static void exec_builtin(char ***to_free,
8547 const struct built_in_command *x, 8608 const struct built_in_command *x,
8548 char **argv) 8609 char **argv)
8549{ 8610{
@@ -8566,8 +8627,7 @@ static void exec_builtin(char ***to_free,
8566#endif 8627#endif
8567} 8628}
8568 8629
8569static void execvp_or_die(char **argv) NORETURN; 8630static NORETURN void execvp_or_die(char **argv)
8570static void execvp_or_die(char **argv)
8571{ 8631{
8572 int e; 8632 int e;
8573 debug_printf_exec("execing '%s'\n", argv[0]); 8633 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8688,10 +8748,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, 8748 * The at_exit handlers apparently confuse the calling process,
8689 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 8749 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8690 */ 8750 */
8691static void pseudo_exec_argv(nommu_save_t *nommu_save, 8751static NORETURN NOINLINE void pseudo_exec_argv(
8692 char **argv, int assignment_cnt, 8752 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, 8753 char **argv, int assignment_cnt,
8696 char **argv_expanded) 8754 char **argv_expanded)
8697{ 8755{
@@ -8717,7 +8775,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8717#if BB_MMU 8775#if BB_MMU
8718 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 8776 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8719#else 8777#else
8720 G.shadowed_vars_pp = &nommu_save->old_vars; 8778 /* cast away volatility */
8779 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8721 G.var_nest_level++; 8780 G.var_nest_level++;
8722#endif 8781#endif
8723 set_vars_and_save_old(new_env); 8782 set_vars_and_save_old(new_env);
@@ -8844,10 +8903,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8844 8903
8845/* Called after [v]fork() in run_pipe 8904/* Called after [v]fork() in run_pipe
8846 */ 8905 */
8847static void pseudo_exec(nommu_save_t *nommu_save, 8906static NORETURN void pseudo_exec(
8848 struct command *command, 8907 volatile nommu_save_t *nommu_save,
8849 char **argv_expanded) NORETURN;
8850static void pseudo_exec(nommu_save_t *nommu_save,
8851 struct command *command, 8908 struct command *command,
8852 char **argv_expanded) 8909 char **argv_expanded)
8853{ 8910{
@@ -9732,8 +9789,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9732 9789
9733 /* Stores to nommu_save list of env vars putenv'ed 9790 /* Stores to nommu_save list of env vars putenv'ed
9734 * (NOMMU, on MMU we don't need that) */ 9791 * (NOMMU, on MMU we don't need that) */
9735 /* cast away volatility... */ 9792 pseudo_exec(&nommu_save, command, argv_expanded);
9736 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9737 /* pseudo_exec() does not return */ 9793 /* pseudo_exec() does not return */
9738 } 9794 }
9739 9795
@@ -10121,7 +10177,7 @@ static int run_list(struct pipe *pi)
10121 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10177 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10122 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); 10178 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10123 if (G.errexit_depth == 0) 10179 if (G.errexit_depth == 0)
10124 hush_exit(rcode); 10180 save_history_run_exit_trap_and_exit(rcode);
10125 } 10181 }
10126 G.errexit_depth = sv_errexit_depth; 10182 G.errexit_depth = sv_errexit_depth;
10127 10183
@@ -10195,6 +10251,53 @@ static int run_and_free_list(struct pipe *pi)
10195/* 10251/*
10196 * Initialization and main 10252 * Initialization and main
10197 */ 10253 */
10254#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING
10255static void init_line_editing(void)
10256{
10257 G.line_input_state = new_line_input_t(FOR_SHELL);
10258# if ENABLE_FEATURE_TAB_COMPLETION
10259 G.line_input_state->get_exe_name = hush_command_name;
10260# endif
10261# if EDITING_HAS_sh_get_var
10262 G.line_input_state->sh_get_var = get_local_var_value;
10263# endif
10264# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10265 {
10266 const char *hp = get_local_var_value("HISTFILE");
10267 if (!hp) {
10268 hp = get_local_var_value("HOME");
10269 if (hp) {
10270 hp = concat_path_file(hp, ".hush_history");
10271 /* Make HISTFILE set on exit (else history won't be saved) */
10272 set_local_var_from_halves("HISTFILE", hp);
10273 }
10274 } else {
10275 hp = xstrdup(hp);
10276 }
10277 if (hp) {
10278 G.line_input_state->hist_file = hp;
10279 }
10280# if ENABLE_FEATURE_SH_HISTFILESIZE
10281 hp = get_local_var_value("HISTSIZE");
10282 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10283 * users may set HISTFILESIZE=0 in their profile scripts
10284 * to prevent _saving_ of history files, but still want to have
10285 * non-zero history limit for in-memory list.
10286 */
10287// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10288// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10289 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10290// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10291// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10292// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10293# endif
10294 }
10295# endif
10296}
10297#else
10298# define init_line_editing() ((void)0)
10299#endif
10300
10198static void install_sighandlers(unsigned mask) 10301static void install_sighandlers(unsigned mask)
10199{ 10302{
10200 sighandler_t old_handler; 10303 sighandler_t old_handler;
@@ -10333,7 +10436,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10333int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10436int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10334int hush_main(int argc, char **argv) 10437int hush_main(int argc, char **argv)
10335{ 10438{
10336 pid_t cached_getpid;
10337 enum { 10439 enum {
10338 OPT_login = (1 << 0), 10440 OPT_login = (1 << 0),
10339 }; 10441 };
@@ -10346,6 +10448,11 @@ int hush_main(int argc, char **argv)
10346 struct variable *shell_ver; 10448 struct variable *shell_ver;
10347 10449
10348 INIT_G(); 10450 INIT_G();
10451#if ENABLE_HUSH_JOB
10452 die_func = fflush_restore_ttypgrp_and__exit;
10453#else
10454 die_func = fflush_and__exit;
10455#endif
10349 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 10456 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10350 G.last_exitcode = EXIT_SUCCESS; 10457 G.last_exitcode = EXIT_SUCCESS;
10351#if !BB_MMU 10458#if !BB_MMU
@@ -10373,9 +10480,8 @@ int hush_main(int argc, char **argv)
10373 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 10480 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10374#endif 10481#endif
10375 10482
10376 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 10483 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10377 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ 10484 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10378 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10379 10485
10380 /* Deal with HUSH_VERSION */ 10486 /* Deal with HUSH_VERSION */
10381 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10487 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10424,7 +10530,7 @@ int hush_main(int argc, char **argv)
10424 if (!get_local_var_value("PATH")) 10530 if (!get_local_var_value("PATH"))
10425 set_local_var_from_halves("PATH", bb_default_root_path); 10531 set_local_var_from_halves("PATH", bb_default_root_path);
10426 10532
10427 /* PS1/PS2 are set later, if we determine that we are interactive */ 10533 /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */
10428 10534
10429 /* bash also exports SHLVL and _, 10535 /* bash also exports SHLVL and _,
10430 * and sets (but doesn't export) the following variables: 10536 * and sets (but doesn't export) the following variables:
@@ -10446,7 +10552,6 @@ int hush_main(int argc, char **argv)
10446 * BASH_SOURCE=() 10552 * BASH_SOURCE=()
10447 * DIRSTACK=() 10553 * DIRSTACK=()
10448 * PIPESTATUS=([0]="0") 10554 * PIPESTATUS=([0]="0")
10449 * HISTFILE=/<xxx>/.bash_history
10450 * HISTFILESIZE=500 10555 * HISTFILESIZE=500
10451 * HISTSIZE=500 10556 * HISTSIZE=500
10452 * MAILCHECK=60 10557 * MAILCHECK=60
@@ -10459,27 +10564,24 @@ int hush_main(int argc, char **argv)
10459 * PS4='+ ' 10564 * PS4='+ '
10460 */ 10565 */
10461 10566
10567 /* Shell is non-interactive at first. We need to call
10568 * install_special_sighandlers() if we are going to execute "sh <script>",
10569 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10570 * If we later decide that we are interactive, we run
10571 * install_special_sighandlers() in order to intercept more signals.
10572 */
10573 install_special_sighandlers();
10574
10462#if NUM_SCRIPTS > 0 10575#if NUM_SCRIPTS > 0
10463 if (argc < 0) { 10576 if (argc < 0) {
10464 char *script = get_script_content(-argc - 1); 10577 char *script = get_script_content(-argc - 1);
10465 G.global_argv = argv; 10578 G.global_argv = argv;
10466 G.global_argc = string_array_len(argv); 10579 G.global_argc = string_array_len(argv);
10467 //install_special_sighandlers(); - needed?
10468 parse_and_run_string(script); 10580 parse_and_run_string(script);
10469 goto final_return; 10581 goto final_return;
10470 } 10582 }
10471#endif 10583#endif
10472 10584
10473 /* Initialize some more globals to non-zero values */
10474 die_func = restore_ttypgrp_and__exit;
10475
10476 /* Shell is non-interactive at first. We need to call
10477 * install_special_sighandlers() if we are going to execute "sh <script>",
10478 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10479 * If we later decide that we are interactive, we run install_special_sighandlers()
10480 * in order to intercept (more) signals.
10481 */
10482
10483 /* Parse options */ 10585 /* Parse options */
10484 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10586 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10485 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10543,6 +10645,7 @@ int hush_main(int argc, char **argv)
10543 case '$': { 10645 case '$': {
10544 unsigned long long empty_trap_mask; 10646 unsigned long long empty_trap_mask;
10545 10647
10648 G.reexeced_on_NOMMU = 1;
10546 G.root_pid = bb_strtou(optarg, &optarg, 16); 10649 G.root_pid = bb_strtou(optarg, &optarg, 16);
10547 optarg++; 10650 optarg++;
10548 G.root_ppid = bb_strtou(optarg, &optarg, 16); 10651 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10556,7 +10659,6 @@ int hush_main(int argc, char **argv)
10556 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 10659 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10557 if (empty_trap_mask != 0) { 10660 if (empty_trap_mask != 0) {
10558 IF_HUSH_TRAP(int sig;) 10661 IF_HUSH_TRAP(int sig;)
10559 install_special_sighandlers();
10560# if ENABLE_HUSH_TRAP 10662# if ENABLE_HUSH_TRAP
10561 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 10663 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10562 for (sig = 1; sig < NSIG; sig++) { 10664 for (sig = 1; sig < NSIG; sig++) {
@@ -10622,7 +10724,9 @@ int hush_main(int argc, char **argv)
10622 G.global_argv[0] = argv[0]; 10724 G.global_argv[0] = argv[0];
10623 10725
10624 /* If we are login shell... */ 10726 /* If we are login shell... */
10625 if (flags & OPT_login) { 10727 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
10728 && (flags & OPT_login)
10729 ) {
10626 const char *hp = NULL; 10730 const char *hp = NULL;
10627 HFILE *input; 10731 HFILE *input;
10628 10732
@@ -10630,7 +10734,6 @@ int hush_main(int argc, char **argv)
10630 input = hfopen("/etc/profile"); 10734 input = hfopen("/etc/profile");
10631 run_profile: 10735 run_profile:
10632 if (input != NULL) { 10736 if (input != NULL) {
10633 install_special_sighandlers();
10634 parse_and_run_file(input); 10737 parse_and_run_file(input);
10635 hfclose(input); 10738 hfclose(input);
10636 } 10739 }
@@ -10667,8 +10770,6 @@ int hush_main(int argc, char **argv)
10667 */ 10770 */
10668 char *script; 10771 char *script;
10669 10772
10670 install_special_sighandlers();
10671
10672 G.global_argc--; 10773 G.global_argc--;
10673 G.global_argv++; 10774 G.global_argv++;
10674#if !BB_MMU 10775#if !BB_MMU
@@ -10719,7 +10820,6 @@ int hush_main(int argc, char **argv)
10719 bb_simple_perror_msg_and_die(G.global_argv[0]); 10820 bb_simple_perror_msg_and_die(G.global_argv[0]);
10720 } 10821 }
10721 xfunc_error_retval = 1; 10822 xfunc_error_retval = 1;
10722 install_special_sighandlers();
10723 parse_and_run_file(input); 10823 parse_and_run_file(input);
10724#if ENABLE_FEATURE_CLEAN_UP 10824#if ENABLE_FEATURE_CLEAN_UP
10725 hfclose(input); 10825 hfclose(input);
@@ -10735,126 +10835,86 @@ int hush_main(int argc, char **argv)
10735 10835
10736 /* A shell is interactive if the '-i' flag was given, 10836 /* A shell is interactive if the '-i' flag was given,
10737 * or if all of the following conditions are met: 10837 * or if all of the following conditions are met:
10738 * no -c command 10838 * not -c 'CMD'
10739 * no arguments remaining or the -s flag given 10839 * not running a script (no arguments remaining, or -s flag given)
10740 * standard input is a terminal 10840 * standard input is a terminal
10741 * standard output is a terminal 10841 * standard output is a terminal
10742 * Refer to Posix.2, the description of the 'sh' utility. 10842 * Refer to Posix.2, the description of the 'sh' utility.
10743 */ 10843 */
10744#if ENABLE_HUSH_JOB 10844#if ENABLE_HUSH_INTERACTIVE
10745 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10845 if (!G_reexeced_on_NOMMU
10746 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 10846 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10747 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 10847 ) {
10748 if (G_saved_tty_pgrp < 0) 10848 /* Try to dup stdin to high fd#, >= 255 */
10749 G_saved_tty_pgrp = 0;
10750
10751 /* try to dup stdin to high fd#, >= 255 */
10752 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10849 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10753 if (G_interactive_fd < 0) { 10850 if (G_interactive_fd < 0) {
10754 /* try to dup to any fd */ 10851 /* Try to dup to any fd */
10755 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 10852 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10756 if (G_interactive_fd < 0) { 10853 if (G_interactive_fd < 0)
10757 /* give up */ 10854 /* Give up */
10758 G_interactive_fd = 0; 10855 G_interactive_fd = 0;
10759 G_saved_tty_pgrp = 0;
10760 }
10761 } 10856 }
10762 } 10857 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10763 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10858 if (G_interactive_fd) {
10764 if (G_interactive_fd) { 10859// TODO? bash:
10765 if (G_saved_tty_pgrp) { 10860// if interactive but not a login shell, sources ~/.bashrc
10766 /* If we were run as 'hush &', sleep until we are 10861// (--norc turns this off, --rcfile <file> overrides)
10767 * in the foreground (tty pgrp == our pgrp). 10862# if ENABLE_HUSH_JOB
10768 * If we get started under a job aware app (like bash), 10863 /* Can we do job control? */
10769 * make sure we are now in charge so we don't fight over 10864 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10770 * who gets the foreground */ 10865 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10771 while (1) { 10866 if (G_saved_tty_pgrp < 0)
10772 pid_t shell_pgrp = getpgrp(); 10867 G_saved_tty_pgrp = 0; /* no */
10773 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 10868 if (G_saved_tty_pgrp) {
10774 if (G_saved_tty_pgrp == shell_pgrp) 10869 /* If we were run as 'hush &', sleep until we are
10775 break; 10870 * in the foreground (tty pgrp == our pgrp).
10776 /* send TTIN to ourself (should stop us) */ 10871 * If we get started under a job aware app (like bash),
10777 kill(- shell_pgrp, SIGTTIN); 10872 * make sure we are now in charge so we don't fight over
10873 * who gets the foreground */
10874 while (1) {
10875 pid_t shell_pgrp = getpgrp();
10876 if (G_saved_tty_pgrp == shell_pgrp) {
10877/* Often both pgrps here are set to our pid - but not always!
10878 * Example: sh -c 'echo $$; hush; echo FIN'
10879 * Here, the parent shell is not interactive, so it does NOT set up
10880 * a separate process group for its children, and we (hush) initially
10881 * run in parent's process group (until we set up our own a few lines down).
10882 */
10883 //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp);
10884 break;
10885 }
10886 /* Send TTIN to ourself (should stop us) */
10887 kill(- shell_pgrp, SIGTTIN);
10888 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10889 }
10778 } 10890 }
10779 }
10780
10781 /* Install more signal handlers */
10782 install_special_sighandlers();
10783
10784 if (G_saved_tty_pgrp) {
10785 /* Set other signals to restore saved_tty_pgrp */
10786 install_fatal_sighandlers();
10787 /* Put ourselves in our own process group
10788 * (bash, too, does this only if ctty is available) */
10789 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10790 /* Grab control of the terminal */
10791 tcsetpgrp(G_interactive_fd, cached_getpid);
10792 }
10793 enable_restore_tty_pgrp_on_exit();
10794
10795# if ENABLE_FEATURE_EDITING
10796 G.line_input_state = new_line_input_t(FOR_SHELL);
10797# if ENABLE_FEATURE_TAB_COMPLETION
10798 G.line_input_state->get_exe_name = hush_command_name;
10799# endif
10800# if EDITING_HAS_sh_get_var
10801 G.line_input_state->sh_get_var = get_local_var_value;
10802# endif
10803# endif 10891# endif
10804# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10892 /* Install more signal handlers */
10805 { 10893 install_special_sighandlers();
10806 const char *hp = get_local_var_value("HISTFILE"); 10894# if ENABLE_HUSH_JOB
10807 if (!hp) { 10895 if (G_saved_tty_pgrp) {
10808 hp = get_local_var_value("HOME"); 10896 /* Set fatal signals to restore saved_tty_pgrp */
10809 if (hp) 10897 install_fatal_sighandlers();
10810 hp = concat_path_file(hp, ".hush_history"); 10898 /* (The if() is an optimization: can avoid two redundant syscalls) */
10811 } else { 10899 if (G_saved_tty_pgrp != G.root_pid) {
10812 hp = xstrdup(hp); 10900 /* Put ourselves in our own process group
10813 } 10901 * (bash, too, does this only if ctty is available) */
10814 if (hp) { 10902 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10815 G.line_input_state->hist_file = hp; 10903 /* Grab control of the terminal */
10816 //set_local_var(xasprintf("HISTFILE=%s", ...)); 10904 tcsetpgrp(G_interactive_fd, G.root_pid);
10905 }
10817 } 10906 }
10818# if ENABLE_FEATURE_SH_HISTFILESIZE
10819 hp = get_local_var_value("HISTFILESIZE");
10820 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10821# endif
10822 }
10823# endif 10907# endif
10824 } else { 10908# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10825 install_special_sighandlers(); 10909 /* Set (but not export) PS1/2 unless already set */
10826 } 10910 if (!get_local_var_value("PS1"))
10827#elif ENABLE_HUSH_INTERACTIVE 10911 set_local_var_from_halves("PS1", "\\w \\$ ");
10828 /* No job control compiled in, only prompt/line editing */ 10912 if (!get_local_var_value("PS2"))
10829 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10913 set_local_var_from_halves("PS2", "> ");
10830 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10914# endif
10831 if (G_interactive_fd < 0) { 10915 init_line_editing();
10832 /* try to dup to any fd */
10833 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10834 if (G_interactive_fd < 0)
10835 /* give up */
10836 G_interactive_fd = 0;
10837 }
10838 }
10839 install_special_sighandlers();
10840#else
10841 /* We have interactiveness code disabled */
10842 install_special_sighandlers();
10843#endif
10844 /* bash:
10845 * if interactive but not a login shell, sources ~/.bashrc
10846 * (--norc turns this off, --rcfile <file> overrides)
10847 */
10848 10916
10849 if (G_interactive_fd) { 10917# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10850#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10851 /* Set (but not export) PS1/2 unless already set */
10852 if (!get_local_var_value("PS1"))
10853 set_local_var_from_halves("PS1", "\\w \\$ ");
10854 if (!get_local_var_value("PS2"))
10855 set_local_var_from_halves("PS2", "> ");
10856#endif
10857 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10858 /* note: ash and hush share this string */ 10918 /* note: ash and hush share this string */
10859 printf("\n\n%s %s\n" 10919 printf("\n\n%s %s\n"
10860 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 10920 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
@@ -10862,13 +10922,15 @@ int hush_main(int argc, char **argv)
10862 bb_banner, 10922 bb_banner,
10863 "hush - the humble shell" 10923 "hush - the humble shell"
10864 ); 10924 );
10865 } 10925# endif
10866 } 10926 } /* if become interactive */
10927 } /* if on tty */
10928#endif /* if INTERACTIVE is allowed by build config */
10867 10929
10868 parse_and_run_file(hfopen(NULL)); /* stdin */ 10930 parse_and_run_file(hfopen(NULL)); /* stdin */
10869 10931
10870 final_return: 10932 final_return:
10871 hush_exit(G.last_exitcode); 10933 save_history_run_exit_trap_and_exit(G.last_exitcode);
10872} 10934}
10873 10935
10874/* 10936/*
@@ -11054,19 +11116,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11054 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" 11116 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11055 */ 11117 */
11056 11118
11057 /* note: EXIT trap is run by hush_exit */ 11119 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11058 argv = skip_dash_dash(argv); 11120 argv = skip_dash_dash(argv);
11059 if (argv[0] == NULL) { 11121 if (argv[0] == NULL) {
11060#if ENABLE_HUSH_TRAP 11122#if ENABLE_HUSH_TRAP
11061 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ 11123 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11062 hush_exit(G.pre_trap_exitcode); 11124 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11063#endif 11125#endif
11064 hush_exit(G.last_exitcode); 11126 save_history_run_exit_trap_and_exit(G.last_exitcode);
11065 } 11127 }
11066 /* mimic bash: exit 123abc == exit 255 + error msg */ 11128 /* mimic bash: exit 123abc == exit 255 + error msg */
11067 xfunc_error_retval = 255; 11129 xfunc_error_retval = 255;
11068 /* bash: exit -2 == exit 254, no error msg */ 11130 /* bash: exit -2 == exit 254, no error msg */
11069 hush_exit(xatoi(argv[0]) & 0xff); 11131 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11070} 11132}
11071 11133
11072#if ENABLE_HUSH_TYPE 11134#if ENABLE_HUSH_TYPE
@@ -11172,6 +11234,11 @@ static int FAST_FUNC builtin_read(char **argv)
11172 goto again; 11234 goto again;
11173 } 11235 }
11174 11236
11237 if ((uintptr_t)r == 2) /* -t SEC timeout? */
11238 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
11239 /* The actual value observed with bash 5.2.15: */
11240 return 128 + SIGALRM;
11241
11175 if ((uintptr_t)r > 1) { 11242 if ((uintptr_t)r > 1) {
11176 bb_simple_error_msg(r); 11243 bb_simple_error_msg(r);
11177 r = (char*)(uintptr_t)1; 11244 r = (char*)(uintptr_t)1;
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-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-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/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-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-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/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/cut.tests b/testsuite/cut.tests
index a31f41f7f..21cfea809 100755
--- a/testsuite/cut.tests
+++ b/testsuite/cut.tests
@@ -23,14 +23,30 @@ the quick brown fox jumps over the lazy dog
23 23
24testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" "" 24testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" ""
25 25
26testing "cut -b overlaps" "cut -b 1-3,2-5,7-9,9-10 input" \ 26testing "cut -b overlaps" \
27 "one:to:th\nalphabeta\nthe qick \n" "$abc" "" 27 "cut -b 1-3,2-5,7-9,9-10 input" \
28testing "-b encapsulated" "cut -b 3-8,4-6 input" "e:two:\npha:be\ne quic\n" \ 28 "\
29 "$abc" "" 29one:to:th
30# --output-delimiter not implemnted (yet?) 30alphabeta
31#testing "cut -bO overlaps" \ 31the qick \n" \
32# "cut --output-delimiter ' ' -b 1-3,2-5,7-9,9-10 input" \ 32 "$abc" ""
33# "one:t o:th\nalpha beta\nthe q ick \n" "$abc" "" 33testing "-b encapsulated" \
34 "cut -b 3-8,4-6 input" \
35 "\
36e:two:
37pha:be
38e quic\n" \
39 "$abc" ""
40optional LONG_OPTS
41testing "cut -b --output-delimiter overlaps" \
42 "cut --output-delimiter='^' -b 1-3,2-5,7-9,9-10 input" \
43 "\
44one:t^o:th
45alpha^beta
46the q^ick \n" \
47 "$abc" ""
48SKIP=
49
34testing "cut high-low error" "cut -b 8-3 input 2>/dev/null || echo err" "err\n" \ 50testing "cut high-low error" "cut -b 8-3 input 2>/dev/null || echo err" "err\n" \
35 "$abc" "" 51 "$abc" ""
36 52
@@ -68,10 +84,27 @@ testing "cut with -d -f( ) -s" "cut -d' ' -f3 -s input && echo yes" "yes\n" "$in
68testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" 84testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" ""
69testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" 85testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" ""
70 86
87input="\
88
89foo bar baz
90
91bing bong boop
92
93"
94testing "cut with -d -s omits blank lines" "cut -d' ' -f2 -s input" "bar\nbong\n" "$input" ""
95
71# substitute for awk 96# substitute for awk
72optional FEATURE_CUT_REGEX 97optional FEATURE_CUT_REGEX
73testing "cut -DF" "cut -DF 2,7,5" \ 98testing "cut -DF unordered" "cut -DF 2,7,5" \
74 "said and your\nare\nis demand. supply\nforecast :\nyou you better,\n\nEm: Took hate\n" "" \ 99 "\
100said and your
101are
102is demand. supply
103forecast :
104you you better,
105
106Em: Took hate
107" "" \
75"Bother, said Pooh. It's your husband, and he has a gun. 108"Bother, said Pooh. It's your husband, and he has a gun.
76Cheerios are donut seeds. 109Cheerios are donut seeds.
77Talk is cheap because supply exceeds demand. 110Talk is cheap because supply exceeds demand.
@@ -79,9 +112,92 @@ Weather forecast for tonight : dark.
79Apple: you can buy better, but you can't pay more. 112Apple: you can buy better, but you can't pay more.
80Subcalifragilisticexpialidocious. 113Subcalifragilisticexpialidocious.
81Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy." 114Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy."
115
116# No delimiter found: print entire line regardless of -F RANGES
117testing "cut -F1" "cut -d: -F1" \
118 "the_only_field\n" "" \
119 "the_only_field\n"
120testing "cut -F2" "cut -d: -F2" \
121 "the_only_field\n" "" \
122 "the_only_field\n"
123# No delimiter found and -s: skip entire line
124testing "cut -sF1" "cut -d: -sF1" \
125 "" "" \
126 "the_only_field\n"
127#^^^ the above is probably mishandled by toybox, it prints the line
128testing "cut -sF2" "cut -d: -sF2" \
129 "" "" \
130 "the_only_field\n"
131# -D disables special handling of lines with no delimiters, the line is treated as the 1st field
132testing "cut -DF1" "cut -d: -DF1" \
133 "the_only_field\n" "" \
134 "the_only_field\n"
135testing "cut -DF2" "cut -d: -DF2" \
136 "\n" "" \
137 "the_only_field\n"
138
139optional FEATURE_CUT_REGEX LONG_OPTS
140testing "cut -F preserves intermediate delimiters" \
141 "cut --output-delimiter=: -F2,4-6,7" \
142 "2:4 5 6:7\n" \
143 "" "1 2 3 4\t\t5 6 7 8\n"
144SKIP=
145
146optional LONG_OPTS
147testing "cut -f does not preserve intermediate delimiters" \
148 "cut --output-delimiter=: -d' ' -f2,4-6,7" \
149 "2:4:5:6:7\n" \
150 "" "1 2 3 4 5 6 7 8\n"
151SKIP=
152
153testing "cut empty field" "cut -d ':' -f 1-3" \
154 "a::b\n" \
155 "" "a::b\n"
156testing "cut empty field 2" "cut -d ':' -f 3-5" \
157 "b::c\n" \
158 "" "a::b::c:d\n"
159testing "cut non-existing field" "cut -d ':' -f1,3" \
160 "1\n" \
161 "" "1:\n"
162
163# cut -d$'\n' has a special meaning: "select input lines".
164# I didn't find any documentation for this feature.
165testing "cut -dNEWLINE" \
166 "cut -d'
167' -f4,2,6-8" \
168 "2\n4\n6\n7\n" \
169 "" "1\n2\n3\n4\n5\n6\n7"
170
171optional LONG_OPTS
172testing "cut -dNEWLINE --output-delimiter" \
173 "cut -d'
174' --output-delimiter=@@ -f4,2,6-8" \
175 "2@@4@@6@@7\n" \
176 "" "1\n2\n3\n4\n5\n6\n7"
177
178testing "cut -dNEWLINE --output-delimiter 2" \
179 "cut -d'
180' --output-delimiter=@@ -f4,2,6-8" \
181 "2@@4@@6@@7\n" \
182 "" "1\n2\n3\n4\n5\n6\n7\n"
183
184testing "cut -dNEWLINE --output-delimiter EMPTY_INPUT" \
185 "cut -d'
186' --output-delimiter=@@ -f4,2,6-8" \
187 "" \
188 "" ""
82SKIP= 189SKIP=
83 190
84testing "cut empty field" "cut -d ':' -f 1-3" "a::b\n" "" "a::b\n" 191# This seems to work as if delimiter is never found.
85testing "cut empty field 2" "cut -d ':' -f 3-5" "b::c\n" "" "a::b::c:d\n" 192# We test here that -d '' does *not* operate as if there was no -d
193# and delimiter has defaulted to TAB:
194testing "cut -d EMPTY" \
195 "cut -d '' -f2-" \
196 "1 2\t3 4 5\n" \
197 "" "1 2\t3 4 5\n"
198testing "cut -d EMPTY -s" \
199 "cut -d '' -f2- -s" \
200 "" \
201 "" "1 2\t3 4 5\n"
86 202
87exit $FAILCOUNT 203exit $FAILCOUNT
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index be0379cfc..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,27 +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" \
90 "" "$input"
91SKIP=
92
93$little_endian && SKIP=1
94testing "hexdump -e /2 %d (big endian)" \
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=
105
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)" \
134 "hexdump -n4 -e '\"%u\"'" \
135 "12345678" \
78 "" \ 136 "" \
79"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\ 137 "\x4e\x61\xbc\x00AAAA"
80"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\ 138SKIP=
81"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\ 139
82"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ 140$little_endian && SKIP=1
83"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ 141testing "hexdump -n4 -e '\"%u\"' (big endian)" \
142 "hexdump -n4 -e '\"%u\"'" \
143 "1315027968" \
144 "" \
145 "\x4e\x61\xbc\x00AAAA"
146SKIP=
84 147
85exit $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/make.tests b/testsuite/make.tests
index 376bdcc15..fabdbe45c 100755
--- a/testsuite/make.tests
+++ b/testsuite/make.tests
@@ -86,6 +86,16 @@ test.k:
86' 86'
87cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 87cd .. || exit 1; rm -rf make.tempdir 2>/dev/null
88 88
89# Check that single-suffix inference rules work.
90mkdir make.tempdir && cd make.tempdir || exit 1
91touch test.sh
92testing "make single-suffix inference rule works" \
93 "make -f - -s; echo $?" \
94 "0\n" "" '
95test:
96'
97cd .. || exit 1; rm -rf make.tempdir 2>/dev/null
98
89# There was a bug where the failure of a build command didn't result 99# There was a bug where the failure of a build command didn't result
90# in make returning a non-zero exit status. 100# in make returning a non-zero exit status.
91testing "make return error if command fails" \ 101testing "make return error if command fails" \
@@ -541,6 +551,36 @@ testing "make chained inference rules" \
541' 551'
542cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 552cd .. || exit 1; rm -rf make.tempdir 2>/dev/null
543 553
554# Suffixes with multiple periods are supported
555mkdir make.tempdir && cd make.tempdir || exit 1
556touch x.c.c
557testing "make support multi-period suffixes" \
558 "make -f -" "cat x.c.c >x.o.o\nx\n" "" '
559.SUFFIXES: .c.c .o.o
560x.o.o:
561.c.c.o.o:
562 cat $< >$@
563 @echo $*
564'
565cd .. || exit 1; rm -rf make.tempdir 2>/dev/null
566
567# Suffixes with no periods are supported
568mkdir make.tempdir && cd make.tempdir || exit 1
569touch filex
570testing "make support suffixes without any periods" \
571 "make -f -" "cp filex fileh\nfile\ncp filex filez\nfile\n" "" '
572.SUFFIXES: x h z
573all: fileh filez
574fileh:
575filez: filex
576 cp filex filez
577 @echo $*
578xh:
579 cp $< $@
580 @echo $*
581'
582cd .. || exit 1; rm -rf make.tempdir 2>/dev/null
583
544# make supports *, ? and [] wildcards in targets and prerequisites 584# make supports *, ? and [] wildcards in targets and prerequisites
545mkdir make.tempdir && cd make.tempdir || exit 1 585mkdir make.tempdir && cd make.tempdir || exit 1
546touch -t 202206171201 t1a t2aa t3b 586touch -t 202206171201 t1a t2aa t3b
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/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 7a5198ccf..061e7bac6 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -2538,3 +2538,34 @@ char * FAST_FUNC exe_relative_path(const char *tail)
2538 free(exepath); 2538 free(exepath);
2539 return relpath; 2539 return relpath;
2540} 2540}
2541
2542int mingw_shell_execute(SHELLEXECUTEINFO *info)
2543{
2544 DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *);
2545 char *lpath;
2546 int ret;
2547
2548 if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) {
2549 errno = ENOSYS;
2550 return FALSE;
2551 }
2552
2553 // ShellExecuteEx() needs backslash as separator in UNC paths.
2554 lpath = xstrdup(info->lpFile);
2555 slash_to_bs(lpath);
2556 info->lpFile = lpath;
2557
2558 ret = ShellExecuteExA(info);
2559
2560 free(lpath);
2561 return ret;
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/process.c b/win32/process.c
index e7c9ca187..0d120936e 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -331,7 +331,9 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv,
331 } else { 331 } else {
332 errno = ENOENT; 332 errno = ENOENT;
333 } 333 }
334#if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1
334 done: 335 done:
336#endif
335 free(path); 337 free(path);
336 free(new_argv); 338 free(new_argv);
337 return ret; 339 return ret;
@@ -499,6 +501,38 @@ static NORETURN void wait_for_child(HANDLE child, const char *cmd)
499 exit((int)code); 501 exit((int)code);
500} 502}
501 503
504static intptr_t
505shell_execute(const char *path, char *const *argv)
506{
507 SHELLEXECUTEINFO info;
508 char *args;
509
510 memset(&info, 0, sizeof(SHELLEXECUTEINFO));
511 info.cbSize = sizeof(SHELLEXECUTEINFO);
512 info.fMask = SEE_MASK_NOCLOSEPROCESS;
513 /* info.hwnd = NULL; */
514 info.lpVerb = "runas";
515 info.lpFile = path;
516
517 args = NULL;
518 if (*argv++) {
519 while (*argv) {
520 char *q = quote_arg(*argv++);
521 args = xappendword(args, q);
522 free(q);
523 }
524 }
525
526 info.lpParameters = args;
527 /* info.lpDirectory = NULL; */
528 info.nShow = SW_SHOWNORMAL;
529
530 mingw_shell_execute(&info);
531
532 free(args);
533 return info.hProcess ? (intptr_t)info.hProcess : -1;
534}
535
502int 536int
503mingw_execvp(const char *cmd, char *const *argv) 537mingw_execvp(const char *cmd, char *const *argv)
504{ 538{
@@ -512,6 +546,16 @@ int
512mingw_execve(const char *cmd, char *const *argv, char *const *envp) 546mingw_execve(const char *cmd, char *const *argv, char *const *envp)
513{ 547{
514 intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); 548 intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0);
549
550 if (ret == -1 && GetLastError() == ERROR_ELEVATION_REQUIRED) {
551 // Command exists but failed because it wants elevated privileges.
552 // Try again using ShellExecuteEx().
553 SetLastError(0);
554 ret = shell_execute(cmd, argv);
555 if (GetLastError())
556 exit(1);
557 }
558
515 if (ret != -1) 559 if (ret != -1)
516 wait_for_child((HANDLE)ret, cmd); 560 wait_for_child((HANDLE)ret, cmd);
517 return ret; 561 return ret;
@@ -751,7 +795,7 @@ UNUSED_PARAM
751 return sp; 795 return sp;
752} 796}
753 797
754void 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)
755{ 799{
756 const char *str, *cmdline; 800 const char *str, *cmdline;
757 801
@@ -763,6 +807,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
763 else 807 else
764 cmdline = comm; 808 cmdline = comm;
765 safe_strncpy(buf, cmdline, col); 809 safe_strncpy(buf, cmdline, col);
810 return 0;
766} 811}
767 812
768/** 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..9736f0568 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;