diff options
Diffstat (limited to '')
294 files changed, 10574 insertions, 4001 deletions
| @@ -218,6 +218,13 @@ config FEATURE_INSTALLER | |||
| 218 | busybox at runtime to create hard links or symlinks for all the | 218 | busybox at runtime to create hard links or symlinks for all the |
| 219 | applets that are compiled into busybox. | 219 | applets that are compiled into busybox. |
| 220 | 220 | ||
| 221 | config FEATURE_VERSION | ||
| 222 | bool "Support --version" | ||
| 223 | default y | ||
| 224 | depends on BUSYBOX | ||
| 225 | help | ||
| 226 | Enable 'busybox --version' support. | ||
| 227 | |||
| 221 | config INSTALL_NO_USR | 228 | config INSTALL_NO_USR |
| 222 | bool "Don't use /usr" | 229 | bool "Don't use /usr" |
| 223 | default n | 230 | default n |
| @@ -396,27 +403,6 @@ config GLOBBING | |||
| 396 | use BusyBox applets from the Windows Command Prompt you can stop | 403 | use BusyBox applets from the Windows Command Prompt you can stop |
| 397 | busybox.exe from expanding wildcards by setting this to 'N'. | 404 | busybox.exe from expanding wildcards by setting this to 'N'. |
| 398 | 405 | ||
| 399 | choice | ||
| 400 | prompt "Random number generator" | ||
| 401 | default FEATURE_PRNG_SHELL | ||
| 402 | depends on PLATFORM_MINGW32 | ||
| 403 | help | ||
| 404 | BusyBox on Microsoft Windows uses a pseudo-random number | ||
| 405 | generator to emulate the Linux /dev/urandom device. There | ||
| 406 | are two options: | ||
| 407 | - The shell's built-in PRNG. | ||
| 408 | - Bob Jenkins' ISAAC. This is intended to be a secure PRNG. It's | ||
| 409 | slightly faster than the shell's PRNG but is larger both in terms | ||
| 410 | of code and runtime memory. | ||
| 411 | |||
| 412 | config FEATURE_PRNG_SHELL | ||
| 413 | bool "Use shell PRNG" | ||
| 414 | |||
| 415 | config FEATURE_PRNG_ISAAC | ||
| 416 | bool "Use ISAAC PRNG" | ||
| 417 | |||
| 418 | endchoice | ||
| 419 | |||
| 420 | config FEATURE_RESOURCES | 406 | config FEATURE_RESOURCES |
| 421 | bool "Include resources in binary" | 407 | bool "Include resources in binary" |
| 422 | default y | 408 | default y |
| @@ -468,8 +454,10 @@ config FEATURE_ICON | |||
| 468 | depends on FEATURE_RESOURCES | 454 | depends on FEATURE_RESOURCES |
| 469 | help | 455 | help |
| 470 | Microsoft Windows applications can contain icons which are used in | 456 | Microsoft Windows applications can contain icons which are used in |
| 471 | various places in the user interface. Each icon adds 15 Kbytes to | 457 | various places in the user interface. Two icons are provided in |
| 472 | the size of the binary. | 458 | two formats. ICO icons are 15KB and are supported in all versions |
| 459 | of Windows. PNG icons are 1-2KB and are supported since Windows | ||
| 460 | Vista. | ||
| 473 | 461 | ||
| 474 | choice | 462 | choice |
| 475 | prompt "Application icon" | 463 | prompt "Application icon" |
| @@ -477,13 +465,22 @@ choice | |||
| 477 | depends on FEATURE_ICON | 465 | depends on FEATURE_ICON |
| 478 | 466 | ||
| 479 | config FEATURE_ICON_ATERM | 467 | config FEATURE_ICON_ATERM |
| 480 | bool "Adwaita terminal" | 468 | bool "ICO Adwaita terminal" |
| 481 | 469 | ||
| 482 | config FEATURE_ICON_STERM | 470 | config FEATURE_ICON_STERM |
| 483 | bool "Adwaita terminal (symbolic)" | 471 | bool "ICO Adwaita terminal (symbolic)" |
| 484 | 472 | ||
| 485 | config FEATURE_ICON_ALL | 473 | config FEATURE_ICON_ALL |
| 486 | bool "All available icons" | 474 | bool "All available ICO icons" |
| 475 | |||
| 476 | config FEATURE_PNG_ATERM | ||
| 477 | bool "PNG Adwaita terminal" | ||
| 478 | |||
| 479 | config FEATURE_PNG_STERM | ||
| 480 | bool "PNG Adwaita terminal (symbolic)" | ||
| 481 | |||
| 482 | config FEATURE_PNG_ALL | ||
| 483 | bool "All available PNG icons" | ||
| 487 | 484 | ||
| 488 | endchoice | 485 | endchoice |
| 489 | 486 | ||
| @@ -566,6 +563,15 @@ config OVERRIDE_APPLETS | |||
| 566 | environment variable. BB_OVERRIDE_APPLETS is checked first, if it | 563 | environment variable. BB_OVERRIDE_APPLETS is checked first, if it |
| 567 | allows the applet and this list is non-empty it is checked too. | 564 | allows the applet and this list is non-empty it is checked too. |
| 568 | 565 | ||
| 566 | config UNWIND_TABLES | ||
| 567 | bool "Include stack unwind tables in the binary" | ||
| 568 | default y | ||
| 569 | depends on PLATFORM_MINGW32 | ||
| 570 | help | ||
| 571 | Binaries can include support for stack unwinding. This isn't | ||
| 572 | normally required in BusyBox, but Windows' Control Flow Guard | ||
| 573 | needs it. Disabling this reduces the size of the binaries. | ||
| 574 | |||
| 569 | comment 'Build Options' | 575 | comment 'Build Options' |
| 570 | 576 | ||
| 571 | config STATIC | 577 | config STATIC |
| @@ -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 |
| 695 | quiet_cmd_sysmap = SYSMAP | 700 | quiet_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 \ | |||
| 156 | docs/BusyBox.txt: docs/busybox.pod | 156 | docs/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 | ||
| 161 | docs/busybox.1: docs/busybox.pod | 161 | docs/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 | ||
| 166 | docs/BusyBox.html: docs/busybox.net/BusyBox.html | 166 | docs/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,) | |||
| 73 | endif | 73 | endif |
| 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): |
| 76 | ifneq ($(CONFIG_UNWIND_TABLES),y) | ||
| 76 | CFLAGS += $(call cc-option,-fno-unwind-tables,) | 77 | CFLAGS += $(call cc-option,-fno-unwind-tables,) |
| 77 | CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,) | 78 | CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,) |
| 79 | else | ||
| 80 | CFLAGS += $(call cc-option,-funwind-tables,) | ||
| 81 | CFLAGS += $(call cc-option,-fasynchronous-unwind-tables,) | ||
| 82 | endif | ||
| 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) |
| 80 | CFLAGS += $(call cc-option,-fno-builtin-printf,) | 85 | CFLAGS += $(call cc-option,-fno-builtin-printf,) |
| @@ -151,11 +156,15 @@ ifeq ($(CONFIG_PLATFORM_MINGW32),y) | |||
| 151 | CFLAGS += -Iwin32 -DHAVE_STRING_H=1 -DHAVE_CONFIG_H=0 -fno-builtin-stpcpy -fno-builtin-stpncpy -fno-ident -fno-builtin-strndup | 156 | CFLAGS += -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 |
| 153 | ifeq ($(lastword $(subst -, ,$(CC))),clang) | 158 | ifeq ($(lastword $(subst -, ,$(CC))),clang) |
| 159 | ifeq ($(CONFIG_UNWIND_TABLES),y) | ||
| 160 | CFLAGS += $(call cc-option,-fexceptions,) | ||
| 161 | else | ||
| 154 | CFLAGS += $(call cc-option,-fsjlj-exceptions,) | 162 | CFLAGS += $(call cc-option,-fsjlj-exceptions,) |
| 155 | endif | 163 | endif |
| 164 | endif | ||
| 156 | 165 | ||
| 157 | EXEEXT = .exe | 166 | EXEEXT = .exe |
| 158 | LDLIBS += ws2_32 | 167 | LDLIBS += ws2_32 bcrypt secur32 |
| 159 | endif | 168 | endif |
| 160 | 169 | ||
| 161 | ifneq ($(CONFIG_PLATFORM_MINGW32),y) | 170 | ifneq ($(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 | |||
| 336 | sha1sum - noexec. runner | 336 | sha1sum - noexec. runner |
| 337 | sha256sum - noexec. runner | 337 | sha256sum - noexec. runner |
| 338 | sha3sum - noexec. runner | 338 | sha3sum - noexec. runner |
| 339 | sha384sum - noexec. runner | ||
| 339 | sha512sum - noexec. runner | 340 | sha512sum - noexec. runner |
| 340 | showkey - interactive, longterm | 341 | showkey - interactive, longterm |
| 341 | shred - runner | 342 | shred - 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 | ||
| 38 | config 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 | |||
| 38 | endmenu | 49 | endmenu |
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/header_list.c b/archival/libarchive/header_list.c index 0621aa406..9490b3635 100644 --- a/archival/libarchive/header_list.c +++ b/archival/libarchive/header_list.c | |||
| @@ -8,5 +8,5 @@ | |||
| 8 | void FAST_FUNC header_list(const file_header_t *file_header) | 8 | void FAST_FUNC header_list(const file_header_t *file_header) |
| 9 | { | 9 | { |
| 10 | //TODO: cpio -vp DIR should output "DIR/NAME", not just "NAME" */ | 10 | //TODO: cpio -vp DIR should output "DIR/NAME", not just "NAME" */ |
| 11 | puts(file_header->name); | 11 | puts(printable_string(file_header->name)); |
| 12 | } | 12 | } |
diff --git a/archival/libarchive/header_verbose_list.c b/archival/libarchive/header_verbose_list.c index a575a08a0..e7a09430d 100644 --- a/archival/libarchive/header_verbose_list.c +++ b/archival/libarchive/header_verbose_list.c | |||
| @@ -57,13 +57,13 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header) | |||
| 57 | ptm->tm_hour, | 57 | ptm->tm_hour, |
| 58 | ptm->tm_min, | 58 | ptm->tm_min, |
| 59 | ptm->tm_sec, | 59 | ptm->tm_sec, |
| 60 | file_header->name); | 60 | printable_string(file_header->name)); |
| 61 | 61 | ||
| 62 | #endif /* FEATURE_TAR_UNAME_GNAME */ | 62 | #endif /* FEATURE_TAR_UNAME_GNAME */ |
| 63 | 63 | ||
| 64 | /* NB: GNU tar shows "->" for symlinks and "link to" for hardlinks */ | 64 | /* NB: GNU tar shows "->" for symlinks and "link to" for hardlinks */ |
| 65 | if (file_header->link_target) { | 65 | if (file_header->link_target) { |
| 66 | printf(" -> %s", file_header->link_target); | 66 | printf(" -> %s", printable_string(file_header->link_target)); |
| 67 | } | 67 | } |
| 68 | bb_putchar('\n'); | 68 | bb_putchar('\n'); |
| 69 | } | 69 | } |
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 | */ |
| 167 | static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed) | 167 | static 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 | */ |
| 250 | int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed) | 250 | int 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 | ||
| 274 | static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed) | 276 | static 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 | ||
| 299 | int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed) | 301 | int 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..f02452053 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 | # Thu Nov 6 08:23:16 2025 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
| @@ -44,8 +44,6 @@ CONFIG_BUSYBOX_EXEC_PATH="" | |||
| 44 | # Settings for MINGW32 | 44 | # Settings for MINGW32 |
| 45 | # | 45 | # |
| 46 | CONFIG_GLOBBING=y | 46 | CONFIG_GLOBBING=y |
| 47 | CONFIG_FEATURE_PRNG_SHELL=y | ||
| 48 | # CONFIG_FEATURE_PRNG_ISAAC is not set | ||
| 49 | CONFIG_FEATURE_RESOURCES=y | 47 | CONFIG_FEATURE_RESOURCES=y |
| 50 | CONFIG_FEATURE_VERSIONINFO=y | 48 | CONFIG_FEATURE_VERSIONINFO=y |
| 51 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set | 49 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set |
| @@ -55,6 +53,9 @@ CONFIG_FEATURE_ICON=y | |||
| 55 | # CONFIG_FEATURE_ICON_ATERM is not set | 53 | # CONFIG_FEATURE_ICON_ATERM is not set |
| 56 | # CONFIG_FEATURE_ICON_STERM is not set | 54 | # CONFIG_FEATURE_ICON_STERM is not set |
| 57 | CONFIG_FEATURE_ICON_ALL=y | 55 | CONFIG_FEATURE_ICON_ALL=y |
| 56 | # CONFIG_FEATURE_PNG_ATERM is not set | ||
| 57 | # CONFIG_FEATURE_PNG_STERM is not set | ||
| 58 | # CONFIG_FEATURE_PNG_ALL is not set | ||
| 58 | CONFIG_FEATURE_EURO=y | 59 | CONFIG_FEATURE_EURO=y |
| 59 | # CONFIG_FEATURE_UTF8_INPUT is not set | 60 | # CONFIG_FEATURE_UTF8_INPUT is not set |
| 60 | # CONFIG_FEATURE_UTF8_OUTPUT is not set | 61 | # CONFIG_FEATURE_UTF8_OUTPUT is not set |
| @@ -62,6 +63,7 @@ CONFIG_TERMINAL_MODE=5 | |||
| 62 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y | 63 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y |
| 63 | CONFIG_FEATURE_EXTRA_FILE_DATA=y | 64 | CONFIG_FEATURE_EXTRA_FILE_DATA=y |
| 64 | CONFIG_OVERRIDE_APPLETS="" | 65 | CONFIG_OVERRIDE_APPLETS="" |
| 66 | # CONFIG_UNWIND_TABLES is not set | ||
| 65 | 67 | ||
| 66 | # | 68 | # |
| 67 | # Build Options | 69 | # Build Options |
| @@ -120,6 +122,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y | |||
| 120 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set | 122 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set |
| 121 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set | 123 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set |
| 122 | CONFIG_PASSWORD_MINLEN=6 | 124 | CONFIG_PASSWORD_MINLEN=6 |
| 125 | # CONFIG_FEATURE_USE_CNG_API is not set | ||
| 123 | CONFIG_MD5_SMALL=1 | 126 | CONFIG_MD5_SMALL=1 |
| 124 | CONFIG_SHA1_SMALL=3 | 127 | CONFIG_SHA1_SMALL=3 |
| 125 | # CONFIG_SHA1_HWACCEL is not set | 128 | # CONFIG_SHA1_HWACCEL is not set |
| @@ -225,6 +228,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y | |||
| 225 | CONFIG_FEATURE_UNZIP_LZMA=y | 228 | CONFIG_FEATURE_UNZIP_LZMA=y |
| 226 | CONFIG_FEATURE_UNZIP_XZ=y | 229 | CONFIG_FEATURE_UNZIP_XZ=y |
| 227 | CONFIG_FEATURE_LZMA_FAST=y | 230 | CONFIG_FEATURE_LZMA_FAST=y |
| 231 | # CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set | ||
| 228 | 232 | ||
| 229 | # | 233 | # |
| 230 | # Coreutils | 234 | # Coreutils |
| @@ -272,7 +276,7 @@ CONFIG_DD=y | |||
| 272 | CONFIG_FEATURE_DD_IBS_OBS=y | 276 | CONFIG_FEATURE_DD_IBS_OBS=y |
| 273 | CONFIG_FEATURE_DD_STATUS=y | 277 | CONFIG_FEATURE_DD_STATUS=y |
| 274 | CONFIG_DF=y | 278 | CONFIG_DF=y |
| 275 | # CONFIG_FEATURE_DF_FANCY is not set | 279 | CONFIG_FEATURE_DF_FANCY=y |
| 276 | # CONFIG_FEATURE_SKIP_ROOTFS is not set | 280 | # CONFIG_FEATURE_SKIP_ROOTFS is not set |
| 277 | CONFIG_DIRNAME=y | 281 | CONFIG_DIRNAME=y |
| 278 | CONFIG_DOS2UNIX=y | 282 | CONFIG_DOS2UNIX=y |
| @@ -312,11 +316,12 @@ CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y | |||
| 312 | CONFIG_MD5SUM=y | 316 | CONFIG_MD5SUM=y |
| 313 | CONFIG_SHA1SUM=y | 317 | CONFIG_SHA1SUM=y |
| 314 | CONFIG_SHA256SUM=y | 318 | CONFIG_SHA256SUM=y |
| 319 | CONFIG_SHA384SUM=y | ||
| 315 | CONFIG_SHA512SUM=y | 320 | CONFIG_SHA512SUM=y |
| 316 | CONFIG_SHA3SUM=y | 321 | CONFIG_SHA3SUM=y |
| 317 | 322 | ||
| 318 | # | 323 | # |
| 319 | # Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum | 324 | # Common options for md5sum, sha1sum, sha256sum, ..., sha3sum |
| 320 | # | 325 | # |
| 321 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y | 326 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y |
| 322 | CONFIG_MKDIR=y | 327 | CONFIG_MKDIR=y |
| @@ -351,7 +356,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y | |||
| 351 | CONFIG_STAT=y | 356 | CONFIG_STAT=y |
| 352 | CONFIG_FEATURE_STAT_FORMAT=y | 357 | CONFIG_FEATURE_STAT_FORMAT=y |
| 353 | CONFIG_FEATURE_STAT_FILESYSTEM=y | 358 | CONFIG_FEATURE_STAT_FILESYSTEM=y |
| 354 | # CONFIG_STTY is not set | 359 | CONFIG_STTY=y |
| 355 | CONFIG_SUM=y | 360 | CONFIG_SUM=y |
| 356 | CONFIG_SYNC=y | 361 | CONFIG_SYNC=y |
| 357 | # CONFIG_FEATURE_SYNC_FANCY is not set | 362 | # CONFIG_FEATURE_SYNC_FANCY is not set |
| @@ -555,6 +560,7 @@ CONFIG_INIT_TERMINAL_TYPE="" | |||
| 555 | # CONFIG_USE_BB_SHADOW is not set | 560 | # CONFIG_USE_BB_SHADOW is not set |
| 556 | # CONFIG_USE_BB_CRYPT is not set | 561 | # CONFIG_USE_BB_CRYPT is not set |
| 557 | # CONFIG_USE_BB_CRYPT_SHA is not set | 562 | # CONFIG_USE_BB_CRYPT_SHA is not set |
| 563 | # CONFIG_USE_BB_CRYPT_YES is not set | ||
| 558 | # CONFIG_ADD_SHELL is not set | 564 | # CONFIG_ADD_SHELL is not set |
| 559 | # CONFIG_REMOVE_SHELL is not set | 565 | # CONFIG_REMOVE_SHELL is not set |
| 560 | # CONFIG_ADDGROUP is not set | 566 | # CONFIG_ADDGROUP is not set |
| @@ -657,7 +663,7 @@ CONFIG_CAL=y | |||
| 657 | # CONFIG_FEATURE_GPT_LABEL is not set | 663 | # CONFIG_FEATURE_GPT_LABEL is not set |
| 658 | # CONFIG_FEATURE_FDISK_ADVANCED is not set | 664 | # CONFIG_FEATURE_FDISK_ADVANCED is not set |
| 659 | # CONFIG_FINDFS is not set | 665 | # CONFIG_FINDFS is not set |
| 660 | # CONFIG_FLOCK is not set | 666 | CONFIG_FLOCK=y |
| 661 | # CONFIG_FDFLUSH is not set | 667 | # CONFIG_FDFLUSH is not set |
| 662 | # CONFIG_FREERAMDISK is not set | 668 | # CONFIG_FREERAMDISK is not set |
| 663 | # CONFIG_FSCK_MINIX is not set | 669 | # CONFIG_FSCK_MINIX is not set |
| @@ -898,7 +904,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y | |||
| 898 | # CONFIG_FEATURE_ETC_NETWORKS is not set | 904 | # CONFIG_FEATURE_ETC_NETWORKS is not set |
| 899 | # CONFIG_FEATURE_ETC_SERVICES is not set | 905 | # CONFIG_FEATURE_ETC_SERVICES is not set |
| 900 | # CONFIG_FEATURE_HWIB is not set | 906 | # CONFIG_FEATURE_HWIB is not set |
| 907 | CONFIG_FEATURE_TLS_INTERNAL=y | ||
| 908 | # CONFIG_FEATURE_TLS_SCHANNEL is not set | ||
| 901 | # CONFIG_FEATURE_TLS_SHA1 is not set | 909 | # CONFIG_FEATURE_TLS_SHA1 is not set |
| 910 | # CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set | ||
| 902 | # CONFIG_ARP is not set | 911 | # CONFIG_ARP is not set |
| 903 | # CONFIG_ARPING is not set | 912 | # CONFIG_ARPING is not set |
| 904 | # CONFIG_BRCTL is not set | 913 | # CONFIG_BRCTL is not set |
| @@ -964,6 +973,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" | |||
| 964 | # CONFIG_IPNEIGH is not set | 973 | # CONFIG_IPNEIGH is not set |
| 965 | # CONFIG_FEATURE_IP_ADDRESS is not set | 974 | # CONFIG_FEATURE_IP_ADDRESS is not set |
| 966 | # CONFIG_FEATURE_IP_LINK is not set | 975 | # CONFIG_FEATURE_IP_LINK is not set |
| 976 | CONFIG_FEATURE_IP_LINK_CAN=y | ||
| 967 | # CONFIG_FEATURE_IP_ROUTE is not set | 977 | # CONFIG_FEATURE_IP_ROUTE is not set |
| 968 | CONFIG_FEATURE_IP_ROUTE_DIR="" | 978 | CONFIG_FEATURE_IP_ROUTE_DIR="" |
| 969 | # CONFIG_FEATURE_IP_TUNNEL is not set | 979 | # CONFIG_FEATURE_IP_TUNNEL is not set |
| @@ -1187,6 +1197,7 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1187 | # CONFIG_CTTYHACK is not set | 1197 | # CONFIG_CTTYHACK is not set |
| 1188 | # CONFIG_HUSH is not set | 1198 | # CONFIG_HUSH is not set |
| 1189 | # CONFIG_SHELL_HUSH is not set | 1199 | # CONFIG_SHELL_HUSH is not set |
| 1200 | # CONFIG_HUSH_NEED_FOR_SPEED is not set | ||
| 1190 | # CONFIG_HUSH_BASH_COMPAT is not set | 1201 | # CONFIG_HUSH_BASH_COMPAT is not set |
| 1191 | # CONFIG_HUSH_BRACE_EXPANSION is not set | 1202 | # CONFIG_HUSH_BRACE_EXPANSION is not set |
| 1192 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set | 1203 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set |
| @@ -1198,7 +1209,9 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1198 | # CONFIG_HUSH_IF is not set | 1209 | # CONFIG_HUSH_IF is not set |
| 1199 | # CONFIG_HUSH_LOOPS is not set | 1210 | # CONFIG_HUSH_LOOPS is not set |
| 1200 | # CONFIG_HUSH_CASE is not set | 1211 | # CONFIG_HUSH_CASE is not set |
| 1212 | # CONFIG_HUSH_ALIAS is not set | ||
| 1201 | # CONFIG_HUSH_FUNCTIONS is not set | 1213 | # CONFIG_HUSH_FUNCTIONS is not set |
| 1214 | # CONFIG_HUSH_FUNCTION_KEYWORD is not set | ||
| 1202 | # CONFIG_HUSH_LOCAL is not set | 1215 | # CONFIG_HUSH_LOCAL is not set |
| 1203 | # CONFIG_HUSH_RANDOM_SUPPORT is not set | 1216 | # CONFIG_HUSH_RANDOM_SUPPORT is not set |
| 1204 | # CONFIG_HUSH_MODE_X is not set | 1217 | # CONFIG_HUSH_MODE_X is not set |
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index fbd2bc6e3..c5c50834e 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 | # Thu Nov 6 08:23:16 2025 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
| @@ -44,8 +44,6 @@ CONFIG_BUSYBOX_EXEC_PATH="" | |||
| 44 | # Settings for MINGW32 | 44 | # Settings for MINGW32 |
| 45 | # | 45 | # |
| 46 | CONFIG_GLOBBING=y | 46 | CONFIG_GLOBBING=y |
| 47 | CONFIG_FEATURE_PRNG_SHELL=y | ||
| 48 | # CONFIG_FEATURE_PRNG_ISAAC is not set | ||
| 49 | CONFIG_FEATURE_RESOURCES=y | 47 | CONFIG_FEATURE_RESOURCES=y |
| 50 | CONFIG_FEATURE_VERSIONINFO=y | 48 | CONFIG_FEATURE_VERSIONINFO=y |
| 51 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set | 49 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set |
| @@ -55,6 +53,9 @@ CONFIG_FEATURE_ICON=y | |||
| 55 | # CONFIG_FEATURE_ICON_ATERM is not set | 53 | # CONFIG_FEATURE_ICON_ATERM is not set |
| 56 | # CONFIG_FEATURE_ICON_STERM is not set | 54 | # CONFIG_FEATURE_ICON_STERM is not set |
| 57 | CONFIG_FEATURE_ICON_ALL=y | 55 | CONFIG_FEATURE_ICON_ALL=y |
| 56 | # CONFIG_FEATURE_PNG_ATERM is not set | ||
| 57 | # CONFIG_FEATURE_PNG_STERM is not set | ||
| 58 | # CONFIG_FEATURE_PNG_ALL is not set | ||
| 58 | CONFIG_FEATURE_EURO=y | 59 | CONFIG_FEATURE_EURO=y |
| 59 | # CONFIG_FEATURE_UTF8_INPUT is not set | 60 | # CONFIG_FEATURE_UTF8_INPUT is not set |
| 60 | # CONFIG_FEATURE_UTF8_OUTPUT is not set | 61 | # CONFIG_FEATURE_UTF8_OUTPUT is not set |
| @@ -62,6 +63,7 @@ CONFIG_TERMINAL_MODE=5 | |||
| 62 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y | 63 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y |
| 63 | CONFIG_FEATURE_EXTRA_FILE_DATA=y | 64 | CONFIG_FEATURE_EXTRA_FILE_DATA=y |
| 64 | CONFIG_OVERRIDE_APPLETS="" | 65 | CONFIG_OVERRIDE_APPLETS="" |
| 66 | CONFIG_UNWIND_TABLES=y | ||
| 65 | 67 | ||
| 66 | # | 68 | # |
| 67 | # Build Options | 69 | # Build Options |
| @@ -120,6 +122,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y | |||
| 120 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set | 122 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set |
| 121 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set | 123 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set |
| 122 | CONFIG_PASSWORD_MINLEN=6 | 124 | CONFIG_PASSWORD_MINLEN=6 |
| 125 | # CONFIG_FEATURE_USE_CNG_API is not set | ||
| 123 | CONFIG_MD5_SMALL=1 | 126 | CONFIG_MD5_SMALL=1 |
| 124 | CONFIG_SHA1_SMALL=3 | 127 | CONFIG_SHA1_SMALL=3 |
| 125 | # CONFIG_SHA1_HWACCEL is not set | 128 | # CONFIG_SHA1_HWACCEL is not set |
| @@ -225,6 +228,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y | |||
| 225 | CONFIG_FEATURE_UNZIP_LZMA=y | 228 | CONFIG_FEATURE_UNZIP_LZMA=y |
| 226 | CONFIG_FEATURE_UNZIP_XZ=y | 229 | CONFIG_FEATURE_UNZIP_XZ=y |
| 227 | CONFIG_FEATURE_LZMA_FAST=y | 230 | CONFIG_FEATURE_LZMA_FAST=y |
| 231 | # CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set | ||
| 228 | 232 | ||
| 229 | # | 233 | # |
| 230 | # Coreutils | 234 | # Coreutils |
| @@ -272,7 +276,7 @@ CONFIG_DD=y | |||
| 272 | CONFIG_FEATURE_DD_IBS_OBS=y | 276 | CONFIG_FEATURE_DD_IBS_OBS=y |
| 273 | CONFIG_FEATURE_DD_STATUS=y | 277 | CONFIG_FEATURE_DD_STATUS=y |
| 274 | CONFIG_DF=y | 278 | CONFIG_DF=y |
| 275 | # CONFIG_FEATURE_DF_FANCY is not set | 279 | CONFIG_FEATURE_DF_FANCY=y |
| 276 | # CONFIG_FEATURE_SKIP_ROOTFS is not set | 280 | # CONFIG_FEATURE_SKIP_ROOTFS is not set |
| 277 | CONFIG_DIRNAME=y | 281 | CONFIG_DIRNAME=y |
| 278 | CONFIG_DOS2UNIX=y | 282 | CONFIG_DOS2UNIX=y |
| @@ -312,11 +316,12 @@ CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y | |||
| 312 | CONFIG_MD5SUM=y | 316 | CONFIG_MD5SUM=y |
| 313 | CONFIG_SHA1SUM=y | 317 | CONFIG_SHA1SUM=y |
| 314 | CONFIG_SHA256SUM=y | 318 | CONFIG_SHA256SUM=y |
| 319 | CONFIG_SHA384SUM=y | ||
| 315 | CONFIG_SHA512SUM=y | 320 | CONFIG_SHA512SUM=y |
| 316 | CONFIG_SHA3SUM=y | 321 | CONFIG_SHA3SUM=y |
| 317 | 322 | ||
| 318 | # | 323 | # |
| 319 | # Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum | 324 | # Common options for md5sum, sha1sum, sha256sum, ..., sha3sum |
| 320 | # | 325 | # |
| 321 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y | 326 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y |
| 322 | CONFIG_MKDIR=y | 327 | CONFIG_MKDIR=y |
| @@ -351,7 +356,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y | |||
| 351 | CONFIG_STAT=y | 356 | CONFIG_STAT=y |
| 352 | CONFIG_FEATURE_STAT_FORMAT=y | 357 | CONFIG_FEATURE_STAT_FORMAT=y |
| 353 | CONFIG_FEATURE_STAT_FILESYSTEM=y | 358 | CONFIG_FEATURE_STAT_FILESYSTEM=y |
| 354 | # CONFIG_STTY is not set | 359 | CONFIG_STTY=y |
| 355 | CONFIG_SUM=y | 360 | CONFIG_SUM=y |
| 356 | CONFIG_SYNC=y | 361 | CONFIG_SYNC=y |
| 357 | # CONFIG_FEATURE_SYNC_FANCY is not set | 362 | # CONFIG_FEATURE_SYNC_FANCY is not set |
| @@ -555,6 +560,7 @@ CONFIG_INIT_TERMINAL_TYPE="" | |||
| 555 | # CONFIG_USE_BB_SHADOW is not set | 560 | # CONFIG_USE_BB_SHADOW is not set |
| 556 | # CONFIG_USE_BB_CRYPT is not set | 561 | # CONFIG_USE_BB_CRYPT is not set |
| 557 | # CONFIG_USE_BB_CRYPT_SHA is not set | 562 | # CONFIG_USE_BB_CRYPT_SHA is not set |
| 563 | # CONFIG_USE_BB_CRYPT_YES is not set | ||
| 558 | # CONFIG_ADD_SHELL is not set | 564 | # CONFIG_ADD_SHELL is not set |
| 559 | # CONFIG_REMOVE_SHELL is not set | 565 | # CONFIG_REMOVE_SHELL is not set |
| 560 | # CONFIG_ADDGROUP is not set | 566 | # CONFIG_ADDGROUP is not set |
| @@ -657,7 +663,7 @@ CONFIG_CAL=y | |||
| 657 | # CONFIG_FEATURE_GPT_LABEL is not set | 663 | # CONFIG_FEATURE_GPT_LABEL is not set |
| 658 | # CONFIG_FEATURE_FDISK_ADVANCED is not set | 664 | # CONFIG_FEATURE_FDISK_ADVANCED is not set |
| 659 | # CONFIG_FINDFS is not set | 665 | # CONFIG_FINDFS is not set |
| 660 | # CONFIG_FLOCK is not set | 666 | CONFIG_FLOCK=y |
| 661 | # CONFIG_FDFLUSH is not set | 667 | # CONFIG_FDFLUSH is not set |
| 662 | # CONFIG_FREERAMDISK is not set | 668 | # CONFIG_FREERAMDISK is not set |
| 663 | # CONFIG_FSCK_MINIX is not set | 669 | # CONFIG_FSCK_MINIX is not set |
| @@ -898,7 +904,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y | |||
| 898 | # CONFIG_FEATURE_ETC_NETWORKS is not set | 904 | # CONFIG_FEATURE_ETC_NETWORKS is not set |
| 899 | # CONFIG_FEATURE_ETC_SERVICES is not set | 905 | # CONFIG_FEATURE_ETC_SERVICES is not set |
| 900 | # CONFIG_FEATURE_HWIB is not set | 906 | # CONFIG_FEATURE_HWIB is not set |
| 907 | CONFIG_FEATURE_TLS_INTERNAL=y | ||
| 908 | # CONFIG_FEATURE_TLS_SCHANNEL is not set | ||
| 901 | # CONFIG_FEATURE_TLS_SHA1 is not set | 909 | # CONFIG_FEATURE_TLS_SHA1 is not set |
| 910 | # CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set | ||
| 902 | # CONFIG_ARP is not set | 911 | # CONFIG_ARP is not set |
| 903 | # CONFIG_ARPING is not set | 912 | # CONFIG_ARPING is not set |
| 904 | # CONFIG_BRCTL is not set | 913 | # CONFIG_BRCTL is not set |
| @@ -964,6 +973,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" | |||
| 964 | # CONFIG_IPNEIGH is not set | 973 | # CONFIG_IPNEIGH is not set |
| 965 | # CONFIG_FEATURE_IP_ADDRESS is not set | 974 | # CONFIG_FEATURE_IP_ADDRESS is not set |
| 966 | # CONFIG_FEATURE_IP_LINK is not set | 975 | # CONFIG_FEATURE_IP_LINK is not set |
| 976 | CONFIG_FEATURE_IP_LINK_CAN=y | ||
| 967 | # CONFIG_FEATURE_IP_ROUTE is not set | 977 | # CONFIG_FEATURE_IP_ROUTE is not set |
| 968 | CONFIG_FEATURE_IP_ROUTE_DIR="" | 978 | CONFIG_FEATURE_IP_ROUTE_DIR="" |
| 969 | # CONFIG_FEATURE_IP_TUNNEL is not set | 979 | # CONFIG_FEATURE_IP_TUNNEL is not set |
| @@ -1187,6 +1197,7 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1187 | # CONFIG_CTTYHACK is not set | 1197 | # CONFIG_CTTYHACK is not set |
| 1188 | # CONFIG_HUSH is not set | 1198 | # CONFIG_HUSH is not set |
| 1189 | # CONFIG_SHELL_HUSH is not set | 1199 | # CONFIG_SHELL_HUSH is not set |
| 1200 | # CONFIG_HUSH_NEED_FOR_SPEED is not set | ||
| 1190 | # CONFIG_HUSH_BASH_COMPAT is not set | 1201 | # CONFIG_HUSH_BASH_COMPAT is not set |
| 1191 | # CONFIG_HUSH_BRACE_EXPANSION is not set | 1202 | # CONFIG_HUSH_BRACE_EXPANSION is not set |
| 1192 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set | 1203 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set |
| @@ -1198,7 +1209,9 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1198 | # CONFIG_HUSH_IF is not set | 1209 | # CONFIG_HUSH_IF is not set |
| 1199 | # CONFIG_HUSH_LOOPS is not set | 1210 | # CONFIG_HUSH_LOOPS is not set |
| 1200 | # CONFIG_HUSH_CASE is not set | 1211 | # CONFIG_HUSH_CASE is not set |
| 1212 | # CONFIG_HUSH_ALIAS is not set | ||
| 1201 | # CONFIG_HUSH_FUNCTIONS is not set | 1213 | # CONFIG_HUSH_FUNCTIONS is not set |
| 1214 | # CONFIG_HUSH_FUNCTION_KEYWORD is not set | ||
| 1202 | # CONFIG_HUSH_LOCAL is not set | 1215 | # CONFIG_HUSH_LOCAL is not set |
| 1203 | # CONFIG_HUSH_RANDOM_SUPPORT is not set | 1216 | # CONFIG_HUSH_RANDOM_SUPPORT is not set |
| 1204 | # CONFIG_HUSH_MODE_X is not set | 1217 | # CONFIG_HUSH_MODE_X is not set |
diff --git a/configs/mingw64a_defconfig b/configs/mingw64a_defconfig index e9a88bf62..6daca11db 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 | # Thu Nov 6 08:23:16 2025 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
| @@ -44,24 +44,26 @@ CONFIG_BUSYBOX_EXEC_PATH="" | |||
| 44 | # Settings for MINGW32 | 44 | # Settings for MINGW32 |
| 45 | # | 45 | # |
| 46 | CONFIG_GLOBBING=y | 46 | CONFIG_GLOBBING=y |
| 47 | CONFIG_FEATURE_PRNG_SHELL=y | ||
| 48 | # CONFIG_FEATURE_PRNG_ISAAC is not set | ||
| 49 | CONFIG_FEATURE_RESOURCES=y | 47 | CONFIG_FEATURE_RESOURCES=y |
| 50 | CONFIG_FEATURE_VERSIONINFO=y | 48 | CONFIG_FEATURE_VERSIONINFO=y |
| 51 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set | 49 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set |
| 52 | CONFIG_FEATURE_APP_MANIFEST=y | 50 | # CONFIG_FEATURE_APP_MANIFEST is not set |
| 53 | # CONFIG_FEATURE_UTF8_MANIFEST is not set | 51 | CONFIG_FEATURE_UTF8_MANIFEST=y |
| 54 | CONFIG_FEATURE_ICON=y | 52 | CONFIG_FEATURE_ICON=y |
| 55 | # CONFIG_FEATURE_ICON_ATERM is not set | 53 | # CONFIG_FEATURE_ICON_ATERM is not set |
| 56 | # CONFIG_FEATURE_ICON_STERM is not set | 54 | # CONFIG_FEATURE_ICON_STERM is not set |
| 57 | CONFIG_FEATURE_ICON_ALL=y | 55 | # CONFIG_FEATURE_ICON_ALL is not set |
| 56 | # CONFIG_FEATURE_PNG_ATERM is not set | ||
| 57 | # CONFIG_FEATURE_PNG_STERM is not set | ||
| 58 | CONFIG_FEATURE_PNG_ALL=y | ||
| 58 | CONFIG_FEATURE_EURO=y | 59 | CONFIG_FEATURE_EURO=y |
| 59 | # CONFIG_FEATURE_UTF8_INPUT is not set | 60 | CONFIG_FEATURE_UTF8_INPUT=y |
| 60 | # CONFIG_FEATURE_UTF8_OUTPUT is not set | 61 | CONFIG_FEATURE_UTF8_OUTPUT=y |
| 61 | CONFIG_TERMINAL_MODE=5 | 62 | CONFIG_TERMINAL_MODE=5 |
| 62 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y | 63 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y |
| 63 | CONFIG_FEATURE_EXTRA_FILE_DATA=y | 64 | CONFIG_FEATURE_EXTRA_FILE_DATA=y |
| 64 | CONFIG_OVERRIDE_APPLETS="" | 65 | CONFIG_OVERRIDE_APPLETS="" |
| 66 | # CONFIG_UNWIND_TABLES is not set | ||
| 65 | 67 | ||
| 66 | # | 68 | # |
| 67 | # Build Options | 69 | # Build Options |
| @@ -120,6 +122,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y | |||
| 120 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set | 122 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set |
| 121 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set | 123 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set |
| 122 | CONFIG_PASSWORD_MINLEN=6 | 124 | CONFIG_PASSWORD_MINLEN=6 |
| 125 | CONFIG_FEATURE_USE_CNG_API=y | ||
| 123 | CONFIG_MD5_SMALL=1 | 126 | CONFIG_MD5_SMALL=1 |
| 124 | CONFIG_SHA1_SMALL=3 | 127 | CONFIG_SHA1_SMALL=3 |
| 125 | # CONFIG_SHA1_HWACCEL is not set | 128 | # CONFIG_SHA1_HWACCEL is not set |
| @@ -145,13 +148,13 @@ CONFIG_FEATURE_EDITING_FANCY_PROMPT=y | |||
| 145 | # CONFIG_FEATURE_EDITING_WINCH is not set | 148 | # CONFIG_FEATURE_EDITING_WINCH is not set |
| 146 | # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set | 149 | # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set |
| 147 | # CONFIG_LOCALE_SUPPORT is not set | 150 | # CONFIG_LOCALE_SUPPORT is not set |
| 148 | # CONFIG_UNICODE_SUPPORT is not set | 151 | CONFIG_UNICODE_SUPPORT=y |
| 149 | # CONFIG_UNICODE_USING_LOCALE is not set | 152 | # CONFIG_UNICODE_USING_LOCALE is not set |
| 150 | # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set | 153 | CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y |
| 151 | CONFIG_SUBST_WCHAR=0 | 154 | CONFIG_SUBST_WCHAR=63 |
| 152 | CONFIG_LAST_SUPPORTED_WCHAR=0 | 155 | CONFIG_LAST_SUPPORTED_WCHAR=1114111 |
| 153 | # CONFIG_UNICODE_COMBINING_WCHARS is not set | 156 | CONFIG_UNICODE_COMBINING_WCHARS=y |
| 154 | # CONFIG_UNICODE_WIDE_WCHARS is not set | 157 | CONFIG_UNICODE_WIDE_WCHARS=y |
| 155 | # CONFIG_UNICODE_BIDI_SUPPORT is not set | 158 | # CONFIG_UNICODE_BIDI_SUPPORT is not set |
| 156 | # CONFIG_UNICODE_NEUTRAL_TABLE is not set | 159 | # CONFIG_UNICODE_NEUTRAL_TABLE is not set |
| 157 | # CONFIG_UNICODE_PRESERVE_BROKEN is not set | 160 | # CONFIG_UNICODE_PRESERVE_BROKEN is not set |
| @@ -225,6 +228,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y | |||
| 225 | CONFIG_FEATURE_UNZIP_LZMA=y | 228 | CONFIG_FEATURE_UNZIP_LZMA=y |
| 226 | CONFIG_FEATURE_UNZIP_XZ=y | 229 | CONFIG_FEATURE_UNZIP_XZ=y |
| 227 | CONFIG_FEATURE_LZMA_FAST=y | 230 | CONFIG_FEATURE_LZMA_FAST=y |
| 231 | # CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set | ||
| 228 | 232 | ||
| 229 | # | 233 | # |
| 230 | # Coreutils | 234 | # Coreutils |
| @@ -272,7 +276,7 @@ CONFIG_DD=y | |||
| 272 | CONFIG_FEATURE_DD_IBS_OBS=y | 276 | CONFIG_FEATURE_DD_IBS_OBS=y |
| 273 | CONFIG_FEATURE_DD_STATUS=y | 277 | CONFIG_FEATURE_DD_STATUS=y |
| 274 | CONFIG_DF=y | 278 | CONFIG_DF=y |
| 275 | # CONFIG_FEATURE_DF_FANCY is not set | 279 | CONFIG_FEATURE_DF_FANCY=y |
| 276 | # CONFIG_FEATURE_SKIP_ROOTFS is not set | 280 | # CONFIG_FEATURE_SKIP_ROOTFS is not set |
| 277 | CONFIG_DIRNAME=y | 281 | CONFIG_DIRNAME=y |
| 278 | CONFIG_DOS2UNIX=y | 282 | CONFIG_DOS2UNIX=y |
| @@ -312,11 +316,12 @@ CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y | |||
| 312 | CONFIG_MD5SUM=y | 316 | CONFIG_MD5SUM=y |
| 313 | CONFIG_SHA1SUM=y | 317 | CONFIG_SHA1SUM=y |
| 314 | CONFIG_SHA256SUM=y | 318 | CONFIG_SHA256SUM=y |
| 319 | CONFIG_SHA384SUM=y | ||
| 315 | CONFIG_SHA512SUM=y | 320 | CONFIG_SHA512SUM=y |
| 316 | CONFIG_SHA3SUM=y | 321 | CONFIG_SHA3SUM=y |
| 317 | 322 | ||
| 318 | # | 323 | # |
| 319 | # Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum | 324 | # Common options for md5sum, sha1sum, sha256sum, ..., sha3sum |
| 320 | # | 325 | # |
| 321 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y | 326 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y |
| 322 | CONFIG_MKDIR=y | 327 | CONFIG_MKDIR=y |
| @@ -351,7 +356,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y | |||
| 351 | CONFIG_STAT=y | 356 | CONFIG_STAT=y |
| 352 | CONFIG_FEATURE_STAT_FORMAT=y | 357 | CONFIG_FEATURE_STAT_FORMAT=y |
| 353 | CONFIG_FEATURE_STAT_FILESYSTEM=y | 358 | CONFIG_FEATURE_STAT_FILESYSTEM=y |
| 354 | # CONFIG_STTY is not set | 359 | CONFIG_STTY=y |
| 355 | CONFIG_SUM=y | 360 | CONFIG_SUM=y |
| 356 | CONFIG_SYNC=y | 361 | CONFIG_SYNC=y |
| 357 | # CONFIG_FEATURE_SYNC_FANCY is not set | 362 | # CONFIG_FEATURE_SYNC_FANCY is not set |
| @@ -555,6 +560,7 @@ CONFIG_INIT_TERMINAL_TYPE="" | |||
| 555 | # CONFIG_USE_BB_SHADOW is not set | 560 | # CONFIG_USE_BB_SHADOW is not set |
| 556 | # CONFIG_USE_BB_CRYPT is not set | 561 | # CONFIG_USE_BB_CRYPT is not set |
| 557 | # CONFIG_USE_BB_CRYPT_SHA is not set | 562 | # CONFIG_USE_BB_CRYPT_SHA is not set |
| 563 | # CONFIG_USE_BB_CRYPT_YES is not set | ||
| 558 | # CONFIG_ADD_SHELL is not set | 564 | # CONFIG_ADD_SHELL is not set |
| 559 | # CONFIG_REMOVE_SHELL is not set | 565 | # CONFIG_REMOVE_SHELL is not set |
| 560 | # CONFIG_ADDGROUP is not set | 566 | # CONFIG_ADDGROUP is not set |
| @@ -657,7 +663,7 @@ CONFIG_CAL=y | |||
| 657 | # CONFIG_FEATURE_GPT_LABEL is not set | 663 | # CONFIG_FEATURE_GPT_LABEL is not set |
| 658 | # CONFIG_FEATURE_FDISK_ADVANCED is not set | 664 | # CONFIG_FEATURE_FDISK_ADVANCED is not set |
| 659 | # CONFIG_FINDFS is not set | 665 | # CONFIG_FINDFS is not set |
| 660 | # CONFIG_FLOCK is not set | 666 | CONFIG_FLOCK=y |
| 661 | # CONFIG_FDFLUSH is not set | 667 | # CONFIG_FDFLUSH is not set |
| 662 | # CONFIG_FREERAMDISK is not set | 668 | # CONFIG_FREERAMDISK is not set |
| 663 | # CONFIG_FSCK_MINIX is not set | 669 | # CONFIG_FSCK_MINIX is not set |
| @@ -898,7 +904,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y | |||
| 898 | # CONFIG_FEATURE_ETC_NETWORKS is not set | 904 | # CONFIG_FEATURE_ETC_NETWORKS is not set |
| 899 | # CONFIG_FEATURE_ETC_SERVICES is not set | 905 | # CONFIG_FEATURE_ETC_SERVICES is not set |
| 900 | # CONFIG_FEATURE_HWIB is not set | 906 | # CONFIG_FEATURE_HWIB is not set |
| 907 | # CONFIG_FEATURE_TLS_INTERNAL is not set | ||
| 908 | CONFIG_FEATURE_TLS_SCHANNEL=y | ||
| 901 | # CONFIG_FEATURE_TLS_SHA1 is not set | 909 | # CONFIG_FEATURE_TLS_SHA1 is not set |
| 910 | # CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set | ||
| 902 | # CONFIG_ARP is not set | 911 | # CONFIG_ARP is not set |
| 903 | # CONFIG_ARPING is not set | 912 | # CONFIG_ARPING is not set |
| 904 | # CONFIG_BRCTL is not set | 913 | # CONFIG_BRCTL is not set |
| @@ -964,6 +973,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" | |||
| 964 | # CONFIG_IPNEIGH is not set | 973 | # CONFIG_IPNEIGH is not set |
| 965 | # CONFIG_FEATURE_IP_ADDRESS is not set | 974 | # CONFIG_FEATURE_IP_ADDRESS is not set |
| 966 | # CONFIG_FEATURE_IP_LINK is not set | 975 | # CONFIG_FEATURE_IP_LINK is not set |
| 976 | CONFIG_FEATURE_IP_LINK_CAN=y | ||
| 967 | # CONFIG_FEATURE_IP_ROUTE is not set | 977 | # CONFIG_FEATURE_IP_ROUTE is not set |
| 968 | CONFIG_FEATURE_IP_ROUTE_DIR="" | 978 | CONFIG_FEATURE_IP_ROUTE_DIR="" |
| 969 | # CONFIG_FEATURE_IP_TUNNEL is not set | 979 | # CONFIG_FEATURE_IP_TUNNEL is not set |
| @@ -1187,6 +1197,7 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1187 | # CONFIG_CTTYHACK is not set | 1197 | # CONFIG_CTTYHACK is not set |
| 1188 | # CONFIG_HUSH is not set | 1198 | # CONFIG_HUSH is not set |
| 1189 | # CONFIG_SHELL_HUSH is not set | 1199 | # CONFIG_SHELL_HUSH is not set |
| 1200 | # CONFIG_HUSH_NEED_FOR_SPEED is not set | ||
| 1190 | # CONFIG_HUSH_BASH_COMPAT is not set | 1201 | # CONFIG_HUSH_BASH_COMPAT is not set |
| 1191 | # CONFIG_HUSH_BRACE_EXPANSION is not set | 1202 | # CONFIG_HUSH_BRACE_EXPANSION is not set |
| 1192 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set | 1203 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set |
| @@ -1198,7 +1209,9 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1198 | # CONFIG_HUSH_IF is not set | 1209 | # CONFIG_HUSH_IF is not set |
| 1199 | # CONFIG_HUSH_LOOPS is not set | 1210 | # CONFIG_HUSH_LOOPS is not set |
| 1200 | # CONFIG_HUSH_CASE is not set | 1211 | # CONFIG_HUSH_CASE is not set |
| 1212 | # CONFIG_HUSH_ALIAS is not set | ||
| 1201 | # CONFIG_HUSH_FUNCTIONS is not set | 1213 | # CONFIG_HUSH_FUNCTIONS is not set |
| 1214 | # CONFIG_HUSH_FUNCTION_KEYWORD is not set | ||
| 1202 | # CONFIG_HUSH_LOCAL is not set | 1215 | # CONFIG_HUSH_LOCAL is not set |
| 1203 | # CONFIG_HUSH_RANDOM_SUPPORT is not set | 1216 | # CONFIG_HUSH_RANDOM_SUPPORT is not set |
| 1204 | # CONFIG_HUSH_MODE_X is not set | 1217 | # CONFIG_HUSH_MODE_X is not set |
diff --git a/configs/mingw64u_defconfig b/configs/mingw64u_defconfig index 61753699d..eac615259 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 | # Thu Nov 6 08:23:16 2025 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
| @@ -44,8 +44,6 @@ CONFIG_BUSYBOX_EXEC_PATH="" | |||
| 44 | # Settings for MINGW32 | 44 | # Settings for MINGW32 |
| 45 | # | 45 | # |
| 46 | CONFIG_GLOBBING=y | 46 | CONFIG_GLOBBING=y |
| 47 | CONFIG_FEATURE_PRNG_SHELL=y | ||
| 48 | # CONFIG_FEATURE_PRNG_ISAAC is not set | ||
| 49 | CONFIG_FEATURE_RESOURCES=y | 47 | CONFIG_FEATURE_RESOURCES=y |
| 50 | CONFIG_FEATURE_VERSIONINFO=y | 48 | CONFIG_FEATURE_VERSIONINFO=y |
| 51 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set | 49 | # CONFIG_FEATURE_TOOLCHAIN_MANIFEST is not set |
| @@ -54,7 +52,10 @@ CONFIG_FEATURE_UTF8_MANIFEST=y | |||
| 54 | CONFIG_FEATURE_ICON=y | 52 | CONFIG_FEATURE_ICON=y |
| 55 | # CONFIG_FEATURE_ICON_ATERM is not set | 53 | # CONFIG_FEATURE_ICON_ATERM is not set |
| 56 | # CONFIG_FEATURE_ICON_STERM is not set | 54 | # CONFIG_FEATURE_ICON_STERM is not set |
| 57 | CONFIG_FEATURE_ICON_ALL=y | 55 | # CONFIG_FEATURE_ICON_ALL is not set |
| 56 | # CONFIG_FEATURE_PNG_ATERM is not set | ||
| 57 | # CONFIG_FEATURE_PNG_STERM is not set | ||
| 58 | CONFIG_FEATURE_PNG_ALL=y | ||
| 58 | CONFIG_FEATURE_EURO=y | 59 | CONFIG_FEATURE_EURO=y |
| 59 | CONFIG_FEATURE_UTF8_INPUT=y | 60 | CONFIG_FEATURE_UTF8_INPUT=y |
| 60 | CONFIG_FEATURE_UTF8_OUTPUT=y | 61 | CONFIG_FEATURE_UTF8_OUTPUT=y |
| @@ -62,6 +63,7 @@ CONFIG_TERMINAL_MODE=5 | |||
| 62 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y | 63 | CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y |
| 63 | CONFIG_FEATURE_EXTRA_FILE_DATA=y | 64 | CONFIG_FEATURE_EXTRA_FILE_DATA=y |
| 64 | CONFIG_OVERRIDE_APPLETS="" | 65 | CONFIG_OVERRIDE_APPLETS="" |
| 66 | CONFIG_UNWIND_TABLES=y | ||
| 65 | 67 | ||
| 66 | # | 68 | # |
| 67 | # Build Options | 69 | # Build Options |
| @@ -120,6 +122,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y | |||
| 120 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set | 122 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set |
| 121 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set | 123 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set |
| 122 | CONFIG_PASSWORD_MINLEN=6 | 124 | CONFIG_PASSWORD_MINLEN=6 |
| 125 | CONFIG_FEATURE_USE_CNG_API=y | ||
| 123 | CONFIG_MD5_SMALL=1 | 126 | CONFIG_MD5_SMALL=1 |
| 124 | CONFIG_SHA1_SMALL=3 | 127 | CONFIG_SHA1_SMALL=3 |
| 125 | # CONFIG_SHA1_HWACCEL is not set | 128 | # CONFIG_SHA1_HWACCEL is not set |
| @@ -225,6 +228,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y | |||
| 225 | CONFIG_FEATURE_UNZIP_LZMA=y | 228 | CONFIG_FEATURE_UNZIP_LZMA=y |
| 226 | CONFIG_FEATURE_UNZIP_XZ=y | 229 | CONFIG_FEATURE_UNZIP_XZ=y |
| 227 | CONFIG_FEATURE_LZMA_FAST=y | 230 | CONFIG_FEATURE_LZMA_FAST=y |
| 231 | # CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set | ||
| 228 | 232 | ||
| 229 | # | 233 | # |
| 230 | # Coreutils | 234 | # Coreutils |
| @@ -272,7 +276,7 @@ CONFIG_DD=y | |||
| 272 | CONFIG_FEATURE_DD_IBS_OBS=y | 276 | CONFIG_FEATURE_DD_IBS_OBS=y |
| 273 | CONFIG_FEATURE_DD_STATUS=y | 277 | CONFIG_FEATURE_DD_STATUS=y |
| 274 | CONFIG_DF=y | 278 | CONFIG_DF=y |
| 275 | # CONFIG_FEATURE_DF_FANCY is not set | 279 | CONFIG_FEATURE_DF_FANCY=y |
| 276 | # CONFIG_FEATURE_SKIP_ROOTFS is not set | 280 | # CONFIG_FEATURE_SKIP_ROOTFS is not set |
| 277 | CONFIG_DIRNAME=y | 281 | CONFIG_DIRNAME=y |
| 278 | CONFIG_DOS2UNIX=y | 282 | CONFIG_DOS2UNIX=y |
| @@ -312,11 +316,12 @@ CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y | |||
| 312 | CONFIG_MD5SUM=y | 316 | CONFIG_MD5SUM=y |
| 313 | CONFIG_SHA1SUM=y | 317 | CONFIG_SHA1SUM=y |
| 314 | CONFIG_SHA256SUM=y | 318 | CONFIG_SHA256SUM=y |
| 319 | CONFIG_SHA384SUM=y | ||
| 315 | CONFIG_SHA512SUM=y | 320 | CONFIG_SHA512SUM=y |
| 316 | CONFIG_SHA3SUM=y | 321 | CONFIG_SHA3SUM=y |
| 317 | 322 | ||
| 318 | # | 323 | # |
| 319 | # Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum | 324 | # Common options for md5sum, sha1sum, sha256sum, ..., sha3sum |
| 320 | # | 325 | # |
| 321 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y | 326 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y |
| 322 | CONFIG_MKDIR=y | 327 | CONFIG_MKDIR=y |
| @@ -351,7 +356,7 @@ CONFIG_FEATURE_SPLIT_FANCY=y | |||
| 351 | CONFIG_STAT=y | 356 | CONFIG_STAT=y |
| 352 | CONFIG_FEATURE_STAT_FORMAT=y | 357 | CONFIG_FEATURE_STAT_FORMAT=y |
| 353 | CONFIG_FEATURE_STAT_FILESYSTEM=y | 358 | CONFIG_FEATURE_STAT_FILESYSTEM=y |
| 354 | # CONFIG_STTY is not set | 359 | CONFIG_STTY=y |
| 355 | CONFIG_SUM=y | 360 | CONFIG_SUM=y |
| 356 | CONFIG_SYNC=y | 361 | CONFIG_SYNC=y |
| 357 | # CONFIG_FEATURE_SYNC_FANCY is not set | 362 | # CONFIG_FEATURE_SYNC_FANCY is not set |
| @@ -555,6 +560,7 @@ CONFIG_INIT_TERMINAL_TYPE="" | |||
| 555 | # CONFIG_USE_BB_SHADOW is not set | 560 | # CONFIG_USE_BB_SHADOW is not set |
| 556 | # CONFIG_USE_BB_CRYPT is not set | 561 | # CONFIG_USE_BB_CRYPT is not set |
| 557 | # CONFIG_USE_BB_CRYPT_SHA is not set | 562 | # CONFIG_USE_BB_CRYPT_SHA is not set |
| 563 | # CONFIG_USE_BB_CRYPT_YES is not set | ||
| 558 | # CONFIG_ADD_SHELL is not set | 564 | # CONFIG_ADD_SHELL is not set |
| 559 | # CONFIG_REMOVE_SHELL is not set | 565 | # CONFIG_REMOVE_SHELL is not set |
| 560 | # CONFIG_ADDGROUP is not set | 566 | # CONFIG_ADDGROUP is not set |
| @@ -657,7 +663,7 @@ CONFIG_CAL=y | |||
| 657 | # CONFIG_FEATURE_GPT_LABEL is not set | 663 | # CONFIG_FEATURE_GPT_LABEL is not set |
| 658 | # CONFIG_FEATURE_FDISK_ADVANCED is not set | 664 | # CONFIG_FEATURE_FDISK_ADVANCED is not set |
| 659 | # CONFIG_FINDFS is not set | 665 | # CONFIG_FINDFS is not set |
| 660 | # CONFIG_FLOCK is not set | 666 | CONFIG_FLOCK=y |
| 661 | # CONFIG_FDFLUSH is not set | 667 | # CONFIG_FDFLUSH is not set |
| 662 | # CONFIG_FREERAMDISK is not set | 668 | # CONFIG_FREERAMDISK is not set |
| 663 | # CONFIG_FSCK_MINIX is not set | 669 | # CONFIG_FSCK_MINIX is not set |
| @@ -898,7 +904,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y | |||
| 898 | # CONFIG_FEATURE_ETC_NETWORKS is not set | 904 | # CONFIG_FEATURE_ETC_NETWORKS is not set |
| 899 | # CONFIG_FEATURE_ETC_SERVICES is not set | 905 | # CONFIG_FEATURE_ETC_SERVICES is not set |
| 900 | # CONFIG_FEATURE_HWIB is not set | 906 | # CONFIG_FEATURE_HWIB is not set |
| 907 | # CONFIG_FEATURE_TLS_INTERNAL is not set | ||
| 908 | CONFIG_FEATURE_TLS_SCHANNEL=y | ||
| 901 | # CONFIG_FEATURE_TLS_SHA1 is not set | 909 | # CONFIG_FEATURE_TLS_SHA1 is not set |
| 910 | # CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set | ||
| 902 | # CONFIG_ARP is not set | 911 | # CONFIG_ARP is not set |
| 903 | # CONFIG_ARPING is not set | 912 | # CONFIG_ARPING is not set |
| 904 | # CONFIG_BRCTL is not set | 913 | # CONFIG_BRCTL is not set |
| @@ -964,6 +973,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" | |||
| 964 | # CONFIG_IPNEIGH is not set | 973 | # CONFIG_IPNEIGH is not set |
| 965 | # CONFIG_FEATURE_IP_ADDRESS is not set | 974 | # CONFIG_FEATURE_IP_ADDRESS is not set |
| 966 | # CONFIG_FEATURE_IP_LINK is not set | 975 | # CONFIG_FEATURE_IP_LINK is not set |
| 976 | CONFIG_FEATURE_IP_LINK_CAN=y | ||
| 967 | # CONFIG_FEATURE_IP_ROUTE is not set | 977 | # CONFIG_FEATURE_IP_ROUTE is not set |
| 968 | CONFIG_FEATURE_IP_ROUTE_DIR="" | 978 | CONFIG_FEATURE_IP_ROUTE_DIR="" |
| 969 | # CONFIG_FEATURE_IP_TUNNEL is not set | 979 | # CONFIG_FEATURE_IP_TUNNEL is not set |
| @@ -1187,6 +1197,7 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1187 | # CONFIG_CTTYHACK is not set | 1197 | # CONFIG_CTTYHACK is not set |
| 1188 | # CONFIG_HUSH is not set | 1198 | # CONFIG_HUSH is not set |
| 1189 | # CONFIG_SHELL_HUSH is not set | 1199 | # CONFIG_SHELL_HUSH is not set |
| 1200 | # CONFIG_HUSH_NEED_FOR_SPEED is not set | ||
| 1190 | # CONFIG_HUSH_BASH_COMPAT is not set | 1201 | # CONFIG_HUSH_BASH_COMPAT is not set |
| 1191 | # CONFIG_HUSH_BRACE_EXPANSION is not set | 1202 | # CONFIG_HUSH_BRACE_EXPANSION is not set |
| 1192 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set | 1203 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set |
| @@ -1198,7 +1209,9 @@ CONFIG_ASH_GLOB_OPTIONS=y | |||
| 1198 | # CONFIG_HUSH_IF is not set | 1209 | # CONFIG_HUSH_IF is not set |
| 1199 | # CONFIG_HUSH_LOOPS is not set | 1210 | # CONFIG_HUSH_LOOPS is not set |
| 1200 | # CONFIG_HUSH_CASE is not set | 1211 | # CONFIG_HUSH_CASE is not set |
| 1212 | # CONFIG_HUSH_ALIAS is not set | ||
| 1201 | # CONFIG_HUSH_FUNCTIONS is not set | 1213 | # CONFIG_HUSH_FUNCTIONS is not set |
| 1214 | # CONFIG_HUSH_FUNCTION_KEYWORD is not set | ||
| 1202 | # CONFIG_HUSH_LOCAL is not set | 1215 | # CONFIG_HUSH_LOCAL is not set |
| 1203 | # CONFIG_HUSH_RANDOM_SUPPORT is not set | 1216 | # CONFIG_HUSH_RANDOM_SUPPORT is not set |
| 1204 | # CONFIG_HUSH_MODE_X is not set | 1217 | # CONFIG_HUSH_MODE_X is not set |
diff --git a/console-tools/reset.c b/console-tools/reset.c index 74fe3cb50..d7d9857a0 100644 --- a/console-tools/reset.c +++ b/console-tools/reset.c | |||
| @@ -17,8 +17,12 @@ | |||
| 17 | // NOTE: For WIN32 this applet is NOFORK so we can change the screen | 17 | // NOTE: For WIN32 this applet is NOFORK so we can change the screen |
| 18 | // buffer for the current process. | 18 | // buffer for the current process. |
| 19 | 19 | ||
| 20 | //applet:IF_PLATFORM_MINGW32(IF_RESET(APPLET_NOFORK(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset))) | 20 | //applet:IF_PLATFORM_MINGW32( |
| 21 | //applet:IF_PLATFORM_POSIX(IF_RESET(APPLET_NOEXEC(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset))) | 21 | //applet:IF_RESET(APPLET_NOFORK(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset)) |
| 22 | //applet:) | ||
| 23 | //applet:IF_PLATFORM_POSIX( | ||
| 24 | //applet:IF_RESET(APPLET_NOEXEC(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset)) | ||
| 25 | //applet:) | ||
| 22 | 26 | ||
| 23 | //kbuild:lib-$(CONFIG_RESET) += reset.o | 27 | //kbuild:lib-$(CONFIG_RESET) += reset.o |
| 24 | 28 | ||
diff --git a/console-tools/showkey.c b/console-tools/showkey.c index 87532a8ac..5767f2098 100644 --- a/console-tools/showkey.c +++ b/console-tools/showkey.c | |||
| @@ -70,8 +70,8 @@ int showkey_main(int argc UNUSED_PARAM, char **argv) | |||
| 70 | 70 | ||
| 71 | INIT_G(); | 71 | INIT_G(); |
| 72 | 72 | ||
| 73 | // FIXME: aks are all mutually exclusive | 73 | // a,k,s are all mutually exclusive |
| 74 | getopt32(argv, "aks"); | 74 | getopt32(argv, "^" "aks" "\0" "a--ks:k--as:s--ak"); |
| 75 | 75 | ||
| 76 | // prepare for raw mode | 76 | // prepare for raw mode |
| 77 | xget1(&tio, &tio0); | 77 | xget1(&tio, &tio0); |
diff --git a/coreutils/cp.c b/coreutils/cp.c index ee40af50b..961de5b42 100644 --- a/coreutils/cp.c +++ b/coreutils/cp.c | |||
| @@ -215,7 +215,8 @@ int cp_main(int argc, char **argv) | |||
| 215 | (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); | 215 | (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); |
| 216 | if (s_flags < 0) /* error other than ENOENT */ | 216 | if (s_flags < 0) /* error other than ENOENT */ |
| 217 | return EXIT_FAILURE; | 217 | return EXIT_FAILURE; |
| 218 | d_flags = cp_mv_stat(last, &dest_stat); | 218 | d_flags = cp_mv_stat2(last, &dest_stat, |
| 219 | (flags & FILEUTILS_NO_TARGET_DIR) ? lstat : stat); | ||
| 219 | if (d_flags < 0) /* error other than ENOENT */ | 220 | if (d_flags < 0) /* error other than ENOENT */ |
| 220 | return EXIT_FAILURE; | 221 | return EXIT_FAILURE; |
| 221 | 222 | ||
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 | ||
| 58 | typedef 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 | ||
| 77 | struct cut_list { | 85 | #define opt_REGEX (option_mask32 & OPT_REGEX) |
| 78 | int startpos; | 86 | |
| 79 | int endpos; | 87 | struct cut_range { |
| 88 | unsigned startpos; | ||
| 89 | unsigned endpos; | ||
| 80 | }; | 90 | }; |
| 81 | 91 | ||
| 82 | static int cmpfunc(const void *a, const void *b) | 92 | static 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 | |||
| 88 | static void cut_file(FILE *file, const char *delim, const char *odelim, | 102 | static 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(®, 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(®, 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 | ||
| 215 | int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 291 | int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 216 | int cut_main(int argc UNUSED_PARAM, char **argv) | 292 | int 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(<ok, "-"); | ||
| 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(<ok, "-"); | 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(®, delim, REG_EXTENDED); |
| 324 | if (!(opt & CUT_OPT_NOSORT_FLGS)) | 413 | delim = (void*) ® |
| 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/dd.c b/coreutils/dd.c index e6b88acf5..f439326fd 100644 --- a/coreutils/dd.c +++ b/coreutils/dd.c | |||
| @@ -290,12 +290,6 @@ static bool write_and_stats(const void *buf, size_t len, size_t obs, | |||
| 290 | return 1; | 290 | return 1; |
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | #if ENABLE_LFS | ||
| 294 | # define XATOU_SFX xatoull_sfx | ||
| 295 | #else | ||
| 296 | # define XATOU_SFX xatoul_sfx | ||
| 297 | #endif | ||
| 298 | |||
| 299 | #if ENABLE_FEATURE_DD_IBS_OBS | 293 | #if ENABLE_FEATURE_DD_IBS_OBS |
| 300 | static int parse_comma_flags(char *val, const char *words, const char *error_in) | 294 | static int parse_comma_flags(char *val, const char *words, const char *error_in) |
| 301 | { | 295 | { |
| @@ -488,15 +482,15 @@ int dd_main(int argc UNUSED_PARAM, char **argv) | |||
| 488 | /* These can be large: */ | 482 | /* These can be large: */ |
| 489 | if (what == OP_count) { | 483 | if (what == OP_count) { |
| 490 | G.flags |= FLAG_COUNT; | 484 | G.flags |= FLAG_COUNT; |
| 491 | count = XATOU_SFX(val, cwbkMG_suffixes); | 485 | count = XATOOFF_SFX(val, cwbkMG_suffixes); |
| 492 | /*continue;*/ | 486 | /*continue;*/ |
| 493 | } | 487 | } |
| 494 | if (what == OP_seek) { | 488 | if (what == OP_seek) { |
| 495 | seek = XATOU_SFX(val, cwbkMG_suffixes); | 489 | seek = XATOOFF_SFX(val, cwbkMG_suffixes); |
| 496 | /*continue;*/ | 490 | /*continue;*/ |
| 497 | } | 491 | } |
| 498 | if (what == OP_skip) { | 492 | if (what == OP_skip) { |
| 499 | skip = XATOU_SFX(val, cwbkMG_suffixes); | 493 | skip = XATOOFF_SFX(val, cwbkMG_suffixes); |
| 500 | /*continue;*/ | 494 | /*continue;*/ |
| 501 | } | 495 | } |
| 502 | if (what == OP_if) { | 496 | if (what == OP_if) { |
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 | |||
| 112 | int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 122 | int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 113 | int df_main(int argc UNUSED_PARAM, char **argv) | 123 | int 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. */ |
| 101 | enum { | 105 | enum { |
| 102 | INTEGER, | 106 | INTEGER, |
diff --git a/coreutils/ls.c b/coreutils/ls.c index 5d2e96a1e..b8d276e53 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
| @@ -98,6 +98,7 @@ | |||
| 98 | 98 | ||
| 99 | //usage:#define ls_trivial_usage | 99 | //usage:#define ls_trivial_usage |
| 100 | //usage: "[-1AaCxd" | 100 | //usage: "[-1AaCxd" |
| 101 | //usage: IF_PLATFORM_MINGW32("g") | ||
| 101 | //usage: IF_FEATURE_LS_FOLLOWLINKS("LH") | 102 | //usage: IF_FEATURE_LS_FOLLOWLINKS("LH") |
| 102 | //usage: IF_FEATURE_LS_RECURSIVE("R") | 103 | //usage: IF_FEATURE_LS_RECURSIVE("R") |
| 103 | //usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" | 104 | //usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" |
| @@ -113,6 +114,7 @@ | |||
| 113 | //usage: "\n -A Like -a, but exclude . and .." | 114 | //usage: "\n -A Like -a, but exclude . and .." |
| 114 | //usage: IF_PLATFORM_MINGW32( | 115 | //usage: IF_PLATFORM_MINGW32( |
| 115 | //usage: "\n -aa,-AA Like -a,-A but omit hidden system files" | 116 | //usage: "\n -aa,-AA Like -a,-A but omit hidden system files" |
| 117 | //usage: "\n -C List by columns" | ||
| 116 | //usage: ) | 118 | //usage: ) |
| 117 | ////usage: "\n -C List by columns" - don't show, this is a default anyway | 119 | ////usage: "\n -C List by columns" - don't show, this is a default anyway |
| 118 | //usage: "\n -x List by lines" | 120 | //usage: "\n -x List by lines" |
| @@ -129,6 +131,11 @@ | |||
| 129 | //usage: "\n -F Append indicator (one of */=@|) to names" | 131 | //usage: "\n -F Append indicator (one of */=@|) to names" |
| 130 | //usage: ) | 132 | //usage: ) |
| 131 | //usage: "\n -l Long format" | 133 | //usage: "\n -l Long format" |
| 134 | //usage: IF_PLATFORM_MINGW32( | ||
| 135 | //usage: "\n -g Long format without group column" | ||
| 136 | //usage: ) | ||
| 137 | ////usage: "\n -g Long format without group column" | ||
| 138 | ////TODO: support -G too ("suppress owner column", GNUism) | ||
| 132 | //usage: "\n -i List inode numbers" | 139 | //usage: "\n -i List inode numbers" |
| 133 | //usage: "\n -n List numeric UIDs and GIDs instead of names" | 140 | //usage: "\n -n List numeric UIDs and GIDs instead of names" |
| 134 | //usage: "\n -s List allocated blocks" | 141 | //usage: "\n -s List allocated blocks" |
| @@ -162,6 +169,8 @@ | |||
| 162 | //usage: IF_FEATURE_LS_WIDTH( | 169 | //usage: IF_FEATURE_LS_WIDTH( |
| 163 | //usage: "\n -w N Format N columns wide" | 170 | //usage: "\n -w N Format N columns wide" |
| 164 | //usage: ) | 171 | //usage: ) |
| 172 | ////usage: "\n -Q Double-quote names" | ||
| 173 | ////usage: "\n -q Replace unprintable chars with '?'" | ||
| 165 | //usage: IF_FEATURE_LS_COLOR( | 174 | //usage: IF_FEATURE_LS_COLOR( |
| 166 | //usage: "\n --color[={always,never,auto}]" | 175 | //usage: "\n --color[={always,never,auto}]" |
| 167 | //usage: ) | 176 | //usage: ) |
| @@ -199,27 +208,47 @@ SPLIT_SUBDIR = 2, | |||
| 199 | 208 | ||
| 200 | /* -Cadi1l Std options, busybox always supports */ | 209 | /* -Cadi1l Std options, busybox always supports */ |
| 201 | /* -gnsxA Std options, busybox always supports */ | 210 | /* -gnsxA Std options, busybox always supports */ |
| 202 | /* -Q GNU option, busybox always supports */ | 211 | /* -Q GNU option, busybox always supports: */ |
| 203 | /* -k Std option, busybox always supports (by ignoring) */ | 212 | /* -Q, --quote-name */ |
| 204 | /* It means "for -s, show sizes in kbytes" */ | 213 | /* enclose entry names in double quotes */ |
| 205 | /* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */ | ||
| 206 | /* since otherwise -s shows kbytes anyway */ | ||
| 207 | /* -LHRctur Std options, busybox optionally supports */ | 214 | /* -LHRctur Std options, busybox optionally supports */ |
| 208 | /* -Fp Std options, busybox optionally supports */ | 215 | /* -Fp Std options, busybox optionally supports */ |
| 209 | /* -SXvhTw GNU options, busybox optionally supports */ | 216 | /* -SXvhTw GNU options, busybox optionally supports */ |
| 210 | /* -T WIDTH Ignored (we don't use tabs on output) */ | 217 | /* -T WIDTH Ignored (we don't use tabs on output) */ |
| 211 | /* -Z SELinux mandated option, busybox optionally supports */ | 218 | /* -Z SELinux mandated option, busybox optionally supports */ |
| 219 | /* -q Std option, busybox always supports: */ | ||
| 220 | /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */ | ||
| 221 | /* Force each instance of non-printable filename characters and */ | ||
| 222 | /* <tab> characters to be written as the <question-mark> ('?') */ | ||
| 223 | /* character. Implementations may provide this option by default */ | ||
| 224 | /* if the output is to a terminal device. */ | ||
| 225 | /* -k Std option, busybox always supports (by ignoring) */ | ||
| 226 | /* It means "for -s, show sizes in kbytes" */ | ||
| 227 | /* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */ | ||
| 228 | /* since otherwise -s shows kbytes anyway */ | ||
| 212 | #define ls_options \ | 229 | #define ls_options \ |
| 213 | "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ | 230 | "Cadi1lgnsxA" /* 11 opts, total 11 */ \ |
| 214 | IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ | 231 | IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \ |
| 215 | IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ | 232 | IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \ |
| 216 | IF_SELINUX("Z") /* 1, 16 */ \ | 233 | IF_SELINUX("Z") /* 1, 15 */ \ |
| 217 | "Q" /* 1, 17 */ \ | 234 | "Q" /* 1, 16 */ \ |
| 218 | IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ | 235 | IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \ |
| 219 | IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ | 236 | IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \ |
| 220 | IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ | 237 | IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \ |
| 221 | IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ | 238 | IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \ |
| 222 | IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ | 239 | IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \ |
| 240 | IF_LONG_OPTS("\xff") /* 1, 29 */ \ | ||
| 241 | IF_LONG_OPTS("\xfe") /* 1, 30 */ \ | ||
| 242 | IF_LONG_OPTS("\xfd::") /* 1, 31 */ \ | ||
| 243 | "qk" /* 2, 33 */ | ||
| 244 | |||
| 245 | #if ENABLE_LONG_OPTS | ||
| 246 | static const char ls_longopts[] ALIGN1 = | ||
| 247 | "full-time\0" No_argument "\xff" | ||
| 248 | "group-directories-first\0" No_argument "\xfe" | ||
| 249 | IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd") | ||
| 250 | ; | ||
| 251 | #endif | ||
| 223 | 252 | ||
| 224 | enum { | 253 | enum { |
| 225 | OPT_C = (1 << 0), | 254 | OPT_C = (1 << 0), |
| @@ -233,29 +262,31 @@ enum { | |||
| 233 | OPT_s = (1 << 8), | 262 | OPT_s = (1 << 8), |
| 234 | OPT_x = (1 << 9), | 263 | OPT_x = (1 << 9), |
| 235 | OPT_A = (1 << 10), | 264 | OPT_A = (1 << 10), |
| 236 | //OPT_k = (1 << 11), | ||
| 237 | 265 | ||
| 238 | OPTBIT_F = 12, | 266 | OPTBIT_F = 11, |
| 239 | OPTBIT_p, /* 13 */ | 267 | OPTBIT_p, /* 12 */ |
| 240 | OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, | 268 | OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, |
| 241 | OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, | 269 | OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, |
| 242 | OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, | 270 | OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, |
| 243 | OPTBIT_c, /* 17 */ | 271 | OPTBIT_c, /* 16 */ |
| 244 | OPTBIT_t, /* 18 */ | 272 | OPTBIT_t, /* 17 */ |
| 245 | OPTBIT_u, /* 19 */ | 273 | OPTBIT_u, /* 18 */ |
| 246 | OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, | 274 | OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, |
| 247 | OPTBIT_X, /* 21 */ | 275 | OPTBIT_X, /* 20 */ |
| 248 | OPTBIT_r, /* 22 */ | 276 | OPTBIT_r, /* 21 */ |
| 249 | OPTBIT_v, /* 23 */ | 277 | OPTBIT_v, /* 22 */ |
| 250 | OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, | 278 | OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, |
| 251 | OPTBIT_H, /* 25 */ | 279 | OPTBIT_H, /* 24 */ |
| 252 | OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, | 280 | OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, |
| 253 | OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, | 281 | OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, |
| 254 | OPTBIT_w, /* 28 */ | 282 | OPTBIT_w, /* 27 */ |
| 255 | OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, | 283 | OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, |
| 256 | OPTBIT_dirs_first, | 284 | OPTBIT_dirs_first, |
| 257 | OPTBIT_color, /* 31 */ | 285 | OPTBIT_color, /* 30 */ |
| 258 | /* with long opts, we use all 32 bits */ | 286 | OPTBIT_q = OPTBIT_color + 1, /* 31 */ |
| 287 | OPTBIT_k = OPTBIT_q + 1, /* 32 */ | ||
| 288 | /* with all options enabled, we use all 32 bits and even one extra bit! */ | ||
| 289 | /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */ | ||
| 259 | 290 | ||
| 260 | OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, | 291 | OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, |
| 261 | OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, | 292 | OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, |
| @@ -277,6 +308,8 @@ enum { | |||
| 277 | OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, | 308 | OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, |
| 278 | OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, | 309 | OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, |
| 279 | OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, | 310 | OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, |
| 311 | OPT_q = (1 << OPTBIT_q), | ||
| 312 | //-k is ignored: OPT_k = (1 << OPTBIT_k), | ||
| 280 | }; | 313 | }; |
| 281 | 314 | ||
| 282 | /* | 315 | /* |
| @@ -333,6 +366,7 @@ struct globals { | |||
| 333 | #endif | 366 | #endif |
| 334 | smallint exit_code; | 367 | smallint exit_code; |
| 335 | smallint show_dirname; | 368 | smallint show_dirname; |
| 369 | smallint tty_out; | ||
| 336 | #if ENABLE_FEATURE_LS_WIDTH | 370 | #if ENABLE_FEATURE_LS_WIDTH |
| 337 | unsigned terminal_width; | 371 | unsigned terminal_width; |
| 338 | # define G_terminal_width (G.terminal_width) | 372 | # define G_terminal_width (G.terminal_width) |
| @@ -353,16 +387,21 @@ struct globals { | |||
| 353 | setup_common_bufsiz(); \ | 387 | setup_common_bufsiz(); \ |
| 354 | /* we have to zero it out because of NOEXEC */ \ | 388 | /* we have to zero it out because of NOEXEC */ \ |
| 355 | memset(&G, 0, sizeof(G)); \ | 389 | memset(&G, 0, sizeof(G)); \ |
| 356 | IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ | 390 | IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \ |
| 357 | IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ | 391 | IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ |
| 358 | } while (0) | 392 | } while (0) |
| 359 | 393 | ||
| 360 | #define ESC "\033" | 394 | #define ESC "\033" |
| 361 | 395 | ||
| 396 | static int G_isatty(void) | ||
| 397 | { | ||
| 398 | if (!G.tty_out) /* not known yet? */ | ||
| 399 | G.tty_out = isatty(STDOUT_FILENO) + 1; | ||
| 400 | return (G.tty_out == 2); | ||
| 401 | } | ||
| 362 | 402 | ||
| 363 | /*** Output code ***/ | 403 | /*** Output code ***/ |
| 364 | 404 | ||
| 365 | |||
| 366 | /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket | 405 | /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket |
| 367 | * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file | 406 | * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file |
| 368 | * 3/7:multiplexed char/block device) | 407 | * 3/7:multiplexed char/block device) |
| @@ -425,56 +464,93 @@ static char append_char(mode_t mode) | |||
| 425 | } | 464 | } |
| 426 | #endif | 465 | #endif |
| 427 | 466 | ||
| 467 | /* Return the number of used columns. | ||
| 468 | * Note that only columnar output uses return value. | ||
| 469 | * -l and -1 modes don't care. | ||
| 470 | * coreutils 7.2 also supports: | ||
| 471 | * ls -b (--escape) = octal escapes (although it doesn't look like working) | ||
| 472 | * ls -N (--literal) = not escape at all | ||
| 473 | */ | ||
| 428 | static unsigned calc_name_len(const char *name) | 474 | static unsigned calc_name_len(const char *name) |
| 429 | { | 475 | { |
| 430 | unsigned len; | 476 | unsigned len; |
| 431 | uni_stat_t uni_stat; | 477 | uni_stat_t uni_stat; |
| 432 | 478 | ||
| 433 | // TODO: quote tab as \t, etc, if -Q | 479 | if (!(option_mask32 & (OPT_q|OPT_Q))) |
| 434 | name = printable_string2(&uni_stat, name); | 480 | return strlen(name); |
| 435 | 481 | ||
| 436 | if (!(option_mask32 & OPT_Q)) { | 482 | if (!(option_mask32 & OPT_Q)) { |
| 483 | /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */ | ||
| 484 | printable_string2(&uni_stat, name); | ||
| 437 | return uni_stat.unicode_width; | 485 | return uni_stat.unicode_width; |
| 438 | } | 486 | } |
| 439 | 487 | ||
| 440 | len = 2 + uni_stat.unicode_width; | 488 | len = 2 + strlen(name); |
| 441 | while (*name) { | 489 | while (*name) { |
| 490 | unsigned char ch = (unsigned char)*name; | ||
| 491 | if (ch < ' ' || ch > 0x7e) { | ||
| 492 | ch -= 7; | ||
| 493 | if (ch <= 6) { | ||
| 494 | /* quote chars 7..13 as \a,b,t,n,v,f,r */ | ||
| 495 | goto two; | ||
| 496 | } | ||
| 497 | /* other chars <32 or >126 as \ooo octal */ | ||
| 498 | len += 3; | ||
| 499 | goto next; | ||
| 500 | } | ||
| 442 | if (*name == '"' || *name == '\\') { | 501 | if (*name == '"' || *name == '\\') { |
| 502 | two: | ||
| 443 | len++; | 503 | len++; |
| 444 | } | 504 | } |
| 505 | next: | ||
| 445 | name++; | 506 | name++; |
| 446 | } | 507 | } |
| 447 | return len; | 508 | return len; |
| 448 | } | 509 | } |
| 449 | |||
| 450 | /* Return the number of used columns. | ||
| 451 | * Note that only columnar output uses return value. | ||
| 452 | * -l and -1 modes don't care. | ||
| 453 | * coreutils 7.2 also supports: | ||
| 454 | * ls -b (--escape) = octal escapes (although it doesn't look like working) | ||
| 455 | * ls -N (--literal) = not escape at all | ||
| 456 | */ | ||
| 457 | static unsigned print_name(const char *name) | 510 | static unsigned print_name(const char *name) |
| 458 | { | 511 | { |
| 459 | unsigned len; | 512 | unsigned len; |
| 460 | uni_stat_t uni_stat; | 513 | uni_stat_t uni_stat; |
| 461 | 514 | ||
| 462 | // TODO: quote tab as \t, etc, if -Q | 515 | if (!(option_mask32 & (OPT_q|OPT_Q))) { |
| 463 | name = printable_string2(&uni_stat, name); | 516 | fputs_stdout(name); |
| 517 | return strlen(name); | ||
| 518 | } | ||
| 464 | 519 | ||
| 465 | if (!(option_mask32 & OPT_Q)) { | 520 | if (!(option_mask32 & OPT_Q)) { |
| 521 | /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */ | ||
| 522 | name = printable_string2(&uni_stat, name); | ||
| 466 | fputs_stdout(name); | 523 | fputs_stdout(name); |
| 467 | return uni_stat.unicode_width; | 524 | return uni_stat.unicode_width; |
| 468 | } | 525 | } |
| 469 | 526 | ||
| 470 | len = 2 + uni_stat.unicode_width; | 527 | len = 2 + strlen(name); |
| 471 | putchar('"'); | 528 | putchar('"'); |
| 472 | while (*name) { | 529 | while (*name) { |
| 473 | if (*name == '"' || *name == '\\') { | 530 | unsigned char ch = (unsigned char)*name; |
| 531 | if (ch < ' ' || ch > 0x7e) { | ||
| 532 | putchar('\\'); | ||
| 533 | ch -= 7; | ||
| 534 | if (ch <= 6) { | ||
| 535 | /* quote chars 7..13 as \a,b,t,n,v,f,r */ | ||
| 536 | ch = c_escape_conv_str07[1 + 3 * ch]; | ||
| 537 | goto two; | ||
| 538 | } | ||
| 539 | /* other chars <32 or >126 as \ooo octal */ | ||
| 540 | ch = (unsigned char)*name; | ||
| 541 | putchar('0' + (ch>>6)); | ||
| 542 | putchar('0' + ((ch>>3) & 7)); | ||
| 543 | ch = '0' + (ch & 7); | ||
| 544 | len += 3; | ||
| 545 | goto put_ch; | ||
| 546 | } | ||
| 547 | if (ch == '"' || ch == '\\') { | ||
| 474 | putchar('\\'); | 548 | putchar('\\'); |
| 549 | two: | ||
| 475 | len++; | 550 | len++; |
| 476 | } | 551 | } |
| 477 | putchar(*name); | 552 | put_ch: |
| 553 | putchar(ch); | ||
| 478 | name++; | 554 | name++; |
| 479 | } | 555 | } |
| 480 | putchar('"'); | 556 | putchar('"'); |
| @@ -660,7 +736,7 @@ static void display_files(struct dnode **dn, unsigned nfiles) | |||
| 660 | unsigned i, ncols, nrows, row, nc; | 736 | unsigned i, ncols, nrows, row, nc; |
| 661 | unsigned column; | 737 | unsigned column; |
| 662 | unsigned nexttab; | 738 | unsigned nexttab; |
| 663 | unsigned column_width = 0; /* used only by coulmnal output */ | 739 | unsigned column_width = 0; /* used only by columnar output */ |
| 664 | 740 | ||
| 665 | if (option_mask32 & (OPT_l|OPT_1)) { | 741 | if (option_mask32 & (OPT_l|OPT_1)) { |
| 666 | ncols = 1; | 742 | ncols = 1; |
| @@ -709,6 +785,11 @@ static void display_files(struct dnode **dn, unsigned nfiles) | |||
| 709 | } | 785 | } |
| 710 | nexttab = column + column_width; | 786 | nexttab = column + column_width; |
| 711 | column += display_single(dn[i]); | 787 | column += display_single(dn[i]); |
| 788 | } else { | ||
| 789 | /* if -w999999999, ncols can be very large */ | ||
| 790 | //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1(); | ||
| 791 | /* without "break", we loop millions of times here */ | ||
| 792 | break; | ||
| 712 | } | 793 | } |
| 713 | } | 794 | } |
| 714 | putchar('\n'); | 795 | putchar('\n'); |
| @@ -1155,25 +1236,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
| 1155 | /* need to initialize since --color has _an optional_ argument */ | 1236 | /* need to initialize since --color has _an optional_ argument */ |
| 1156 | const char *color_opt = color_str; /* "always" */ | 1237 | const char *color_opt = color_str; /* "always" */ |
| 1157 | #endif | 1238 | #endif |
| 1158 | #if ENABLE_LONG_OPTS | ||
| 1159 | static const char ls_longopts[] ALIGN1 = | ||
| 1160 | "full-time\0" No_argument "\xff" | ||
| 1161 | "group-directories-first\0" No_argument "\xfe" | ||
| 1162 | IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd") | ||
| 1163 | ; | ||
| 1164 | #endif | ||
| 1165 | 1239 | ||
| 1166 | INIT_G(); | 1240 | INIT_G(); |
| 1167 | 1241 | ||
| 1168 | init_unicode(); | 1242 | init_unicode(); |
| 1169 | 1243 | ||
| 1170 | #if ENABLE_FEATURE_LS_WIDTH | ||
| 1171 | /* obtain the terminal width */ | ||
| 1172 | G_terminal_width = get_terminal_width(STDIN_FILENO); | ||
| 1173 | /* go one less... */ | ||
| 1174 | G_terminal_width--; | ||
| 1175 | #endif | ||
| 1176 | |||
| 1177 | /* process options */ | 1244 | /* process options */ |
| 1178 | opt = getopt32long(argv, "^" | 1245 | opt = getopt32long(argv, "^" |
| 1179 | ls_options | 1246 | ls_options |
| @@ -1211,6 +1278,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
| 1211 | exit(0); | 1278 | exit(0); |
| 1212 | #endif | 1279 | #endif |
| 1213 | 1280 | ||
| 1281 | /* ftpd secret backdoor? */ | ||
| 1282 | if (ENABLE_FTPD && applet_name[0] == 'f') { | ||
| 1283 | /* dirs first are much nicer */ | ||
| 1284 | opt = option_mask32 |= OPT_dirs_first; | ||
| 1285 | /* don't show SEcontext */ | ||
| 1286 | IF_SELINUX(opt = option_mask32 &= ~OPT_Z;) | ||
| 1287 | /* do not query stdout about size and tty-ness */ | ||
| 1288 | IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;) | ||
| 1289 | G.tty_out = 1; /* not a tty */ | ||
| 1290 | goto skip_if_ftpd; | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | #if ENABLE_FEATURE_LS_WIDTH | ||
| 1294 | if ((int)G_terminal_width < 0) { | ||
| 1295 | /* obtain the terminal width */ | ||
| 1296 | G_terminal_width = get_terminal_width(STDIN_FILENO); | ||
| 1297 | /* go one less... */ | ||
| 1298 | G_terminal_width--; | ||
| 1299 | } | ||
| 1300 | if (G_terminal_width == 0) /* -w0 */ | ||
| 1301 | G_terminal_width = INT_MAX; /* "infinite" */ | ||
| 1302 | #endif | ||
| 1303 | |||
| 1214 | #if ENABLE_SELINUX | 1304 | #if ENABLE_SELINUX |
| 1215 | if (opt & OPT_Z) { | 1305 | if (opt & OPT_Z) { |
| 1216 | if (!is_selinux_enabled()) | 1306 | if (!is_selinux_enabled()) |
| @@ -1229,7 +1319,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
| 1229 | # endif | 1319 | # endif |
| 1230 | /* LS_COLORS is unset, or (not empty && not "none") ? */ | 1320 | /* LS_COLORS is unset, or (not empty && not "none") ? */ |
| 1231 | if (!p || (p[0] && strcmp(p, "none") != 0)) { | 1321 | if (!p || (p[0] && strcmp(p, "none") != 0)) { |
| 1232 | if (isatty(STDOUT_FILENO)) { | 1322 | if (G_isatty()) { |
| 1233 | /* check isatty() last because it's expensive (syscall) */ | 1323 | /* check isatty() last because it's expensive (syscall) */ |
| 1234 | G_show_color = 1; | 1324 | G_show_color = 1; |
| 1235 | } | 1325 | } |
| @@ -1238,23 +1328,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
| 1238 | if (opt & OPT_color) { | 1328 | if (opt & OPT_color) { |
| 1239 | if (color_opt[0] == 'n') | 1329 | if (color_opt[0] == 'n') |
| 1240 | G_show_color = 0; | 1330 | G_show_color = 0; |
| 1241 | else switch (index_in_substrings(color_str, color_opt)) { | 1331 | else if (!G_show_color) { |
| 1242 | case 3: | 1332 | /* if() is not needed, but avoids extra isatty() if G_show_color is already set */ |
| 1243 | case 4: | 1333 | /* Check --color=COLOR_OPT and maybe set show_color=1 */ |
| 1244 | case 5: | 1334 | switch (index_in_substrings(color_str, color_opt)) { |
| 1245 | if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { | 1335 | case 3: // auto |
| 1246 | case 0: | 1336 | case 4: // tty |
| 1247 | case 1: | 1337 | case 5: // if-tty |
| 1248 | case 2: | 1338 | if (!is_TERM_dumb() && G_isatty()) { |
| 1249 | G_show_color = 1; | 1339 | case 0: // always |
| 1340 | case 1: // yes | ||
| 1341 | case 2: // force | ||
| 1342 | G_show_color = 1; | ||
| 1343 | } | ||
| 1250 | } | 1344 | } |
| 1251 | } | 1345 | } |
| 1252 | } | 1346 | } |
| 1253 | #endif | 1347 | #endif |
| 1348 | skip_if_ftpd: | ||
| 1254 | 1349 | ||
| 1255 | /* sort out which command line options take precedence */ | 1350 | /* sort out which command line options take precedence */ |
| 1256 | if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) | 1351 | if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) |
| 1257 | option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ | 1352 | opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ |
| 1258 | if (!(opt & OPT_l)) { /* not -l? */ | 1353 | if (!(opt & OPT_l)) { /* not -l? */ |
| 1259 | if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { | 1354 | if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { |
| 1260 | /* when to sort by time? -t[cu] sorts by time even with -l */ | 1355 | /* when to sort by time? -t[cu] sorts by time even with -l */ |
| @@ -1262,19 +1357,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
| 1262 | /* without -l, bare -c or -u enable sort too */ | 1357 | /* without -l, bare -c or -u enable sort too */ |
| 1263 | /* (with -l, bare -c or -u just select which time to show) */ | 1358 | /* (with -l, bare -c or -u just select which time to show) */ |
| 1264 | if (opt & (OPT_c|OPT_u)) { | 1359 | if (opt & (OPT_c|OPT_u)) { |
| 1265 | option_mask32 |= OPT_t; | 1360 | opt = option_mask32 |= OPT_t; |
| 1266 | } | 1361 | } |
| 1267 | } | 1362 | } |
| 1268 | } | 1363 | } |
| 1269 | 1364 | ||
| 1270 | /* choose a display format if one was not already specified by an option */ | 1365 | /* choose a display format if one was not already specified by an option */ |
| 1271 | if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) | 1366 | if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C))) |
| 1272 | option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); | 1367 | opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1); |
| 1273 | 1368 | ||
| 1274 | if (ENABLE_FTPD && applet_name[0] == 'f') { | 1369 | if (!(opt & OPT_q) && G_isatty()) |
| 1275 | /* ftpd secret backdoor. dirs first are much nicer */ | 1370 | opt = option_mask32 |= OPT_q; |
| 1276 | option_mask32 |= OPT_dirs_first; | ||
| 1277 | } | ||
| 1278 | 1371 | ||
| 1279 | #if ENABLE_FEATURE_EXTRA_FILE_DATA | 1372 | #if ENABLE_FEATURE_EXTRA_FILE_DATA |
| 1280 | /* Enable accurate link counts for directories */ | 1373 | /* Enable accurate link counts for directories */ |
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c index 978d328f1..4506aeb56 100644 --- a/coreutils/md5_sha1_sum.c +++ b/coreutils/md5_sha1_sum.c | |||
| @@ -23,6 +23,12 @@ | |||
| 23 | //config: help | 23 | //config: help |
| 24 | //config: Compute and check SHA256 message digest | 24 | //config: Compute and check SHA256 message digest |
| 25 | //config: | 25 | //config: |
| 26 | //config:config SHA384SUM | ||
| 27 | //config: bool "sha384sum (7.3 kb)" | ||
| 28 | //config: default y | ||
| 29 | //config: help | ||
| 30 | //config: Compute and check SHA384 message digest | ||
| 31 | //config: | ||
| 26 | //config:config SHA512SUM | 32 | //config:config SHA512SUM |
| 27 | //config: bool "sha512sum (7.3 kb)" | 33 | //config: bool "sha512sum (7.3 kb)" |
| 28 | //config: default y | 34 | //config: default y |
| @@ -35,13 +41,13 @@ | |||
| 35 | //config: help | 41 | //config: help |
| 36 | //config: Compute and check SHA3 message digest | 42 | //config: Compute and check SHA3 message digest |
| 37 | //config: | 43 | //config: |
| 38 | //config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" | 44 | //config:comment "Common options for md5sum, sha1sum, sha256sum, ..., sha3sum" |
| 39 | //config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM | 45 | //config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM |
| 40 | //config: | 46 | //config: |
| 41 | //config:config FEATURE_MD5_SHA1_SUM_CHECK | 47 | //config:config FEATURE_MD5_SHA1_SUM_CHECK |
| 42 | //config: bool "Enable -c, -s and -w options" | 48 | //config: bool "Enable -c, -s and -w options" |
| 43 | //config: default y | 49 | //config: default y |
| 44 | //config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM | 50 | //config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM |
| 45 | //config: help | 51 | //config: help |
| 46 | //config: Enabling the -c options allows files to be checked | 52 | //config: Enabling the -c options allows files to be checked |
| 47 | //config: against pre-calculated hash values. | 53 | //config: against pre-calculated hash values. |
| @@ -51,11 +57,13 @@ | |||
| 51 | //applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) | 57 | //applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) |
| 52 | //applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) | 58 | //applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) |
| 53 | //applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) | 59 | //applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) |
| 60 | //applet:IF_SHA384SUM(APPLET_NOEXEC(sha384sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha384sum)) | ||
| 54 | //applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) | 61 | //applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) |
| 55 | 62 | ||
| 56 | //kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o | 63 | //kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o |
| 57 | //kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o | 64 | //kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o |
| 58 | //kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o | 65 | //kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o |
| 66 | //kbuild:lib-$(CONFIG_SHA384SUM) += md5_sha1_sum.o | ||
| 59 | //kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o | 67 | //kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o |
| 60 | //kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o | 68 | //kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o |
| 61 | 69 | ||
| @@ -99,6 +107,16 @@ | |||
| 99 | //usage: "\n -w Warn about improperly formatted checksum lines" | 107 | //usage: "\n -w Warn about improperly formatted checksum lines" |
| 100 | //usage: ) | 108 | //usage: ) |
| 101 | //usage: | 109 | //usage: |
| 110 | //usage:#define sha384sum_trivial_usage | ||
| 111 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." | ||
| 112 | //usage:#define sha384sum_full_usage "\n\n" | ||
| 113 | //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA384 checksums" | ||
| 114 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" | ||
| 115 | //usage: "\n -c Check sums against list in FILEs" | ||
| 116 | //usage: "\n -s Don't output anything, status code shows success" | ||
| 117 | //usage: "\n -w Warn about improperly formatted checksum lines" | ||
| 118 | //usage: ) | ||
| 119 | //usage: | ||
| 102 | //usage:#define sha512sum_trivial_usage | 120 | //usage:#define sha512sum_trivial_usage |
| 103 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." | 121 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." |
| 104 | //usage:#define sha512sum_full_usage "\n\n" | 122 | //usage:#define sha512sum_full_usage "\n\n" |
| @@ -130,11 +148,12 @@ | |||
| 130 | 148 | ||
| 131 | enum { | 149 | enum { |
| 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 |
| 159 | static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width) | 178 | static 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/paste.c b/coreutils/paste.c index 3e5f20158..78a5c2a14 100644 --- a/coreutils/paste.c +++ b/coreutils/paste.c | |||
| @@ -34,15 +34,46 @@ | |||
| 34 | 34 | ||
| 35 | static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) | 35 | static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) |
| 36 | { | 36 | { |
| 37 | #if ENABLE_PLATFORM_MINGW32 | ||
| 38 | char **line = xmalloc(file_cnt * sizeof(char *)); | ||
| 39 | #else | ||
| 37 | char *line; | 40 | char *line; |
| 41 | #endif | ||
| 38 | char delim; | 42 | char delim; |
| 39 | int active_files = file_cnt; | 43 | int active_files = file_cnt; |
| 40 | int i; | 44 | int i; |
| 41 | 45 | ||
| 42 | while (active_files > 0) { | 46 | while (active_files > 0) { |
| 43 | int del_idx = 0; | 47 | int del_idx = 0; |
| 48 | #if ENABLE_PLATFORM_MINGW32 | ||
| 49 | int got_line = FALSE; | ||
| 50 | #endif | ||
| 44 | 51 | ||
| 45 | for (i = 0; i < file_cnt; ++i) { | 52 | for (i = 0; i < file_cnt; ++i) { |
| 53 | #if ENABLE_PLATFORM_MINGW32 | ||
| 54 | if (files[i]) { | ||
| 55 | line[i] = xmalloc_fgetline(files[i]); | ||
| 56 | if (!line[i]) { | ||
| 57 | fclose_if_not_stdin(files[i]); | ||
| 58 | files[i] = NULL; | ||
| 59 | --active_files; | ||
| 60 | } else { | ||
| 61 | got_line = TRUE; | ||
| 62 | } | ||
| 63 | } else { | ||
| 64 | line[i] = NULL; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | if (!got_line) | ||
| 69 | break; | ||
| 70 | |||
| 71 | for (i = 0; i < file_cnt; ++i) { | ||
| 72 | if (line[i]) { | ||
| 73 | fputs_stdout(line[i]); | ||
| 74 | free(line[i]); | ||
| 75 | } | ||
| 76 | #else | ||
| 46 | if (files[i] == NULL) | 77 | if (files[i] == NULL) |
| 47 | continue; | 78 | continue; |
| 48 | 79 | ||
| @@ -55,6 +86,7 @@ static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) | |||
| 55 | } | 86 | } |
| 56 | fputs_stdout(line); | 87 | fputs_stdout(line); |
| 57 | free(line); | 88 | free(line); |
| 89 | #endif | ||
| 58 | delim = '\n'; | 90 | delim = '\n'; |
| 59 | if (i != file_cnt - 1) { | 91 | if (i != file_cnt - 1) { |
| 60 | delim = delims[del_idx++]; | 92 | delim = delims[del_idx++]; |
| @@ -65,6 +97,9 @@ static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) | |||
| 65 | fputc(delim, stdout); | 97 | fputc(delim, stdout); |
| 66 | } | 98 | } |
| 67 | } | 99 | } |
| 100 | #if ENABLE_PLATFORM_MINGW32 | ||
| 101 | free(line); | ||
| 102 | #endif | ||
| 68 | } | 103 | } |
| 69 | 104 | ||
| 70 | static void paste_files_separate(FILE** files, char* delims, int del_cnt) | 105 | static void paste_files_separate(FILE** files, char* delims, int del_cnt) |
diff --git a/coreutils/stty.c b/coreutils/stty.c index c88ef07f4..c064ad90a 100644 --- a/coreutils/stty.c +++ b/coreutils/stty.c | |||
| @@ -20,14 +20,29 @@ | |||
| 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 size" | ||
| 45 | //usage: ) | ||
| 31 | 46 | ||
| 32 | /* If no args are given, write to stdout the baud rate and settings that | 47 | /* 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 | 48 | * have been changed from their defaults. Mode reading and changes |
| @@ -294,6 +309,7 @@ struct mode_info { | |||
| 294 | const tcflag_t bits; /* Bits to set for this mode */ | 309 | const tcflag_t bits; /* Bits to set for this mode */ |
| 295 | }; | 310 | }; |
| 296 | 311 | ||
| 312 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 297 | enum { | 313 | enum { |
| 298 | /* Must match mode_name[] and mode_info[] order! */ | 314 | /* Must match mode_name[] and mode_info[] order! */ |
| 299 | IDX_evenp = 0, | 315 | IDX_evenp = 0, |
| @@ -320,19 +336,30 @@ enum { | |||
| 320 | IDX_LCASE, | 336 | IDX_LCASE, |
| 321 | #endif | 337 | #endif |
| 322 | }; | 338 | }; |
| 339 | #else | ||
| 340 | enum { | ||
| 341 | /* Must match mode_name[] and mode_info[] order! */ | ||
| 342 | IDX_sane = 0, | ||
| 343 | IDX_cooked, | ||
| 344 | IDX_raw, | ||
| 345 | }; | ||
| 346 | #endif | ||
| 323 | 347 | ||
| 324 | #define MI_ENTRY(N,T,F,B,M) N "\0" | 348 | #define MI_ENTRY(N,T,F,B,M) N "\0" |
| 325 | 349 | ||
| 326 | /* Mode names given on command line */ | 350 | /* Mode names given on command line */ |
| 327 | static const char mode_name[] ALIGN1 = | 351 | static const char mode_name[] ALIGN1 = |
| 352 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 328 | MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) | 353 | MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) |
| 329 | MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) | 354 | MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) |
| 330 | MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) | 355 | MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) |
| 331 | MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) | 356 | MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) |
| 332 | MI_ENTRY("ek", combination, OMIT, 0, 0 ) | 357 | MI_ENTRY("ek", combination, OMIT, 0, 0 ) |
| 358 | #endif | ||
| 333 | MI_ENTRY("sane", combination, OMIT, 0, 0 ) | 359 | MI_ENTRY("sane", combination, OMIT, 0, 0 ) |
| 334 | MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) | 360 | MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) |
| 335 | MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) | 361 | MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) |
| 362 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 336 | MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) | 363 | MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) |
| 337 | MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) | 364 | MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) |
| 338 | MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) | 365 | MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) |
| @@ -454,7 +481,9 @@ static const char mode_name[] ALIGN1 = | |||
| 454 | #if IEXTEN | 481 | #if IEXTEN |
| 455 | MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) | 482 | MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) |
| 456 | #endif | 483 | #endif |
| 484 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 457 | MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) | 485 | MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) |
| 486 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 458 | MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) | 487 | MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) |
| 459 | MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) | 488 | MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) |
| 460 | MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) | 489 | MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) |
| @@ -482,6 +511,7 @@ static const char mode_name[] ALIGN1 = | |||
| 482 | #ifdef EXTPROC | 511 | #ifdef EXTPROC |
| 483 | MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) | 512 | MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) |
| 484 | #endif | 513 | #endif |
| 514 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 485 | ; | 515 | ; |
| 486 | 516 | ||
| 487 | #undef MI_ENTRY | 517 | #undef MI_ENTRY |
| @@ -489,14 +519,17 @@ static const char mode_name[] ALIGN1 = | |||
| 489 | 519 | ||
| 490 | static const struct mode_info mode_info[] ALIGN4 = { | 520 | static const struct mode_info mode_info[] ALIGN4 = { |
| 491 | /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ | 521 | /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ |
| 522 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 492 | MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) | 523 | MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) |
| 493 | MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) | 524 | MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) |
| 494 | MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) | 525 | MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) |
| 495 | MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) | 526 | MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) |
| 496 | MI_ENTRY("ek", combination, OMIT, 0, 0 ) | 527 | MI_ENTRY("ek", combination, OMIT, 0, 0 ) |
| 528 | #endif | ||
| 497 | MI_ENTRY("sane", combination, OMIT, 0, 0 ) | 529 | MI_ENTRY("sane", combination, OMIT, 0, 0 ) |
| 498 | MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) | 530 | MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) |
| 499 | MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) | 531 | MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) |
| 532 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 500 | MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) | 533 | MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) |
| 501 | MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) | 534 | MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) |
| 502 | MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) | 535 | MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) |
| @@ -618,7 +651,9 @@ static const struct mode_info mode_info[] ALIGN4 = { | |||
| 618 | #if IEXTEN | 651 | #if IEXTEN |
| 619 | MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) | 652 | MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) |
| 620 | #endif | 653 | #endif |
| 654 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 621 | MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) | 655 | MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) |
| 656 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 622 | MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) | 657 | MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) |
| 623 | MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) | 658 | MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) |
| 624 | MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) | 659 | MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) |
| @@ -646,6 +681,7 @@ static const struct mode_info mode_info[] ALIGN4 = { | |||
| 646 | #ifdef EXTPROC | 681 | #ifdef EXTPROC |
| 647 | MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) | 682 | MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) |
| 648 | #endif | 683 | #endif |
| 684 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 649 | }; | 685 | }; |
| 650 | 686 | ||
| 651 | enum { | 687 | enum { |
| @@ -653,6 +689,7 @@ enum { | |||
| 653 | }; | 689 | }; |
| 654 | 690 | ||
| 655 | 691 | ||
| 692 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 656 | /* Control characters */ | 693 | /* Control characters */ |
| 657 | struct control_info { | 694 | struct control_info { |
| 658 | const uint8_t saneval; /* Value to set for 'stty sane' */ | 695 | const uint8_t saneval; /* Value to set for 'stty sane' */ |
| @@ -786,6 +823,7 @@ static const struct control_info control_info[] ALIGN2 = { | |||
| 786 | enum { | 823 | enum { |
| 787 | NUM_control_info = ARRAY_SIZE(control_info) | 824 | NUM_control_info = ARRAY_SIZE(control_info) |
| 788 | }; | 825 | }; |
| 826 | #endif | ||
| 789 | 827 | ||
| 790 | 828 | ||
| 791 | struct globals { | 829 | struct globals { |
| @@ -803,6 +841,7 @@ struct globals { | |||
| 803 | G.current_col = 0; /* we are noexec, must clear */ \ | 841 | G.current_col = 0; /* we are noexec, must clear */ \ |
| 804 | } while (0) | 842 | } while (0) |
| 805 | 843 | ||
| 844 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 806 | static void set_speed_or_die(enum speed_setting type, const char *arg, | 845 | static void set_speed_or_die(enum speed_setting type, const char *arg, |
| 807 | struct termios *mode) | 846 | struct termios *mode) |
| 808 | { | 847 | { |
| @@ -817,6 +856,7 @@ static void set_speed_or_die(enum speed_setting type, const char *arg, | |||
| 817 | cfsetospeed(mode, baud); | 856 | cfsetospeed(mode, baud); |
| 818 | } | 857 | } |
| 819 | } | 858 | } |
| 859 | #endif | ||
| 820 | 860 | ||
| 821 | static NORETURN void perror_on_device_and_die(const char *fmt) | 861 | static NORETURN void perror_on_device_and_die(const char *fmt) |
| 822 | { | 862 | { |
| @@ -867,6 +907,7 @@ static void newline(void) | |||
| 867 | wrapf("\n"); | 907 | wrapf("\n"); |
| 868 | } | 908 | } |
| 869 | 909 | ||
| 910 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 870 | #ifdef TIOCGWINSZ | 911 | #ifdef TIOCGWINSZ |
| 871 | static void set_window_size(int rows, int cols) | 912 | static void set_window_size(int rows, int cols) |
| 872 | { | 913 | { |
| @@ -889,6 +930,7 @@ bail: | |||
| 889 | perror_on_device("%s"); | 930 | perror_on_device("%s"); |
| 890 | } | 931 | } |
| 891 | #endif | 932 | #endif |
| 933 | #endif | ||
| 892 | 934 | ||
| 893 | static void display_window_size(int fancy) | 935 | static void display_window_size(int fancy) |
| 894 | { | 936 | { |
| @@ -918,6 +960,7 @@ static const struct mode_info *find_mode(const char *name) | |||
| 918 | return i >= 0 ? &mode_info[i] : NULL; | 960 | return i >= 0 ? &mode_info[i] : NULL; |
| 919 | } | 961 | } |
| 920 | 962 | ||
| 963 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 921 | static const struct control_info *find_control(const char *name) | 964 | static const struct control_info *find_control(const char *name) |
| 922 | { | 965 | { |
| 923 | int i = index_in_strings(control_name, name); | 966 | int i = index_in_strings(control_name, name); |
| @@ -954,7 +997,32 @@ static int find_param(const char *name) | |||
| 954 | i |= 0x80; | 997 | i |= 0x80; |
| 955 | return i; | 998 | return i; |
| 956 | } | 999 | } |
| 1000 | #else | ||
| 1001 | enum { | ||
| 1002 | param_need_arg = 0x80, | ||
| 1003 | param_rows = 1 | 0x80, | ||
| 1004 | param_cols = 2 | 0x80, | ||
| 1005 | param_columns = 3 | 0x80, | ||
| 1006 | param_size = 4, | ||
| 1007 | }; | ||
| 1008 | |||
| 1009 | static int find_param(const char *name) | ||
| 1010 | { | ||
| 1011 | static const char params[] ALIGN1 = | ||
| 1012 | "rows\0" /* 1 */ | ||
| 1013 | "cols\0" /* 2 */ | ||
| 1014 | "columns\0" /* 3 */ | ||
| 1015 | "size\0"; /* 4 */ | ||
| 1016 | int i = index_in_strings(params, name) + 1; | ||
| 1017 | if (i == 0) | ||
| 1018 | return 0; | ||
| 1019 | if (i != 4) | ||
| 1020 | i |= 0x80; | ||
| 1021 | return i; | ||
| 1022 | } | ||
| 1023 | #endif | ||
| 957 | 1024 | ||
| 1025 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 958 | static int recover_mode(const char *arg, struct termios *mode) | 1026 | static int recover_mode(const char *arg, struct termios *mode) |
| 959 | { | 1027 | { |
| 960 | int i, n; | 1028 | int i, n; |
| @@ -1013,6 +1081,9 @@ static void display_speed(const struct termios *mode, int fancy) | |||
| 1013 | if (fancy) fmt_str += 9; | 1081 | if (fancy) fmt_str += 9; |
| 1014 | wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); | 1082 | wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); |
| 1015 | } | 1083 | } |
| 1084 | #else | ||
| 1085 | # define display_speed(m, f) ((void)0) | ||
| 1086 | #endif | ||
| 1016 | 1087 | ||
| 1017 | static void do_display(const struct termios *mode, int all) | 1088 | static void do_display(const struct termios *mode, int all) |
| 1018 | { | 1089 | { |
| @@ -1030,6 +1101,7 @@ static void do_display(const struct termios *mode, int all) | |||
| 1030 | newline(); | 1101 | newline(); |
| 1031 | #endif | 1102 | #endif |
| 1032 | 1103 | ||
| 1104 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1033 | for (i = 0; i != CIDX_min; ++i) { | 1105 | for (i = 0; i != CIDX_min; ++i) { |
| 1034 | char ch; | 1106 | char ch; |
| 1035 | char buf10[10]; | 1107 | char buf10[10]; |
| @@ -1059,6 +1131,7 @@ static void do_display(const struct termios *mode, int all) | |||
| 1059 | #endif | 1131 | #endif |
| 1060 | wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); | 1132 | wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); |
| 1061 | newline(); | 1133 | newline(); |
| 1134 | #endif | ||
| 1062 | 1135 | ||
| 1063 | for (i = 0; i < NUM_mode_info; ++i) { | 1136 | for (i = 0; i < NUM_mode_info; ++i) { |
| 1064 | if (mode_info[i].flags & OMIT) | 1137 | if (mode_info[i].flags & OMIT) |
| @@ -1086,6 +1159,7 @@ static void do_display(const struct termios *mode, int all) | |||
| 1086 | 1159 | ||
| 1087 | static void sane_mode(struct termios *mode) | 1160 | static void sane_mode(struct termios *mode) |
| 1088 | { | 1161 | { |
| 1162 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1089 | int i; | 1163 | int i; |
| 1090 | 1164 | ||
| 1091 | for (i = 0; i < NUM_control_info; ++i) { | 1165 | for (i = 0; i < NUM_control_info; ++i) { |
| @@ -1110,6 +1184,11 @@ static void sane_mode(struct termios *mode) | |||
| 1110 | *bitsp = val & ~mode_info[i].bits; | 1184 | *bitsp = val & ~mode_info[i].bits; |
| 1111 | } | 1185 | } |
| 1112 | } | 1186 | } |
| 1187 | #else | ||
| 1188 | mode->c_lflag |= ECHO; | ||
| 1189 | mode->w_mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | | ||
| 1190 | ENABLE_PROCESSED_INPUT; | ||
| 1191 | #endif | ||
| 1113 | } | 1192 | } |
| 1114 | 1193 | ||
| 1115 | static void set_mode(const struct mode_info *info, int reversed, | 1194 | static void set_mode(const struct mode_info *info, int reversed, |
| @@ -1129,6 +1208,7 @@ static void set_mode(const struct mode_info *info, int reversed, | |||
| 1129 | } | 1208 | } |
| 1130 | 1209 | ||
| 1131 | /* !bitsp - it's a "combination" mode */ | 1210 | /* !bitsp - it's a "combination" mode */ |
| 1211 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1132 | if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { | 1212 | if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { |
| 1133 | if (reversed) | 1213 | if (reversed) |
| 1134 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; | 1214 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; |
| @@ -1150,9 +1230,14 @@ static void set_mode(const struct mode_info *info, int reversed, | |||
| 1150 | } else if (info == &mode_info[IDX_ek]) { | 1230 | } else if (info == &mode_info[IDX_ek]) { |
| 1151 | mode->c_cc[VERASE] = CERASE; | 1231 | mode->c_cc[VERASE] = CERASE; |
| 1152 | mode->c_cc[VKILL] = CKILL; | 1232 | mode->c_cc[VKILL] = CKILL; |
| 1153 | } else if (info == &mode_info[IDX_sane]) { | 1233 | } |
| 1234 | else | ||
| 1235 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 1236 | if (info == &mode_info[IDX_sane]) { | ||
| 1154 | sane_mode(mode); | 1237 | sane_mode(mode); |
| 1155 | } else if (info == &mode_info[IDX_cbreak]) { | 1238 | } |
| 1239 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1240 | else if (info == &mode_info[IDX_cbreak]) { | ||
| 1156 | if (reversed) | 1241 | if (reversed) |
| 1157 | mode->c_lflag |= ICANON; | 1242 | mode->c_lflag |= ICANON; |
| 1158 | else | 1243 | else |
| @@ -1175,11 +1260,14 @@ static void set_mode(const struct mode_info *info, int reversed, | |||
| 1175 | mode->c_iflag &= ~ISTRIP; | 1260 | mode->c_iflag &= ~ISTRIP; |
| 1176 | mode->c_oflag &= ~OPOST; | 1261 | mode->c_oflag &= ~OPOST; |
| 1177 | } | 1262 | } |
| 1178 | } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { | 1263 | } |
| 1264 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 1265 | else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { | ||
| 1179 | if ((info == &mode_info[IDX_raw] && reversed) | 1266 | if ((info == &mode_info[IDX_raw] && reversed) |
| 1180 | || (info == &mode_info[IDX_cooked] && !reversed) | 1267 | || (info == &mode_info[IDX_cooked] && !reversed) |
| 1181 | ) { | 1268 | ) { |
| 1182 | /* Cooked mode */ | 1269 | /* Cooked mode */ |
| 1270 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1183 | mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; | 1271 | mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; |
| 1184 | mode->c_oflag |= OPOST; | 1272 | mode->c_oflag |= OPOST; |
| 1185 | mode->c_lflag |= ISIG | ICANON; | 1273 | mode->c_lflag |= ISIG | ICANON; |
| @@ -1189,15 +1277,23 @@ static void set_mode(const struct mode_info *info, int reversed, | |||
| 1189 | #if VTIME == VEOL | 1277 | #if VTIME == VEOL |
| 1190 | mode->c_cc[VEOL] = CEOL; | 1278 | mode->c_cc[VEOL] = CEOL; |
| 1191 | #endif | 1279 | #endif |
| 1280 | #else | ||
| 1281 | mode->w_mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; | ||
| 1282 | #endif | ||
| 1192 | } else { | 1283 | } else { |
| 1193 | /* Raw mode */ | 1284 | /* Raw mode */ |
| 1285 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1194 | mode->c_iflag = 0; | 1286 | mode->c_iflag = 0; |
| 1195 | mode->c_oflag &= ~OPOST; | 1287 | mode->c_oflag &= ~OPOST; |
| 1196 | mode->c_lflag &= ~(ISIG | ICANON | XCASE); | 1288 | mode->c_lflag &= ~(ISIG | ICANON | XCASE); |
| 1197 | mode->c_cc[VMIN] = 1; | 1289 | mode->c_cc[VMIN] = 1; |
| 1198 | mode->c_cc[VTIME] = 0; | 1290 | mode->c_cc[VTIME] = 0; |
| 1291 | #else | ||
| 1292 | mode->w_mode &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); | ||
| 1293 | #endif | ||
| 1199 | } | 1294 | } |
| 1200 | } | 1295 | } |
| 1296 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1201 | #if IXANY | 1297 | #if IXANY |
| 1202 | else if (info == &mode_info[IDX_decctlq]) { | 1298 | else if (info == &mode_info[IDX_decctlq]) { |
| 1203 | if (reversed) | 1299 | if (reversed) |
| @@ -1244,8 +1340,10 @@ static void set_mode(const struct mode_info *info, int reversed, | |||
| 1244 | mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; | 1340 | mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; |
| 1245 | if (IXANY) mode->c_iflag &= ~IXANY; | 1341 | if (IXANY) mode->c_iflag &= ~IXANY; |
| 1246 | } | 1342 | } |
| 1343 | #endif /*!ENABLE_PLATFORM_MINGW32 */ | ||
| 1247 | } | 1344 | } |
| 1248 | 1345 | ||
| 1346 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1249 | static void set_control_char_or_die(const struct control_info *info, | 1347 | static void set_control_char_or_die(const struct control_info *info, |
| 1250 | const char *arg, struct termios *mode) | 1348 | const char *arg, struct termios *mode) |
| 1251 | { | 1349 | { |
| @@ -1265,6 +1363,7 @@ static void set_control_char_or_die(const struct control_info *info, | |||
| 1265 | value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); | 1363 | value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); |
| 1266 | mode->c_cc[info->offset] = value; | 1364 | mode->c_cc[info->offset] = value; |
| 1267 | } | 1365 | } |
| 1366 | #endif | ||
| 1268 | 1367 | ||
| 1269 | #define STTY_require_set_attr (1 << 0) | 1368 | #define STTY_require_set_attr (1 << 0) |
| 1270 | #define STTY_speed_was_set (1 << 1) | 1369 | #define STTY_speed_was_set (1 << 1) |
| @@ -1277,7 +1376,9 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1277 | { | 1376 | { |
| 1278 | struct termios mode; | 1377 | struct termios mode; |
| 1279 | void (*output_func)(const struct termios *, int); | 1378 | void (*output_func)(const struct termios *, int); |
| 1379 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1280 | const char *file_name = NULL; | 1380 | const char *file_name = NULL; |
| 1381 | #endif | ||
| 1281 | int display_all = 0; | 1382 | int display_all = 0; |
| 1282 | int stty_state; | 1383 | int stty_state; |
| 1283 | int k; | 1384 | int k; |
| @@ -1291,7 +1392,9 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1291 | k = 0; | 1392 | k = 0; |
| 1292 | while (argv[++k]) { | 1393 | while (argv[++k]) { |
| 1293 | const struct mode_info *mp; | 1394 | const struct mode_info *mp; |
| 1395 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1294 | const struct control_info *cp; | 1396 | const struct control_info *cp; |
| 1397 | #endif | ||
| 1295 | const char *arg = argv[k]; | 1398 | const char *arg = argv[k]; |
| 1296 | const char *argnext = argv[k+1]; | 1399 | const char *argnext = argv[k+1]; |
| 1297 | int param; | 1400 | int param; |
| @@ -1314,6 +1417,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1314 | output_func = do_display; | 1417 | output_func = do_display; |
| 1315 | display_all = 1; | 1418 | display_all = 1; |
| 1316 | break; | 1419 | break; |
| 1420 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1317 | case 'g': | 1421 | case 'g': |
| 1318 | stty_state |= STTY_recoverable_output; | 1422 | stty_state |= STTY_recoverable_output; |
| 1319 | output_func = display_recoverable; | 1423 | output_func = display_recoverable; |
| @@ -1334,11 +1438,14 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1334 | } | 1438 | } |
| 1335 | } | 1439 | } |
| 1336 | goto end_option; | 1440 | goto end_option; |
| 1441 | #endif | ||
| 1337 | default: | 1442 | default: |
| 1338 | goto invalid_argument; | 1443 | goto invalid_argument; |
| 1339 | } | 1444 | } |
| 1340 | } | 1445 | } |
| 1446 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1341 | end_option: | 1447 | end_option: |
| 1448 | #endif | ||
| 1342 | continue; | 1449 | continue; |
| 1343 | } | 1450 | } |
| 1344 | 1451 | ||
| @@ -1348,6 +1455,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1348 | continue; | 1455 | continue; |
| 1349 | } | 1456 | } |
| 1350 | 1457 | ||
| 1458 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1351 | cp = find_control(arg); | 1459 | cp = find_control(arg); |
| 1352 | if (cp) { | 1460 | if (cp) { |
| 1353 | if (!argnext) | 1461 | if (!argnext) |
| @@ -1358,13 +1466,16 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1358 | ++k; | 1466 | ++k; |
| 1359 | continue; | 1467 | continue; |
| 1360 | } | 1468 | } |
| 1469 | #endif | ||
| 1361 | 1470 | ||
| 1362 | param = find_param(arg); | 1471 | param = find_param(arg); |
| 1472 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1363 | if (param & param_need_arg) { | 1473 | if (param & param_need_arg) { |
| 1364 | if (!argnext) | 1474 | if (!argnext) |
| 1365 | bb_error_msg_and_die(bb_msg_requires_arg, arg); | 1475 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
| 1366 | ++k; | 1476 | ++k; |
| 1367 | } | 1477 | } |
| 1478 | #endif | ||
| 1368 | 1479 | ||
| 1369 | switch (param) { | 1480 | switch (param) { |
| 1370 | #ifdef __linux__ | 1481 | #ifdef __linux__ |
| @@ -1381,7 +1492,11 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1381 | xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); | 1492 | xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); |
| 1382 | break; | 1493 | break; |
| 1383 | case param_size: | 1494 | case param_size: |
| 1495 | # if ENABLE_PLATFORM_MINGW32 | ||
| 1496 | break; | ||
| 1497 | # endif | ||
| 1384 | #endif | 1498 | #endif |
| 1499 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1385 | case param_speed: | 1500 | case param_speed: |
| 1386 | break; | 1501 | break; |
| 1387 | case param_ispeed: | 1502 | case param_ispeed: |
| @@ -1392,15 +1507,19 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1392 | /* called for the side effect of xfunc death only */ | 1507 | /* called for the side effect of xfunc death only */ |
| 1393 | set_speed_or_die(output_speed, argnext, &mode); | 1508 | set_speed_or_die(output_speed, argnext, &mode); |
| 1394 | break; | 1509 | break; |
| 1510 | #endif | ||
| 1395 | default: | 1511 | default: |
| 1512 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1396 | if (recover_mode(arg, &mode) == 1) break; | 1513 | if (recover_mode(arg, &mode) == 1) break; |
| 1397 | if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; | 1514 | if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; |
| 1515 | #endif | ||
| 1398 | invalid_argument: | 1516 | invalid_argument: |
| 1399 | bb_error_msg_and_die("invalid argument '%s'", arg); | 1517 | bb_error_msg_and_die("invalid argument '%s'", arg); |
| 1400 | } | 1518 | } |
| 1401 | stty_state &= ~STTY_noargs; | 1519 | stty_state &= ~STTY_noargs; |
| 1402 | } | 1520 | } |
| 1403 | 1521 | ||
| 1522 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1404 | /* Specifying both -a and -g is an error */ | 1523 | /* Specifying both -a and -g is an error */ |
| 1405 | if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == | 1524 | if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == |
| 1406 | (STTY_verbose_output | STTY_recoverable_output) | 1525 | (STTY_verbose_output | STTY_recoverable_output) |
| @@ -1413,13 +1532,22 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1413 | ) { | 1532 | ) { |
| 1414 | bb_simple_error_msg_and_die("modes may not be set when -a or -g is used"); | 1533 | bb_simple_error_msg_and_die("modes may not be set when -a or -g is used"); |
| 1415 | } | 1534 | } |
| 1535 | #else | ||
| 1536 | /* Specifying -a with non-options is an error */ | ||
| 1537 | if ((stty_state & STTY_verbose_output) && !(stty_state & STTY_noargs) | ||
| 1538 | ) { | ||
| 1539 | bb_simple_error_msg_and_die("modes may not be set when -a is used"); | ||
| 1540 | } | ||
| 1541 | #endif | ||
| 1416 | 1542 | ||
| 1543 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1417 | /* Now it is safe to start doing things */ | 1544 | /* Now it is safe to start doing things */ |
| 1418 | if (file_name) { | 1545 | if (file_name) { |
| 1419 | G.device_name = file_name; | 1546 | G.device_name = file_name; |
| 1420 | xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); | 1547 | xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); |
| 1421 | ndelay_off(STDIN_FILENO); | 1548 | ndelay_off(STDIN_FILENO); |
| 1422 | } | 1549 | } |
| 1550 | #endif | ||
| 1423 | 1551 | ||
| 1424 | /* Initialize to all zeroes so there is no risk memcmp will report a | 1552 | /* Initialize to all zeroes so there is no risk memcmp will report a |
| 1425 | spurious difference in an uninitialized portion of the structure */ | 1553 | spurious difference in an uninitialized portion of the structure */ |
| @@ -1437,9 +1565,13 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1437 | k = 0; | 1565 | k = 0; |
| 1438 | while (argv[++k]) { | 1566 | while (argv[++k]) { |
| 1439 | const struct mode_info *mp; | 1567 | const struct mode_info *mp; |
| 1568 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1440 | const struct control_info *cp; | 1569 | const struct control_info *cp; |
| 1570 | #endif | ||
| 1441 | const char *arg = argv[k]; | 1571 | const char *arg = argv[k]; |
| 1572 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1442 | const char *argnext = argv[k+1]; | 1573 | const char *argnext = argv[k+1]; |
| 1574 | #endif | ||
| 1443 | int param; | 1575 | int param; |
| 1444 | 1576 | ||
| 1445 | if (arg[0] == '-') { | 1577 | if (arg[0] == '-') { |
| @@ -1459,6 +1591,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1459 | continue; | 1591 | continue; |
| 1460 | } | 1592 | } |
| 1461 | 1593 | ||
| 1594 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1462 | cp = find_control(arg); | 1595 | cp = find_control(arg); |
| 1463 | if (cp) { | 1596 | if (cp) { |
| 1464 | ++k; | 1597 | ++k; |
| @@ -1466,6 +1599,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1466 | stty_state |= STTY_require_set_attr; | 1599 | stty_state |= STTY_require_set_attr; |
| 1467 | continue; | 1600 | continue; |
| 1468 | } | 1601 | } |
| 1602 | #endif | ||
| 1469 | 1603 | ||
| 1470 | param = find_param(arg); | 1604 | param = find_param(arg); |
| 1471 | if (param & param_need_arg) { | 1605 | if (param & param_need_arg) { |
| @@ -1479,6 +1613,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1479 | stty_state |= STTY_require_set_attr; | 1613 | stty_state |= STTY_require_set_attr; |
| 1480 | break; | 1614 | break; |
| 1481 | #endif | 1615 | #endif |
| 1616 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1482 | #ifdef TIOCGWINSZ | 1617 | #ifdef TIOCGWINSZ |
| 1483 | case param_cols: | 1618 | case param_cols: |
| 1484 | case param_columns: | 1619 | case param_columns: |
| @@ -1510,15 +1645,24 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1510 | stty_state |= (STTY_require_set_attr | STTY_speed_was_set); | 1645 | stty_state |= (STTY_require_set_attr | STTY_speed_was_set); |
| 1511 | } /* else - impossible (caught in the first pass): | 1646 | } /* else - impossible (caught in the first pass): |
| 1512 | bb_error_msg_and_die("invalid argument '%s'", arg); */ | 1647 | bb_error_msg_and_die("invalid argument '%s'", arg); */ |
| 1648 | #endif | ||
| 1513 | } | 1649 | } |
| 1514 | } | 1650 | } |
| 1515 | 1651 | ||
| 1516 | if (stty_state & STTY_require_set_attr) { | 1652 | if (stty_state & STTY_require_set_attr) { |
| 1653 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1517 | struct termios new_mode; | 1654 | struct termios new_mode; |
| 1655 | #else | ||
| 1656 | if (mode.c_lflag & ECHO) | ||
| 1657 | mode.w_mode |= ENABLE_ECHO_INPUT; | ||
| 1658 | else | ||
| 1659 | mode.w_mode &= ~ENABLE_ECHO_INPUT; | ||
| 1660 | #endif | ||
| 1518 | 1661 | ||
| 1519 | if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) | 1662 | if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) |
| 1520 | perror_on_device_and_die("%s"); | 1663 | perror_on_device_and_die("%s"); |
| 1521 | 1664 | ||
| 1665 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1522 | /* POSIX (according to Zlotnick's book) tcsetattr returns zero if | 1666 | /* POSIX (according to Zlotnick's book) tcsetattr returns zero if |
| 1523 | it performs *any* of the requested operations. This means it | 1667 | it performs *any* of the requested operations. This means it |
| 1524 | can report 'success' when it has actually failed to perform | 1668 | can report 'success' when it has actually failed to perform |
| @@ -1554,6 +1698,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv) | |||
| 1554 | #endif | 1698 | #endif |
| 1555 | perror_on_device_and_die("%s: cannot perform all requested operations"); | 1699 | perror_on_device_and_die("%s: cannot perform all requested operations"); |
| 1556 | } | 1700 | } |
| 1701 | #endif | ||
| 1557 | } | 1702 | } |
| 1558 | 1703 | ||
| 1559 | return EXIT_SUCCESS; | 1704 | return EXIT_SUCCESS; |
diff --git a/coreutils/tr.c b/coreutils/tr.c index 8d779d8ea..f51c4caea 100644 --- a/coreutils/tr.c +++ b/coreutils/tr.c | |||
| @@ -117,6 +117,13 @@ static unsigned expand(char *arg, char **buffer_p) | |||
| 117 | arg++; | 117 | arg++; |
| 118 | z = arg; | 118 | z = arg; |
| 119 | ac = bb_process_escape_sequence(&z); | 119 | ac = bb_process_escape_sequence(&z); |
| 120 | #if ENABLE_PLATFORM_MINGW32 | ||
| 121 | if (ac == '\\' && *z == '-') { | ||
| 122 | /* An escaped dash isn't a range, don't fall through */ | ||
| 123 | buffer[pos++] = *z; | ||
| 124 | continue; | ||
| 125 | } | ||
| 126 | #endif | ||
| 120 | arg = (char *)z; | 127 | arg = (char *)z; |
| 121 | arg--; | 128 | arg--; |
| 122 | *arg = ac; | 129 | *arg = ac; |
diff --git a/coreutils/truncate.c b/coreutils/truncate.c index 8826e6b4c..87a47bb09 100644 --- a/coreutils/truncate.c +++ b/coreutils/truncate.c | |||
| @@ -73,6 +73,12 @@ int truncate_main(int argc UNUSED_PARAM, char **argv) | |||
| 73 | * do not report error, exitcode is also 0. | 73 | * do not report error, exitcode is also 0. |
| 74 | */ | 74 | */ |
| 75 | } else { | 75 | } else { |
| 76 | #if ENABLE_PLATFORM_MINGW32 | ||
| 77 | struct stat st; | ||
| 78 | |||
| 79 | if (fstat(fd, &st) == 0 && size > st.st_size) | ||
| 80 | make_sparse(fd, st.st_size, size); | ||
| 81 | #endif | ||
| 76 | if (ftruncate(fd, size) == -1) { | 82 | if (ftruncate(fd, size) == -1) { |
| 77 | bb_perror_msg("%s: truncate", *argv); | 83 | bb_perror_msg("%s: truncate", *argv); |
| 78 | ret = EXIT_FAILURE; | 84 | ret = EXIT_FAILURE; |
diff --git a/debianutils/which.c b/debianutils/which.c index 5d564e01d..0e0e97055 100644 --- a/debianutils/which.c +++ b/debianutils/which.c | |||
| @@ -15,8 +15,12 @@ | |||
| 15 | // NOTE: For WIN32 this applet is NOEXEC as file_is_win32_exe() and | 15 | // NOTE: For WIN32 this applet is NOEXEC as file_is_win32_exe() and |
| 16 | // find_executable() both allocate memory. | 16 | // find_executable() both allocate memory. |
| 17 | 17 | ||
| 18 | //applet:IF_PLATFORM_MINGW32(IF_WHICH(APPLET_NOEXEC(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which))) | 18 | //applet:IF_PLATFORM_MINGW32( |
| 19 | //applet:IF_PLATFORM_POSIX(IF_WHICH(APPLET_NOFORK(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which))) | 19 | //applet:IF_WHICH(APPLET_NOEXEC(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which)) |
| 20 | //applet:) | ||
| 21 | //applet:IF_PLATFORM_POSIX( | ||
| 22 | //applet:IF_WHICH(APPLET_NOFORK(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which)) | ||
| 23 | //applet:) | ||
| 20 | 24 | ||
| 21 | //kbuild:lib-$(CONFIG_WHICH) += which.o | 25 | //kbuild:lib-$(CONFIG_WHICH) += which.o |
| 22 | 26 | ||
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 | ||
| 258 | static void cleanup_outname(void) | 258 | static 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/editors/vi.c b/editors/vi.c index e59083ddb..7ec3427f9 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
| @@ -3108,6 +3108,10 @@ static void colon(char *buf) | |||
| 3108 | else if (cmd[0] == '!') { // run a cmd | 3108 | else if (cmd[0] == '!') { // run a cmd |
| 3109 | int retcode; | 3109 | int retcode; |
| 3110 | // :!ls run the <cmd> | 3110 | // :!ls run the <cmd> |
| 3111 | if (GOT_ADDRESS) { | ||
| 3112 | status_line_bold("Range not allowed"); | ||
| 3113 | goto ret; | ||
| 3114 | } | ||
| 3111 | exp = expand_args(buf + 1); | 3115 | exp = expand_args(buf + 1); |
| 3112 | if (exp == NULL) | 3116 | if (exp == NULL) |
| 3113 | goto ret; | 3117 | goto ret; |
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..09ebad1f6 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 \ |
| @@ -322,6 +342,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
| 322 | /* "long" is long enough on this system */ | 342 | /* "long" is long enough on this system */ |
| 323 | typedef unsigned long uoff_t; | 343 | typedef unsigned long uoff_t; |
| 324 | # define XATOOFF(a) xatoul_range((a), 0, LONG_MAX) | 344 | # define XATOOFF(a) xatoul_range((a), 0, LONG_MAX) |
| 345 | # define XATOOFF_SFX(a, s) xatoul_range_sfx((a), 0, LONG_MAX, s) | ||
| 325 | /* usage: sz = BB_STRTOOFF(s, NULL, 10); if (errno || sz < 0) die(); */ | 346 | /* usage: sz = BB_STRTOOFF(s, NULL, 10); if (errno || sz < 0) die(); */ |
| 326 | # define BB_STRTOOFF bb_strtoul | 347 | # define BB_STRTOOFF bb_strtoul |
| 327 | # define STRTOOFF strtoul | 348 | # define STRTOOFF strtoul |
| @@ -331,6 +352,7 @@ typedef unsigned long uoff_t; | |||
| 331 | /* "long" is too short, need "long long" */ | 352 | /* "long" is too short, need "long long" */ |
| 332 | typedef unsigned long long uoff_t; | 353 | typedef unsigned long long uoff_t; |
| 333 | # define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) | 354 | # define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) |
| 355 | # define XATOOFF_SFX(a, s) xatoull_range_sfx((a), 0, LLONG_MAX, s) | ||
| 334 | # define BB_STRTOOFF bb_strtoull | 356 | # define BB_STRTOOFF bb_strtoull |
| 335 | # define STRTOOFF strtoull | 357 | # define STRTOOFF strtoull |
| 336 | # define OFF_FMT LL_FMT | 358 | # define OFF_FMT LL_FMT |
| @@ -346,12 +368,14 @@ typedef unsigned long long uoff_t; | |||
| 346 | # if UINT_MAX == ULONG_MAX | 368 | # if UINT_MAX == ULONG_MAX |
| 347 | typedef unsigned long uoff_t; | 369 | typedef unsigned long uoff_t; |
| 348 | # define XATOOFF(a) xatoi_positive(a) | 370 | # define XATOOFF(a) xatoi_positive(a) |
| 371 | # define XATOOFF_SFX(a, s) xatoul_range_sfx((a), 0, INT_MAX, s) | ||
| 349 | # define BB_STRTOOFF bb_strtou | 372 | # define BB_STRTOOFF bb_strtou |
| 350 | # define STRTOOFF strtol | 373 | # define STRTOOFF strtol |
| 351 | # define OFF_FMT "l" | 374 | # define OFF_FMT "l" |
| 352 | # else | 375 | # else |
| 353 | typedef unsigned long uoff_t; | 376 | typedef unsigned long uoff_t; |
| 354 | # define XATOOFF(a) xatoul_range((a), 0, LONG_MAX) | 377 | # define XATOOFF(a) xatoul_range((a), 0, LONG_MAX) |
| 378 | # define XATOOFF_SFX(a, s) xatoul_range_sfx((a), 0, LONG_MAX, s) | ||
| 355 | # define BB_STRTOOFF bb_strtoul | 379 | # define BB_STRTOOFF bb_strtoul |
| 356 | # define STRTOOFF strtol | 380 | # define STRTOOFF strtol |
| 357 | # define OFF_FMT "l" | 381 | # define OFF_FMT "l" |
| @@ -699,6 +723,8 @@ int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC; | |||
| 699 | int sigprocmask_allsigs(int how) FAST_FUNC; | 723 | int sigprocmask_allsigs(int how) FAST_FUNC; |
| 700 | /* Return old set in the same set: */ | 724 | /* Return old set in the same set: */ |
| 701 | int sigprocmask2(int how, sigset_t *set) FAST_FUNC; | 725 | int sigprocmask2(int how, sigset_t *set) FAST_FUNC; |
| 726 | /* SIG_BLOCK all signals, return old set: */ | ||
| 727 | int sigblockall(sigset_t *set) FAST_FUNC; | ||
| 702 | #else | 728 | #else |
| 703 | #define bb_signals(s, f) | 729 | #define bb_signals(s, f) |
| 704 | #define kill_myself_with_sig(s) | 730 | #define kill_myself_with_sig(s) |
| @@ -899,7 +925,36 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC; | |||
| 899 | // Also mount.c and inetd.c are using gethostbyname(), | 925 | // Also mount.c and inetd.c are using gethostbyname(), |
| 900 | // + inet_common.c has additional IPv4-only stuff | 926 | // + inet_common.c has additional IPv4-only stuff |
| 901 | 927 | ||
| 928 | #if defined CONFIG_FEATURE_TLS_SCHANNEL | ||
| 929 | enum schannel_connection_state { | ||
| 930 | BB_SCHANNEL_OPEN = 0, | ||
| 931 | BB_SCHANNEL_CLOSED = 1, | ||
| 932 | BB_SCHANNEL_CLOSED_AND_FREED = 2 | ||
| 933 | }; | ||
| 934 | |||
| 935 | typedef struct tls_state { | ||
| 936 | int ofd; | ||
| 937 | int ifd; | ||
| 938 | |||
| 939 | // handles | ||
| 940 | CredHandle cred_handle; | ||
| 941 | CtxtHandle ctx_handle; | ||
| 942 | |||
| 943 | // buffers | ||
| 944 | char in_buffer[16384 + 256]; // input buffer (to read from server), length is maximum TLS packet size | ||
| 945 | unsigned long in_buffer_offset; | ||
| 946 | |||
| 947 | char *out_buffer; // output buffer (for decrypted data, offset from in_buffer) | ||
| 948 | unsigned long out_buffer_length; | ||
| 949 | unsigned long out_buffer_extra; | ||
| 902 | 950 | ||
| 951 | // data | ||
| 952 | char *hostname; | ||
| 953 | SecPkgContext_StreamSizes stream_sizes; | ||
| 954 | bool initialized; | ||
| 955 | enum schannel_connection_state connection_state; | ||
| 956 | } tls_state_t; | ||
| 957 | #else | ||
| 903 | struct tls_aes { | 958 | struct tls_aes { |
| 904 | uint32_t key[60]; | 959 | uint32_t key[60]; |
| 905 | unsigned rounds; | 960 | unsigned rounds; |
| @@ -956,12 +1011,14 @@ typedef struct tls_state { | |||
| 956 | struct tls_aes aes_decrypt; | 1011 | struct tls_aes aes_decrypt; |
| 957 | uint8_t H[16]; //used by AES_GCM | 1012 | uint8_t H[16]; //used by AES_GCM |
| 958 | } tls_state_t; | 1013 | } tls_state_t; |
| 1014 | #endif | ||
| 959 | 1015 | ||
| 960 | static inline tls_state_t *new_tls_state(void) | 1016 | static inline tls_state_t *new_tls_state(void) |
| 961 | { | 1017 | { |
| 962 | tls_state_t *tls = xzalloc(sizeof(*tls)); | 1018 | tls_state_t *tls = xzalloc(sizeof(*tls)); |
| 963 | return tls; | 1019 | return tls; |
| 964 | } | 1020 | } |
| 1021 | |||
| 965 | void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC; | 1022 | void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC; |
| 966 | #define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) | 1023 | #define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) |
| 967 | void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; | 1024 | void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; |
| @@ -1071,13 +1128,13 @@ unsigned bb_clk_tck(void) FAST_FUNC; | |||
| 1071 | 1128 | ||
| 1072 | #if SEAMLESS_COMPRESSION | 1129 | #if SEAMLESS_COMPRESSION |
| 1073 | /* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ | 1130 | /* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ |
| 1074 | int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC; | 1131 | int setup_unzip_on_fd(int fd, int die_if_not_compressed) FAST_FUNC; |
| 1075 | /* Autodetects .gz etc */ | 1132 | /* Autodetects .gz etc */ |
| 1076 | extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC; | 1133 | extern int open_zipped(const char *fname, int die_if_not_compressed) FAST_FUNC; |
| 1077 | extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; | 1134 | extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; |
| 1078 | #else | 1135 | #else |
| 1079 | # define setup_unzip_on_fd(...) (0) | 1136 | # define setup_unzip_on_fd(...) (0) |
| 1080 | # define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY); | 1137 | # 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)) | 1138 | # define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p)) |
| 1082 | #endif | 1139 | #endif |
| 1083 | /* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */ | 1140 | /* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */ |
| @@ -1173,6 +1230,32 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC; | |||
| 1173 | /* Reverse */ | 1230 | /* Reverse */ |
| 1174 | char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; | 1231 | char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; |
| 1175 | 1232 | ||
| 1233 | /* Returns strlen as a bonus */ | ||
| 1234 | //size_t replace_char(char *s, char what, char with) FAST_FUNC; | ||
| 1235 | static inline size_t replace_char(char *str, char from, char to) | ||
| 1236 | { | ||
| 1237 | char *p = str; | ||
| 1238 | while (*p) { | ||
| 1239 | if (*p == from) | ||
| 1240 | *p = to; | ||
| 1241 | p++; | ||
| 1242 | } | ||
| 1243 | return p - str; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | extern const char c_escape_conv_str00[]; | ||
| 1247 | #define c_escape_conv_str07 (c_escape_conv_str00+3) | ||
| 1248 | |||
| 1249 | void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count); | ||
| 1250 | void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count); | ||
| 1251 | void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask); | ||
| 1252 | void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2); | ||
| 1253 | #if BB_UNALIGNED_MEMACCESS_OK | ||
| 1254 | # define xorbuf16(buf,mask) xorbuf16_aligned_long(buf,mask) | ||
| 1255 | #else | ||
| 1256 | void FAST_FUNC xorbuf16(void* buf, const void* mask); | ||
| 1257 | #endif | ||
| 1258 | |||
| 1176 | /* Generate a UUID */ | 1259 | /* Generate a UUID */ |
| 1177 | void generate_uuid(uint8_t *buf) FAST_FUNC; | 1260 | void generate_uuid(uint8_t *buf) FAST_FUNC; |
| 1178 | 1261 | ||
| @@ -1887,18 +1970,25 @@ extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_F | |||
| 1887 | extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; | 1970 | extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; |
| 1888 | /* | 1971 | /* |
| 1889 | * rnd is additional random input. New one is returned. | 1972 | * rnd is additional random input. New one is returned. |
| 1890 | * Useful if you call crypt_make_salt many times in a row: | 1973 | * Useful if you call crypt_make_rand64encoded many times in a row: |
| 1891 | * rnd = crypt_make_salt(buf1, 4, 0); | 1974 | * rnd = crypt_make_rand64encoded(buf1, 4, 0); |
| 1892 | * rnd = crypt_make_salt(buf2, 4, rnd); | 1975 | * rnd = crypt_make_rand64encoded(buf2, 4, rnd); |
| 1893 | * rnd = crypt_make_salt(buf3, 4, rnd); | 1976 | * rnd = crypt_make_rand64encoded(buf3, 4, rnd); |
| 1894 | * (otherwise we risk having same salt generated) | 1977 | * (otherwise we risk having same salt generated) |
| 1895 | */ | 1978 | */ |
| 1896 | extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; | 1979 | extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC; |
| 1897 | /* "$N$" + sha_salt_16_bytes + NUL */ | 1980 | /* Size of char salt[] to hold randomly-generated salt string |
| 1898 | #define MAX_PW_SALT_LEN (3 + 16 + 1) | 1981 | * sha256/512: |
| 1982 | * "$5$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>" | ||
| 1983 | * "$6$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>" | ||
| 1984 | * #define MAX_PW_SALT_LEN (3 + sizeof("rounds=999999999$")-1 + 16 + 1) | ||
| 1985 | * yescrypt: | ||
| 1986 | * "$y$" <up to 8 params of up to 6 chars each> "$" <up to 86 chars salt><NUL> | ||
| 1987 | * (86 chars are ascii64-encoded 64 binary bytes) | ||
| 1988 | */ | ||
| 1989 | #define MAX_PW_SALT_LEN (3 + 8*6 + 1 + 86 + 1) | ||
| 1899 | extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; | 1990 | extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; |
| 1900 | 1991 | ||
| 1901 | |||
| 1902 | /* Returns number of lines changed, or -1 on error */ | 1992 | /* Returns number of lines changed, or -1 on error */ |
| 1903 | #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) | 1993 | #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) |
| 1904 | #define update_passwd(filename, username, data, member) \ | 1994 | #define update_passwd(filename, username, data, member) \ |
| @@ -2041,6 +2131,10 @@ int64_t windows_read_key(int fd, char *buffer, int timeout) FAST_FUNC; | |||
| 2041 | int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC; | 2131 | int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC; |
| 2042 | void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; | 2132 | void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; |
| 2043 | 2133 | ||
| 2134 | int check_got_signal_and_poll(struct pollfd pfd[1], int timeout) FAST_FUNC; | ||
| 2135 | #if ENABLE_PLATFORM_MINGW32 | ||
| 2136 | # define check_got_signal_and_poll(p, t) poll(p, 1, t) | ||
| 2137 | #endif | ||
| 2044 | 2138 | ||
| 2045 | #if ENABLE_FEATURE_EDITING | 2139 | #if ENABLE_FEATURE_EDITING |
| 2046 | /* It's NOT just ENABLEd or disabled. It's a number: */ | 2140 | /* It's NOT just ENABLEd or disabled. It's a number: */ |
| @@ -2087,7 +2181,7 @@ typedef struct line_input_t { | |||
| 2087 | # if MAX_HISTORY | 2181 | # if MAX_HISTORY |
| 2088 | int cnt_history; | 2182 | int cnt_history; |
| 2089 | int cur_history; | 2183 | int cur_history; |
| 2090 | int max_history; /* must never be <= 0 */ | 2184 | int max_history; /* must never be < 0 */ |
| 2091 | # if ENABLE_FEATURE_EDITING_SAVEHISTORY | 2185 | # if ENABLE_FEATURE_EDITING_SAVEHISTORY |
| 2092 | /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: | 2186 | /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: |
| 2093 | * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are | 2187 | * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are |
| @@ -2153,33 +2247,6 @@ enum { COMM_LEN = 16 }; | |||
| 2153 | # endif | 2247 | # endif |
| 2154 | #endif | 2248 | #endif |
| 2155 | 2249 | ||
| 2156 | struct 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 | ||
| 2180 | int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | ||
| 2181 | void (*cb)(struct smaprec *, void *), void *data); | ||
| 2182 | |||
| 2183 | typedef struct procps_status_t { | 2250 | typedef struct procps_status_t { |
| 2184 | #if !ENABLE_PLATFORM_MINGW32 | 2251 | #if !ENABLE_PLATFORM_MINGW32 |
| 2185 | DIR *dir; | 2252 | DIR *dir; |
| @@ -2215,7 +2282,13 @@ typedef struct procps_status_t { | |||
| 2215 | #endif | 2282 | #endif |
| 2216 | unsigned tty_major,tty_minor; | 2283 | unsigned tty_major,tty_minor; |
| 2217 | #if ENABLE_FEATURE_TOPMEM | 2284 | #if ENABLE_FEATURE_TOPMEM |
| 2218 | struct smaprec smaps; | 2285 | unsigned long mapped_rw; |
| 2286 | unsigned long mapped_ro; | ||
| 2287 | unsigned long shared_clean; | ||
| 2288 | unsigned long shared_dirty; | ||
| 2289 | unsigned long private_clean; | ||
| 2290 | unsigned long private_dirty; | ||
| 2291 | unsigned long stack; | ||
| 2219 | #endif | 2292 | #endif |
| 2220 | char state[4]; | 2293 | char state[4]; |
| 2221 | /* basename of executable in exec(2), read from /proc/N/stat | 2294 | /* basename of executable in exec(2), read from /proc/N/stat |
| @@ -2264,11 +2337,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC; | |||
| 2264 | procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; | 2337 | procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; |
| 2265 | /* Format cmdline (up to col chars) into char buf[size] */ | 2338 | /* Format cmdline (up to col chars) into char buf[size] */ |
| 2266 | /* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ | 2339 | /* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ |
| 2267 | void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; | 2340 | int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; |
| 2268 | pid_t *find_pid_by_name(const char* procName) FAST_FUNC; | 2341 | pid_t *find_pid_by_name(const char* procName) FAST_FUNC; |
| 2269 | pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; | 2342 | pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; |
| 2270 | int starts_with_cpu(const char *str) FAST_FUNC; | 2343 | int starts_with_cpu(const char *str) FAST_FUNC; |
| 2271 | unsigned get_cpu_count(void) FAST_FUNC; | 2344 | unsigned get_cpu_count(void) FAST_FUNC; |
| 2345 | /* Some internals reused by pmap: */ | ||
| 2346 | unsigned long FAST_FUNC fast_strtoul_10(char **endptr); | ||
| 2347 | unsigned long long FAST_FUNC fast_strtoull_16(char **endptr); | ||
| 2348 | char* FAST_FUNC skip_fields(char *str, int count); | ||
| 2272 | 2349 | ||
| 2273 | 2350 | ||
| 2274 | /* Use strict=1 if you process input from untrusted source: | 2351 | /* Use strict=1 if you process input from untrusted source: |
| @@ -2294,6 +2371,56 @@ char *decode_base64(char *dst, const char **pp_src) FAST_FUNC; | |||
| 2294 | char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; | 2371 | char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; |
| 2295 | void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; | 2372 | void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; |
| 2296 | 2373 | ||
| 2374 | int FAST_FUNC i2a64(int i); | ||
| 2375 | int FAST_FUNC a2i64(char c); | ||
| 2376 | char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n); | ||
| 2377 | |||
| 2378 | enum { | ||
| 2379 | /* how many bytes XYZ_end() fills */ | ||
| 2380 | MD5_OUTSIZE = 16, | ||
| 2381 | SHA1_OUTSIZE = 20, | ||
| 2382 | SHA256_OUTSIZE = 32, | ||
| 2383 | SHA384_OUTSIZE = 48, | ||
| 2384 | SHA512_OUTSIZE = 64, | ||
| 2385 | //SHA3-224_OUTSIZE = 28, | ||
| 2386 | /* size of input block */ | ||
| 2387 | SHA2_INSIZE = 64, | ||
| 2388 | }; | ||
| 2389 | |||
| 2390 | #if defined CONFIG_FEATURE_USE_CNG_API | ||
| 2391 | struct bcrypt_hash_ctx_t { | ||
| 2392 | void *handle; | ||
| 2393 | void *hash_obj; | ||
| 2394 | unsigned int output_size; | ||
| 2395 | }; | ||
| 2396 | typedef struct bcrypt_hash_ctx_t md5_ctx_t; | ||
| 2397 | typedef struct bcrypt_hash_ctx_t sha1_ctx_t; | ||
| 2398 | typedef struct bcrypt_hash_ctx_t sha256_ctx_t; | ||
| 2399 | typedef struct bcrypt_hash_ctx_t sha384_ctx_t; | ||
| 2400 | typedef struct bcrypt_hash_ctx_t sha512_ctx_t; | ||
| 2401 | typedef struct sha3_ctx_t { | ||
| 2402 | uint64_t state[25]; | ||
| 2403 | unsigned bytes_queued; | ||
| 2404 | unsigned input_block_bytes; | ||
| 2405 | } sha3_ctx_t; | ||
| 2406 | void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; | ||
| 2407 | void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; | ||
| 2408 | void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; | ||
| 2409 | void sha384_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; | ||
| 2410 | void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; | ||
| 2411 | void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; | ||
| 2412 | unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC; | ||
| 2413 | # define md5_hash generic_hash | ||
| 2414 | # define sha1_hash generic_hash | ||
| 2415 | # define sha256_hash generic_hash | ||
| 2416 | # define sha384_hash generic_hash | ||
| 2417 | # define sha512_hash generic_hash | ||
| 2418 | # define md5_end generic_end | ||
| 2419 | # define sha1_end generic_end | ||
| 2420 | # define sha256_end generic_end | ||
| 2421 | # define sha384_end generic_end | ||
| 2422 | # define sha512_end generic_end | ||
| 2423 | #else | ||
| 2297 | typedef struct md5_ctx_t { | 2424 | typedef struct md5_ctx_t { |
| 2298 | uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ | 2425 | uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ |
| 2299 | void (*process_block)(struct md5_ctx_t*) FAST_FUNC; | 2426 | void (*process_block)(struct md5_ctx_t*) FAST_FUNC; |
| @@ -2307,6 +2434,7 @@ typedef struct sha512_ctx_t { | |||
| 2307 | uint64_t hash[8]; | 2434 | uint64_t hash[8]; |
| 2308 | uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ | 2435 | uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ |
| 2309 | } sha512_ctx_t; | 2436 | } sha512_ctx_t; |
| 2437 | typedef struct sha512_ctx_t sha384_ctx_t; | ||
| 2310 | typedef struct sha3_ctx_t { | 2438 | typedef struct sha3_ctx_t { |
| 2311 | uint64_t state[25]; | 2439 | uint64_t state[25]; |
| 2312 | unsigned bytes_queued; | 2440 | unsigned bytes_queued; |
| @@ -2324,20 +2452,69 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC; | |||
| 2324 | void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; | 2452 | void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; |
| 2325 | void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; | 2453 | void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; |
| 2326 | unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; | 2454 | unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; |
| 2455 | void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC; | ||
| 2456 | #define sha384_hash sha512_hash | ||
| 2457 | unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC; | ||
| 2458 | #endif | ||
| 2327 | void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; | 2459 | void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; |
| 2328 | void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; | 2460 | void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; |
| 2329 | unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; | 2461 | unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; |
| 2462 | void 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 */ | 2463 | /* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ |
| 2464 | #if defined CONFIG_FEATURE_USE_CNG_API | ||
| 2465 | typedef struct bcrypt_hash_ctx_t md5sha_ctx_t; | ||
| 2466 | #define md5sha_hash generic_hash | ||
| 2467 | #define sha_end generic_end | ||
| 2468 | #else | ||
| 2331 | typedef struct md5_ctx_t md5sha_ctx_t; | 2469 | typedef struct md5_ctx_t md5sha_ctx_t; |
| 2332 | #define md5sha_hash md5_hash | 2470 | #define md5sha_hash md5_hash |
| 2333 | #define sha_end sha1_end | 2471 | #define sha_end sha1_end |
| 2334 | enum { | 2472 | #endif |
| 2335 | MD5_OUTSIZE = 16, | 2473 | |
| 2336 | SHA1_OUTSIZE = 20, | 2474 | /* RFC 2104 HMAC (hash-based message authentication code) */ |
| 2337 | SHA256_OUTSIZE = 32, | 2475 | #if !ENABLE_FEATURE_USE_CNG_API |
| 2338 | SHA512_OUTSIZE = 64, | 2476 | typedef struct hmac_ctx { |
| 2339 | SHA3_OUTSIZE = 28, | 2477 | md5sha_ctx_t hashed_key_xor_ipad; |
| 2340 | }; | 2478 | md5sha_ctx_t hashed_key_xor_opad; |
| 2479 | } hmac_ctx_t; | ||
| 2480 | #else | ||
| 2481 | typedef struct bcrypt_hash_ctx_t hmac_ctx_t; | ||
| 2482 | #endif | ||
| 2483 | #define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1) | ||
| 2484 | typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC; | ||
| 2485 | #if !ENABLE_FEATURE_USE_CNG_API | ||
| 2486 | #if HMAC_ONLY_SHA256 | ||
| 2487 | #define hmac_begin(ctx,key,key_size,begin) \ | ||
| 2488 | hmac_begin(ctx,key,key_size) | ||
| 2489 | #endif | ||
| 2490 | void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin); | ||
| 2491 | static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len) | ||
| 2492 | { | ||
| 2493 | md5sha_hash(&ctx->hashed_key_xor_ipad, in, len); | ||
| 2494 | } | ||
| 2495 | #else | ||
| 2496 | # if HMAC_ONLY_SHA256 | ||
| 2497 | # define hmac_begin(pre,key,key_size,begin) \ | ||
| 2498 | _hmac_begin(pre, key, key_size, sha256_begin_hmac) | ||
| 2499 | # else | ||
| 2500 | # define hmac_begin _hmac_begin | ||
| 2501 | # endif | ||
| 2502 | void _hmac_begin(hmac_ctx_t *pre, uint8_t *key, unsigned key_size, | ||
| 2503 | BCRYPT_ALG_HANDLE alg_handle); | ||
| 2504 | void hmac_uninit(hmac_ctx_t *pre); | ||
| 2505 | #endif | ||
| 2506 | unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out); | ||
| 2507 | #if HMAC_ONLY_SHA256 | ||
| 2508 | #define hmac_block(key,key_size,begin,in,sz,out) \ | ||
| 2509 | hmac_block(key,key_size,in,sz,out) | ||
| 2510 | #endif | ||
| 2511 | unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, | ||
| 2512 | md5sha_begin_func *begin, | ||
| 2513 | const void *in, unsigned sz, | ||
| 2514 | uint8_t *out); | ||
| 2515 | /* HMAC helpers for TLS: */ | ||
| 2516 | void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va); | ||
| 2517 | unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...); | ||
| 2341 | 2518 | ||
| 2342 | extern uint32_t *global_crc32_table; | 2519 | extern uint32_t *global_crc32_table; |
| 2343 | uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; | 2520 | uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; |
| @@ -2473,31 +2650,10 @@ extern struct globals *BB_GLOBAL_CONST ptr_to_globals; | |||
| 2473 | #define barrier() asm volatile ("":::"memory") | 2650 | #define barrier() asm volatile ("":::"memory") |
| 2474 | 2651 | ||
| 2475 | #if defined(__clang_major__) && __clang_major__ >= 9 | 2652 | #if defined(__clang_major__) && __clang_major__ >= 9 |
| 2476 | /* Clang/llvm drops assignment to "constant" storage. Silently. | 2653 | /* {ASSIGN,XZALLOC}_CONST_PTR() are out-of-line functions |
| 2477 | * Needs serious convincing to not eliminate the store. | 2654 | * to prevent clang from reading pointer before it is assigned. |
| 2478 | */ | ||
| 2479 | static 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. */ | ||
| 2496 | void 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 | */ | 2655 | */ |
| 2656 | void ASSIGN_CONST_PTR(const void *pptr, void *v) FAST_FUNC; | ||
| 2501 | void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC; | 2657 | void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC; |
| 2502 | #else | 2658 | #else |
| 2503 | # define ASSIGN_CONST_PTR(pptr, v) do { \ | 2659 | # define ASSIGN_CONST_PTR(pptr, v) do { \ |
diff --git a/include/mingw.h b/include/mingw.h index c41c0f91e..0a8d52d84 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | #define IMPL(name,ret,retval,...) static inline ret name(__VA_ARGS__) { return retval; } | 3 | #define IMPL(name,ret,retval,...) static inline ret name(__VA_ARGS__) { return retval; } |
| 4 | 4 | ||
| 5 | /* Use 64-bit time on 32-bit platforms. */ | 5 | /* Use 64-bit time on 32-bit platforms. */ |
| 6 | #if !defined(_WIN64) | 6 | #if !defined(_WIN64) && __MINGW64_VERSION_MAJOR >= 10 |
| 7 | # define time_t __time64_t | 7 | # define time_t __time64_t |
| 8 | # define ctime(t) _ctime64(t) | 8 | # define ctime(t) _ctime64(t) |
| 9 | # define localtime(t) _localtime64(t) | 9 | # define localtime(t) _localtime64(t) |
| @@ -255,10 +255,22 @@ int ffs(int i); | |||
| 255 | #endif | 255 | #endif |
| 256 | 256 | ||
| 257 | /* | 257 | /* |
| 258 | * sys/file.h | ||
| 259 | */ | ||
| 260 | |||
| 261 | int flock(int fd, int op); | ||
| 262 | |||
| 263 | #define LOCK_SH 1 | ||
| 264 | #define LOCK_EX 2 | ||
| 265 | #define LOCK_UN 8 | ||
| 266 | #define LOCK_NB 4 | ||
| 267 | |||
| 268 | /* | ||
| 258 | * sys/ioctl.h | 269 | * sys/ioctl.h |
| 259 | */ | 270 | */ |
| 260 | 271 | ||
| 261 | #define TIOCGWINSZ 0x5413 | 272 | #define TIOCGWINSZ 0x5413 |
| 273 | #define TIOCSWINSZ 0x5414 | ||
| 262 | 274 | ||
| 263 | int ioctl(int fd, int code, ...); | 275 | int ioctl(int fd, int code, ...); |
| 264 | 276 | ||
| @@ -500,7 +512,7 @@ int kill(pid_t pid, int sig); | |||
| 500 | int link(const char *oldpath, const char *newpath); | 512 | int link(const char *oldpath, const char *newpath); |
| 501 | NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM); | 513 | NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM); |
| 502 | /* order of devices must match that in get_dev_type */ | 514 | /* order of devices must match that in get_dev_type */ |
| 503 | enum {DEV_NULL, DEV_ZERO, DEV_URANDOM, NOT_DEVICE = -1}; | 515 | enum {DEV_NULL, DEV_TTY, DEV_ZERO, DEV_URANDOM, NOT_DEVICE = -1}; |
| 504 | int get_dev_type(const char *filename); | 516 | int get_dev_type(const char *filename); |
| 505 | void update_special_fd(int dev, int fd); | 517 | void update_special_fd(int dev, int fd); |
| 506 | int mingw_open (const char *filename, int oflags, ...); | 518 | int mingw_open (const char *filename, int oflags, ...); |
| @@ -639,7 +651,6 @@ int err_win_to_posix(void); | |||
| 639 | ULONGLONG CompatGetTickCount64(void); | 651 | ULONGLONG CompatGetTickCount64(void); |
| 640 | #define GetTickCount64 CompatGetTickCount64 | 652 | #define GetTickCount64 CompatGetTickCount64 |
| 641 | 653 | ||
| 642 | ssize_t get_random_bytes(void *buf, ssize_t count); | ||
| 643 | int enumerate_links(const char *file, char *name); | 654 | int enumerate_links(const char *file, char *name); |
| 644 | 655 | ||
| 645 | int unc_root_len(const char *dir) FAST_FUNC; | 656 | int unc_root_len(const char *dir) FAST_FUNC; |
| @@ -669,3 +680,5 @@ enum { | |||
| 669 | }; | 680 | }; |
| 670 | int elevation_state(void); | 681 | int elevation_state(void); |
| 671 | void set_interp(int i) FAST_FUNC; | 682 | void set_interp(int i) FAST_FUNC; |
| 683 | int mingw_shell_execute(SHELLEXECUTEINFO *info); | ||
| 684 | void mingw_die_if_error(NTSTATUS status, const char *function_name); | ||
diff --git a/include/platform.h b/include/platform.h index 5795a0cf3..0db8bf345 100644 --- a/include/platform.h +++ b/include/platform.h | |||
| @@ -208,7 +208,7 @@ | |||
| 208 | #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN | 208 | #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN |
| 209 | # define BB_BIG_ENDIAN 0 | 209 | # define BB_BIG_ENDIAN 0 |
| 210 | # define BB_LITTLE_ENDIAN 1 | 210 | # define BB_LITTLE_ENDIAN 1 |
| 211 | #elif defined(__386__) | 211 | #elif defined(__i386__) |
| 212 | # define BB_BIG_ENDIAN 0 | 212 | # define BB_BIG_ENDIAN 0 |
| 213 | # define BB_LITTLE_ENDIAN 1 | 213 | # define BB_LITTLE_ENDIAN 1 |
| 214 | #else | 214 | #else |
| @@ -230,6 +230,8 @@ | |||
| 230 | # define SWAP_LE64(x) bb_bswap_64(x) | 230 | # define SWAP_LE64(x) bb_bswap_64(x) |
| 231 | # define IF_BIG_ENDIAN(...) __VA_ARGS__ | 231 | # define IF_BIG_ENDIAN(...) __VA_ARGS__ |
| 232 | # define IF_LITTLE_ENDIAN(...) | 232 | # define IF_LITTLE_ENDIAN(...) |
| 233 | /* How do bytes a,b,c,d (sequential in memory) look if fetched into uint32_t? */ | ||
| 234 | # define PACK32_BYTES(a,b,c,d) (uint32_t)((d)+((c)<<8)+((b)<<16)+((a)<<24)) | ||
| 233 | #else | 235 | #else |
| 234 | # define SWAP_BE16(x) bswap_16(x) | 236 | # define SWAP_BE16(x) bswap_16(x) |
| 235 | # define SWAP_BE32(x) bswap_32(x) | 237 | # define SWAP_BE32(x) bswap_32(x) |
| @@ -239,6 +241,7 @@ | |||
| 239 | # define SWAP_LE64(x) (x) | 241 | # define SWAP_LE64(x) (x) |
| 240 | # define IF_BIG_ENDIAN(...) | 242 | # define IF_BIG_ENDIAN(...) |
| 241 | # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ | 243 | # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ |
| 244 | # define PACK32_BYTES(a,b,c,d) (uint32_t)((a)+((b)<<8)+((c)<<16)+((d)<<24)) | ||
| 242 | #endif | 245 | #endif |
| 243 | 246 | ||
| 244 | 247 | ||
diff --git a/include/usage.src.h b/include/usage.src.h index 5d2038834..0881337f8 100644 --- a/include/usage.src.h +++ b/include/usage.src.h | |||
| @@ -17,11 +17,11 @@ | |||
| 17 | #define scripted_trivial_usage NOUSAGE_STR | 17 | #define scripted_trivial_usage NOUSAGE_STR |
| 18 | #define scripted_full_usage "" | 18 | #define scripted_full_usage "" |
| 19 | 19 | ||
| 20 | #if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA | 20 | #if !ENABLE_USE_BB_CRYPT |
| 21 | # define CRYPT_METHODS_HELP_STR "des,md5,sha256/512" \ | 21 | # define CRYPT_METHODS_HELP_STR "des,md5,sha256/512,yescrypt" \ |
| 22 | " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" | 22 | " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" |
| 23 | #else | 23 | #else |
| 24 | # define CRYPT_METHODS_HELP_STR "des,md5" \ | 24 | # define CRYPT_METHODS_HELP_STR "des,md5"IF_USE_BB_CRYPT_SHA(",sha256/512")IF_USE_BB_CRYPT_YES(",yescrypt") \ |
| 25 | " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" | 25 | " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" |
| 26 | #endif | 26 | #endif |
| 27 | 27 | ||
diff --git a/init/bootchartd.c b/init/bootchartd.c index 0929890a3..a5447c6ad 100644 --- a/init/bootchartd.c +++ b/init/bootchartd.c | |||
| @@ -133,7 +133,7 @@ static void dump_file(FILE *fp, const char *filename) | |||
| 133 | static int dump_procs(FILE *fp, int look_for_login_process) | 133 | static 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 | ||
| 40 | config 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 | |||
| 40 | config MD5_SMALL | 48 | config 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 | |||
| 67 | config SHA1_HWACCEL | 75 | config 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 | |||
| 74 | config SHA256_HWACCEL | 83 | config 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 | |||
| 182 | config FEATURE_EDITING_HISTORY | 192 | config 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..496d320cd 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" |
| @@ -1122,14 +1122,14 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
| 1122 | #endif | 1122 | #endif |
| 1123 | 1123 | ||
| 1124 | if (strcmp(argv[1], "--help") == 0) { | 1124 | if (strcmp(argv[1], "--help") == 0) { |
| 1125 | /* "busybox --help [<applet>]" */ | 1125 | /* "busybox --help [APPLET]" */ |
| 1126 | if (!argv[2] | 1126 | if (!argv[2] |
| 1127 | # if ENABLE_FEATURE_SH_STANDALONE && ENABLE_FEATURE_TAB_COMPLETION | 1127 | # if ENABLE_FEATURE_SH_STANDALONE && ENABLE_FEATURE_TAB_COMPLETION |
| 1128 | || strcmp(argv[2], "busybox") == 0 /* prevent getting "No help available" */ | 1128 | || strcmp(argv[2], "busybox") == 0 /* prevent getting "No help available" */ |
| 1129 | # endif | 1129 | # endif |
| 1130 | ) | 1130 | ) |
| 1131 | goto help; | 1131 | goto help; |
| 1132 | /* convert to "<applet> --help" */ | 1132 | /* convert to "APPLET --help" */ |
| 1133 | applet_name = argv[0] = argv[2]; | 1133 | applet_name = argv[0] = argv[2]; |
| 1134 | argv[2] = NULL; | 1134 | argv[2] = NULL; |
| 1135 | if (find_applet_by_name_internal(applet_name) >= 0) { | 1135 | if (find_applet_by_name_internal(applet_name) >= 0) { |
| @@ -1137,8 +1137,16 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
| 1137 | xfunc_error_retval = 0; | 1137 | xfunc_error_retval = 0; |
| 1138 | bb_show_usage(); | 1138 | bb_show_usage(); |
| 1139 | } /* else: unknown applet, fall through (causes "applet not found" later) */ | 1139 | } /* else: unknown applet, fall through (causes "applet not found" later) */ |
| 1140 | } else { | 1140 | } |
| 1141 | /* "busybox <applet> arg1 arg2 ..." */ | 1141 | # if ENABLE_FEATURE_VERSION |
| 1142 | else if (!argv[2] && strcmp(argv[1], "--version") == 0) { | ||
| 1143 | full_write1_str(bb_banner); /* reuse const string */ | ||
| 1144 | full_write1_str("\n"); | ||
| 1145 | return 0; | ||
| 1146 | } | ||
| 1147 | # endif | ||
| 1148 | else { | ||
| 1149 | /* "busybox APPLET arg1 arg2 ..." */ | ||
| 1142 | argv++; | 1150 | argv++; |
| 1143 | /* We support "busybox /a/path/to/applet args..." too. Allows for | 1151 | /* We support "busybox /a/path/to/applet args..." too. Allows for |
| 1144 | * "#!/bin/busybox"-style wrappers | 1152 | * "#!/bin/busybox"-style wrappers |
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 | |||
| 12 | void 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 | |||
| 30 | void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count) | ||
| 31 | { | ||
| 32 | xorbuf_3(dst, dst, src, count); | ||
| 33 | } | ||
| 34 | |||
| 35 | void 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 | |||
| 82 | void 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 | ||
| 119 | void 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 | |||
| 11 | const 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 | ||
| 18 | char* FAST_FUNC concat_path_file(const char *path, const char *filename) | 18 | char* 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 */ | ||
| 77 | static 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 | |||
| 89 | char* 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 |
| 12 | void 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 | */ | ||
| 15 | static 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 | 25 | void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, void *v) | |
| 17 | # if ENABLE_PLATFORM_MINGW32 | 26 | { |
| 18 | void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, const void *v) | 27 | *(void**)not_const_pp(pptr) = v; |
| 28 | barrier(); | ||
| 29 | } | ||
| 30 | void 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 | ||
| 515 | static 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 | |||
| 526 | static void conv_c(PR *pr, unsigned char *p) | 517 | static 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 | ||
| 22 | void 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 | |||
| 60 | unsigned 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 | |||
| 68 | unsigned 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 | |||
| 78 | void 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 | ||
| 94 | void _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 | |||
| 114 | unsigned 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 | |||
| 124 | void 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 | |||
| 134 | void 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 | */ | ||
| 144 | unsigned 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 | |||
| 31 | static 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 | |||
| 48 | void FAST_FUNC md5_begin(md5_ctx_t *ctx) | ||
| 49 | { | ||
| 50 | generic_init(ctx, BCRYPT_MD5_ALG_HANDLE); | ||
| 51 | } | ||
| 52 | |||
| 53 | void 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) */ | ||
| 60 | void 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) */ | ||
| 68 | void 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) */ | ||
| 77 | void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) | ||
| 78 | { | ||
| 79 | generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE); | ||
| 80 | } | ||
| 81 | #endif /* NEED_SHA512 */ | ||
| 82 | |||
| 83 | void 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 | |||
| 92 | unsigned 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 */ |
| 84 | static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed) | 171 | static 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 | 1320 | static 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) */ |
| 1357 | void 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) */ | ||
| 1261 | void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) | 1367 | void 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 |
| 1335 | unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) | 1441 | static 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 | } | ||
| 1475 | unsigned 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 | ||
| 1482 | unsigned 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 | ||
| 1905 | unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) | 2022 | unsigned 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 | |||
| 12 | void FAST_FUNC | ||
| 13 | sha256_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 |
| 47 | sha256_process_block64_shaNI: | 47 | sha256_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 |
| 50 | sha256_process_block64_shaNI: | 50 | sha256_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..7dc39ca80 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
| @@ -457,7 +457,7 @@ static void put_cur_glyph_and_inc_cursor(void) | |||
| 457 | * have automargin (IOW: it is moving cursor to next line | 457 | * have automargin (IOW: it is moving cursor to next line |
| 458 | * by itself (which is wrong for VT-10x terminals)), | 458 | * by itself (which is wrong for VT-10x terminals)), |
| 459 | * this will break things: there will be one extra empty line */ | 459 | * this will break things: there will be one extra empty line */ |
| 460 | puts("\r"); /* + implicit '\n' */ | 460 | fputs("\r\n", stderr); |
| 461 | #else | 461 | #else |
| 462 | /* VT-10x terminals don't wrap cursor to next line when last char | 462 | /* VT-10x terminals don't wrap cursor to next line when last char |
| 463 | * on the line is printed - cursor stays "over" this char. | 463 | * on the line is printed - cursor stays "over" this char. |
| @@ -1302,9 +1302,10 @@ static void showfiles(void) | |||
| 1302 | ); | 1302 | ); |
| 1303 | } | 1303 | } |
| 1304 | if (ENABLE_UNICODE_SUPPORT) | 1304 | if (ENABLE_UNICODE_SUPPORT) |
| 1305 | puts(printable_string(matches[n])); | 1305 | fputs(printable_string(matches[n]), stderr); |
| 1306 | else | 1306 | else |
| 1307 | puts(matches[n]); | 1307 | fputs(matches[n], stderr); |
| 1308 | bb_putchar_stderr('\n'); | ||
| 1308 | } | 1309 | } |
| 1309 | } | 1310 | } |
| 1310 | 1311 | ||
| @@ -1595,8 +1596,8 @@ unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp) | |||
| 1595 | # endif | 1596 | # endif |
| 1596 | if (hp) { | 1597 | if (hp) { |
| 1597 | size = atoi(hp); | 1598 | size = atoi(hp); |
| 1598 | if (size <= 0) | 1599 | if (size < 0) |
| 1599 | return 1; | 1600 | return 0; |
| 1600 | if (size > MAX_HISTORY) | 1601 | if (size > MAX_HISTORY) |
| 1601 | return MAX_HISTORY; | 1602 | return MAX_HISTORY; |
| 1602 | } | 1603 | } |
| @@ -1690,18 +1691,21 @@ static void load_history(line_input_t *st_parm) | |||
| 1690 | /* NB: do not trash old history if file can't be opened */ | 1691 | /* NB: do not trash old history if file can't be opened */ |
| 1691 | 1692 | ||
| 1692 | fp = fopen_for_read(st_parm->hist_file); | 1693 | fp = fopen_for_read(st_parm->hist_file); |
| 1693 | if (fp) { | 1694 | if (!fp) |
| 1694 | /* clean up old history */ | 1695 | return; |
| 1695 | for (idx = st_parm->cnt_history; idx > 0;) { | 1696 | |
| 1696 | idx--; | 1697 | /* clean up old history */ |
| 1697 | free(st_parm->history[idx]); | 1698 | for (idx = st_parm->cnt_history; idx > 0;) { |
| 1698 | st_parm->history[idx] = NULL; | 1699 | idx--; |
| 1699 | } | 1700 | free(st_parm->history[idx]); |
| 1701 | st_parm->history[idx] = NULL; | ||
| 1702 | } | ||
| 1700 | 1703 | ||
| 1701 | /* fill temp_h[], retaining only last MAX_HISTORY lines */ | 1704 | /* fill temp_h[], retaining only last max_history lines */ |
| 1702 | memset(temp_h, 0, sizeof(temp_h)); | 1705 | memset(temp_h, 0, sizeof(temp_h)); |
| 1703 | idx = 0; | 1706 | idx = 0; |
| 1704 | st_parm->cnt_history_in_file = 0; | 1707 | st_parm->cnt_history_in_file = 0; |
| 1708 | if (st_parm->max_history != 0) { | ||
| 1705 | while ((line = xmalloc_fgetline(fp)) != NULL) { | 1709 | while ((line = xmalloc_fgetline(fp)) != NULL) { |
| 1706 | if (line[0] == '\0') { | 1710 | if (line[0] == '\0') { |
| 1707 | free(line); | 1711 | free(line); |
| @@ -1714,34 +1718,34 @@ static void load_history(line_input_t *st_parm) | |||
| 1714 | if (idx == st_parm->max_history) | 1718 | if (idx == st_parm->max_history) |
| 1715 | idx = 0; | 1719 | idx = 0; |
| 1716 | } | 1720 | } |
| 1717 | fclose(fp); | 1721 | } |
| 1718 | 1722 | fclose(fp); | |
| 1719 | /* find first non-NULL temp_h[], if any */ | ||
| 1720 | if (st_parm->cnt_history_in_file) { | ||
| 1721 | while (temp_h[idx] == NULL) { | ||
| 1722 | idx++; | ||
| 1723 | if (idx == st_parm->max_history) | ||
| 1724 | idx = 0; | ||
| 1725 | } | ||
| 1726 | } | ||
| 1727 | 1723 | ||
| 1728 | /* copy temp_h[] to st_parm->history[] */ | 1724 | /* find first non-NULL temp_h[], if any */ |
| 1729 | for (i = 0; i < st_parm->max_history;) { | 1725 | if (st_parm->cnt_history_in_file != 0) { |
| 1730 | line = temp_h[idx]; | 1726 | while (temp_h[idx] == NULL) { |
| 1731 | if (!line) | ||
| 1732 | break; | ||
| 1733 | idx++; | 1727 | idx++; |
| 1734 | if (idx == st_parm->max_history) | 1728 | if (idx == st_parm->max_history) |
| 1735 | idx = 0; | 1729 | idx = 0; |
| 1736 | line_len = strlen(line); | ||
| 1737 | if (line_len >= MAX_LINELEN) | ||
| 1738 | line[MAX_LINELEN-1] = '\0'; | ||
| 1739 | st_parm->history[i++] = line; | ||
| 1740 | } | 1730 | } |
| 1741 | st_parm->cnt_history = i; | ||
| 1742 | if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) | ||
| 1743 | st_parm->cnt_history_in_file = i; | ||
| 1744 | } | 1731 | } |
| 1732 | |||
| 1733 | /* copy temp_h[] to st_parm->history[] */ | ||
| 1734 | for (i = 0; i < st_parm->max_history;) { | ||
| 1735 | line = temp_h[idx]; | ||
| 1736 | if (!line) | ||
| 1737 | break; | ||
| 1738 | idx++; | ||
| 1739 | if (idx == st_parm->max_history) | ||
| 1740 | idx = 0; | ||
| 1741 | line_len = strlen(line); | ||
| 1742 | if (line_len >= MAX_LINELEN) | ||
| 1743 | line[MAX_LINELEN-1] = '\0'; | ||
| 1744 | st_parm->history[i++] = line; | ||
| 1745 | } | ||
| 1746 | st_parm->cnt_history = i; | ||
| 1747 | if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) | ||
| 1748 | st_parm->cnt_history_in_file = i; | ||
| 1745 | } | 1749 | } |
| 1746 | 1750 | ||
| 1747 | # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 1751 | # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
| @@ -1749,17 +1753,27 @@ void FAST_FUNC save_history(line_input_t *st) | |||
| 1749 | { | 1753 | { |
| 1750 | FILE *fp; | 1754 | FILE *fp; |
| 1751 | 1755 | ||
| 1752 | if (!st || !st->hist_file) | 1756 | /* bash compat: HISTFILE="" disables history saving */ |
| 1757 | if (!st || !st->hist_file || !st->hist_file[0]) | ||
| 1753 | return; | 1758 | return; |
| 1754 | if (st->cnt_history <= st->cnt_history_in_file) | 1759 | if (st->cnt_history <= st->cnt_history_in_file) |
| 1755 | return; | 1760 | return; /* no new entries were added */ |
| 1761 | /* note: if st->max_history is 0, we do not abort: we truncate the history to 0 lines */ | ||
| 1756 | 1762 | ||
| 1757 | fp = fopen(st->hist_file, "a"); | 1763 | fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a")); |
| 1758 | if (fp) { | 1764 | if (fp) { |
| 1759 | int i, fd; | 1765 | int i, fd; |
| 1760 | char *new_name; | 1766 | char *new_name; |
| 1761 | line_input_t *st_temp; | 1767 | line_input_t *st_temp; |
| 1762 | 1768 | ||
| 1769 | /* max_history==0 needs special-casing in general code, | ||
| 1770 | * just handle it in a simpler way: */ | ||
| 1771 | if (st->max_history == 0) { | ||
| 1772 | /* fopen("w") already truncated it */ | ||
| 1773 | fclose(fp); | ||
| 1774 | return; | ||
| 1775 | } | ||
| 1776 | |||
| 1763 | for (i = st->cnt_history_in_file; i < st->cnt_history; i++) | 1777 | for (i = st->cnt_history_in_file; i < st->cnt_history; i++) |
| 1764 | fprintf(fp, "%s\n", st->history[i]); | 1778 | fprintf(fp, "%s\n", st->history[i]); |
| 1765 | fclose(fp); | 1779 | fclose(fp); |
| @@ -1769,6 +1783,8 @@ void FAST_FUNC save_history(line_input_t *st) | |||
| 1769 | st_temp = new_line_input_t(st->flags); | 1783 | st_temp = new_line_input_t(st->flags); |
| 1770 | st_temp->hist_file = st->hist_file; | 1784 | st_temp->hist_file = st->hist_file; |
| 1771 | st_temp->max_history = st->max_history; | 1785 | st_temp->max_history = st->max_history; |
| 1786 | /* load no more than max_history last lines */ | ||
| 1787 | /* (in unlikely case that file disappeared, st_temp gets empty history) */ | ||
| 1772 | load_history(st_temp); | 1788 | load_history(st_temp); |
| 1773 | 1789 | ||
| 1774 | /* write out temp file and replace hist_file atomically */ | 1790 | /* write out temp file and replace hist_file atomically */ |
| @@ -1792,13 +1808,13 @@ static void save_history(char *str) | |||
| 1792 | int fd; | 1808 | int fd; |
| 1793 | int len, len2; | 1809 | int len, len2; |
| 1794 | 1810 | ||
| 1795 | if (!state->hist_file) | 1811 | /* bash compat: HISTFILE="" disables history saving */ |
| 1812 | if (!state->hist_file || !state->hist_file[0]) | ||
| 1796 | return; | 1813 | return; |
| 1797 | 1814 | ||
| 1798 | fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); | 1815 | fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); |
| 1799 | if (fd < 0) | 1816 | if (fd < 0) |
| 1800 | return; | 1817 | return; |
| 1801 | xlseek(fd, 0, SEEK_END); /* paranoia */ | ||
| 1802 | len = strlen(str); | 1818 | len = strlen(str); |
| 1803 | str[len] = '\n'; /* we (try to) do atomic write */ | 1819 | str[len] = '\n'; /* we (try to) do atomic write */ |
| 1804 | len2 = full_write(fd, str, len + 1); | 1820 | len2 = full_write(fd, str, len + 1); |
| @@ -1853,13 +1869,10 @@ static void remember_in_history(char *str) | |||
| 1853 | if (str[0] == '\0') | 1869 | if (str[0] == '\0') |
| 1854 | return; | 1870 | return; |
| 1855 | i = state->cnt_history; | 1871 | i = state->cnt_history; |
| 1856 | /* Don't save dupes */ | 1872 | /* Don't save dups */ |
| 1857 | if (i && strcmp(state->history[i-1], str) == 0) | 1873 | if (i != 0 && strcmp(state->history[i-1], str) == 0) |
| 1858 | return; | 1874 | return; |
| 1859 | 1875 | ||
| 1860 | free(state->history[state->max_history]); /* redundant, paranoia */ | ||
| 1861 | state->history[state->max_history] = NULL; /* redundant, paranoia */ | ||
| 1862 | |||
| 1863 | /* If history[] is full, remove the oldest command */ | 1876 | /* If history[] is full, remove the oldest command */ |
| 1864 | /* we need to keep history[state->max_history] empty, hence >=, not > */ | 1877 | /* we need to keep history[state->max_history] empty, hence >=, not > */ |
| 1865 | if (i >= state->max_history) { | 1878 | if (i >= state->max_history) { |
| @@ -1872,7 +1885,7 @@ static void remember_in_history(char *str) | |||
| 1872 | state->cnt_history_in_file--; | 1885 | state->cnt_history_in_file--; |
| 1873 | # endif | 1886 | # endif |
| 1874 | } | 1887 | } |
| 1875 | /* i <= state->max_history-1 */ | 1888 | /* i < state->max_history */ |
| 1876 | state->history[i++] = xstrdup(str); | 1889 | state->history[i++] = xstrdup(str); |
| 1877 | /* i <= state->max_history */ | 1890 | /* i <= state->max_history */ |
| 1878 | state->cur_history = i; | 1891 | state->cur_history = i; |
| @@ -2260,7 +2273,7 @@ static void parse_and_put_prompt(const char *prmt_ptr) | |||
| 2260 | if (c == 'w') | 2273 | if (c == 'w') |
| 2261 | break; | 2274 | break; |
| 2262 | cp = strrchr(pbuf, '/'); | 2275 | cp = strrchr(pbuf, '/'); |
| 2263 | if (cp) | 2276 | if (cp && cp[1]) |
| 2264 | pbuf = (char*)cp + 1; | 2277 | pbuf = (char*)cp + 1; |
| 2265 | break; | 2278 | break; |
| 2266 | // bb_process_escape_sequence does this now: | 2279 | // bb_process_escape_sequence does this now: |
| @@ -2388,7 +2401,6 @@ static int lineedit_read_key(char *read_key_buffer, int timeout) | |||
| 2388 | errno = EINTR; | 2401 | errno = EINTR; |
| 2389 | return -1; | 2402 | return -1; |
| 2390 | } | 2403 | } |
| 2391 | //FIXME: still races here with signals, but small window to poll() inside read_key | ||
| 2392 | IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) | 2404 | IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) |
| 2393 | /* errno = 0; - read_key does this itself */ | 2405 | /* errno = 0; - read_key does this itself */ |
| 2394 | ic = read_key(STDIN_FILENO, read_key_buffer, timeout); | 2406 | ic = read_key(STDIN_FILENO, read_key_buffer, timeout); |
diff --git a/libbb/poll_with_signals.c b/libbb/poll_with_signals.c new file mode 100644 index 000000000..d3c005418 --- /dev/null +++ b/libbb/poll_with_signals.c | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Utility routines. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2025 Denys Vlasenko <vda.linux@googlemail.com> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | //kbuild:lib-$(CONFIG_PLATFORM_POSIX) += poll_with_signals.o | ||
| 10 | |||
| 11 | #include "libbb.h" | ||
| 12 | |||
| 13 | /* Shells, for example, need their line input and "read" builtin | ||
| 14 | * to be interruptible, and the naive handling of it a-la: | ||
| 15 | * if (bb_got_signal) { | ||
| 16 | * errno = EINTR; | ||
| 17 | * return -1; | ||
| 18 | * } | ||
| 19 | * poll(pfd, 1, -1); // signal here would set EINTR | ||
| 20 | * is racy. | ||
| 21 | * This is a bit heavy-handed, but safe wrt races: | ||
| 22 | */ | ||
| 23 | int FAST_FUNC check_got_signal_and_poll(struct pollfd pfd[1], int timeout) | ||
| 24 | { | ||
| 25 | int n; | ||
| 26 | struct timespec tv; | ||
| 27 | sigset_t orig_mask; | ||
| 28 | |||
| 29 | if (bb_got_signal) /* optimization */ | ||
| 30 | goto eintr; | ||
| 31 | |||
| 32 | if (timeout >= 0) { | ||
| 33 | tv.tv_sec = timeout / 1000; | ||
| 34 | tv.tv_nsec = (timeout % 1000) * 1000000; | ||
| 35 | } | ||
| 36 | /* test bb_got_signal, then poll(), atomically wrt signals */ | ||
| 37 | sigfillset(&orig_mask); | ||
| 38 | sigprocmask2(SIG_BLOCK, &orig_mask); | ||
| 39 | if (bb_got_signal) { | ||
| 40 | sigprocmask2(SIG_SETMASK, &orig_mask); | ||
| 41 | eintr: | ||
| 42 | errno = EINTR; /* inform the caller that we got a signal */ | ||
| 43 | return -1; | ||
| 44 | } | ||
| 45 | n = ppoll(pfd, 1, timeout >= 0 ? &tv : NULL, &orig_mask); | ||
| 46 | sigprocmask2(SIG_SETMASK, &orig_mask); | ||
| 47 | return n; | ||
| 48 | } | ||
diff --git a/libbb/procps.c b/libbb/procps.c index 8c9cac125..dfea8af6b 100644 --- a/libbb/procps.c +++ b/libbb/procps.c | |||
| @@ -110,7 +110,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp) | |||
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP | 112 | #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP |
| 113 | static unsigned long long fast_strtoull_16(char **endptr) | 113 | unsigned 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 */ |
| 134 | static unsigned long fast_strtoul_10(char **endptr) | 134 | unsigned 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 */ | ||
| 149 | static 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 |
| 149 | static long fast_strtol_10(char **endptr) | 167 | static 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 | ||
| 159 | static char *skip_fields(char *str, int count) | 177 | char* 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 |
| 171 | static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) | 189 | static 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 | ||
| 180 | int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | ||
| 181 | void (*cb)(struct smaprec *, void *), void *data) | ||
| 182 | { | ||
| 183 | FILE *file; | 197 | FILE *file; |
| 184 | struct smaprec currec; | ||
| 185 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; | 198 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; |
| 186 | char buf[PROCPS_BUFSIZE]; | 199 | char buf[PROCPS_BUFSIZE] ALIGN4; |
| 187 | #if !ENABLE_PMAP | ||
| 188 | void (*cb)(struct smaprec *, void *) = NULL; | ||
| 189 | void *data = NULL; | ||
| 190 | #endif | ||
| 191 | 200 | ||
| 192 | sprintf(filename, "/proc/%u/smaps", (int)pid); | 201 | sprintf(filename, "/proc/%u/smaps", (int)pid); |
| 193 | 202 | ||
| 194 | file = fopen_for_read(filename); | 203 | file = fopen_for_read(filename); |
| 195 | if (!file) | 204 | if (!file) |
| 196 | return 1; | 205 | return; |
| 197 | 206 | ||
| 198 | memset(&currec, 0, sizeof(currec)); | ||
| 199 | while (fgets(buf, PROCPS_BUFSIZE, file)) { | 207 | while (fgets(buf, PROCPS_BUFSIZE, file)) { |
| 200 | // Each mapping datum has this form: | 208 | // Each mapping datum has this form: |
| 201 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | 209 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME |
| @@ -203,80 +211,60 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, | |||
| 203 | // Rss: nnn kB | 211 | // Rss: nnn kB |
| 204 | // ..... | 212 | // ..... |
| 205 | 213 | ||
| 206 | char *tp, *p; | 214 | char *tp; |
| 207 | 215 | #define bytes4 *(uint32_t*)buf | |
| 216 | #define Priv PACK32_BYTES('P','r','i','v') | ||
| 217 | #define Shar PACK32_BYTES('S','h','a','r') | ||
| 208 | #define SCAN(S, X) \ | 218 | #define SCAN(S, X) \ |
| 209 | if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ | 219 | if (memcmp(buf+4, S, sizeof(S)-1) == 0) { \ |
| 210 | total->X += currec.X = fast_strtoul_10(&tp); \ | 220 | tp = skip_whitespace(buf+4 + sizeof(S)-1); \ |
| 211 | continue; \ | 221 | sp->X += fast_strtoul_10(&tp); \ |
| 222 | continue; \ | ||
| 223 | } | ||
| 224 | if (bytes4 == Priv) { | ||
| 225 | SCAN("ate_Dirty:", private_dirty) | ||
| 226 | SCAN("ate_Clean:", private_clean) | ||
| 212 | } | 227 | } |
| 213 | if (cb) { | 228 | if (bytes4 == Shar) { |
| 214 | SCAN("Pss:" , smap_pss ); | 229 | SCAN("ed_Dirty:" , shared_dirty ) |
| 215 | SCAN("Swap:" , smap_swap ); | 230 | SCAN("ed_Clean:" , shared_clean ) |
| 216 | } | 231 | } |
| 217 | SCAN("Private_Dirty:", private_dirty); | 232 | #undef bytes4 |
| 218 | SCAN("Private_Clean:", private_clean); | 233 | #undef Priv |
| 219 | SCAN("Shared_Dirty:" , shared_dirty ); | 234 | #undef Shar |
| 220 | SCAN("Shared_Clean:" , shared_clean ); | ||
| 221 | #undef SCAN | 235 | #undef SCAN |
| 222 | tp = strchr(buf, '-'); | 236 | tp = strchr(buf, '-'); |
| 223 | if (tp) { | 237 | if (tp) { |
| 224 | // We reached next mapping - the line of this form: | 238 | // We reached next mapping - the line of this form: |
| 225 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | 239 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME |
| 226 | 240 | ||
| 227 | if (cb) { | 241 | char *rwx; |
| 228 | /* If we have a previous record, there's nothing more | 242 | unsigned long long sz; |
| 229 | * for it, call the callback and clear currec | ||
| 230 | */ | ||
| 231 | if (currec.smap_size) | ||
| 232 | cb(&currec, data); | ||
| 233 | free(currec.smap_name); | ||
| 234 | } | ||
| 235 | memset(&currec, 0, sizeof(currec)); | ||
| 236 | 243 | ||
| 237 | *tp = ' '; | 244 | *tp = ' '; |
| 238 | tp = buf; | 245 | tp = buf; |
| 239 | currec.smap_start = fast_strtoull_16(&tp); | 246 | sz = fast_strtoull_16(&tp); // start |
| 240 | currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; | 247 | sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start |
| 241 | 248 | // tp -> "rw-s" string | |
| 242 | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); | 249 | rwx = tp; |
| 243 | |||
| 244 | // skipping "rw-s FILEOFS M:m INODE " | 250 | // skipping "rw-s FILEOFS M:m INODE " |
| 245 | tp = skip_whitespace(skip_fields(tp, 4)); | 251 | tp = skip_whitespace(skip_fields(tp, 4)); |
| 246 | // filter out /dev/something (something != zero) | 252 | // if not a device memory mapped... |
| 247 | if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { | 253 | if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something" |
| 248 | if (currec.smap_mode[1] == 'w') { | 254 | || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device) |
| 249 | currec.mapped_rw = currec.smap_size; | 255 | ) { |
| 250 | total->mapped_rw += currec.smap_size; | 256 | if (rwx[1] == 'w') |
| 251 | } else if (currec.smap_mode[1] == '-') { | 257 | sp->mapped_rw += sz; |
| 252 | currec.mapped_ro = currec.smap_size; | 258 | else if (rwx[0] == 'r' || rwx[2] == 'x') |
| 253 | total->mapped_ro += currec.smap_size; | 259 | sp->mapped_ro += sz; |
| 254 | } | 260 | // else: seen "---p" mappings (mmap guard gaps?), |
| 261 | // do NOT account these as VSZ, they aren't really | ||
| 255 | } | 262 | } |
| 256 | |||
| 257 | if (strcmp(tp, "[stack]\n") == 0) | 263 | if (strcmp(tp, "[stack]\n") == 0) |
| 258 | total->stack += currec.smap_size; | 264 | sp->stack += sz; |
| 259 | if (cb) { | ||
| 260 | p = skip_non_whitespace(tp); | ||
| 261 | if (p == tp) { | ||
| 262 | currec.smap_name = xstrdup(" [ anon ]"); | ||
| 263 | } else { | ||
| 264 | *p = '\0'; | ||
| 265 | currec.smap_name = xstrdup(tp); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | total->smap_size += currec.smap_size; | ||
| 269 | } | 265 | } |
| 270 | } | 266 | } |
| 271 | fclose(file); | 267 | fclose(file); |
| 272 | |||
| 273 | if (cb) { | ||
| 274 | if (currec.smap_size) | ||
| 275 | cb(&currec, data); | ||
| 276 | free(currec.smap_name); | ||
| 277 | } | ||
| 278 | |||
| 279 | return 0; | ||
| 280 | } | 268 | } |
| 281 | #endif | 269 | #endif |
| 282 | 270 | ||
| @@ -371,7 +359,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 371 | char *cp, *comm1; | 359 | char *cp, *comm1; |
| 372 | int tty; | 360 | int tty; |
| 373 | #if !ENABLE_FEATURE_FAST_TOP | 361 | #if !ENABLE_FEATURE_FAST_TOP |
| 374 | unsigned long vsz, rss; | 362 | unsigned long long vsz; |
| 363 | unsigned long rss; | ||
| 375 | #endif | 364 | #endif |
| 376 | /* see proc(5) for some details on this */ | 365 | /* see proc(5) for some details on this */ |
| 377 | strcpy(filename_tail, "stat"); | 366 | strcpy(filename_tail, "stat"); |
| @@ -397,7 +386,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 397 | "%ld " /* nice */ | 386 | "%ld " /* nice */ |
| 398 | "%*s %*s " /* timeout, it_real_value */ | 387 | "%*s %*s " /* timeout, it_real_value */ |
| 399 | "%lu " /* start_time */ | 388 | "%lu " /* start_time */ |
| 400 | "%lu " /* vsize */ | 389 | "%llu " /* vsize - can be very large */ |
| 401 | "%lu " /* rss */ | 390 | "%lu " /* rss */ |
| 402 | # if ENABLE_FEATURE_TOP_SMP_PROCESS | 391 | # if ENABLE_FEATURE_TOP_SMP_PROCESS |
| 403 | "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ | 392 | "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ |
| @@ -450,7 +439,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 450 | cp = skip_fields(cp, 2); /* timeout, it_real_value */ | 439 | cp = skip_fields(cp, 2); /* timeout, it_real_value */ |
| 451 | sp->start_time = fast_strtoul_10(&cp); | 440 | sp->start_time = fast_strtoul_10(&cp); |
| 452 | /* vsz is in bytes and we want kb */ | 441 | /* vsz is in bytes and we want kb */ |
| 453 | sp->vsz = fast_strtoul_10(&cp) >> 10; | 442 | sp->vsz = fast_strtoull_10(&cp) >> 10; |
| 454 | /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ | 443 | /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ |
| 455 | sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; | 444 | sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; |
| 456 | # if ENABLE_FEATURE_TOP_SMP_PROCESS | 445 | # if ENABLE_FEATURE_TOP_SMP_PROCESS |
| @@ -484,7 +473,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 484 | 473 | ||
| 485 | #if ENABLE_FEATURE_TOPMEM | 474 | #if ENABLE_FEATURE_TOPMEM |
| 486 | if (flags & PSSCAN_SMAPS) | 475 | if (flags & PSSCAN_SMAPS) |
| 487 | procps_read_smaps(pid, &sp->smaps, NULL, NULL); | 476 | procps_read_smaps(pid, sp); |
| 488 | #endif /* TOPMEM */ | 477 | #endif /* TOPMEM */ |
| 489 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS | 478 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS |
| 490 | if (flags & PSSCAN_RUIDGID) { | 479 | if (flags & PSSCAN_RUIDGID) { |
| @@ -567,36 +556,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) | |||
| 567 | return sp; | 556 | return sp; |
| 568 | } | 557 | } |
| 569 | 558 | ||
| 570 | void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | 559 | int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) |
| 571 | { | 560 | { |
| 572 | int sz; | 561 | int sz; |
| 573 | char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; | 562 | char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; |
| 574 | 563 | ||
| 575 | sprintf(filename, "/proc/%u/cmdline", pid); | 564 | sprintf(filename, "/proc/%u/cmdline", pid); |
| 576 | sz = open_read_close(filename, buf, col - 1); | 565 | sz = open_read_close(filename, buf, col - 1); |
| 566 | if (sz < 0) | ||
| 567 | return sz; | ||
| 577 | if (sz > 0) { | 568 | if (sz > 0) { |
| 578 | const char *base; | 569 | const char *program_basename; |
| 579 | int comm_len; | 570 | int comm_len; |
| 580 | 571 | ||
| 581 | buf[sz] = '\0'; | 572 | buf[sz] = '\0'; |
| 582 | while (--sz >= 0 && buf[sz] == '\0') | 573 | while (--sz >= 0 && buf[sz] == '\0') |
| 583 | continue; | 574 | continue; |
| 584 | /* Prevent basename("process foo/bar") = "bar" */ | 575 | |
| 585 | strchrnul(buf, ' ')[0] = '\0'; | 576 | /* Find "program" in "[-][/PATH/TO/]program" */ |
| 586 | base = bb_basename(buf); /* before we replace argv0's NUL with space */ | 577 | strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */ |
| 578 | program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf); | ||
| 579 | /* ^^^ note: must do it *before* replacing argv0's NUL with space */ | ||
| 580 | |||
| 581 | /* Prevent stuff like this: | ||
| 582 | * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c | ||
| 583 | * messing up top and ps output (or worse). | ||
| 584 | * This also replaces NULs with spaces, converting | ||
| 585 | * list of NUL-strings into one string. | ||
| 586 | */ | ||
| 587 | while (sz >= 0) { | 587 | while (sz >= 0) { |
| 588 | if ((unsigned char)(buf[sz]) < ' ') | 588 | if ((unsigned char)(buf[sz]) < ' ') |
| 589 | buf[sz] = ' '; | 589 | buf[sz] = (buf[sz] ? /*ctrl*/'?' : /*NUL*/' '); |
| 590 | sz--; | 590 | sz--; |
| 591 | } | 591 | } |
| 592 | if (base[0] == '-') /* "-sh" (login shell)? */ | ||
| 593 | base++; | ||
| 594 | 592 | ||
| 595 | /* If comm differs from argv0, prepend "{comm} ". | 593 | /* If comm differs from argv0, prepend "{comm} ". |
| 596 | * It allows to see thread names set by prctl(PR_SET_NAME). | 594 | * It allows to see thread names set by prctl(PR_SET_NAME). |
| 597 | */ | 595 | */ |
| 598 | if (!comm) | 596 | if (!comm) |
| 599 | return; | 597 | return 0; |
| 600 | comm_len = strlen(comm); | 598 | comm_len = strlen(comm); |
| 601 | /* Why compare up to comm_len, not COMM_LEN-1? | 599 | /* Why compare up to comm_len, not COMM_LEN-1? |
| 602 | * Well, some processes rewrite argv, and use _spaces_ there | 600 | * Well, some processes rewrite argv, and use _spaces_ there |
| @@ -604,19 +602,20 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
| 604 | * I prefer to still treat argv0 "process foo bar" | 602 | * I prefer to still treat argv0 "process foo bar" |
| 605 | * as 'equal' to comm "process". | 603 | * as 'equal' to comm "process". |
| 606 | */ | 604 | */ |
| 607 | if (strncmp(base, comm, comm_len) != 0) { | 605 | if (strncmp(program_basename, comm, comm_len) != 0) { |
| 608 | comm_len += 3; | 606 | comm_len += 3; |
| 609 | if (col > comm_len) | 607 | if (col > comm_len) |
| 610 | memmove(buf + comm_len, buf, col - comm_len); | 608 | memmove(buf + comm_len, buf, col - comm_len); |
| 611 | snprintf(buf, col, "{%s}", comm); | 609 | snprintf(buf, col, "{%s}", comm); |
| 612 | if (col <= comm_len) | 610 | if (col <= comm_len) |
| 613 | return; | 611 | return 0; |
| 614 | buf[comm_len - 1] = ' '; | 612 | buf[comm_len - 1] = ' '; |
| 615 | buf[col - 1] = '\0'; | 613 | buf[col - 1] = '\0'; |
| 616 | } | 614 | } |
| 617 | } else { | 615 | } else { |
| 618 | snprintf(buf, col, "[%s]", comm ? comm : "?"); | 616 | snprintf(buf, col, "[%s]", comm ? comm : "?"); |
| 619 | } | 617 | } |
| 618 | return 0; | ||
| 620 | } | 619 | } |
| 621 | 620 | ||
| 622 | #endif /* ENABLE_PLATFORM_MINGW32 */ | 621 | #endif /* ENABLE_PLATFORM_MINGW32 */ |
diff --git a/libbb/pw_ascii64.c b/libbb/pw_ascii64.c new file mode 100644 index 000000000..3993932ca --- /dev/null +++ b/libbb/pw_ascii64.c | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Utility routines. | ||
| 4 | * | ||
| 5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | |||
| 10 | /* Returns >=64 for invalid chars */ | ||
| 11 | int 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 | */ | ||
| 38 | int 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 | |||
| 53 | char* FAST_FUNC | ||
| 54 | num2str64_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 | |||
| 63 | static void | ||
| 64 | num2str64_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 | |||
| 72 | int 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 | |||
| 20 | static 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 | |||
| 34 | int 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 | ||
| 55 | char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo) | 18 | char* 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(¶ms, 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 | |||
| 81 | static char* | ||
| 82 | to64(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 */ |
| 110 | static char *my_crypt(const char *key, const char *salt) | 109 | static 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 | ||
| 189 | static 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 | |||
| 200 | static const uint8_t bits8[8] ALIGN1 = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; | 189 | static const uint8_t bits8[8] ALIGN1 = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; |
| 201 | 190 | ||
| 202 | 191 | ||
| 203 | static int | ||
| 204 | ascii_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 | ||
| 697 | static void | ||
| 698 | to64_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 | |||
| 712 | static char * | 674 | static char * |
| 713 | NOINLINE | 675 | NOINLINE |
| 714 | des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE], | 676 | des_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) \ |
| 199 | do { \ | 198 | do { \ |
| 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 | |||
| 10 | static char * | ||
| 11 | yes_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 | ||
| 12 | int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) | 12 | int64_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 */ | ||
| 52 | size_t FAST_FUNC replace_char(char *str, char from, char to) | ||
| 53 | { | ||
| 54 | char *p = str; | ||
| 55 | while (*p) { | ||
| 56 | if (*p == from) | ||
| 57 | *p = to; | ||
| 58 | p++; | ||
| 59 | } | ||
| 60 | return p - str; | ||
| 61 | } | ||
| 62 | #endif | ||
diff --git a/libbb/signals.c b/libbb/signals.c index c09a562ed..a1ac2005b 100644 --- a/libbb/signals.c +++ b/libbb/signals.c | |||
| @@ -25,13 +25,6 @@ int FAST_FUNC sigaction_set(int signum, const struct sigaction *act) | |||
| 25 | return sigaction(signum, act, NULL); | 25 | return sigaction(signum, act, NULL); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | int FAST_FUNC sigprocmask_allsigs(int how) | ||
| 29 | { | ||
| 30 | sigset_t set; | ||
| 31 | sigfillset(&set); | ||
| 32 | return sigprocmask(how, &set, NULL); | ||
| 33 | } | ||
| 34 | |||
| 35 | int FAST_FUNC sigprocmask2(int how, sigset_t *set) | 28 | int FAST_FUNC sigprocmask2(int how, sigset_t *set) |
| 36 | { | 29 | { |
| 37 | // Grr... gcc 8.1.1: | 30 | // Grr... gcc 8.1.1: |
| @@ -41,7 +34,26 @@ int FAST_FUNC sigprocmask2(int how, sigset_t *set) | |||
| 41 | oset = set; | 34 | oset = set; |
| 42 | return sigprocmask(how, set, oset); | 35 | return sigprocmask(how, set, oset); |
| 43 | } | 36 | } |
| 37 | |||
| 38 | int FAST_FUNC sigprocmask_allsigs(int how) | ||
| 39 | { | ||
| 40 | sigset_t set; | ||
| 41 | sigfillset(&set); | ||
| 42 | return sigprocmask2(how, &set); | ||
| 43 | } | ||
| 44 | |||
| 45 | int FAST_FUNC sigblockall(sigset_t *set) | ||
| 46 | { | ||
| 47 | #if 0 /* nope. set can be NULL */ | ||
| 48 | sigfillset(set); | ||
| 49 | return sigprocmask2(SIG_SETMASK, set); | ||
| 50 | #else | ||
| 51 | sigset_t mask; | ||
| 52 | sigfillset(&mask); | ||
| 53 | return sigprocmask(SIG_SETMASK, &mask, set); | ||
| 44 | #endif | 54 | #endif |
| 55 | } | ||
| 56 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | ||
| 45 | 57 | ||
| 46 | void FAST_FUNC bb_signals(int sigs, void (*f)(int)) | 58 | void FAST_FUNC bb_signals(int sigs, void (*f)(int)) |
| 47 | { | 59 | { |
| @@ -84,7 +96,7 @@ void FAST_FUNC sig_block(int sig) | |||
| 84 | sigset_t ss; | 96 | sigset_t ss; |
| 85 | sigemptyset(&ss); | 97 | sigemptyset(&ss); |
| 86 | sigaddset(&ss, sig); | 98 | sigaddset(&ss, sig); |
| 87 | sigprocmask(SIG_BLOCK, &ss, NULL); | 99 | sigprocmask2(SIG_BLOCK, &ss); |
| 88 | } | 100 | } |
| 89 | 101 | ||
| 90 | void FAST_FUNC sig_unblock(int sig) | 102 | void FAST_FUNC sig_unblock(int sig) |
| @@ -92,7 +104,7 @@ void FAST_FUNC sig_unblock(int sig) | |||
| 92 | sigset_t ss; | 104 | sigset_t ss; |
| 93 | sigemptyset(&ss); | 105 | sigemptyset(&ss); |
| 94 | sigaddset(&ss, sig); | 106 | sigaddset(&ss, sig); |
| 95 | sigprocmask(SIG_UNBLOCK, &ss, NULL); | 107 | sigprocmask2(SIG_UNBLOCK, &ss); |
| 96 | } | 108 | } |
| 97 | 109 | ||
| 98 | void FAST_FUNC wait_for_any_sig(void) | 110 | void FAST_FUNC wait_for_any_sig(void) |
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/xfuncs_printf.c b/libbb/xfuncs_printf.c index 0ead3b2eb..5f3de5828 100644 --- a/libbb/xfuncs_printf.c +++ b/libbb/xfuncs_printf.c | |||
| @@ -701,6 +701,14 @@ pid_t FAST_FUNC xfork(void) | |||
| 701 | } | 701 | } |
| 702 | #endif | 702 | #endif |
| 703 | 703 | ||
| 704 | #if 0 | ||
| 705 | /* DO NOT DO THIS. This can't be a function. | ||
| 706 | * It works on some arches (x86) but fails on others (ppc64le: SEGV). | ||
| 707 | * The reason is: the child returns from this function | ||
| 708 | * and likely pops up the stack in an arch-dependent way. | ||
| 709 | * When child eventually exits or execs, parent "reappear" | ||
| 710 | * in the now-unwound stack (!) and the behavior is undefined. | ||
| 711 | */ | ||
| 704 | void FAST_FUNC xvfork_parent_waits_and_exits(void) | 712 | void FAST_FUNC xvfork_parent_waits_and_exits(void) |
| 705 | { | 713 | { |
| 706 | pid_t pid; | 714 | pid_t pid; |
| @@ -716,6 +724,7 @@ void FAST_FUNC xvfork_parent_waits_and_exits(void) | |||
| 716 | } | 724 | } |
| 717 | /* Child continues */ | 725 | /* Child continues */ |
| 718 | } | 726 | } |
| 727 | #endif | ||
| 719 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | 728 | #endif /* !ENABLE_PLATFORM_MINGW32 */ |
| 720 | 729 | ||
| 721 | // Useful when we do know that pid is valid, and we just want to wait | 730 | // Useful when we do know that pid is valid, and we just want to wait |
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 | |||
| 7 | lib-y:= | ||
| 8 | |||
| 9 | INSERT | ||
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 | |||
| 3 | yescrypt is very flexible, but configuring it optimally is complicated. | ||
| 4 | Here are some guidelines to simplify near-optimal configuration. We | ||
| 5 | start by listing the parameters and their typical values, and then give | ||
| 6 | currently recommended parameter sets by use case. | ||
| 7 | |||
| 8 | |||
| 9 | Parameters and their typical values. | ||
| 10 | |||
| 11 | Set flags (yescrypt flavor) to YESCRYPT_DEFAULTS to use the currently | ||
| 12 | recommended flavor. (Other flags values exist for compatibility and for | ||
| 13 | specialized cases where you think you know what you're doing.) | ||
| 14 | |||
| 15 | Set N (block count) based on target memory usage and running time, as | ||
| 16 | well as on the value of r (block size in 128 byte units). N must be a | ||
| 17 | power of two. | ||
| 18 | |||
| 19 | Set r (block size) to 8 (so that N is in KiB, which is convenient) or to | ||
| 20 | another small value (if more optimal or for fine-tuning of the total | ||
| 21 | size and/or running time). Reasonable values for r are from 8 to 96. | ||
| 22 | |||
| 23 | Set p (parallelism) to 1 meaning no thread-level parallelism within one | ||
| 24 | computation of yescrypt. (Use of thread-level parallelism within | ||
| 25 | yescrypt makes sense for ROM initialization and for key derivation at | ||
| 26 | high memory usage, but usually not for password hashing where | ||
| 27 | parallelism is available through concurrent authentication attempts. | ||
| 28 | Don't use p > 1 unnecessarily.) | ||
| 29 | |||
| 30 | Set t (time) to 0 to use the optimal running time for a given memory | ||
| 31 | usage. This will allow you to maximize the memory usage (the value of | ||
| 32 | N*r) while staying within your running time constraints. (Non-zero t | ||
| 33 | makes sense in specialized cases where you can't afford higher memory | ||
| 34 | usage but can afford more time.) | ||
| 35 | |||
| 36 | Set g (upgrades) to 0 because there have been no hash upgrades yet. | ||
| 37 | |||
| 38 | Set NROM (block count of ROM) to 0 unless you use a ROM (see below). | ||
| 39 | NROM must be a power of two. | ||
| 40 | |||
| 41 | |||
| 42 | Password hashing for user authentication, no ROM. | ||
| 43 | |||
| 44 | Small and fast (memory usage 2 MiB, performance like bcrypt cost 2^5 - | ||
| 45 | latency 2-3 ms and throughput 10,000+ per second on a 16-core server): | ||
| 46 | |||
| 47 | flags = YESCRYPT_DEFAULTS, N = 2048, r = 8, p = 1, t = 0, g = 0, NROM = 0 | ||
| 48 | |||
| 49 | Large and slow (memory usage 16 MiB, performance like bcrypt cost 2^8 - | ||
| 50 | latency 10-30 ms and throughput 1000+ per second on a 16-core server): | ||
| 51 | |||
| 52 | flags = YESCRYPT_DEFAULTS, N = 4096, r = 32, p = 1, t = 0, g = 0, NROM = 0 | ||
| 53 | |||
| 54 | Of course, even heavier and slower settings are possible, if affordable. | ||
| 55 | Simply double the value of N as many times as needed. Since N must be a | ||
| 56 | power of two, you may use r (in the range of 8 to 32) or/and t (in the | ||
| 57 | range of 0 to 2) for fine-tuning the running time, but first bring N to | ||
| 58 | the maximum you can afford. If this feels too complicated, just use one | ||
| 59 | of the two parameter sets given above (preferably the second) as-is. | ||
| 60 | |||
| 61 | |||
| 62 | Password hashing for user authentication, with ROM. | ||
| 63 | |||
| 64 | It's similar to the above, except that you need to adjust r, set NROM, | ||
| 65 | and initialize the ROM. | ||
| 66 | |||
| 67 | First decide on a ROM size, such as making it a large portion of your | ||
| 68 | dedicated authentication servers' RAM sizes. Since NROM (block count) | ||
| 69 | must be a power of two, you might need to choose r (block size) based on | ||
| 70 | how your desired ROM size corresponds to a power of two. Also tuning | ||
| 71 | for performance on current hardware, you'll likely end up with r in the | ||
| 72 | range from slightly below 16 to 32. For example, to use 15/16 of a | ||
| 73 | server's 256 GiB RAM as ROM (thus, making it 240 GiB), you could use | ||
| 74 | r=15 or r=30. To use 23/24 of a server's 384 GiB RAM as ROM (thus, | ||
| 75 | making it 368 GiB), you'd use r=23. Then set NROM to your desired ROM | ||
| 76 | size in KiB divided by 128*r. Note that these examples might (or might | ||
| 77 | not) be too extreme, leaving little memory for the rest of the system. | ||
| 78 | You could as well opt for 7/8 with r=14 or 11/12 with r=11 or r=22. | ||
| 79 | |||
| 80 | Note that higher r may make placing of ROM in e.g. NVMe flash memory | ||
| 81 | instead of in RAM more reasonable (or less unreasonable) than it would | ||
| 82 | have been with a lower r. If this is a concern as it relates to | ||
| 83 | possible attacks and you do not intend to ever do it defensively, you | ||
| 84 | might want to keep r lower (e.g., prefer r=15 over r=30 in the example | ||
| 85 | above, even if 30 performs slightly faster). | ||
| 86 | |||
| 87 | Your adjustments to r, if you deviate from powers of two, will also | ||
| 88 | result in weirder memory usage per hash. Like 1.75 MiB at r=14 instead | ||
| 89 | of 2 MiB at r=8 that you would have used without a ROM. That's OK. | ||
| 90 | |||
| 91 | For ROM initialization, which you do with yescrypt_init_shared(), use | ||
| 92 | the same r and NROM that you'd later use for password hashing, choose p | ||
| 93 | based on your servers' physical and/or logical CPU count (maybe | ||
| 94 | considering eventual upgrades as you won't be able to change this later, | ||
| 95 | but without going unnecessarily high - e.g., p=28, p=56, or p=112 make | ||
| 96 | sense on servers that currently have 28 physical / 56 logical CPUs), and | ||
| 97 | set the rest of the parameters to: | ||
| 98 | |||
| 99 | flags = YESCRYPT_DEFAULTS, N = 0, t = 0, g = 0 | ||
| 100 | |||
| 101 | N is set to 0 because it isn't relevant during ROM initialization (you | ||
| 102 | can use different values of N for hashing passwords with the same ROM). | ||
| 103 | |||
| 104 | To keep the ROM in e.g. SysV shared memory and reuse it across your | ||
| 105 | authentication service restarts, you'd need to allocate the memory and | ||
| 106 | set the flags to "YESCRYPT_DEFAULTS | YESCRYPT_SHARED_PREALLOCATED". | ||
| 107 | |||
| 108 | For actual password hashing, you'd use your chosen values for N, r, | ||
| 109 | NROM, and set the rest of the parameters to: | ||
| 110 | |||
| 111 | flags = YESCRYPT_DEFAULTS, p = 1, t = 0, g = 0 | ||
| 112 | |||
| 113 | Note that although you'd use a large p for ROM initialization, you | ||
| 114 | should use p=1 for actual password hashing like you would without a ROM. | ||
| 115 | |||
| 116 | Do not forget to pass the ROM into the actual password hashing (and keep | ||
| 117 | r and NROM set accordingly). | ||
| 118 | |||
| 119 | Since N must be a power of two and r is dependent on ROM size, you may | ||
| 120 | use t (in the range of 0 to 2) for fine-tuning the running time, but | ||
| 121 | first bring N to the maximum you can afford. | ||
| 122 | |||
| 123 | If this feels too complicated, or even if it doesn't, please consider | ||
| 124 | engaging 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 | |||
| 131 | Use settings similar to those for password hashing without a ROM, but | ||
| 132 | adjusted for higher memory usage and running time, and optionally with | ||
| 133 | thread-level parallelism. | ||
| 134 | |||
| 135 | Small and fast (memory usage 128 MiB, running time under 100 ms on a | ||
| 136 | fast desktop): | ||
| 137 | |||
| 138 | flags = YESCRYPT_DEFAULTS, N = 32768, r = 32, p = 1, t = 0, g = 0, NROM = 0 | ||
| 139 | |||
| 140 | Large and fast (memory usage 1 GiB, running time under 200 ms on a fast | ||
| 141 | quad-core desktop not including memory allocation overhead, under 250 ms | ||
| 142 | with the overhead included), but requires build with OpenMP support (or | ||
| 143 | otherwise will run as slow as yet be weaker than its p=1 alternative): | ||
| 144 | |||
| 145 | flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 4, t = 0, g = 0, NROM = 0 | ||
| 146 | |||
| 147 | Large and slower (memory usage 1 GiB, running time under 300 ms on a | ||
| 148 | fast quad-core desktop not including memory allocation overhead, under | ||
| 149 | 350 ms with the overhead included), also requires build with OpenMP | ||
| 150 | support (or otherwise will run slower than the p=1 alternative below): | ||
| 151 | |||
| 152 | flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 4, t = 2, g = 0, NROM = 0 | ||
| 153 | |||
| 154 | Large and slow (memory usage 1 GiB, running time under 600 ms on a fast | ||
| 155 | desktop not including memory allocation overhead, under 650 ms with the | ||
| 156 | overhead included): | ||
| 157 | |||
| 158 | flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 1, t = 0, g = 0, NROM = 0 | ||
| 159 | |||
| 160 | Just like with password hashing, even heavier and slower settings are | ||
| 161 | possible, if affordable, and you achieve them by adjusting N, r, t in | ||
| 162 | the same way and in the same preferred ranges (please see the section on | ||
| 163 | password hashing without a ROM, above). Unlike with password hashing, | ||
| 164 | it makes some sense to go above t=2 if you expect that your users might | ||
| 165 | not be able to afford more memory but can afford more time. However, | ||
| 166 | increasing the memory usage provides better protection, and we don't | ||
| 167 | recommend forcing your users to wait for more than 1 second as they | ||
| 168 | could as well type more characters in that time. If this feels too | ||
| 169 | complicated, just use one of the above parameter sets as-is. | ||
| 170 | |||
| 171 | |||
| 172 | Amortization of memory allocation overhead. | ||
| 173 | |||
| 174 | It takes a significant fraction of yescrypt's total running time to | ||
| 175 | allocate memory from the operating system, especially considering that | ||
| 176 | the kernel zeroizes the memory before handing it over to your program. | ||
| 177 | |||
| 178 | Unless you naturally need to compute yescrypt just once per process, you | ||
| 179 | may achieve greater efficiency by fully using advanced yescrypt APIs | ||
| 180 | that let you preserve and reuse the memory allocation across yescrypt | ||
| 181 | invocations. This is done by reusing the structure pointed to by the | ||
| 182 | "yescrypt_local_t *local" argument of yescrypt_r() or yescrypt_kdf() | ||
| 183 | without calling yescrypt_free_local() inbetween the repeated invocations | ||
| 184 | of yescrypt. | ||
| 185 | |||
| 186 | |||
| 187 | YESCRYPT_DEFAULTS macro. | ||
| 188 | |||
| 189 | Please note that the value of the YESCRYPT_DEFAULTS macro might change | ||
| 190 | later, so if you use the macro like it's recommended here then for | ||
| 191 | results reproducible across versions you might need to store its value | ||
| 192 | somewhere along with the hashes or the encrypted data. | ||
| 193 | |||
| 194 | If you use yescrypt's standard hash string encoding, then yescrypt | ||
| 195 | already encodes and decodes this value for you, so you don't need to | ||
| 196 | worry 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 @@ | |||
| 1 | The yescrypt code in this directory is adapted from libxcrypt-4.4.38 | ||
| 2 | with minimal edits, hopefully making it easier to track | ||
| 3 | backports by resetting the tree to the commit which created this file, | ||
| 4 | then 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 | */ | ||
| 33 | static void | ||
| 34 | PBKDF2_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 | */ | ||
| 43 | static 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 | ||
| 108 | static 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 | ||
| 151 | static 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 */ | ||
| 219 | static 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 | |||
| 270 | static 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 | |||
| 293 | char *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 | |||
| 45 | typedef union { | ||
| 46 | uint32_t w[16]; | ||
| 47 | uint64_t d[8]; | ||
| 48 | } salsa20_blk_t; | ||
| 49 | |||
| 50 | static void salsa20_simd_shuffle( | ||
| 51 | const salsa20_blk_t *Bin, | ||
| 52 | salsa20_blk_t *Bout) | ||
| 53 | { | ||
| 54 | #define COMBINE(out, in1, in2) \ | ||
| 55 | do { \ | ||
| 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 | |||
| 69 | static void salsa20_simd_unshuffle( | ||
| 70 | const salsa20_blk_t *Bin, | ||
| 71 | salsa20_blk_t *Bout) | ||
| 72 | { | ||
| 73 | #define UNCOMBINE(out, in1, in2) \ | ||
| 74 | do { \ | ||
| 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) \ | ||
| 96 | do { \ | ||
| 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) \ | ||
| 108 | do { \ | ||
| 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 | */ | ||
| 120 | static 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) \ | ||
| 234 | do { \ | ||
| 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) \ | ||
| 246 | do { \ | ||
| 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) \ | ||
| 254 | do { \ | ||
| 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) \ | ||
| 264 | do { \ | ||
| 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 | */ | ||
| 276 | static 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 | |||
| 291 | static 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) \ | ||
| 329 | do { \ | ||
| 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 \ | ||
| 339 | do { \ | ||
| 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 \ | ||
| 347 | do { \ | ||
| 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 \ | ||
| 364 | do { \ | ||
| 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 \ | ||
| 371 | do { \ | ||
| 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 \ | ||
| 391 | do { \ | ||
| 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 | |||
| 410 | typedef 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 | */ | ||
| 422 | static 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 | |||
| 458 | static 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 | |||
| 499 | static 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 | */ | ||
| 543 | static 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 | ||
| 566 | static 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 | ||
| 695 | static 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 | */ | ||
| 784 | static 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 | ||
| 807 | static 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 | |||
| 890 | static 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 | |||
| 917 | static 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 | */ | ||
| 945 | static 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 | */ | ||
| 1158 | static | ||
| 1159 | int 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 | */ | ||
| 193 | typedef 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 | */ | ||
| 205 | typedef 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 | |||
| 215 | typedef 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 | */ | ||
| 243 | extern 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 | ||
| 94 | config 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 | |||
| 94 | INSERT | 105 | INSERT |
| 95 | 106 | ||
| 96 | endmenu | 107 | endmenu |
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. | |||
| 84 | int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 84 | int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 85 | int cryptpw_main(int argc UNUSED_PARAM, char **argv) | 85 | int 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/make_single_applets.sh b/make_single_applets.sh index 7df53397e..45240de14 100755 --- a/make_single_applets.sh +++ b/make_single_applets.sh | |||
| @@ -15,6 +15,7 @@ grep ^IF_ include/applets.h \ | |||
| 15 | | grep -v '^IF_FEATURE_' \ | 15 | | grep -v '^IF_FEATURE_' \ |
| 16 | | sed 's/IF_\([A-Z0-9._-]*\)(.*/\1/' \ | 16 | | sed 's/IF_\([A-Z0-9._-]*\)(.*/\1/' \ |
| 17 | | grep -v '^BUSYBOX$' \ | 17 | | grep -v '^BUSYBOX$' \ |
| 18 | | grep -v '^PLATFORM_' \ | ||
| 18 | | sort | uniq | 19 | | sort | uniq |
| 19 | `" | 20 | `" |
| 20 | 21 | ||
| @@ -31,6 +32,15 @@ done | |||
| 31 | allno="`echo "$allno" | sed "s/^CONFIG_BUSYBOX=y\$/# CONFIG_BUSYBOX is not set/"`" | 32 | allno="`echo "$allno" | sed "s/^CONFIG_BUSYBOX=y\$/# CONFIG_BUSYBOX is not set/"`" |
| 32 | # disable any CONFIG_script_DEPENDENCIES as well | 33 | # disable any CONFIG_script_DEPENDENCIES as well |
| 33 | allno="`echo "$allno" | sed "s/^\(CONFIG_.*_DEPENDENCIES\)=y\$/# \1 is not set/"`" | 34 | allno="`echo "$allno" | sed "s/^\(CONFIG_.*_DEPENDENCIES\)=y\$/# \1 is not set/"`" |
| 35 | # Select platform | ||
| 36 | if grep -q CONFIG_PLATFORM_MINGW32=y .config | ||
| 37 | then | ||
| 38 | allno="`echo "$allno" | sed "s/^# CONFIG_PLATFORM_MINGW32 is not set\$/CONFIG_PLATFORM_MINGW32=y/"`" | ||
| 39 | EXE=".exe" | ||
| 40 | else | ||
| 41 | allno="`echo "$allno" | sed "s/^# CONFIG_PLATFORM_POSIX is not set\$/CONFIG_PLATFORM_POSIX=y/"`" | ||
| 42 | EXE="" | ||
| 43 | fi | ||
| 34 | #echo "$allno" >.config_allno | 44 | #echo "$allno" >.config_allno |
| 35 | 45 | ||
| 36 | trap 'test -f .config.SV && mv .config.SV .config && touch .config' EXIT | 46 | trap 'test -f .config.SV && mv .config.SV .config && touch .config' EXIT |
| @@ -69,7 +79,7 @@ for app; do | |||
| 69 | mv .config busybox_config_${app} | 79 | mv .config busybox_config_${app} |
| 70 | elif ! grep -q '^#define NUM_APPLETS 1$' include/NUM_APPLETS.h; then | 80 | elif ! grep -q '^#define NUM_APPLETS 1$' include/NUM_APPLETS.h; then |
| 71 | grep -i -e error: -e warning: busybox_make_${app}.log | 81 | grep -i -e error: -e warning: busybox_make_${app}.log |
| 72 | mv busybox busybox_${app} | 82 | mv busybox${EXE} busybox_${app}${EXE} |
| 73 | fail=$((fail+1)) | 83 | fail=$((fail+1)) |
| 74 | echo "NUM_APPLETS != 1 for ${app}: `cat include/NUM_APPLETS.h`" | 84 | echo "NUM_APPLETS != 1 for ${app}: `cat include/NUM_APPLETS.h`" |
| 75 | mv .config busybox_config_${app} | 85 | mv .config busybox_config_${app} |
| @@ -86,7 +96,7 @@ for app; do | |||
| 86 | fi | 96 | fi |
| 87 | grep -i -e error: -e warning: busybox_make_${app}.log \ | 97 | grep -i -e error: -e warning: busybox_make_${app}.log \ |
| 88 | || rm busybox_make_${app}.log | 98 | || rm busybox_make_${app}.log |
| 89 | mv busybox busybox_${app} | 99 | mv busybox${EXE} busybox_${app}${EXE} |
| 90 | #mv .config busybox_config_${app} | 100 | #mv .config busybox_config_${app} |
| 91 | fi | 101 | fi |
| 92 | mv .config.SV .config | 102 | mv .config.SV .config |
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..8026917b3 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 | ||
| 262 | struct globals { | 267 | struct globals { |
| @@ -271,6 +276,7 @@ struct globals { | |||
| 271 | time_t ar_mtime; | 276 | time_t ar_mtime; |
| 272 | int lineno; // Physical line number in file | 277 | int lineno; // Physical line number in file |
| 273 | int dispno; // Line number for display purposes | 278 | int dispno; // Line number for display purposes |
| 279 | struct cmd *curr_cmd; | ||
| 274 | const char *rulepos; | 280 | const char *rulepos; |
| 275 | int rule_idx; | 281 | int rule_idx; |
| 276 | #define IF_MAX 10 | 282 | #define IF_MAX 10 |
| @@ -302,6 +308,7 @@ struct globals { | |||
| 302 | #define ar_mtime (G.ar_mtime) | 308 | #define ar_mtime (G.ar_mtime) |
| 303 | #define lineno (G.lineno) | 309 | #define lineno (G.lineno) |
| 304 | #define dispno (G.dispno) | 310 | #define dispno (G.dispno) |
| 311 | #define curr_cmd (G.curr_cmd) | ||
| 305 | #define rulepos (G.rulepos) | 312 | #define rulepos (G.rulepos) |
| 306 | #define rule_idx (G.rule_idx) | 313 | #define rule_idx (G.rule_idx) |
| 307 | #define clevel (G.clevel) | 314 | #define clevel (G.clevel) |
| @@ -320,11 +327,8 @@ struct globals { | |||
| 320 | #endif | 327 | #endif |
| 321 | 328 | ||
| 322 | static int make(struct name *np, int level); | 329 | static int make(struct name *np, int level); |
| 323 | 330 | static struct name *dyndep(struct name *np, struct rule *infrule, | |
| 324 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name | 331 | 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 | 332 | ||
| 329 | /* | 333 | /* |
| 330 | * Utility functions. | 334 | * Utility functions. |
| @@ -336,9 +340,20 @@ static int make(struct name *np, int level); | |||
| 336 | static void | 340 | static void |
| 337 | vwarning(FILE *stream, const char *msg, va_list list) | 341 | vwarning(FILE *stream, const char *msg, va_list list) |
| 338 | { | 342 | { |
| 343 | const char *m = NULL; | ||
| 344 | int d = 0; | ||
| 345 | |||
| 346 | if (curr_cmd) { | ||
| 347 | m = curr_cmd->c_makefile; | ||
| 348 | d = curr_cmd->c_dispno; | ||
| 349 | } else if (makefile) { | ||
| 350 | m = makefile; | ||
| 351 | d = dispno; | ||
| 352 | } | ||
| 353 | |||
| 339 | fprintf(stream, "%s: ", applet_name); | 354 | fprintf(stream, "%s: ", applet_name); |
| 340 | if (makefile) | 355 | if (m) |
| 341 | fprintf(stream, "(%s:%d): ", makefile, dispno); | 356 | fprintf(stream, "(%s:%d): ", m, d); |
| 342 | vfprintf(stream, msg, list); | 357 | vfprintf(stream, msg, list); |
| 343 | fputc('\n', stream); | 358 | fputc('\n', stream); |
| 344 | } | 359 | } |
| @@ -469,8 +484,7 @@ newcmd(struct cmd **cphead, char *str) | |||
| 469 | /*(*cphead)->c_next = NULL; - xzalloc did it */ | 484 | /*(*cphead)->c_next = NULL; - xzalloc did it */ |
| 470 | (*cphead)->c_cmd = xstrdup(str); | 485 | (*cphead)->c_cmd = xstrdup(str); |
| 471 | /*(*cphead)->c_refcnt = 0; */ | 486 | /*(*cphead)->c_refcnt = 0; */ |
| 472 | if (makefile) | 487 | (*cphead)->c_makefile = xstrdup(makefile); |
| 473 | (*cphead)->c_makefile = xstrdup(makefile); | ||
| 474 | (*cphead)->c_dispno = dispno; | 488 | (*cphead)->c_dispno = dispno; |
| 475 | } | 489 | } |
| 476 | 490 | ||
| @@ -725,6 +739,7 @@ addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) | |||
| 725 | { | 739 | { |
| 726 | struct rule *rp; | 740 | struct rule *rp; |
| 727 | struct rule **rpp; | 741 | struct rule **rpp; |
| 742 | struct cmd *old_cp; | ||
| 728 | 743 | ||
| 729 | // Can't mix single-colon and double-colon rules | 744 | // Can't mix single-colon and double-colon rules |
| 730 | if (!posix && (np->n_flag & N_TARGET)) { | 745 | if (!posix && (np->n_flag & N_TARGET)) { |
| @@ -741,7 +756,7 @@ addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) | |||
| 741 | return; | 756 | return; |
| 742 | } | 757 | } |
| 743 | 758 | ||
| 744 | if (cp && !(np->n_flag & N_DOUBLE) && getcmd(np)) { | 759 | if (cp && !(np->n_flag & N_DOUBLE) && (old_cp = getcmd(np))) { |
| 745 | // Handle the inference rule redefinition case | 760 | // Handle the inference rule redefinition case |
| 746 | // .DEFAULT rule can also be redefined (as an extension). | 761 | // .DEFAULT rule can also be redefined (as an extension). |
| 747 | if ((np->n_flag & N_INFERENCE) | 762 | if ((np->n_flag & N_INFERENCE) |
| @@ -750,7 +765,17 @@ addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) | |||
| 750 | freerules(np->n_rule); | 765 | freerules(np->n_rule); |
| 751 | np->n_rule = NULL; | 766 | np->n_rule = NULL; |
| 752 | } else { | 767 | } else { |
| 753 | error("commands defined twice for target %s", np->n_name); | 768 | // We're adding commands to a single colon rule which |
| 769 | // already has some. Clear the old ones first. | ||
| 770 | warning("overriding rule for target %s", np->n_name); | ||
| 771 | curr_cmd = old_cp; | ||
| 772 | warning("previous rule for target %s", np->n_name); | ||
| 773 | curr_cmd = NULL; | ||
| 774 | |||
| 775 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 776 | freecmds(rp->r_cmd); | ||
| 777 | rp->r_cmd = NULL; | ||
| 778 | } | ||
| 754 | } | 779 | } |
| 755 | } | 780 | } |
| 756 | 781 | ||
| @@ -993,38 +1018,27 @@ suffix(const char *name) | |||
| 993 | } | 1018 | } |
| 994 | 1019 | ||
| 995 | /* | 1020 | /* |
| 996 | * Dynamic dependency. This routine applies the suffix rules | 1021 | * 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 | 1022 | * to the target suffix 'tsuff'. The basename of the prerequisite |
| 998 | * target. NULL is returned on failure. On success the name of | 1023 | * is 'base'. |
| 999 | * the implicit prerequisite is returned and the details are | ||
| 1000 | * placed in the imprule structure provided by the caller. | ||
| 1001 | */ | 1024 | */ |
| 1002 | static struct name * | 1025 | static struct name * |
| 1003 | dyndep(struct name *np, struct rule *imprule) | 1026 | dyndep0(char *base, const char *tsuff, struct rule *infrule) |
| 1004 | { | 1027 | { |
| 1005 | char *suff, *newsuff; | 1028 | char *psuff; |
| 1006 | char *base, *name, *member; | ||
| 1007 | struct name *xp; // Suffixes | 1029 | struct name *xp; // Suffixes |
| 1008 | struct name *sp; // Suffix rule | 1030 | struct name *sp; // Suffix rule |
| 1009 | struct name *pp = NULL; // Implicit prerequisite | ||
| 1010 | struct rule *rp; | 1031 | struct rule *rp; |
| 1011 | struct depend *dp; | 1032 | struct depend *dp; |
| 1012 | bool chain = FALSE; | 1033 | bool chain = FALSE; |
| 1013 | 1034 | ||
| 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"); | 1035 | xp = newname(".SUFFIXES"); |
| 1022 | retry: | 1036 | retry: |
| 1023 | for (rp = xp->n_rule; rp; rp = rp->r_next) { | 1037 | for (rp = xp->n_rule; rp; rp = rp->r_next) { |
| 1024 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | 1038 | for (dp = rp->r_dep; dp; dp = dp->d_next) { |
| 1025 | // Generate new suffix rule to try | 1039 | // Generate new suffix rule to try |
| 1026 | newsuff = dp->d_name->n_name; | 1040 | psuff = dp->d_name->n_name; |
| 1027 | sp = findname(auto_concat(newsuff, suff)); | 1041 | sp = findname(auto_concat(psuff, tsuff)); |
| 1028 | if (sp && sp->n_rule) { | 1042 | if (sp && sp->n_rule) { |
| 1029 | struct name *ip; | 1043 | struct name *ip; |
| 1030 | int got_ip; | 1044 | int got_ip; |
| @@ -1032,9 +1046,8 @@ dyndep(struct name *np, struct rule *imprule) | |||
| 1032 | // Has rule already been used in this chain? | 1046 | // Has rule already been used in this chain? |
| 1033 | if ((sp->n_flag & N_MARK)) | 1047 | if ((sp->n_flag & N_MARK)) |
| 1034 | continue; | 1048 | continue; |
| 1035 | |||
| 1036 | // Generate a name for an implicit prerequisite | 1049 | // Generate a name for an implicit prerequisite |
| 1037 | ip = newname(auto_concat(base, newsuff)); | 1050 | ip = newname(auto_concat(base, psuff)); |
| 1038 | if ((ip->n_flag & N_DOING)) | 1051 | if ((ip->n_flag & N_DOING)) |
| 1039 | continue; | 1052 | continue; |
| 1040 | 1053 | ||
| @@ -1045,20 +1058,19 @@ dyndep(struct name *np, struct rule *imprule) | |||
| 1045 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); | 1058 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); |
| 1046 | } else { | 1059 | } else { |
| 1047 | sp->n_flag |= N_MARK; | 1060 | sp->n_flag |= N_MARK; |
| 1048 | got_ip = dyndep(ip, NULL) != NULL; | 1061 | got_ip = dyndep(ip, NULL, NULL) != NULL; |
| 1049 | sp->n_flag &= ~N_MARK; | 1062 | sp->n_flag &= ~N_MARK; |
| 1050 | } | 1063 | } |
| 1051 | 1064 | ||
| 1052 | if (got_ip) { | 1065 | if (got_ip) { |
| 1053 | // Prerequisite exists or we know how to make it | 1066 | // Prerequisite exists or we know how to make it |
| 1054 | if (imprule) { | 1067 | if (infrule) { |
| 1055 | dp = NULL; | 1068 | dp = NULL; |
| 1056 | newdep(&dp, ip); | 1069 | newdep(&dp, ip); |
| 1057 | imprule->r_dep = dp; | 1070 | infrule->r_dep = dp; |
| 1058 | imprule->r_cmd = sp->n_rule->r_cmd; | 1071 | infrule->r_cmd = sp->n_rule->r_cmd; |
| 1059 | } | 1072 | } |
| 1060 | pp = ip; | 1073 | return ip; |
| 1061 | goto finish; | ||
| 1062 | } | 1074 | } |
| 1063 | } | 1075 | } |
| 1064 | } | 1076 | } |
| @@ -1069,9 +1081,87 @@ dyndep(struct name *np, struct rule *imprule) | |||
| 1069 | chain = TRUE; | 1081 | chain = TRUE; |
| 1070 | goto retry; | 1082 | goto retry; |
| 1071 | } | 1083 | } |
| 1072 | finish: | 1084 | return NULL; |
| 1073 | free(suff); | 1085 | } |
| 1086 | |||
| 1087 | /* | ||
| 1088 | * If 'name' ends with 'suffix' return an allocated string containing | ||
| 1089 | * the name with the suffix removed, else return NULL. | ||
| 1090 | */ | ||
| 1091 | static char * | ||
| 1092 | has_suffix(const char *name, const char *suffix) | ||
| 1093 | { | ||
| 1094 | ssize_t delta = strlen(name) - strlen(suffix); | ||
| 1095 | char *base = NULL; | ||
| 1096 | |||
| 1097 | if (delta > 0 && strcmp(name + delta, suffix) == 0) { | ||
| 1098 | base = xstrdup(name); | ||
| 1099 | base[delta] = '\0'; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | return base; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | /* | ||
| 1106 | * Dynamic dependency. This routine applies the suffix rules | ||
| 1107 | * to try and find a source and a set of rules for a missing | ||
| 1108 | * target. NULL is returned on failure. On success the name of | ||
| 1109 | * the implicit prerequisite is returned and the rule used is | ||
| 1110 | * placed in the infrule structure provided by the caller. | ||
| 1111 | */ | ||
| 1112 | static struct name * | ||
| 1113 | dyndep(struct name *np, struct rule *infrule, const char **ptsuff) | ||
| 1114 | { | ||
| 1115 | const char *tsuff; | ||
| 1116 | char *base, *name, *member; | ||
| 1117 | struct name *pp = NULL; // Implicit prerequisite | ||
| 1118 | |||
| 1119 | member = NULL; | ||
| 1120 | name = splitlib(np->n_name, &member); | ||
| 1121 | |||
| 1122 | // POSIX only allows inference rules with one or two periods. | ||
| 1123 | // As an extension this restriction is lifted, but not for | ||
| 1124 | // targets of the form lib.a(member.o). | ||
| 1125 | if (!posix && member == NULL) { | ||
| 1126 | struct name *xp = newname(".SUFFIXES"); | ||
| 1127 | int found_suffix = FALSE; | ||
| 1128 | |||
| 1129 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { | ||
| 1130 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 1131 | tsuff = dp->d_name->n_name; | ||
| 1132 | base = has_suffix(name, tsuff); | ||
| 1133 | if (base) { | ||
| 1134 | found_suffix = TRUE; | ||
| 1135 | pp = dyndep0(base, tsuff, infrule); | ||
| 1136 | free(base); | ||
| 1137 | if (pp) { | ||
| 1138 | goto done; | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | if (!found_suffix) { | ||
| 1145 | // The name didn't have a known suffix. Try single-suffix rule. | ||
| 1146 | tsuff = ""; | ||
| 1147 | pp = dyndep0(name, tsuff, infrule); | ||
| 1148 | if (pp) { | ||
| 1149 | done: | ||
| 1150 | if (ptsuff) { | ||
| 1151 | *ptsuff = tsuff; | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | } | ||
| 1155 | } else { | ||
| 1156 | tsuff = xstrdup(suffix(name)); | ||
| 1157 | base = member ? member : name; | ||
| 1158 | *suffix(base) = '\0'; | ||
| 1159 | |||
| 1160 | pp = dyndep0(base, tsuff, infrule); | ||
| 1161 | free((void *)tsuff); | ||
| 1162 | } | ||
| 1074 | free(name); | 1163 | free(name); |
| 1164 | |||
| 1075 | return pp; | 1165 | return pp; |
| 1076 | } | 1166 | } |
| 1077 | 1167 | ||
| @@ -1789,9 +1879,10 @@ readline(FILE *fd, int want_command) | |||
| 1789 | } | 1879 | } |
| 1790 | 1880 | ||
| 1791 | /* | 1881 | /* |
| 1792 | * Return TRUE if the argument is a known suffix. | 1882 | * Return a pointer to the suffix name if the argument is a known suffix |
| 1883 | * or NULL if it isn't. | ||
| 1793 | */ | 1884 | */ |
| 1794 | static int | 1885 | static const char * |
| 1795 | is_suffix(const char *s) | 1886 | is_suffix(const char *s) |
| 1796 | { | 1887 | { |
| 1797 | struct name *np; | 1888 | struct name *np; |
| @@ -1802,7 +1893,39 @@ is_suffix(const char *s) | |||
| 1802 | for (rp = np->n_rule; rp; rp = rp->r_next) { | 1893 | for (rp = np->n_rule; rp; rp = rp->r_next) { |
| 1803 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | 1894 | for (dp = rp->r_dep; dp; dp = dp->d_next) { |
| 1804 | if (strcmp(s, dp->d_name->n_name) == 0) { | 1895 | if (strcmp(s, dp->d_name->n_name) == 0) { |
| 1805 | return TRUE; | 1896 | return dp->d_name->n_name; |
| 1897 | } | ||
| 1898 | } | ||
| 1899 | } | ||
| 1900 | return NULL; | ||
| 1901 | } | ||
| 1902 | |||
| 1903 | /* | ||
| 1904 | * Return TRUE if the argument is formed by concatenating two | ||
| 1905 | * known suffixes. | ||
| 1906 | */ | ||
| 1907 | static int | ||
| 1908 | is_inference_target(const char *s) | ||
| 1909 | { | ||
| 1910 | struct name *np; | ||
| 1911 | struct rule *rp1, *rp2; | ||
| 1912 | struct depend *dp1, *dp2; | ||
| 1913 | |||
| 1914 | np = newname(".SUFFIXES"); | ||
| 1915 | for (rp1 = np->n_rule; rp1; rp1 = rp1->r_next) { | ||
| 1916 | for (dp1 = rp1->r_dep; dp1; dp1 = dp1->d_next) { | ||
| 1917 | const char *suff1 = dp1->d_name->n_name; | ||
| 1918 | size_t len = strlen(suff1); | ||
| 1919 | |||
| 1920 | if (strncmp(s, suff1, len) == 0) { | ||
| 1921 | for (rp2 = np->n_rule; rp2; rp2 = rp2->r_next) { | ||
| 1922 | for (dp2 = rp2->r_dep; dp2; dp2 = dp2->d_next) { | ||
| 1923 | const char *suff2 = dp2->d_name->n_name; | ||
| 1924 | if (strcmp(s + len, suff2) == 0) { | ||
| 1925 | return TRUE; | ||
| 1926 | } | ||
| 1927 | } | ||
| 1928 | } | ||
| 1806 | } | 1929 | } |
| 1807 | } | 1930 | } |
| 1808 | } | 1931 | } |
| @@ -1824,7 +1947,6 @@ enum { | |||
| 1824 | static int | 1947 | static int |
| 1825 | target_type(char *s) | 1948 | target_type(char *s) |
| 1826 | { | 1949 | { |
| 1827 | char *sfx; | ||
| 1828 | int ret; | 1950 | int ret; |
| 1829 | static const char *s_name = | 1951 | static const char *s_name = |
| 1830 | ".DEFAULT\0" | 1952 | ".DEFAULT\0" |
| @@ -1854,9 +1976,6 @@ target_type(char *s) | |||
| 1854 | T_SPECIAL, | 1976 | T_SPECIAL, |
| 1855 | }; | 1977 | }; |
| 1856 | 1978 | ||
| 1857 | if (*s != '.') | ||
| 1858 | return T_NORMAL; | ||
| 1859 | |||
| 1860 | // Check for one of the known special targets | 1979 | // Check for one of the known special targets |
| 1861 | ret = index_in_strings(s_name, s); | 1980 | ret = index_in_strings(s_name, s); |
| 1862 | if (ret >= 0) | 1981 | if (ret >= 0) |
| @@ -1864,16 +1983,23 @@ target_type(char *s) | |||
| 1864 | 1983 | ||
| 1865 | // Check for an inference rule | 1984 | // Check for an inference rule |
| 1866 | ret = T_NORMAL; | 1985 | ret = T_NORMAL; |
| 1867 | sfx = suffix(s); | 1986 | if (!posix) { |
| 1868 | if (is_suffix(sfx)) { | 1987 | if (is_suffix(s) || is_inference_target(s)) { |
| 1869 | if (s == sfx) { // Single suffix rule | ||
| 1870 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | 1988 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; |
| 1871 | } else { | 1989 | } |
| 1872 | // Suffix is valid, check that prefix is too | 1990 | } else { |
| 1873 | *sfx = '\0'; | 1991 | // In POSIX inference rule targets must contain one or two dots |
| 1874 | if (is_suffix(s)) | 1992 | char *sfx = suffix(s); |
| 1993 | if (*s == '.' && is_suffix(sfx)) { | ||
| 1994 | if (s == sfx) { // Single suffix rule | ||
| 1875 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | 1995 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; |
| 1876 | *sfx = '.'; | 1996 | } else { |
| 1997 | // Suffix is valid, check that prefix is too | ||
| 1998 | *sfx = '\0'; | ||
| 1999 | if (is_suffix(s)) | ||
| 2000 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
| 2001 | *sfx = '.'; | ||
| 2002 | } | ||
| 1877 | } | 2003 | } |
| 1878 | } | 2004 | } |
| 1879 | return ret; | 2005 | return ret; |
| @@ -2069,6 +2195,20 @@ pragmas_from_env(void) | |||
| 2069 | #endif | 2195 | #endif |
| 2070 | 2196 | ||
| 2071 | /* | 2197 | /* |
| 2198 | * Determine if a line is a target rule with an inline command. | ||
| 2199 | * Return a pointer to the semicolon separator if it is, else NULL. | ||
| 2200 | */ | ||
| 2201 | static char * | ||
| 2202 | inline_command(char *line) | ||
| 2203 | { | ||
| 2204 | char *p = find_char(line, ':'); | ||
| 2205 | |||
| 2206 | if (p) | ||
| 2207 | p = strchr(p, ';'); | ||
| 2208 | return p; | ||
| 2209 | } | ||
| 2210 | |||
| 2211 | /* | ||
| 2072 | * Parse input from the makefile and construct a tree structure of it. | 2212 | * Parse input from the makefile and construct a tree structure of it. |
| 2073 | */ | 2213 | */ |
| 2074 | static void | 2214 | static void |
| @@ -2287,9 +2427,9 @@ input(FILE *fd, int ilevel) | |||
| 2287 | cp = NULL; | 2427 | cp = NULL; |
| 2288 | s = strchr(q, ';'); | 2428 | s = strchr(q, ';'); |
| 2289 | if (s) { | 2429 | if (s) { |
| 2290 | // Retrieve command from expanded copy of line | 2430 | // Retrieve command from original or expanded copy of line |
| 2291 | char *copy3 = expand_macros(copy, FALSE); | 2431 | char *copy3 = expand_macros(copy, FALSE); |
| 2292 | if ((p = find_colon(copy3)) && (p = strchr(p, ';'))) | 2432 | if ((p = inline_command(copy)) || (p = inline_command(copy3))) |
| 2293 | newcmd(&cp, process_command(p + 1)); | 2433 | newcmd(&cp, process_command(p + 1)); |
| 2294 | free(copy3); | 2434 | free(copy3); |
| 2295 | *s = '\0'; | 2435 | *s = '\0'; |
| @@ -2486,8 +2626,7 @@ docmds(struct name *np, struct cmd *cp) | |||
| 2486 | uint32_t ssilent, signore, sdomake; | 2626 | uint32_t ssilent, signore, sdomake; |
| 2487 | 2627 | ||
| 2488 | // Location of command in makefile (for use in error messages) | 2628 | // Location of command in makefile (for use in error messages) |
| 2489 | makefile = cp->c_makefile; | 2629 | curr_cmd = cp; |
| 2490 | dispno = cp->c_dispno; | ||
| 2491 | opts &= ~OPT_make; // We want to know if $(MAKE) is expanded | 2630 | opts &= ~OPT_make; // We want to know if $(MAKE) is expanded |
| 2492 | q = command = expand_macros(cp->c_cmd, FALSE); | 2631 | q = command = expand_macros(cp->c_cmd, FALSE); |
| 2493 | ssilent = silent || (np->n_flag & N_SILENT) || dotouch; | 2632 | ssilent = silent || (np->n_flag & N_SILENT) || dotouch; |
| @@ -2581,13 +2720,38 @@ docmds(struct name *np, struct cmd *cp) | |||
| 2581 | estat = MAKE_DIDSOMETHING; | 2720 | estat = MAKE_DIDSOMETHING; |
| 2582 | } | 2721 | } |
| 2583 | 2722 | ||
| 2584 | makefile = NULL; | 2723 | curr_cmd = NULL; |
| 2585 | return estat; | 2724 | return estat; |
| 2586 | } | 2725 | } |
| 2587 | 2726 | ||
| 2727 | /* | ||
| 2728 | * Remove the suffix from a name, either the one provided in 'tsuff' | ||
| 2729 | * or, if 'tsuff' is NULL, one of the known suffixes. | ||
| 2730 | */ | ||
| 2731 | static char * | ||
| 2732 | remove_suffix(const char *name, const char *tsuff) | ||
| 2733 | { | ||
| 2734 | char *base = NULL; | ||
| 2735 | |||
| 2736 | if (tsuff != NULL) { | ||
| 2737 | base = has_suffix(name, tsuff); | ||
| 2738 | } else { | ||
| 2739 | struct name *xp = newname(".SUFFIXES"); | ||
| 2740 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { | ||
| 2741 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2742 | base = has_suffix(name, dp->d_name->n_name); | ||
| 2743 | if (base) { | ||
| 2744 | return base; | ||
| 2745 | } | ||
| 2746 | } | ||
| 2747 | } | ||
| 2748 | } | ||
| 2749 | return base; | ||
| 2750 | } | ||
| 2751 | |||
| 2588 | static int | 2752 | static int |
| 2589 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | 2753 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, |
| 2590 | char *dedup, struct name *implicit) | 2754 | char *dedup, struct name *implicit, const char *tsuff) |
| 2591 | { | 2755 | { |
| 2592 | char *name, *member = NULL, *base = NULL, *prereq = NULL; | 2756 | char *name, *member = NULL, *base = NULL, *prereq = NULL; |
| 2593 | 2757 | ||
| @@ -2603,7 +2767,7 @@ make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | |||
| 2603 | char *s; | 2767 | char *s; |
| 2604 | 2768 | ||
| 2605 | // As an extension, if we're not dealing with an implicit | 2769 | // As an extension, if we're not dealing with an implicit |
| 2606 | // rule set $< to the first out-of-date prerequisite. | 2770 | // prerequisite set $< to the first out-of-date prerequisite. |
| 2607 | if (implicit == NULL) { | 2771 | if (implicit == NULL) { |
| 2608 | if (oodate) { | 2772 | if (oodate) { |
| 2609 | s = strchr(oodate, ' '); | 2773 | s = strchr(oodate, ' '); |
| @@ -2614,15 +2778,26 @@ make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | |||
| 2614 | } else | 2778 | } else |
| 2615 | prereq = implicit->n_name; | 2779 | prereq = implicit->n_name; |
| 2616 | 2780 | ||
| 2617 | base = member ? member : name; | 2781 | if (!posix && member == NULL) { |
| 2618 | s = suffix(base); | 2782 | // As an extension remove a suffix that doesn't necessarily |
| 2619 | // As an extension, if we're not dealing with an implicit | 2783 | // start with a period from a target, but not for targets |
| 2620 | // rule and the target ends with a known suffix, remove it | 2784 | // of the form lib.a(member.o). |
| 2621 | // and set $* to the stem, else to an empty string. | 2785 | base = remove_suffix(name, tsuff); |
| 2622 | if (implicit == NULL && !is_suffix(s)) | 2786 | if (base) { |
| 2623 | base = NULL; | 2787 | free(name); |
| 2624 | else | 2788 | name = base; |
| 2625 | *s = '\0'; | 2789 | } |
| 2790 | } else { | ||
| 2791 | base = member ? member : name; | ||
| 2792 | s = suffix(base); | ||
| 2793 | // As an extension, if we're not dealing with an implicit | ||
| 2794 | // prerequisite and the target ends with a known suffix, | ||
| 2795 | // remove it and set $* to the stem, else to an empty string. | ||
| 2796 | if (implicit == NULL && !is_suffix(s)) | ||
| 2797 | base = NULL; | ||
| 2798 | else | ||
| 2799 | *s = '\0'; | ||
| 2800 | } | ||
| 2626 | } | 2801 | } |
| 2627 | setmacro("<", prereq, 0 | M_VALID); | 2802 | setmacro("<", prereq, 0 | M_VALID); |
| 2628 | setmacro("*", base, 0 | M_VALID); | 2803 | setmacro("*", base, 0 | M_VALID); |
| @@ -2667,11 +2842,12 @@ make(struct name *np, int level) | |||
| 2667 | struct depend *dp; | 2842 | struct depend *dp; |
| 2668 | struct rule *rp; | 2843 | struct rule *rp; |
| 2669 | struct name *impdep = NULL; // implicit prerequisite | 2844 | struct name *impdep = NULL; // implicit prerequisite |
| 2670 | struct rule imprule; | 2845 | struct rule infrule; |
| 2671 | struct cmd *sc_cmd = NULL; // commands for single-colon rule | 2846 | struct cmd *sc_cmd = NULL; // commands for single-colon rule |
| 2672 | char *oodate = NULL; | 2847 | char *oodate = NULL; |
| 2673 | char *allsrc = NULL; | 2848 | char *allsrc = NULL; |
| 2674 | char *dedup = NULL; | 2849 | char *dedup = NULL; |
| 2850 | const char *tsuff = NULL; | ||
| 2675 | struct timespec dtim = {1, 0}; | 2851 | struct timespec dtim = {1, 0}; |
| 2676 | int estat = 0; | 2852 | int estat = 0; |
| 2677 | 2853 | ||
| @@ -2690,10 +2866,10 @@ make(struct name *np, int level) | |||
| 2690 | // as an extension, not for phony targets) | 2866 | // as an extension, not for phony targets) |
| 2691 | sc_cmd = getcmd(np); | 2867 | sc_cmd = getcmd(np); |
| 2692 | if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { | 2868 | if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { |
| 2693 | impdep = dyndep(np, &imprule); | 2869 | impdep = dyndep(np, &infrule, &tsuff); |
| 2694 | if (impdep) { | 2870 | if (impdep) { |
| 2695 | sc_cmd = imprule.r_cmd; | 2871 | sc_cmd = infrule.r_cmd; |
| 2696 | addrule(np, imprule.r_dep, NULL, FALSE); | 2872 | addrule(np, infrule.r_dep, NULL, FALSE); |
| 2697 | } | 2873 | } |
| 2698 | } | 2874 | } |
| 2699 | 2875 | ||
| @@ -2708,14 +2884,15 @@ make(struct name *np, int level) | |||
| 2708 | } | 2884 | } |
| 2709 | impdep = np; | 2885 | impdep = np; |
| 2710 | } | 2886 | } |
| 2711 | } | 2887 | } else { |
| 2712 | else { | ||
| 2713 | // If any double-colon rule has no commands we need | 2888 | // If any double-colon rule has no commands we need |
| 2714 | // an inference rule (but, as an extension, not for phony targets) | 2889 | // an inference rule. |
| 2715 | for (rp = np->n_rule; rp; rp = rp->r_next) { | 2890 | for (rp = np->n_rule; rp; rp = rp->r_next) { |
| 2716 | if (!rp->r_cmd) { | 2891 | if (!rp->r_cmd) { |
| 2717 | if (posix || !(np->n_flag & N_PHONY)) | 2892 | // Phony targets don't need an inference rule. |
| 2718 | impdep = dyndep(np, &imprule); | 2893 | if (!posix && (np->n_flag & N_PHONY)) |
| 2894 | continue; | ||
| 2895 | impdep = dyndep(np, &infrule, &tsuff); | ||
| 2719 | if (!impdep) { | 2896 | if (!impdep) { |
| 2720 | if (doinclude) | 2897 | if (doinclude) |
| 2721 | return 1; | 2898 | return 1; |
| @@ -2741,11 +2918,14 @@ make(struct name *np, int level) | |||
| 2741 | // Each double-colon rule is handled separately. | 2918 | // Each double-colon rule is handled separately. |
| 2742 | if ((np->n_flag & N_DOUBLE)) { | 2919 | if ((np->n_flag & N_DOUBLE)) { |
| 2743 | // If the rule has no commands use the inference rule. | 2920 | // If the rule has no commands use the inference rule. |
| 2921 | // Unless there isn't one, as allowed for phony targets. | ||
| 2744 | if (!rp->r_cmd) { | 2922 | if (!rp->r_cmd) { |
| 2745 | locdep = impdep; | 2923 | if (impdep) { |
| 2746 | imprule.r_dep->d_next = rp->r_dep; | 2924 | locdep = impdep; |
| 2747 | rp->r_dep = imprule.r_dep; | 2925 | infrule.r_dep->d_next = rp->r_dep; |
| 2748 | rp->r_cmd = imprule.r_cmd; | 2926 | rp->r_dep = infrule.r_dep; |
| 2927 | rp->r_cmd = infrule.r_cmd; | ||
| 2928 | } | ||
| 2749 | } | 2929 | } |
| 2750 | // A rule with no prerequisities is executed unconditionally. | 2930 | // A rule with no prerequisities is executed unconditionally. |
| 2751 | if (!rp->r_dep) | 2931 | if (!rp->r_dep) |
| @@ -2776,7 +2956,7 @@ make(struct name *np, int level) | |||
| 2776 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { | 2956 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { |
| 2777 | if (!(estat & MAKE_FAILURE)) { | 2957 | if (!(estat & MAKE_FAILURE)) { |
| 2778 | estat |= make1(np, rp->r_cmd, oodate, allsrc, | 2958 | estat |= make1(np, rp->r_cmd, oodate, allsrc, |
| 2779 | dedup, locdep); | 2959 | dedup, locdep, tsuff); |
| 2780 | dtim = (struct timespec){1, 0}; | 2960 | dtim = (struct timespec){1, 0}; |
| 2781 | } | 2961 | } |
| 2782 | free(oodate); | 2962 | free(oodate); |
| @@ -2792,7 +2972,7 @@ make(struct name *np, int level) | |||
| 2792 | } | 2972 | } |
| 2793 | } | 2973 | } |
| 2794 | if ((np->n_flag & N_DOUBLE) && impdep) | 2974 | if ((np->n_flag & N_DOUBLE) && impdep) |
| 2795 | free(imprule.r_dep); | 2975 | free(infrule.r_dep); |
| 2796 | 2976 | ||
| 2797 | np->n_flag |= N_DONE; | 2977 | np->n_flag |= N_DONE; |
| 2798 | np->n_flag &= ~N_DOING; | 2978 | np->n_flag &= ~N_DOING; |
| @@ -2801,7 +2981,8 @@ make(struct name *np, int level) | |||
| 2801 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { | 2981 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { |
| 2802 | if (!(estat & MAKE_FAILURE)) { | 2982 | if (!(estat & MAKE_FAILURE)) { |
| 2803 | if (sc_cmd) | 2983 | if (sc_cmd) |
| 2804 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, impdep); | 2984 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, |
| 2985 | impdep, tsuff); | ||
| 2805 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) | 2986 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) |
| 2806 | warning("nothing to be done for %s", np->n_name); | 2987 | warning("nothing to be done for %s", np->n_name); |
| 2807 | } else if (!doinclude && !quest) { | 2988 | } 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 | ||
| 189 | static void replace(char *s, char what, char with) | ||
| 190 | { | ||
| 191 | while (*s) { | ||
| 192 | if (what == *s) | ||
| 193 | *s = with; | ||
| 194 | ++s; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | static char *filename2modname(const char *filename, char *modname) | 189 | static 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 | ||
| 72 | void 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 | |||
| 81 | int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) | 72 | int 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; | |||
| 47 | module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC; | 47 | module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC; |
| 48 | void moddb_free(module_db *db) FAST_FUNC; | 48 | void moddb_free(module_db *db) FAST_FUNC; |
| 49 | 49 | ||
| 50 | void replace(char *s, char what, char with) FAST_FUNC; | ||
| 51 | int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; | 50 | int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; |
| 52 | char *filename2modname(const char *filename, char *modname) FAST_FUNC; | 51 | char *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 | ||
| 75 | choice | ||
| 76 | prompt "TLS implementation" | ||
| 77 | default FEATURE_TLS_INTERNAL | ||
| 78 | |||
| 79 | config FEATURE_TLS_INTERNAL | ||
| 80 | bool "Internal" | ||
| 81 | depends on TLS | ||
| 82 | help | ||
| 83 | Use the BusyBox default internal TLS implementation. | ||
| 84 | |||
| 85 | config 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 | |||
| 92 | endchoice | ||
| 93 | |||
| 75 | config FEATURE_TLS_SHA1 | 94 | config 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 | ||
| 105 | config 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 | |||
| 86 | INSERT | 114 | INSERT |
| 87 | 115 | ||
| 88 | source networking/udhcp/Config.in | 116 | source 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 | */ | ||
| 193 | static char * | 196 | static char * |
| 194 | escape_text(const char *prepend, const char *str, unsigned escapee) | 197 | escape_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 */ | ||
| 229 | static unsigned | ||
| 230 | replace_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 | |||
| 241 | static void | 226 | static void |
| 242 | verbose_log(const char *str) | 227 | verbose_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..efe9f5326 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: */ |
| @@ -1645,7 +1645,7 @@ update_local_clock(peer_t *p) | |||
| 1645 | /* 65536 is one ppm */ | 1645 | /* 65536 is one ppm */ |
| 1646 | tmx.freq = G.discipline_freq_drift * 65536e6; | 1646 | tmx.freq = G.discipline_freq_drift * 65536e6; |
| 1647 | #endif | 1647 | #endif |
| 1648 | tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST;// | ADJ_MAXERROR | ADJ_ESTERROR; | 1648 | tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; |
| 1649 | 1649 | ||
| 1650 | tmx.offset = (long)(offset * 1000000); /* usec */ | 1650 | tmx.offset = (long)(offset * 1000000); /* usec */ |
| 1651 | if (SLEW_THRESHOLD < STEP_THRESHOLD) { | 1651 | if (SLEW_THRESHOLD < STEP_THRESHOLD) { |
| @@ -1738,16 +1738,23 @@ update_local_clock(peer_t *p) | |||
| 1738 | if (tmx.constant < 0) | 1738 | if (tmx.constant < 0) |
| 1739 | tmx.constant = 0; | 1739 | tmx.constant = 0; |
| 1740 | 1740 | ||
| 1741 | //tmx.esterror = (uint32_t)(clock_jitter * 1e6); | 1741 | /* For ADJ_MAXERROR and ADJ_ESTERROR: */ |
| 1742 | //tmx.maxerror = (uint32_t)((sys_rootdelay / 2 + sys_rootdisp) * 1e6); | 1742 | /* kernel increments this by 500us each second, sets STA_UNSYNC if exceeds 16 seconds: */ |
| 1743 | tmx.maxerror = (uint32_t)((G.rootdelay / 2 + G.rootdisp) * 1000000.0); | ||
| 1744 | /* (without ADJ_MAXERROR, time adjustment still works, but kernel uses | ||
| 1745 | * conservative maxerror value and quickly sets STA_UNSYNC) | ||
| 1746 | */ | ||
| 1747 | /* esterror is not used by kernel, presumably may be used by other programs reading adjtimex result: */ | ||
| 1748 | tmx.esterror = (uint32_t)(G.discipline_jitter * 1000000.0); | ||
| 1749 | |||
| 1743 | rc = adjtimex(&tmx); | 1750 | rc = adjtimex(&tmx); |
| 1744 | if (rc < 0) | 1751 | if (rc < 0) |
| 1745 | bb_simple_perror_msg_and_die("adjtimex"); | 1752 | bb_simple_perror_msg_and_die("adjtimex"); |
| 1746 | /* NB: here kernel returns constant == G.poll_exp, not == G.poll_exp - 4. | 1753 | /* NB: here kernel returns constant == G.poll_exp, not == G.poll_exp - 4. |
| 1747 | * Not sure why. Perhaps it is normal. | 1754 | * Not sure why. Perhaps it is normal. |
| 1748 | */ | 1755 | */ |
| 1749 | VERB4 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld status:0x%x", | 1756 | VERB4 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld esterror:%ld maxerror:%ld status:0x%x", |
| 1750 | rc, (long)tmx.freq, (long)tmx.offset, tmx.status); | 1757 | rc, (long)tmx.freq, (long)tmx.offset, (long)tmx.esterror, (long)tmx.maxerror, tmx.status); |
| 1751 | G.kernel_freq_drift = tmx.freq / 65536; | 1758 | G.kernel_freq_drift = tmx.freq / 65536; |
| 1752 | VERB2 bb_error_msg("update from:%s offset:%+f delay:%f jitter:%f clock drift:%+.3fppm tc:%d", | 1759 | VERB2 bb_error_msg("update from:%s offset:%+f delay:%f jitter:%f clock drift:%+.3fppm tc:%d", |
| 1753 | p->p_dotted, | 1760 | p->p_dotted, |
| @@ -1976,7 +1983,7 @@ recv_and_process_peer_pkt(peer_t *p) | |||
| 1976 | 1983 | ||
| 1977 | p->reachable_bits |= 1; | 1984 | p->reachable_bits |= 1; |
| 1978 | if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) { | 1985 | 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", | 1986 | 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, | 1987 | p->p_dotted, |
| 1981 | offset, | 1988 | offset, |
| 1982 | p->p_raw_delay, | 1989 | 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..97217f435 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 | ||
| 190 | enum { | 191 | enum { |
| 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 | ||
| 338 | static 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 | |||
| 347 | void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count) | ||
| 348 | { | ||
| 349 | xorbuf3(dst, dst, src, count); | ||
| 350 | } | ||
| 351 | |||
| 352 | void 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. | ||
| 406 | typedef struct hmac_precomputed { | ||
| 407 | md5sha_ctx_t hashed_key_xor_ipad; | ||
| 408 | md5sha_ctx_t hashed_key_xor_opad; | ||
| 409 | } hmac_precomputed_t; | ||
| 410 | |||
| 411 | typedef 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 | ||
| 417 | static 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 | |||
| 459 | static 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 | |||
| 482 | static 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 | ||
| 499 | static 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 | ||
| 513 | static 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 | |||
| 658 | static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type) | 534 | static 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,500 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags) | |||
| 2481 | } | 2362 | } |
| 2482 | } | 2363 | } |
| 2483 | } | 2364 | } |
| 2365 | #else | ||
| 2366 | #include <stdbool.h> | ||
| 2367 | |||
| 2368 | #if ENABLE_FEATURE_TLS_SCHANNEL_1_3 | ||
| 2369 | #include <subauth.h> | ||
| 2370 | #endif | ||
| 2371 | |||
| 2372 | #define SCHANNEL_USE_BLACKLISTS | ||
| 2373 | |||
| 2374 | #include <security.h> | ||
| 2375 | #include <schannel.h> | ||
| 2376 | |||
| 2377 | #ifndef SECBUFFER_ALERT | ||
| 2378 | # define SECBUFFER_ALERT 17 | ||
| 2379 | #endif | ||
| 2380 | |||
| 2381 | #ifndef SP_PROT_TLS1_0_CLIENT | ||
| 2382 | # define SP_PROT_TLS1_0_CLIENT 0x00000080 | ||
| 2383 | # define SP_PROT_TLS1_1_CLIENT 0x00000200 | ||
| 2384 | # define SP_PROT_TLS1_2_CLIENT 0x00000800 | ||
| 2385 | # define SCH_USE_STRONG_CRYPTO 0x00400000 | ||
| 2386 | #endif | ||
| 2387 | |||
| 2388 | #define BB_SCHANNEL_ISC_FLAGS \ | ||
| 2389 | (ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT | \ | ||
| 2390 | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM | ISC_REQ_USE_SUPPLIED_CREDS) | ||
| 2391 | |||
| 2392 | #define SEC_STATUS_FAIL(status) ((status) != SEC_E_OK) | ||
| 2393 | |||
| 2394 | static char *hresult_to_error_string(HRESULT result) { | ||
| 2395 | char *output = NULL; | ||
| 2396 | |||
| 2397 | FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | ||
| 2398 | | FORMAT_MESSAGE_IGNORE_INSERTS | | ||
| 2399 | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, result, | ||
| 2400 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||
| 2401 | (char *) &output, 0, NULL); | ||
| 2402 | return output; | ||
| 2403 | } | ||
| 2404 | |||
| 2405 | static void init_sec_buffer(SecBuffer *buffer, void *pvBuffer, unsigned long cbBuffer, unsigned long BufferType) { | ||
| 2406 | buffer->BufferType = BufferType; | ||
| 2407 | buffer->cbBuffer = cbBuffer; | ||
| 2408 | buffer->pvBuffer = pvBuffer; | ||
| 2409 | } | ||
| 2410 | |||
| 2411 | #define init_sec_buffer_empty(buffer, BufferType) init_sec_buffer(buffer, NULL, 0, BufferType) | ||
| 2412 | |||
| 2413 | static void init_sec_buffer_desc(SecBufferDesc *desc, SecBuffer *buffers, unsigned long buffer_count) { | ||
| 2414 | desc->ulVersion = SECBUFFER_VERSION; | ||
| 2415 | desc->cBuffers = buffer_count; | ||
| 2416 | desc->pBuffers = buffers; | ||
| 2417 | } | ||
| 2418 | |||
| 2419 | static ssize_t tls_read(tls_state_t *state, char *buf, ssize_t len) { | ||
| 2420 | ssize_t amount_read = 0; | ||
| 2421 | |||
| 2422 | if (state->connection_state > BB_SCHANNEL_OPEN) { | ||
| 2423 | goto Success; | ||
| 2424 | } | ||
| 2425 | |||
| 2426 | while (len > 0) { | ||
| 2427 | if (state->out_buffer) { | ||
| 2428 | unsigned long copy_amount = | ||
| 2429 | min(len, (ssize_t)state->out_buffer_length); | ||
| 2430 | memcpy(buf, state->out_buffer, copy_amount); | ||
| 2431 | |||
| 2432 | amount_read += copy_amount; | ||
| 2433 | buf += copy_amount; | ||
| 2434 | len -= copy_amount; | ||
| 2435 | |||
| 2436 | if (copy_amount == state->out_buffer_length) { | ||
| 2437 | if (state->out_buffer_extra > 0) { | ||
| 2438 | memmove( | ||
| 2439 | state->in_buffer, | ||
| 2440 | state->in_buffer + | ||
| 2441 | (state->in_buffer_offset - state->out_buffer_extra), | ||
| 2442 | state->in_buffer_offset - (state->in_buffer_offset - | ||
| 2443 | state->out_buffer_extra)); | ||
| 2444 | state->in_buffer_offset = state->out_buffer_extra; | ||
| 2445 | } else { | ||
| 2446 | state->in_buffer_offset = 0; | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | state->out_buffer = NULL; | ||
| 2450 | state->out_buffer_length = 0; | ||
| 2451 | state->out_buffer_extra = 0; | ||
| 2452 | |||
| 2453 | } else { | ||
| 2454 | state->out_buffer_length -= copy_amount; | ||
| 2455 | state->out_buffer += copy_amount; | ||
| 2456 | } | ||
| 2457 | } else { | ||
| 2458 | SecBuffer buffers[4]; | ||
| 2459 | SecBufferDesc desc; | ||
| 2460 | SECURITY_STATUS status; | ||
| 2461 | |||
| 2462 | init_sec_buffer(&buffers[0], | ||
| 2463 | state->in_buffer, | ||
| 2464 | state->in_buffer_offset, | ||
| 2465 | SECBUFFER_DATA); | ||
| 2466 | init_sec_buffer_empty(&buffers[1], SECBUFFER_EMPTY); | ||
| 2467 | init_sec_buffer_empty(&buffers[2], SECBUFFER_EMPTY); | ||
| 2468 | init_sec_buffer_empty(&buffers[3], SECBUFFER_EMPTY); | ||
| 2469 | |||
| 2470 | init_sec_buffer_desc(&desc, buffers, _countof(buffers)); | ||
| 2471 | |||
| 2472 | status = DecryptMessage(&state->ctx_handle, &desc, 0, NULL); | ||
| 2473 | |||
| 2474 | switch (status) { | ||
| 2475 | case SEC_E_OK: { | ||
| 2476 | state->out_buffer = buffers[1].pvBuffer; | ||
| 2477 | state->out_buffer_length = buffers[1].cbBuffer; | ||
| 2478 | state->out_buffer_extra = state->in_buffer_offset; | ||
| 2479 | |||
| 2480 | if (buffers[3].BufferType == SECBUFFER_EXTRA) { | ||
| 2481 | state->out_buffer_extra = buffers[3].cbBuffer; | ||
| 2482 | } else { | ||
| 2483 | state->out_buffer_extra = 0; | ||
| 2484 | } | ||
| 2485 | |||
| 2486 | continue; | ||
| 2487 | } | ||
| 2488 | case SEC_I_CONTEXT_EXPIRED: { | ||
| 2489 | // Shut down the connection | ||
| 2490 | state->connection_state = BB_SCHANNEL_CLOSED; | ||
| 2491 | goto Success; | ||
| 2492 | } | ||
| 2493 | case SEC_E_INCOMPLETE_MESSAGE: { | ||
| 2494 | int result = safe_read(state->ifd, | ||
| 2495 | state->in_buffer + state->in_buffer_offset, | ||
| 2496 | sizeof(state->in_buffer) - state->in_buffer_offset); | ||
| 2497 | if (result == 0) { | ||
| 2498 | state->connection_state = BB_SCHANNEL_CLOSED; | ||
| 2499 | goto Success; | ||
| 2500 | } else if (result < 0) { | ||
| 2501 | bb_error_msg_and_die("schannel: read() failed"); | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | state->in_buffer_offset += result; | ||
| 2505 | continue; | ||
| 2506 | } | ||
| 2507 | case SEC_I_RENEGOTIATE: { | ||
| 2508 | DWORD flags = BB_SCHANNEL_ISC_FLAGS; | ||
| 2509 | |||
| 2510 | SecBuffer in_buffers[2]; | ||
| 2511 | SecBufferDesc in_buffers_desc; | ||
| 2512 | |||
| 2513 | SecBuffer out_buffers[2]; | ||
| 2514 | SecBufferDesc out_buffers_desc; | ||
| 2515 | |||
| 2516 | init_sec_buffer(&in_buffers[0], | ||
| 2517 | buffers[3].pvBuffer, | ||
| 2518 | buffers[3].cbBuffer, | ||
| 2519 | SECBUFFER_TOKEN); | ||
| 2520 | init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY); | ||
| 2521 | |||
| 2522 | init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN); | ||
| 2523 | init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT); | ||
| 2524 | |||
| 2525 | init_sec_buffer_desc( | ||
| 2526 | &in_buffers_desc, in_buffers, _countof(in_buffers)); | ||
| 2527 | init_sec_buffer_desc( | ||
| 2528 | &out_buffers_desc, out_buffers, _countof(out_buffers)); | ||
| 2529 | |||
| 2530 | status = InitializeSecurityContextA(&state->cred_handle, | ||
| 2531 | &state->ctx_handle, | ||
| 2532 | state->hostname, | ||
| 2533 | flags, | ||
| 2534 | 0, | ||
| 2535 | 0, | ||
| 2536 | &in_buffers_desc, | ||
| 2537 | 0, | ||
| 2538 | &state->ctx_handle, | ||
| 2539 | &out_buffers_desc, | ||
| 2540 | &flags, | ||
| 2541 | 0); | ||
| 2542 | |||
| 2543 | if (SEC_STATUS_FAIL(status)) { | ||
| 2544 | bb_error_msg_and_die("schannel: renegotiate failed: (0x%08lx): %s", | ||
| 2545 | status, hresult_to_error_string(status)); | ||
| 2546 | } | ||
| 2547 | |||
| 2548 | if (in_buffers[1].BufferType == SECBUFFER_EXTRA) { | ||
| 2549 | memmove(state->in_buffer, | ||
| 2550 | state->in_buffer + (state->in_buffer_offset - | ||
| 2551 | in_buffers[1].cbBuffer), | ||
| 2552 | in_buffers[1].cbBuffer); | ||
| 2553 | state->in_buffer_offset = in_buffers[1].cbBuffer; | ||
| 2554 | } else { | ||
| 2555 | state->in_buffer_offset = 0; | ||
| 2556 | } | ||
| 2557 | |||
| 2558 | continue; | ||
| 2559 | } | ||
| 2560 | default: { | ||
| 2561 | bb_error_msg_and_die("schannel: DecryptMessage failed: (0x%08lx): %s", | ||
| 2562 | status, hresult_to_error_string(status)); | ||
| 2563 | } | ||
| 2564 | } | ||
| 2565 | } | ||
| 2566 | } | ||
| 2567 | |||
| 2568 | Success: | ||
| 2569 | return amount_read; | ||
| 2570 | } | ||
| 2571 | |||
| 2572 | static void tls_write(tls_state_t *state, char *buf, size_t len) { | ||
| 2573 | if (state->connection_state > BB_SCHANNEL_OPEN) { | ||
| 2574 | bb_error_msg_and_die("schannel: attempted to write to a closed connection"); | ||
| 2575 | } | ||
| 2576 | |||
| 2577 | while (len > 0) { | ||
| 2578 | SECURITY_STATUS status; | ||
| 2579 | unsigned long copy_amount = | ||
| 2580 | min(len, (size_t)state->stream_sizes.cbMaximumMessage); | ||
| 2581 | char *write_buffer = _alloca(sizeof(state->in_buffer)); | ||
| 2582 | |||
| 2583 | SecBuffer buffers[4]; | ||
| 2584 | SecBufferDesc desc; | ||
| 2585 | |||
| 2586 | init_sec_buffer(&buffers[0], | ||
| 2587 | write_buffer, | ||
| 2588 | state->stream_sizes.cbHeader, | ||
| 2589 | SECBUFFER_STREAM_HEADER); | ||
| 2590 | init_sec_buffer(&buffers[1], | ||
| 2591 | write_buffer + state->stream_sizes.cbHeader, | ||
| 2592 | copy_amount, | ||
| 2593 | SECBUFFER_DATA); | ||
| 2594 | init_sec_buffer(&buffers[2], | ||
| 2595 | write_buffer + state->stream_sizes.cbHeader + | ||
| 2596 | copy_amount, | ||
| 2597 | state->stream_sizes.cbTrailer, | ||
| 2598 | SECBUFFER_STREAM_TRAILER); | ||
| 2599 | init_sec_buffer_empty(&buffers[3], SECBUFFER_EMPTY); | ||
| 2600 | |||
| 2601 | init_sec_buffer_desc(&desc, buffers, _countof(buffers)); | ||
| 2602 | |||
| 2603 | memcpy(buffers[1].pvBuffer, buf, copy_amount); | ||
| 2604 | |||
| 2605 | status = EncryptMessage(&state->ctx_handle, 0, &desc, 0); | ||
| 2606 | if (SEC_STATUS_FAIL(status)) { | ||
| 2607 | bb_error_msg_and_die("schannel: EncryptMessage failed: (0x%08lx): %s", | ||
| 2608 | status, hresult_to_error_string(status)); | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | xwrite(state->ofd, write_buffer, | ||
| 2612 | buffers[0].cbBuffer + buffers[1].cbBuffer + | ||
| 2613 | buffers[2].cbBuffer); | ||
| 2614 | |||
| 2615 | len -= copy_amount; | ||
| 2616 | } | ||
| 2617 | } | ||
| 2618 | |||
| 2619 | static void tls_disconnect(tls_state_t * state) { | ||
| 2620 | SECURITY_STATUS status; | ||
| 2621 | |||
| 2622 | DWORD token = SCHANNEL_SHUTDOWN; | ||
| 2623 | DWORD flags = BB_SCHANNEL_ISC_FLAGS; | ||
| 2624 | |||
| 2625 | SecBuffer buf_token; | ||
| 2626 | SecBufferDesc buf_token_desc; | ||
| 2627 | |||
| 2628 | SecBuffer out_buffer; | ||
| 2629 | SecBufferDesc out_buffer_desc; | ||
| 2630 | |||
| 2631 | if (state->connection_state == BB_SCHANNEL_CLOSED_AND_FREED) | ||
| 2632 | return; | ||
| 2633 | state->connection_state = BB_SCHANNEL_CLOSED_AND_FREED; | ||
| 2634 | |||
| 2635 | init_sec_buffer(&buf_token, &token, sizeof(token), SECBUFFER_TOKEN); | ||
| 2636 | init_sec_buffer_desc(&buf_token_desc, &buf_token, 1); | ||
| 2637 | |||
| 2638 | ApplyControlToken(&state->ctx_handle, &buf_token_desc); | ||
| 2639 | |||
| 2640 | init_sec_buffer_empty(&out_buffer, SECBUFFER_TOKEN); | ||
| 2641 | init_sec_buffer_desc(&out_buffer_desc, &out_buffer, 1); | ||
| 2642 | |||
| 2643 | status = InitializeSecurityContextA(&state->cred_handle, | ||
| 2644 | &state->ctx_handle, | ||
| 2645 | state->hostname, | ||
| 2646 | flags, | ||
| 2647 | 0, | ||
| 2648 | 0, | ||
| 2649 | NULL, | ||
| 2650 | 0, | ||
| 2651 | &state->ctx_handle, | ||
| 2652 | &out_buffer_desc, | ||
| 2653 | &flags, | ||
| 2654 | 0); | ||
| 2655 | |||
| 2656 | if ((status == SEC_E_OK) || (status == SEC_I_CONTEXT_EXPIRED)) { | ||
| 2657 | write(state->ofd, out_buffer.pvBuffer, out_buffer.cbBuffer); | ||
| 2658 | FreeContextBuffer(out_buffer.pvBuffer); | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | DeleteSecurityContext(&state->ctx_handle); | ||
| 2662 | FreeCredentialsHandle(&state->cred_handle); | ||
| 2663 | free(state->hostname); | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | |||
| 2667 | void FAST_FUNC tls_handshake(tls_state_t *state, const char *hostname) { | ||
| 2668 | SECURITY_STATUS status; | ||
| 2669 | |||
| 2670 | #if ENABLE_FEATURE_TLS_SCHANNEL_1_3 | ||
| 2671 | SCH_CREDENTIALS credential = {.dwVersion = SCH_CREDENTIALS_VERSION, | ||
| 2672 | .dwCredFormat = 0, | ||
| 2673 | .cCreds = 0, | ||
| 2674 | .paCred = NULL, | ||
| 2675 | .hRootStore = NULL, | ||
| 2676 | .cMappers = 0, | ||
| 2677 | .aphMappers = NULL, | ||
| 2678 | .dwSessionLifespan = 0, | ||
| 2679 | .dwFlags = | ||
| 2680 | SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS | | ||
| 2681 | SCH_USE_STRONG_CRYPTO, | ||
| 2682 | .cTlsParameters = 0, | ||
| 2683 | .pTlsParameters = NULL | ||
| 2684 | }; | ||
| 2685 | #else | ||
| 2686 | SCHANNEL_CRED credential = {.dwVersion = SCHANNEL_CRED_VERSION, | ||
| 2687 | .cCreds = 0, | ||
| 2688 | .paCred = NULL, | ||
| 2689 | .hRootStore = NULL, | ||
| 2690 | .cMappers = 0, | ||
| 2691 | .aphMappers = NULL, | ||
| 2692 | .cSupportedAlgs = 0, | ||
| 2693 | .palgSupportedAlgs = NULL, | ||
| 2694 | .grbitEnabledProtocols = | ||
| 2695 | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | | ||
| 2696 | SP_PROT_TLS1_2_CLIENT, | ||
| 2697 | .dwMinimumCipherStrength = 0, | ||
| 2698 | .dwMaximumCipherStrength = 0, | ||
| 2699 | .dwSessionLifespan = 0, | ||
| 2700 | .dwFlags = | ||
| 2701 | SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS | | ||
| 2702 | SCH_USE_STRONG_CRYPTO, | ||
| 2703 | .dwCredFormat = 0 | ||
| 2704 | }; | ||
| 2705 | #endif | ||
| 2706 | |||
| 2707 | state->in_buffer_offset = 0; | ||
| 2708 | |||
| 2709 | state->out_buffer = NULL; | ||
| 2710 | state->out_buffer_length = 0; | ||
| 2711 | state->out_buffer_extra = 0; | ||
| 2712 | |||
| 2713 | state->hostname = xstrdup(hostname); | ||
| 2714 | state->initialized = false; | ||
| 2715 | state->connection_state = BB_SCHANNEL_OPEN; | ||
| 2716 | |||
| 2717 | if (SEC_STATUS_FAIL(status = AcquireCredentialsHandleA(NULL, | ||
| 2718 | (SEC_CHAR *)UNISP_NAME_A, | ||
| 2719 | SECPKG_CRED_OUTBOUND, | ||
| 2720 | NULL, | ||
| 2721 | &credential, | ||
| 2722 | NULL, | ||
| 2723 | NULL, | ||
| 2724 | &state->cred_handle, | ||
| 2725 | NULL))) { | ||
| 2726 | bb_error_msg_and_die("schannel: AcquireCredentialsHandleA failed: (0x%08lx): %s", | ||
| 2727 | status, hresult_to_error_string(status)); | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | // InitializeSecurityContext loop | ||
| 2731 | while (true) { | ||
| 2732 | DWORD flags = BB_SCHANNEL_ISC_FLAGS; | ||
| 2733 | |||
| 2734 | SecBuffer in_buffers[2]; | ||
| 2735 | SecBufferDesc in_buffers_desc; | ||
| 2736 | |||
| 2737 | SecBuffer out_buffers[2]; | ||
| 2738 | SecBufferDesc out_buffers_desc; | ||
| 2739 | |||
| 2740 | init_sec_buffer(&in_buffers[0], | ||
| 2741 | state->in_buffer, | ||
| 2742 | state->in_buffer_offset, | ||
| 2743 | SECBUFFER_TOKEN); | ||
| 2744 | init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY); | ||
| 2745 | |||
| 2746 | init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN); | ||
| 2747 | init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT); | ||
| 2748 | |||
| 2749 | init_sec_buffer_desc( | ||
| 2750 | &in_buffers_desc, in_buffers, _countof(in_buffers)); | ||
| 2751 | init_sec_buffer_desc( | ||
| 2752 | &out_buffers_desc, out_buffers, _countof(out_buffers)); | ||
| 2753 | |||
| 2754 | status = InitializeSecurityContextA( | ||
| 2755 | &state->cred_handle, | ||
| 2756 | state->initialized ? &state->ctx_handle : NULL, | ||
| 2757 | state->hostname, | ||
| 2758 | flags, | ||
| 2759 | 0, | ||
| 2760 | 0, | ||
| 2761 | state->initialized ? &in_buffers_desc : NULL, | ||
| 2762 | 0, | ||
| 2763 | &state->ctx_handle, | ||
| 2764 | &out_buffers_desc, | ||
| 2765 | &flags, | ||
| 2766 | 0); | ||
| 2767 | |||
| 2768 | state->initialized = true; | ||
| 2769 | |||
| 2770 | if (in_buffers[1].BufferType == SECBUFFER_EXTRA) { | ||
| 2771 | memmove(state->in_buffer, | ||
| 2772 | state->in_buffer + | ||
| 2773 | (state->in_buffer_offset - in_buffers[1].cbBuffer), | ||
| 2774 | in_buffers[1].cbBuffer); | ||
| 2775 | state->in_buffer_offset = in_buffers[1].cbBuffer; | ||
| 2776 | } | ||
| 2777 | |||
| 2778 | switch (status) { | ||
| 2779 | case SEC_E_OK: { | ||
| 2780 | state->in_buffer_offset = | ||
| 2781 | (in_buffers[1].BufferType == SECBUFFER_EXTRA) | ||
| 2782 | ? in_buffers[1].cbBuffer | ||
| 2783 | : 0; | ||
| 2784 | |||
| 2785 | if (out_buffers[0].cbBuffer > 0) { | ||
| 2786 | xwrite(state->ifd, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer); | ||
| 2787 | FreeContextBuffer(out_buffers[0].pvBuffer); | ||
| 2788 | } | ||
| 2789 | |||
| 2790 | goto Success; | ||
| 2791 | } | ||
| 2792 | case SEC_I_CONTINUE_NEEDED: { | ||
| 2793 | xwrite(state->ofd, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer); | ||
| 2794 | FreeContextBuffer(out_buffers[0].pvBuffer); | ||
| 2795 | if (in_buffers[1].BufferType != SECBUFFER_EXTRA) { | ||
| 2796 | state->in_buffer_offset = 0; | ||
| 2797 | } | ||
| 2798 | break; | ||
| 2799 | } | ||
| 2800 | case SEC_E_INCOMPLETE_MESSAGE: { | ||
| 2801 | int amount_read = | ||
| 2802 | safe_read(state->ifd, state->in_buffer + state->in_buffer_offset, | ||
| 2803 | sizeof(state->in_buffer) - state->in_buffer_offset); | ||
| 2804 | if (amount_read <= 0) { | ||
| 2805 | bb_error_msg_and_die("schannel: handshake read() failed"); | ||
| 2806 | } | ||
| 2807 | |||
| 2808 | state->in_buffer_offset += amount_read; | ||
| 2809 | continue; | ||
| 2810 | } | ||
| 2811 | default: { | ||
| 2812 | bb_error_msg_and_die("schannel: handshake failed: (0x%08lx): %s", | ||
| 2813 | status, hresult_to_error_string(status)); | ||
| 2814 | } | ||
| 2815 | } | ||
| 2816 | } | ||
| 2817 | |||
| 2818 | Success: | ||
| 2819 | QueryContextAttributes( | ||
| 2820 | &state->ctx_handle, SECPKG_ATTR_STREAM_SIZES, &state->stream_sizes); | ||
| 2821 | return; | ||
| 2822 | } | ||
| 2823 | |||
| 2824 | void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags) { | ||
| 2825 | char buffer[65536]; | ||
| 2826 | |||
| 2827 | struct pollfd pfds[2]; | ||
| 2828 | |||
| 2829 | pfds[0].fd = STDIN_FILENO; | ||
| 2830 | pfds[0].events = POLLIN; | ||
| 2831 | pfds[1].fd = tls->ifd; | ||
| 2832 | pfds[1].events = POLLIN; | ||
| 2833 | |||
| 2834 | for (;;) { | ||
| 2835 | int nread; | ||
| 2836 | |||
| 2837 | if (safe_poll(pfds, 2, -1) < 0) | ||
| 2838 | bb_simple_perror_msg_and_die("poll"); | ||
| 2839 | |||
| 2840 | if (pfds[0].revents) { | ||
| 2841 | nread = safe_read(STDIN_FILENO, buffer, sizeof(buffer)); | ||
| 2842 | if (nread < 1) { | ||
| 2843 | pfds[0].fd = -1; | ||
| 2844 | tls_disconnect(tls); | ||
| 2845 | if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF) | ||
| 2846 | break; | ||
| 2847 | } else { | ||
| 2848 | tls_write(tls, buffer, nread); | ||
| 2849 | } | ||
| 2850 | } | ||
| 2851 | if (pfds[1].revents) { | ||
| 2852 | nread = tls_read(tls, buffer, sizeof(buffer)); | ||
| 2853 | if (nread < 1) { | ||
| 2854 | tls_disconnect(tls); | ||
| 2855 | break; | ||
| 2856 | } | ||
| 2857 | xwrite(STDOUT_FILENO, buffer, nread); | ||
| 2858 | } | ||
| 2859 | } | ||
| 2860 | } | ||
| 2861 | #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 | ||
| 83 | void tls_get_random(void *buf, unsigned len) FAST_FUNC; | 84 | void tls_get_random(void *buf, unsigned len) FAST_FUNC; |
| 84 | 85 | ||
| 85 | void 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)) |
| 88 | void 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( | |||
| 120 | void curve_P256_compute_pubkey_and_premaster_NEW( | 120 | void 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 */ | ||
| 645 | static 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) | |||
| 624 | static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast) | 646 | static 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/pgrep.c b/procps/pgrep.c index 9773a72c4..adbdd08b7 100644 --- a/procps/pgrep.c +++ b/procps/pgrep.c | |||
| @@ -270,16 +270,16 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv) | |||
| 270 | if (!match) { | 270 | if (!match) { |
| 271 | again: | 271 | again: |
| 272 | match = (regexec(&re_buffer, cmd, 1, re_match, 0) == 0); | 272 | match = (regexec(&re_buffer, cmd, 1, re_match, 0) == 0); |
| 273 | if (match && OPT_ANCHOR) { | ||
| 274 | /* -x requires full string match */ | ||
| 275 | match = (re_match[0].rm_so == 0 && cmd[re_match[0].rm_eo] == '\0'); | ||
| 276 | } | ||
| 273 | if (!match && cmd != proc->comm) { | 277 | if (!match && cmd != proc->comm) { |
| 274 | /* if argv[] did not match, try comm */ | 278 | /* if argv[] did not match, try comm */ |
| 275 | cmdlen = -1; | 279 | cmdlen = -1; |
| 276 | cmd = proc->comm; | 280 | cmd = proc->comm; |
| 277 | goto again; | 281 | goto again; |
| 278 | } | 282 | } |
| 279 | if (match && OPT_ANCHOR) { | ||
| 280 | /* -x requires full string match */ | ||
| 281 | match = (re_match[0].rm_so == 0 && cmd[re_match[0].rm_eo] == '\0'); | ||
| 282 | } | ||
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | /* NB: OPT_INVERT is always 0 or 1 */ | 285 | /* NB: OPT_INVERT is always 0 or 1 */ |
diff --git a/procps/pmap.c b/procps/pmap.c index 49f7688d9..0cac265c8 100644 --- a/procps/pmap.c +++ b/procps/pmap.c | |||
| @@ -29,10 +29,14 @@ | |||
| 29 | 29 | ||
| 30 | #if ULLONG_MAX == 0xffffffff | 30 | #if ULLONG_MAX == 0xffffffff |
| 31 | # define TABS "\t" | 31 | # define TABS "\t" |
| 32 | # define SIZEWIDTHx "7" | ||
| 33 | # define SIZEWIDTH "9" | ||
| 32 | # define AFMTLL "8" | 34 | # define AFMTLL "8" |
| 33 | # define DASHES "" | 35 | # define DASHES "" |
| 34 | #else | 36 | #else |
| 35 | # define TABS "\t\t" | 37 | # define TABS "\t\t" |
| 38 | # define SIZEWIDTHx "15" | ||
| 39 | # define SIZEWIDTH "17" | ||
| 36 | # define AFMTLL "16" | 40 | # define AFMTLL "16" |
| 37 | # define DASHES "--------" | 41 | # define DASHES "--------" |
| 38 | #endif | 42 | #endif |
| @@ -42,49 +46,160 @@ enum { | |||
| 42 | OPT_q = 1 << 1, | 46 | OPT_q = 1 << 1, |
| 43 | }; | 47 | }; |
| 44 | 48 | ||
| 45 | static void print_smaprec(struct smaprec *currec, void *data) | 49 | struct 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 | |||
| 69 | static void print_smaprec(struct smaprec *currec) | ||
| 70 | { | ||
| 49 | printf("%0" AFMTLL "llx ", currec->smap_start); | 71 | printf("%0" AFMTLL "llx ", currec->smap_start); |
| 50 | 72 | ||
| 51 | if (opt & OPT_x) | 73 | if (option_mask32 & OPT_x) |
| 52 | printf("%7lu %7lu %7lu %7lu ", | 74 | printf("%7llu %7lu %7lu %7lu ", |
| 53 | currec->smap_size, | 75 | currec->smap_size, |
| 54 | currec->smap_pss, | 76 | currec->smap_pss, |
| 55 | currec->private_dirty, | 77 | currec->private_dirty, |
| 56 | currec->smap_swap); | 78 | currec->smap_swap); |
| 57 | else | 79 | else |
| 58 | printf("%7luK", currec->smap_size); | 80 | printf("%7lluK", currec->smap_size); |
| 81 | |||
| 82 | printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]"); | ||
| 83 | } | ||
| 84 | |||
| 85 | /* libbb's procps_read_smaps() looks somewhat similar, | ||
| 86 | * but the collected information is sufficiently different | ||
| 87 | * that merging them into one function is not a good idea | ||
| 88 | * (unless you feel masochistic today). | ||
| 89 | */ | ||
| 90 | static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total) | ||
| 91 | { | ||
| 92 | FILE *file; | ||
| 93 | struct smaprec currec; | ||
| 94 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; | ||
| 95 | |||
| 96 | sprintf(filename, "/proc/%u/smaps", (int)pid); | ||
| 97 | |||
| 98 | file = fopen_for_read(filename); | ||
| 99 | if (!file) | ||
| 100 | return 1; | ||
| 101 | |||
| 102 | memset(&currec, 0, sizeof(currec)); | ||
| 103 | while (fgets(buf, PMAP_BUFSZ, file)) { | ||
| 104 | // Each mapping datum has this form: | ||
| 105 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | ||
| 106 | // Size: nnn kB | ||
| 107 | // Rss: nnn kB | ||
| 108 | // ..... | ||
| 109 | |||
| 110 | char *tp, *p; | ||
| 111 | |||
| 112 | #define bytes4 *(uint32_t*)buf | ||
| 113 | #define Pss_ PACK32_BYTES('P','s','s',':') | ||
| 114 | #define Swap PACK32_BYTES('S','w','a','p') | ||
| 115 | #define Priv PACK32_BYTES('P','r','i','v') | ||
| 116 | #define FETCH(X, N) \ | ||
| 117 | tp = skip_whitespace(buf+4 + (N)); \ | ||
| 118 | total->X += currec.X = fast_strtoul_10(&tp); \ | ||
| 119 | continue | ||
| 120 | #define SCAN(S, X) \ | ||
| 121 | if (memcmp(buf+4, S, sizeof(S)-1) == 0) { \ | ||
| 122 | FETCH(X, sizeof(S)-1); \ | ||
| 123 | } | ||
| 124 | if (bytes4 == Pss_) { | ||
| 125 | FETCH(smap_pss, 0); | ||
| 126 | } | ||
| 127 | if (bytes4 == Swap && buf[4] == ':') { | ||
| 128 | FETCH(smap_swap, 1); | ||
| 129 | } | ||
| 130 | if (bytes4 == Priv) { | ||
| 131 | SCAN("ate_Dirty:", private_dirty); | ||
| 132 | } | ||
| 133 | #undef bytes4 | ||
| 134 | #undef Pss_ | ||
| 135 | #undef Swap | ||
| 136 | #undef Priv | ||
| 137 | #undef FETCH | ||
| 138 | #undef SCAN | ||
| 139 | tp = strchr(buf, '-'); | ||
| 140 | if (tp) { | ||
| 141 | // We reached next mapping - the line of this form: | ||
| 142 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | ||
| 143 | |||
| 144 | // If we have a previous record, there's nothing more | ||
| 145 | // for it, print and clear currec | ||
| 146 | if (currec.smap_size) | ||
| 147 | print_smaprec(&currec); | ||
| 148 | free(currec.smap_name); | ||
| 149 | memset(&currec, 0, sizeof(currec)); | ||
| 150 | |||
| 151 | *tp = ' '; | ||
| 152 | tp = buf; | ||
| 153 | currec.smap_start = fast_strtoull_16(&tp); | ||
| 154 | currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; | ||
| 155 | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); | ||
| 156 | |||
| 157 | // skipping "rw-s FILEOFS M:m INODE " | ||
| 158 | tp = skip_fields(tp, 4); | ||
| 159 | tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++" | ||
| 160 | p = strchrnul(tp, '\n'); | ||
| 161 | if (p != tp) { | ||
| 162 | currec.smap_name = xstrndup(tp, p - tp); | ||
| 163 | } | ||
| 164 | total->smap_size += currec.smap_size; | ||
| 165 | } | ||
| 166 | } // while (got line) | ||
| 167 | fclose(file); | ||
| 168 | |||
| 169 | if (currec.smap_size) | ||
| 170 | print_smaprec(&currec); | ||
| 171 | free(currec.smap_name); | ||
| 59 | 172 | ||
| 60 | printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); | 173 | return 0; |
| 61 | } | 174 | } |
| 62 | 175 | ||
| 63 | static int procps_get_maps(pid_t pid, unsigned opt) | 176 | static int procps_get_maps(pid_t pid, unsigned opt) |
| 64 | { | 177 | { |
| 65 | struct smaprec total; | 178 | struct smaprec total; |
| 66 | int ret; | 179 | int ret; |
| 67 | char buf[256]; | 180 | char buf[PMAP_BUFSZ] ALIGN4; |
| 181 | |||
| 182 | ret = read_cmdline(buf, sizeof(buf), pid, NULL); | ||
| 183 | if (ret < 0) | ||
| 184 | return ret; | ||
| 68 | 185 | ||
| 69 | read_cmdline(buf, sizeof(buf), pid, NULL); | ||
| 70 | printf("%u: %s\n", (int)pid, buf); | 186 | printf("%u: %s\n", (int)pid, buf); |
| 71 | 187 | ||
| 72 | if (!(opt & OPT_q) && (opt & OPT_x)) | 188 | if (!(opt & OPT_q) && (opt & OPT_x)) |
| 73 | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); | 189 | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); |
| 74 | 190 | ||
| 75 | memset(&total, 0, sizeof(total)); | 191 | memset(&total, 0, sizeof(total)); |
| 76 | 192 | ret = read_smaps(pid, buf, &total); | |
| 77 | ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt); | ||
| 78 | if (ret) | 193 | if (ret) |
| 79 | return ret; | 194 | return ret; |
| 80 | 195 | ||
| 81 | if (!(opt & OPT_q)) { | 196 | if (!(opt & OPT_q)) { |
| 82 | if (opt & OPT_x) | 197 | if (opt & OPT_x) |
| 83 | printf("--------" DASHES " ------ ------ ------ ------\n" | 198 | printf("--------" DASHES " ------ ------ ------ ------\n" |
| 84 | "total" TABS " %7lu %7lu %7lu %7lu\n", | 199 | "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n", |
| 85 | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); | 200 | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); |
| 86 | else | 201 | else |
| 87 | printf("mapped: %luK\n", total.smap_size); | 202 | printf(" total %"SIZEWIDTH"lluK\n", total.smap_size); |
| 88 | } | 203 | } |
| 89 | 204 | ||
| 90 | return 0; | 205 | return 0; |
diff --git a/procps/top.c b/procps/top.c index 09d31c673..7902e82f0 100644 --- a/procps/top.c +++ b/procps/top.c | |||
| @@ -117,10 +117,15 @@ | |||
| 117 | 117 | ||
| 118 | #include "libbb.h" | 118 | #include "libbb.h" |
| 119 | 119 | ||
| 120 | #define ESC "\033" | 120 | #define ESC "\033" |
| 121 | #define HOME ESC"[H" | ||
| 122 | #define CLREOS ESC"[J" | ||
| 123 | #define CLREOL ESC"[K" | ||
| 124 | #define REVERSE ESC"[7m" | ||
| 125 | #define NORMAL ESC"[m" | ||
| 121 | 126 | ||
| 122 | typedef struct top_status_t { | 127 | typedef struct top_status_t { |
| 123 | unsigned long vsz; | 128 | unsigned long memsize; |
| 124 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 129 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| 125 | unsigned long ticks; | 130 | unsigned long ticks; |
| 126 | unsigned pcpu; /* delta of ticks */ | 131 | unsigned pcpu; /* delta of ticks */ |
| @@ -161,13 +166,16 @@ struct globals { | |||
| 161 | top_status_t *top; | 166 | top_status_t *top; |
| 162 | int ntop; | 167 | int ntop; |
| 163 | smallint inverted; | 168 | smallint inverted; |
| 169 | smallint first_line_printed; | ||
| 164 | #if ENABLE_FEATURE_TOPMEM | 170 | #if ENABLE_FEATURE_TOPMEM |
| 165 | smallint sort_field; | 171 | smallint sort_field; |
| 166 | #endif | 172 | #endif |
| 167 | #if ENABLE_FEATURE_TOP_SMP_CPU | 173 | #if ENABLE_FEATURE_TOP_SMP_CPU |
| 168 | smallint smp_cpu_info; /* one/many cpu info lines? */ | 174 | smallint smp_cpu_info; /* one/many cpu info lines? */ |
| 169 | #endif | 175 | #endif |
| 170 | unsigned lines; /* screen height */ | 176 | int lines_remaining; |
| 177 | unsigned lines; /* screen height */ | ||
| 178 | unsigned scr_width; /* width, clamped <= LINE_BUF_SIZE-2 */ | ||
| 171 | #if ENABLE_FEATURE_TOP_INTERACTIVE | 179 | #if ENABLE_FEATURE_TOP_INTERACTIVE |
| 172 | struct termios initial_settings; | 180 | struct termios initial_settings; |
| 173 | int scroll_ofs; | 181 | int scroll_ofs; |
| @@ -212,7 +220,6 @@ struct globals { | |||
| 212 | #define cpu_prev_jif (G.cpu_prev_jif ) | 220 | #define cpu_prev_jif (G.cpu_prev_jif ) |
| 213 | #define num_cpus (G.num_cpus ) | 221 | #define num_cpus (G.num_cpus ) |
| 214 | #define total_pcpu (G.total_pcpu ) | 222 | #define total_pcpu (G.total_pcpu ) |
| 215 | #define line_buf (G.line_buf ) | ||
| 216 | #define INIT_G() do { \ | 223 | #define INIT_G() do { \ |
| 217 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 224 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
| 218 | BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ | 225 | BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ |
| @@ -241,8 +248,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q) | |||
| 241 | static int mem_sort(top_status_t *P, top_status_t *Q) | 248 | static int mem_sort(top_status_t *P, top_status_t *Q) |
| 242 | { | 249 | { |
| 243 | /* We want to avoid unsigned->signed and truncation errors */ | 250 | /* We want to avoid unsigned->signed and truncation errors */ |
| 244 | if (Q->vsz < P->vsz) return -1; | 251 | if (Q->memsize < P->memsize) return -1; |
| 245 | return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ | 252 | return Q->memsize != P->memsize; /* 0 if ==, 1 if > */ |
| 246 | } | 253 | } |
| 247 | 254 | ||
| 248 | 255 | ||
| @@ -283,9 +290,9 @@ static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) | |||
| 283 | #endif | 290 | #endif |
| 284 | int ret; | 291 | int ret; |
| 285 | 292 | ||
| 286 | if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) | 293 | if (!fgets(G.line_buf, LINE_BUF_SIZE, fp) || G.line_buf[0] != 'c' /* not "cpu" */) |
| 287 | return 0; | 294 | return 0; |
| 288 | ret = sscanf(line_buf, fmt, | 295 | ret = sscanf(G.line_buf, fmt, |
| 289 | &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, | 296 | &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, |
| 290 | &p_jif->iowait, &p_jif->irq, &p_jif->softirq, | 297 | &p_jif->iowait, &p_jif->irq, &p_jif->softirq, |
| 291 | &p_jif->steal); | 298 | &p_jif->steal); |
| @@ -362,7 +369,7 @@ static void do_stats(void) | |||
| 362 | 369 | ||
| 363 | get_jiffy_counts(); | 370 | get_jiffy_counts(); |
| 364 | total_pcpu = 0; | 371 | total_pcpu = 0; |
| 365 | /* total_vsz = 0; */ | 372 | /* total_memsize = 0; */ |
| 366 | new_hist = xmalloc(sizeof(new_hist[0]) * ntop); | 373 | new_hist = xmalloc(sizeof(new_hist[0]) * ntop); |
| 367 | /* | 374 | /* |
| 368 | * Make a pass through the data to get stats. | 375 | * Make a pass through the data to get stats. |
| @@ -394,7 +401,7 @@ static void do_stats(void) | |||
| 394 | i = (i+1) % prev_hist_count; | 401 | i = (i+1) % prev_hist_count; |
| 395 | /* hist_iterations++; */ | 402 | /* hist_iterations++; */ |
| 396 | } while (i != last_i); | 403 | } while (i != last_i); |
| 397 | /* total_vsz += cur->vsz; */ | 404 | /* total_memsize += cur->memsize; */ |
| 398 | } | 405 | } |
| 399 | 406 | ||
| 400 | /* | 407 | /* |
| @@ -407,6 +414,39 @@ static void do_stats(void) | |||
| 407 | 414 | ||
| 408 | #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ | 415 | #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ |
| 409 | 416 | ||
| 417 | static void print_line_buf(void) | ||
| 418 | { | ||
| 419 | const char *fmt; | ||
| 420 | |||
| 421 | G.lines_remaining--; | ||
| 422 | fmt = OPT_BATCH_MODE ? "\n""%.*s" : "\n""%.*s"CLREOL; | ||
| 423 | if (!G.first_line_printed) { | ||
| 424 | G.first_line_printed = 1; | ||
| 425 | /* Go to top */ | ||
| 426 | fmt = OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL; | ||
| 427 | } | ||
| 428 | printf(fmt, G.scr_width - 1, G.line_buf); | ||
| 429 | } | ||
| 430 | |||
| 431 | static void print_line_bold(void) | ||
| 432 | { | ||
| 433 | G.lines_remaining--; | ||
| 434 | //we never print first line in bold | ||
| 435 | // if (!G.first_line_printed) { | ||
| 436 | // printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf); | ||
| 437 | // G.first_line_printed = 1; | ||
| 438 | // } else { | ||
| 439 | printf(OPT_BATCH_MODE ? "\n""%.*s" : "\n"REVERSE"%.*s"NORMAL CLREOL, G.scr_width - 1, G.line_buf); | ||
| 440 | // } | ||
| 441 | } | ||
| 442 | |||
| 443 | static void print_end(void) | ||
| 444 | { | ||
| 445 | fputs_stdout(OPT_BATCH_MODE ? "\n" : CLREOS"\r"); | ||
| 446 | /* next print will be "first line" (will clear the screen) */ | ||
| 447 | G.first_line_printed = 0; | ||
| 448 | } | ||
| 449 | |||
| 410 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS | 450 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS |
| 411 | /* formats 7 char string (8 with terminating NUL) */ | 451 | /* formats 7 char string (8 with terminating NUL) */ |
| 412 | static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) | 452 | static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) |
| @@ -433,7 +473,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) | |||
| 433 | #endif | 473 | #endif |
| 434 | 474 | ||
| 435 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS | 475 | #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS |
| 436 | static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | 476 | static void display_cpus(void) |
| 437 | { | 477 | { |
| 438 | /* | 478 | /* |
| 439 | * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% | 479 | * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% |
| @@ -469,8 +509,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
| 469 | # else | 509 | # else |
| 470 | /* Loop thru CPU(s) */ | 510 | /* Loop thru CPU(s) */ |
| 471 | n_cpu_lines = smp_cpu_info ? num_cpus : 1; | 511 | n_cpu_lines = smp_cpu_info ? num_cpus : 1; |
| 472 | if (n_cpu_lines > *lines_rem_p) | 512 | if (n_cpu_lines > G.lines_remaining) |
| 473 | n_cpu_lines = *lines_rem_p; | 513 | n_cpu_lines = G.lines_remaining; |
| 474 | 514 | ||
| 475 | for (i = 0; i < n_cpu_lines; i++) { | 515 | for (i = 0; i < n_cpu_lines; i++) { |
| 476 | p_jif = &cpu_jif[i]; | 516 | p_jif = &cpu_jif[i]; |
| @@ -488,7 +528,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
| 488 | CALC_STAT(softirq); | 528 | CALC_STAT(softirq); |
| 489 | /*CALC_STAT(steal);*/ | 529 | /*CALC_STAT(steal);*/ |
| 490 | 530 | ||
| 491 | snprintf(scrbuf, scr_width, | 531 | sprintf(G.line_buf, |
| 492 | /* Barely fits in 79 chars when in "decimals" mode. */ | 532 | /* Barely fits in 79 chars when in "decimals" mode. */ |
| 493 | # if ENABLE_FEATURE_TOP_SMP_CPU | 533 | # if ENABLE_FEATURE_TOP_SMP_CPU |
| 494 | "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", | 534 | "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", |
| @@ -501,16 +541,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) | |||
| 501 | /*, SHOW_STAT(steal) - what is this 'steal' thing? */ | 541 | /*, SHOW_STAT(steal) - what is this 'steal' thing? */ |
| 502 | /* I doubt anyone wants to know it */ | 542 | /* I doubt anyone wants to know it */ |
| 503 | ); | 543 | ); |
| 504 | puts(scrbuf); | 544 | print_line_buf(); |
| 505 | } | 545 | } |
| 506 | } | 546 | } |
| 507 | # undef SHOW_STAT | 547 | # undef SHOW_STAT |
| 508 | # undef CALC_STAT | 548 | # undef CALC_STAT |
| 509 | # undef FMT | 549 | # undef FMT |
| 510 | *lines_rem_p -= i; | ||
| 511 | } | 550 | } |
| 512 | #else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ | 551 | #else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ |
| 513 | # define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) | 552 | # define display_cpus() ((void)0) |
| 514 | #endif | 553 | #endif |
| 515 | 554 | ||
| 516 | enum { | 555 | enum { |
| @@ -564,52 +603,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX]) | |||
| 564 | fclose(f); | 603 | fclose(f); |
| 565 | } | 604 | } |
| 566 | 605 | ||
| 567 | static unsigned long display_header(int scr_width, int *lines_rem_p) | 606 | static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm) |
| 607 | { | ||
| 608 | int width = G.scr_width - offset; | ||
| 609 | if (width > 1) /* wider than to fit just the NUL? */ | ||
| 610 | read_cmdline(G.line_buf + offset, width, pid, comm); | ||
| 611 | //TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e | ||
| 612 | print_line_buf(); | ||
| 613 | } | ||
| 614 | |||
| 615 | static unsigned long display_header(void) | ||
| 568 | { | 616 | { |
| 569 | char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */ | ||
| 570 | char *buf; | 617 | char *buf; |
| 571 | unsigned long meminfo[MI_MAX]; | 618 | unsigned long meminfo[MI_MAX]; |
| 572 | 619 | ||
| 573 | parse_meminfo(meminfo); | 620 | parse_meminfo(meminfo); |
| 574 | 621 | ||
| 575 | /* Output memory info */ | 622 | /* Output memory info */ |
| 576 | if (scr_width > (int)sizeof(scrbuf)) | 623 | sprintf(G.line_buf, |
| 577 | scr_width = sizeof(scrbuf); | ||
| 578 | snprintf(scrbuf, scr_width, | ||
| 579 | "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", | 624 | "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", |
| 580 | meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], | 625 | meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], |
| 581 | meminfo[MI_MEMFREE], | 626 | meminfo[MI_MEMFREE], |
| 582 | meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], | 627 | meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], |
| 583 | meminfo[MI_BUFFERS], | 628 | meminfo[MI_BUFFERS], |
| 584 | meminfo[MI_CACHED]); | 629 | meminfo[MI_CACHED]); |
| 585 | /* Go to top & clear to the end of screen */ | 630 | print_line_buf(); |
| 586 | printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf); | ||
| 587 | (*lines_rem_p)--; | ||
| 588 | 631 | ||
| 589 | /* Display CPU time split as percentage of total time. | 632 | /* Display CPU time split as percentage of total time. |
| 590 | * This displays either a cumulative line or one line per CPU. | 633 | * This displays either a cumulative line or one line per CPU. |
| 591 | */ | 634 | */ |
| 592 | display_cpus(scr_width, scrbuf, lines_rem_p); | 635 | display_cpus(); |
| 593 | 636 | ||
| 594 | /* Read load average as a string */ | 637 | /* Read load average as a string */ |
| 595 | buf = stpcpy(scrbuf, "Load average: "); | 638 | buf = stpcpy(G.line_buf, "Load average: "); |
| 596 | open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); | 639 | open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: ")); |
| 597 | scrbuf[scr_width - 1] = '\0'; | 640 | G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */ |
| 598 | strchrnul(buf, '\n')[0] = '\0'; | 641 | strchrnul(buf, '\n')[0] = '\0'; |
| 599 | puts(scrbuf); | 642 | print_line_buf(); |
| 600 | (*lines_rem_p)--; | ||
| 601 | 643 | ||
| 602 | return meminfo[MI_MEMTOTAL]; | 644 | return meminfo[MI_MEMTOTAL]; |
| 603 | } | 645 | } |
| 604 | 646 | ||
| 605 | static NOINLINE void display_process_list(int lines_rem, int scr_width) | 647 | static NOINLINE void display_process_list(void) |
| 606 | { | 648 | { |
| 607 | enum { | 649 | enum { |
| 608 | BITS_PER_INT = sizeof(int) * 8 | 650 | BITS_PER_INT = sizeof(int) * 8 |
| 609 | }; | 651 | }; |
| 610 | 652 | ||
| 611 | top_status_t *s; | 653 | top_status_t *s; |
| 612 | unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ | 654 | unsigned long total_memory = display_header(); |
| 613 | /* xxx_shift and xxx_scale variables allow us to replace | 655 | /* xxx_shift and xxx_scale variables allow us to replace |
| 614 | * expensive divides with multiply and shift */ | 656 | * expensive divides with multiply and shift */ |
| 615 | unsigned pmem_shift, pmem_scale, pmem_half; | 657 | unsigned pmem_shift, pmem_scale, pmem_half; |
| @@ -621,7 +663,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) | |||
| 621 | 663 | ||
| 622 | #if ENABLE_FEATURE_TOP_DECIMALS | 664 | #if ENABLE_FEATURE_TOP_DECIMALS |
| 623 | # define UPSCALE 1000 | 665 | # define UPSCALE 1000 |
| 624 | typedef struct { unsigned quot, rem; } bb_div_t; | 666 | typedef struct { unsigned quot, rem; } bb_div_t; |
| 625 | /* Used to have "div_t name = div((val), 10)" here | 667 | /* Used to have "div_t name = div((val), 10)" here |
| 626 | * (IOW: intended to use libc-compatible way to divide and use | 668 | * (IOW: intended to use libc-compatible way to divide and use |
| 627 | * both result and remainder, but musl does not inline div()...) | 669 | * both result and remainder, but musl does not inline div()...) |
| @@ -629,28 +671,34 @@ typedef struct { unsigned quot, rem; } bb_div_t; | |||
| 629 | */ | 671 | */ |
| 630 | # define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } | 672 | # define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } |
| 631 | # define SHOW_STAT(name) name.quot, '0'+name.rem | 673 | # define SHOW_STAT(name) name.quot, '0'+name.rem |
| 674 | # define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0') | ||
| 632 | # define FMT "%3u.%c" | 675 | # define FMT "%3u.%c" |
| 633 | #else | 676 | #else |
| 634 | # define UPSCALE 100 | 677 | # define UPSCALE 100 |
| 635 | # define CALC_STAT(name, val) unsigned name = (val) | 678 | # define CALC_STAT(name, val) unsigned name = (val) |
| 679 | # define SANITIZE(name) if (name > 99) name = 99 | ||
| 636 | # define SHOW_STAT(name) name | 680 | # define SHOW_STAT(name) name |
| 637 | # define FMT "%4u%%" | 681 | # define FMT "%4u%%" |
| 638 | #endif | 682 | #endif |
| 639 | 683 | ||
| 640 | /* what info of the processes is shown */ | 684 | strcpy(G.line_buf, " PID PPID USER STAT RSS %RSS" |
| 641 | printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, | ||
| 642 | " PID PPID USER STAT VSZ %VSZ" | ||
| 643 | IF_FEATURE_TOP_SMP_PROCESS(" CPU") | 685 | IF_FEATURE_TOP_SMP_PROCESS(" CPU") |
| 644 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") | 686 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") |
| 645 | " COMMAND"); | 687 | " COMMAND"); |
| 646 | lines_rem--; | 688 | print_line_bold(); |
| 647 | 689 | ||
| 648 | /* | 690 | /* %RSS = s->memsize / MemTotal * 100% |
| 649 | * %VSZ = s->vsz/MemTotal | 691 | * Calculate this with multiply and shift. Example: |
| 692 | * shift = 12 | ||
| 693 | * scale = 100 * 0x1000 / total_memory | ||
| 694 | * percent_mem = (size_mem * scale) >> shift | ||
| 695 | * ~= (size_mem >> shift) * scale | ||
| 696 | * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory | ||
| 697 | * ~= size_mem * 100 / total_memory | ||
| 650 | */ | 698 | */ |
| 651 | pmem_shift = BITS_PER_INT-11; | 699 | pmem_shift = BITS_PER_INT-11; |
| 652 | pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; | 700 | pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; |
| 653 | /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ | 701 | /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */ |
| 654 | while (pmem_scale >= 512) { | 702 | while (pmem_scale >= 512) { |
| 655 | pmem_scale /= 4; | 703 | pmem_scale /= 4; |
| 656 | pmem_shift -= 2; | 704 | pmem_shift -= 2; |
| @@ -689,25 +737,29 @@ typedef struct { unsigned quot, rem; } bb_div_t; | |||
| 689 | pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); | 737 | pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); |
| 690 | /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ | 738 | /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ |
| 691 | #endif | 739 | #endif |
| 740 | if (G.lines_remaining > ntop - G_scroll_ofs) | ||
| 741 | G.lines_remaining = ntop - G_scroll_ofs; | ||
| 692 | 742 | ||
| 693 | /* Ok, all preliminary data is ready, go through the list */ | 743 | /* Ok, all preliminary data is ready, go through the list */ |
| 694 | scr_width += 2; /* account for leading '\n' and trailing NUL */ | ||
| 695 | if (lines_rem > ntop - G_scroll_ofs) | ||
| 696 | lines_rem = ntop - G_scroll_ofs; | ||
| 697 | s = top + G_scroll_ofs; | 744 | s = top + G_scroll_ofs; |
| 698 | while (--lines_rem >= 0) { | 745 | while (G.lines_remaining > 0) { |
| 699 | int n; | 746 | int n; |
| 700 | char *ppu; | 747 | char *ppu; |
| 701 | char ppubuf[sizeof(int)*3 * 2 + 12]; | 748 | char ppubuf[sizeof(int)*3 * 2 + 12]; |
| 702 | char vsz_str_buf[8]; | 749 | char memsize_str_buf[8]; |
| 703 | unsigned col; | 750 | unsigned col; |
| 704 | 751 | ||
| 705 | CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); | 752 | CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift); |
| 706 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 753 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| 707 | CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); | 754 | CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); |
| 708 | #endif | 755 | #endif |
| 756 | /* VSZ can be much larger than total memory | ||
| 757 | * (seen values close to 2Tbyte), don't try to display | ||
| 758 | * "uses 12345.6% of MemTotal" (won't fit the column) | ||
| 759 | */ | ||
| 760 | SANITIZE(pmem); | ||
| 709 | 761 | ||
| 710 | smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); | 762 | smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy"); |
| 711 | /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ | 763 | /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ |
| 712 | n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); | 764 | n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); |
| 713 | ppu = ppubuf; | 765 | ppu = ppubuf; |
| @@ -736,27 +788,23 @@ typedef struct { unsigned quot, rem; } bb_div_t; | |||
| 736 | ppu[6+6+8] = '\0'; /* truncate USER */ | 788 | ppu[6+6+8] = '\0'; /* truncate USER */ |
| 737 | } | 789 | } |
| 738 | shortened: | 790 | shortened: |
| 739 | col = snprintf(line_buf, scr_width, | 791 | col = sprintf(G.line_buf, |
| 740 | "\n" "%s %s %.5s" FMT | 792 | "%s %s %.5s" FMT |
| 741 | IF_FEATURE_TOP_SMP_PROCESS(" %3d") | 793 | IF_FEATURE_TOP_SMP_PROCESS(" %3d") |
| 742 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) | 794 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) |
| 743 | " ", | 795 | " ", |
| 744 | ppu, | 796 | ppu, |
| 745 | s->state, vsz_str_buf, | 797 | s->state, memsize_str_buf, |
| 746 | SHOW_STAT(pmem) | 798 | SHOW_STAT(pmem) |
| 747 | IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) | 799 | IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) |
| 748 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) | 800 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) |
| 749 | ); | 801 | ); |
| 750 | if ((int)(scr_width - col) > 1) | 802 | cmdline_to_line_buf_and_print(col, s->pid, s->comm); |
| 751 | read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm); | ||
| 752 | fputs_stdout(line_buf); | ||
| 753 | /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, | 803 | /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, |
| 754 | cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ | 804 | cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ |
| 755 | s++; | 805 | s++; |
| 756 | } | 806 | } |
| 757 | /* printf(" %d", hist_iterations); */ | 807 | /* printf(" %d", hist_iterations); */ |
| 758 | bb_putchar(OPT_BATCH_MODE ? '\n' : '\r'); | ||
| 759 | fflush_all(); | ||
| 760 | } | 808 | } |
| 761 | #undef UPSCALE | 809 | #undef UPSCALE |
| 762 | #undef SHOW_STAT | 810 | #undef SHOW_STAT |
| @@ -828,36 +876,34 @@ static int topmem_sort(char *a, char *b) | |||
| 828 | } | 876 | } |
| 829 | 877 | ||
| 830 | /* display header info (meminfo / loadavg) */ | 878 | /* display header info (meminfo / loadavg) */ |
| 831 | static void display_topmem_header(int scr_width, int *lines_rem_p) | 879 | static void display_topmem_header(void) |
| 832 | { | 880 | { |
| 833 | unsigned long meminfo[MI_MAX]; | 881 | unsigned long meminfo[MI_MAX]; |
| 834 | 882 | ||
| 835 | parse_meminfo(meminfo); | 883 | parse_meminfo(meminfo); |
| 836 | 884 | ||
| 837 | snprintf(line_buf, LINE_BUF_SIZE, | 885 | sprintf(G.line_buf, |
| 838 | "Mem total:%lu anon:%lu map:%lu free:%lu", | 886 | "Mem total:%lu anon:%lu map:%lu free:%lu", |
| 839 | meminfo[MI_MEMTOTAL], | 887 | meminfo[MI_MEMTOTAL], |
| 840 | meminfo[MI_ANONPAGES], | 888 | meminfo[MI_ANONPAGES], |
| 841 | meminfo[MI_MAPPED], | 889 | meminfo[MI_MAPPED], |
| 842 | meminfo[MI_MEMFREE]); | 890 | meminfo[MI_MEMFREE]); |
| 843 | printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); | 891 | print_line_buf(); |
| 844 | 892 | ||
| 845 | snprintf(line_buf, LINE_BUF_SIZE, | 893 | sprintf(G.line_buf, |
| 846 | " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", | 894 | " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", |
| 847 | meminfo[MI_SLAB], | 895 | meminfo[MI_SLAB], |
| 848 | meminfo[MI_BUFFERS], | 896 | meminfo[MI_BUFFERS], |
| 849 | meminfo[MI_CACHED], | 897 | meminfo[MI_CACHED], |
| 850 | meminfo[MI_DIRTY], | 898 | meminfo[MI_DIRTY], |
| 851 | meminfo[MI_WRITEBACK]); | 899 | meminfo[MI_WRITEBACK]); |
| 852 | printf("%.*s\n", scr_width, line_buf); | 900 | print_line_buf(); |
| 853 | 901 | ||
| 854 | snprintf(line_buf, LINE_BUF_SIZE, | 902 | sprintf(G.line_buf, |
| 855 | "Swap total:%lu free:%lu", // TODO: % used? | 903 | "Swap total:%lu free:%lu", // TODO: % used? |
| 856 | meminfo[MI_SWAPTOTAL], | 904 | meminfo[MI_SWAPTOTAL], |
| 857 | meminfo[MI_SWAPFREE]); | 905 | meminfo[MI_SWAPFREE]); |
| 858 | printf("%.*s\n", scr_width, line_buf); | 906 | print_line_buf(); |
| 859 | |||
| 860 | (*lines_rem_p) -= 3; | ||
| 861 | } | 907 | } |
| 862 | 908 | ||
| 863 | /* see http://en.wikipedia.org/wiki/Tera */ | 909 | /* see http://en.wikipedia.org/wiki/Tera */ |
| @@ -870,75 +916,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5]) | |||
| 870 | smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; | 916 | smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; |
| 871 | } | 917 | } |
| 872 | 918 | ||
| 873 | static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) | 919 | static NOINLINE void display_topmem_process_list(void) |
| 874 | { | 920 | { |
| 875 | #define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK" | ||
| 876 | #define MIN_WIDTH sizeof(HDR_STR) | ||
| 877 | const topmem_status_t *s = topmem + G_scroll_ofs; | 921 | const topmem_status_t *s = topmem + G_scroll_ofs; |
| 878 | char *cp, ch; | 922 | char *cp, ch; |
| 879 | 923 | ||
| 880 | display_topmem_header(scr_width, &lines_rem); | 924 | display_topmem_header(); |
| 881 | 925 | ||
| 882 | strcpy(line_buf, HDR_STR " COMMAND"); | 926 | strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND"); |
| 883 | /* Mark the ^FIELD^ we sort by */ | 927 | /* Mark the ^FIELD^ we sort by */ |
| 884 | cp = &line_buf[5 + sort_field * 6]; | 928 | cp = &G.line_buf[5 + sort_field * 6]; |
| 885 | ch = "^_"[inverted]; | 929 | ch = "^_"[inverted]; |
| 886 | cp[6] = ch; | 930 | cp[6] = ch; |
| 887 | do *cp++ = ch; while (*cp == ' '); | 931 | do *cp++ = ch; while (*cp == ' '); |
| 932 | print_line_bold(); | ||
| 888 | 933 | ||
| 889 | printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); | 934 | if (G.lines_remaining > ntop - G_scroll_ofs) |
| 890 | lines_rem--; | 935 | G.lines_remaining = ntop - G_scroll_ofs; |
| 891 | 936 | while (G.lines_remaining > 0) { | |
| 892 | if (lines_rem > ntop - G_scroll_ofs) | ||
| 893 | lines_rem = ntop - G_scroll_ofs; | ||
| 894 | while (--lines_rem >= 0) { | ||
| 895 | /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ | 937 | /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ |
| 896 | int n = sprintf(line_buf, "%5u ", s->pid); | 938 | int n = sprintf(G.line_buf, "%5u ", s->pid); |
| 897 | if (n > 7) { | 939 | if (n > 7) { |
| 898 | /* PID is 7 chars long (up to 4194304) */ | 940 | /* PID is 7 chars long (up to 4194304) */ |
| 899 | ulltoa4_and_space(s->vsz , &line_buf[8]); | 941 | ulltoa4_and_space(s->vsz , &G.line_buf[8]); |
| 900 | ulltoa4_and_space(s->vszrw, &line_buf[8+5]); | 942 | ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]); |
| 901 | /* the next field (RSS) starts at 8+10 = 3*6 */ | 943 | /* the next field (RSS) starts at 8+10 = 3*6 */ |
| 902 | } else { | 944 | } else { |
| 903 | if (n == 7) /* PID is 6 chars long */ | 945 | if (n == 7) /* PID is 6 chars long */ |
| 904 | ulltoa4_and_space(s->vsz, &line_buf[7]); | 946 | ulltoa4_and_space(s->vsz, &G.line_buf[7]); |
| 905 | /* the next field (VSZRW) starts at 7+5 = 2*6 */ | 947 | /* the next field (VSZRW) starts at 7+5 = 2*6 */ |
| 906 | else /* PID is 5 chars or less */ | 948 | else /* PID is 5 chars or less */ |
| 907 | ulltoa5_and_space(s->vsz, &line_buf[6]); | 949 | ulltoa5_and_space(s->vsz, &G.line_buf[6]); |
| 908 | ulltoa5_and_space(s->vszrw, &line_buf[2*6]); | 950 | ulltoa5_and_space(s->vszrw, &G.line_buf[2*6]); |
| 909 | } | ||
| 910 | ulltoa5_and_space(s->rss , &line_buf[3*6]); | ||
| 911 | ulltoa5_and_space(s->rss_sh , &line_buf[4*6]); | ||
| 912 | ulltoa5_and_space(s->dirty , &line_buf[5*6]); | ||
| 913 | ulltoa5_and_space(s->dirty_sh, &line_buf[6*6]); | ||
| 914 | ulltoa5_and_space(s->stack , &line_buf[7*6]); | ||
| 915 | line_buf[8*6] = '\0'; | ||
| 916 | if (scr_width > (int)MIN_WIDTH) { | ||
| 917 | read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm); | ||
| 918 | } | 951 | } |
| 919 | printf("\n""%.*s", scr_width, line_buf); | 952 | ulltoa5_and_space(s->rss , &G.line_buf[3*6]); |
| 953 | ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]); | ||
| 954 | ulltoa5_and_space(s->dirty , &G.line_buf[5*6]); | ||
| 955 | ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]); | ||
| 956 | ulltoa5_and_space(s->stack , &G.line_buf[7*6]); | ||
| 957 | G.line_buf[8*6] = '\0'; | ||
| 958 | cmdline_to_line_buf_and_print(8*6, s->pid, s->comm); | ||
| 920 | s++; | 959 | s++; |
| 921 | } | 960 | } |
| 922 | bb_putchar(OPT_BATCH_MODE ? '\n' : '\r'); | ||
| 923 | fflush_all(); | ||
| 924 | #undef HDR_STR | ||
| 925 | #undef MIN_WIDTH | ||
| 926 | } | 961 | } |
| 927 | 962 | ||
| 928 | #else | 963 | #endif /* end TOPMEM support */ |
| 929 | void display_topmem_process_list(int lines_rem, int scr_width); | ||
| 930 | int topmem_sort(char *a, char *b); | ||
| 931 | #endif /* TOPMEM */ | ||
| 932 | |||
| 933 | /* | ||
| 934 | * end TOPMEM support | ||
| 935 | */ | ||
| 936 | 964 | ||
| 937 | enum { | 965 | enum { |
| 938 | TOP_MASK = 0 | 966 | TOP_MASK = 0 |
| 939 | | PSSCAN_PID | 967 | | PSSCAN_PID |
| 940 | | PSSCAN_PPID | 968 | | PSSCAN_PPID |
| 941 | | PSSCAN_VSZ | 969 | | PSSCAN_RSS |
| 942 | | PSSCAN_STIME | 970 | | PSSCAN_STIME |
| 943 | | PSSCAN_UTIME | 971 | | PSSCAN_UTIME |
| 944 | | PSSCAN_STATE | 972 | | PSSCAN_STATE |
| @@ -950,7 +978,7 @@ enum { | |||
| 950 | | PSSCAN_SMAPS | 978 | | PSSCAN_SMAPS |
| 951 | | PSSCAN_COMM, | 979 | | PSSCAN_COMM, |
| 952 | EXIT_MASK = 0, | 980 | EXIT_MASK = 0, |
| 953 | NO_RESCAN_MASK = (unsigned)-1, | 981 | ONLY_REDRAW = (unsigned)-1, |
| 954 | }; | 982 | }; |
| 955 | 983 | ||
| 956 | #if ENABLE_FEATURE_TOP_INTERACTIVE | 984 | #if ENABLE_FEATURE_TOP_INTERACTIVE |
| @@ -963,15 +991,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
| 963 | } | 991 | } |
| 964 | 992 | ||
| 965 | while (1) { | 993 | while (1) { |
| 966 | int32_t c; | 994 | int32_t c, cc; |
| 967 | 995 | ||
| 968 | c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); | 996 | c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); |
| 969 | if (c == -1 && errno != EAGAIN) { | 997 | if (c == -1) { |
| 970 | /* error/EOF */ | 998 | if (errno != EAGAIN) |
| 971 | option_mask32 |= OPT_EOF; | 999 | /* error/EOF */ |
| 1000 | option_mask32 |= OPT_EOF; | ||
| 1001 | /* else: timeout - rescan and refresh */ | ||
| 972 | break; | 1002 | break; |
| 973 | } | 1003 | } |
| 974 | interval = 0; | 1004 | interval = 0; |
| 1005 | /* "continue" statements below return to do one additional | ||
| 1006 | * quick attempt to read a key. This prevents | ||
| 1007 | * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn" | ||
| 1008 | * to cause lots of rescans. | ||
| 1009 | */ | ||
| 975 | 1010 | ||
| 976 | if (c == initial_settings.c_cc[VINTR]) | 1011 | if (c == initial_settings.c_cc[VINTR]) |
| 977 | return EXIT_MASK; | 1012 | return EXIT_MASK; |
| @@ -1005,9 +1040,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
| 1005 | G_scroll_ofs = ntop - 1; | 1040 | G_scroll_ofs = ntop - 1; |
| 1006 | if (G_scroll_ofs < 0) | 1041 | if (G_scroll_ofs < 0) |
| 1007 | G_scroll_ofs = 0; | 1042 | G_scroll_ofs = 0; |
| 1008 | return NO_RESCAN_MASK; | 1043 | return ONLY_REDRAW; |
| 1009 | } | 1044 | } |
| 1010 | 1045 | ||
| 1046 | cc = c; | ||
| 1011 | c |= 0x20; /* lowercase */ | 1047 | c |= 0x20; /* lowercase */ |
| 1012 | if (c == 'q') | 1048 | if (c == 'q') |
| 1013 | return EXIT_MASK; | 1049 | return EXIT_MASK; |
| @@ -1055,9 +1091,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
| 1055 | continue; | 1091 | continue; |
| 1056 | } | 1092 | } |
| 1057 | # if ENABLE_FEATURE_TOPMEM | 1093 | # if ENABLE_FEATURE_TOPMEM |
| 1094 | if (cc == 'S') { | ||
| 1095 | if (--sort_field < 0) | ||
| 1096 | sort_field = NUM_SORT_FIELD - 1; | ||
| 1097 | if (--sort_field < 0) | ||
| 1098 | sort_field = NUM_SORT_FIELD - 1; | ||
| 1099 | } | ||
| 1058 | if (c == 's') { | 1100 | if (c == 's') { |
| 1059 | scan_mask = TOPMEM_MASK; | ||
| 1060 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; | 1101 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; |
| 1102 | if (scan_mask == TOPMEM_MASK) | ||
| 1103 | return ONLY_REDRAW; | ||
| 1104 | scan_mask = TOPMEM_MASK; | ||
| 1061 | free(prev_hist); | 1105 | free(prev_hist); |
| 1062 | prev_hist = NULL; | 1106 | prev_hist = NULL; |
| 1063 | prev_hist_count = 0; | 1107 | prev_hist_count = 0; |
| @@ -1066,7 +1110,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
| 1066 | # endif | 1110 | # endif |
| 1067 | if (c == 'r') { | 1111 | if (c == 'r') { |
| 1068 | inverted ^= 1; | 1112 | inverted ^= 1; |
| 1069 | continue; | 1113 | return ONLY_REDRAW; |
| 1070 | } | 1114 | } |
| 1071 | # if ENABLE_FEATURE_TOP_SMP_CPU | 1115 | # if ENABLE_FEATURE_TOP_SMP_CPU |
| 1072 | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ | 1116 | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ |
| @@ -1088,8 +1132,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) | |||
| 1088 | } | 1132 | } |
| 1089 | # endif | 1133 | # endif |
| 1090 | # endif | 1134 | # endif |
| 1091 | break; /* unknown key -> force refresh */ | 1135 | /* Unknown key. Eat remaining buffered input (if any) */ |
| 1092 | } | 1136 | } /* while (1) */ |
| 1093 | 1137 | ||
| 1094 | return scan_mask; | 1138 | return scan_mask; |
| 1095 | } | 1139 | } |
| @@ -1155,12 +1199,15 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1155 | { | 1199 | { |
| 1156 | duration_t interval; | 1200 | duration_t interval; |
| 1157 | int iterations; | 1201 | int iterations; |
| 1158 | unsigned col; | 1202 | unsigned opt; |
| 1159 | char *str_interval, *str_iterations; | 1203 | char *str_interval, *str_iterations; |
| 1160 | unsigned scan_mask = TOP_MASK; | 1204 | unsigned scan_mask = TOP_MASK; |
| 1161 | 1205 | ||
| 1162 | INIT_G(); | 1206 | INIT_G(); |
| 1163 | 1207 | ||
| 1208 | //worth it? | ||
| 1209 | // setvbuf(stdout, /*buf*/ NULL, _IOFBF, /*size*/ 0); | ||
| 1210 | |||
| 1164 | interval = 5; /* default update interval is 5 seconds */ | 1211 | interval = 5; /* default update interval is 5 seconds */ |
| 1165 | iterations = 0; /* infinite */ | 1212 | iterations = 0; /* infinite */ |
| 1166 | #if ENABLE_FEATURE_TOP_SMP_CPU | 1213 | #if ENABLE_FEATURE_TOP_SMP_CPU |
| @@ -1172,13 +1219,13 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1172 | 1219 | ||
| 1173 | /* all args are options; -n NUM */ | 1220 | /* all args are options; -n NUM */ |
| 1174 | make_all_argv_opts(argv); /* options can be specified w/o dash */ | 1221 | make_all_argv_opts(argv); /* options can be specified w/o dash */ |
| 1175 | col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); | 1222 | opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); |
| 1176 | /* NB: -m and -H are accepted even if not configured */ | 1223 | /* NB: -m and -H are accepted even if not configured */ |
| 1177 | #if ENABLE_FEATURE_TOPMEM | 1224 | #if ENABLE_FEATURE_TOPMEM |
| 1178 | if (col & OPT_m) /* -m (busybox specific) */ | 1225 | if (opt & OPT_m) /* -m (busybox specific) */ |
| 1179 | scan_mask = TOPMEM_MASK; | 1226 | scan_mask = TOPMEM_MASK; |
| 1180 | #endif | 1227 | #endif |
| 1181 | if (col & OPT_d) { | 1228 | if (opt & OPT_d) { |
| 1182 | /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ | 1229 | /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ |
| 1183 | if (str_interval[0] == '-') | 1230 | if (str_interval[0] == '-') |
| 1184 | str_interval++; | 1231 | str_interval++; |
| @@ -1187,18 +1234,17 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1187 | if (interval > INT_MAX / 1000) | 1234 | if (interval > INT_MAX / 1000) |
| 1188 | interval = INT_MAX / 1000; | 1235 | interval = INT_MAX / 1000; |
| 1189 | } | 1236 | } |
| 1190 | if (col & OPT_n) { | 1237 | if (opt & OPT_n) { |
| 1191 | if (str_iterations[0] == '-') | 1238 | if (str_iterations[0] == '-') |
| 1192 | str_iterations++; | 1239 | str_iterations++; |
| 1193 | iterations = xatou(str_iterations); | 1240 | iterations = xatou(str_iterations); |
| 1194 | } | 1241 | } |
| 1195 | #if ENABLE_FEATURE_SHOW_THREADS | 1242 | #if ENABLE_FEATURE_SHOW_THREADS |
| 1196 | if (col & OPT_H) { | 1243 | if (opt & OPT_H) { |
| 1197 | scan_mask |= PSSCAN_TASKS; | 1244 | scan_mask |= PSSCAN_TASKS; |
| 1198 | } | 1245 | } |
| 1199 | #endif | 1246 | #endif |
| 1200 | 1247 | ||
| 1201 | /* change to /proc */ | ||
| 1202 | xchdir("/proc"); | 1248 | xchdir("/proc"); |
| 1203 | 1249 | ||
| 1204 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 1250 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| @@ -1226,23 +1272,22 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1226 | #endif | 1272 | #endif |
| 1227 | 1273 | ||
| 1228 | while (scan_mask != EXIT_MASK) { | 1274 | while (scan_mask != EXIT_MASK) { |
| 1229 | IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) | 1275 | IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;) |
| 1230 | procps_status_t *p = NULL; | 1276 | procps_status_t *p = NULL; |
| 1231 | 1277 | ||
| 1232 | if (OPT_BATCH_MODE) { | 1278 | G.lines = INT_MAX; |
| 1233 | G.lines = INT_MAX; | 1279 | G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ |
| 1234 | col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ | 1280 | if (!OPT_BATCH_MODE) { |
| 1235 | } else { | ||
| 1236 | G.lines = 24; /* default */ | 1281 | G.lines = 24; /* default */ |
| 1237 | col = 79; | 1282 | G.scr_width = 80; |
| 1238 | /* We output to stdout, we need size of stdout (not stdin)! */ | 1283 | /* We output to stdout, we need size of stdout (not stdin)! */ |
| 1239 | get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); | 1284 | get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines); |
| 1240 | if (G.lines < 5 || col < 10) { | 1285 | if (G.lines < 5 || G.scr_width < 10) { |
| 1241 | sleep_for_duration(interval); | 1286 | sleep_for_duration(interval); |
| 1242 | continue; | 1287 | continue; |
| 1243 | } | 1288 | } |
| 1244 | if (col > LINE_BUF_SIZE - 2) | 1289 | if (G.scr_width > LINE_BUF_SIZE - 2) |
| 1245 | col = LINE_BUF_SIZE - 2; | 1290 | G.scr_width = LINE_BUF_SIZE - 2; |
| 1246 | } | 1291 | } |
| 1247 | 1292 | ||
| 1248 | /* read process IDs & status for all the processes */ | 1293 | /* read process IDs & status for all the processes */ |
| @@ -1255,7 +1300,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1255 | top = xrealloc_vector(top, 6, ntop++); | 1300 | top = xrealloc_vector(top, 6, ntop++); |
| 1256 | top[n].pid = p->pid; | 1301 | top[n].pid = p->pid; |
| 1257 | top[n].ppid = p->ppid; | 1302 | top[n].ppid = p->ppid; |
| 1258 | top[n].vsz = p->vsz; | 1303 | top[n].memsize = p->rss; |
| 1259 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 1304 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| 1260 | top[n].ticks = p->stime + p->utime; | 1305 | top[n].ticks = p->stime + p->utime; |
| 1261 | #endif | 1306 | #endif |
| @@ -1268,20 +1313,20 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1268 | } | 1313 | } |
| 1269 | #if ENABLE_FEATURE_TOPMEM | 1314 | #if ENABLE_FEATURE_TOPMEM |
| 1270 | else { /* TOPMEM */ | 1315 | else { /* TOPMEM */ |
| 1271 | if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) | 1316 | if (!(p->mapped_ro | p->mapped_rw)) |
| 1272 | continue; /* kernel threads are ignored */ | 1317 | continue; /* kernel threads are ignored */ |
| 1273 | n = ntop; | 1318 | n = ntop; |
| 1274 | /* No bug here - top and topmem are the same */ | 1319 | /* No bug here - top and topmem are the same */ |
| 1275 | top = xrealloc_vector(topmem, 6, ntop++); | 1320 | top = xrealloc_vector(topmem, 6, ntop++); |
| 1276 | strcpy(topmem[n].comm, p->comm); | 1321 | strcpy(topmem[n].comm, p->comm); |
| 1277 | topmem[n].pid = p->pid; | 1322 | topmem[n].pid = p->pid; |
| 1278 | topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; | 1323 | topmem[n].vsz = p->mapped_rw + p->mapped_ro; |
| 1279 | topmem[n].vszrw = p->smaps.mapped_rw; | 1324 | topmem[n].vszrw = p->mapped_rw; |
| 1280 | topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; | 1325 | topmem[n].rss_sh = p->shared_clean + p->shared_dirty; |
| 1281 | topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; | 1326 | topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; |
| 1282 | topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; | 1327 | topmem[n].dirty = p->private_dirty + p->shared_dirty; |
| 1283 | topmem[n].dirty_sh = p->smaps.shared_dirty; | 1328 | topmem[n].dirty_sh = p->shared_dirty; |
| 1284 | topmem[n].stack = p->smaps.stack; | 1329 | topmem[n].stack = p->stack; |
| 1285 | } | 1330 | } |
| 1286 | #endif | 1331 | #endif |
| 1287 | } /* end of "while we read /proc" */ | 1332 | } /* end of "while we read /proc" */ |
| @@ -1310,30 +1355,40 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1310 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); | 1355 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); |
| 1311 | } | 1356 | } |
| 1312 | #endif | 1357 | #endif |
| 1313 | IF_FEATURE_TOP_INTERACTIVE(display:) | 1358 | IF_FEATURE_TOP_INTERACTIVE(redraw:) |
| 1359 | G.lines_remaining = G.lines; | ||
| 1314 | IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { | 1360 | IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { |
| 1315 | display_process_list(G.lines, col); | 1361 | display_process_list(); |
| 1316 | } | 1362 | } |
| 1317 | #if ENABLE_FEATURE_TOPMEM | 1363 | #if ENABLE_FEATURE_TOPMEM |
| 1318 | else { /* TOPMEM */ | 1364 | else { /* TOPMEM */ |
| 1319 | display_topmem_process_list(G.lines, col); | 1365 | display_topmem_process_list(); |
| 1320 | } | 1366 | } |
| 1321 | #endif | 1367 | #endif |
| 1368 | print_end(); | ||
| 1369 | fflush_all(); | ||
| 1322 | if (iterations >= 0 && !--iterations) | 1370 | if (iterations >= 0 && !--iterations) |
| 1323 | break; | 1371 | break; |
| 1324 | #if !ENABLE_FEATURE_TOP_INTERACTIVE | 1372 | #if !ENABLE_FEATURE_TOP_INTERACTIVE |
| 1325 | clearmems(); | 1373 | clearmems(); |
| 1326 | sleep_for_duration(interval); | 1374 | sleep_for_duration(interval); |
| 1327 | #else | 1375 | #else |
| 1328 | new_mask = handle_input(scan_mask, interval); | 1376 | new_mask = handle_input(scan_mask, |
| 1329 | if (new_mask == NO_RESCAN_MASK) | 1377 | /* After "redraw with no rescan", have one |
| 1330 | goto display; | 1378 | * key timeout shorter that normal |
| 1379 | * (IOW: rescan sooner): | ||
| 1380 | */ | ||
| 1381 | (new_mask == ONLY_REDRAW ? 1 : interval) | ||
| 1382 | ); | ||
| 1383 | if (new_mask == ONLY_REDRAW) | ||
| 1384 | goto redraw; | ||
| 1331 | scan_mask = new_mask; | 1385 | scan_mask = new_mask; |
| 1332 | clearmems(); | 1386 | clearmems(); |
| 1333 | #endif | 1387 | #endif |
| 1334 | } /* end of "while (not Q)" */ | 1388 | } /* end of "while (not Q)" */ |
| 1335 | 1389 | ||
| 1336 | bb_putchar('\n'); | 1390 | if (!OPT_BATCH_MODE) |
| 1391 | bb_putchar('\n'); | ||
| 1337 | #if ENABLE_FEATURE_TOP_INTERACTIVE | 1392 | #if ENABLE_FEATURE_TOP_INTERACTIVE |
| 1338 | reset_term(); | 1393 | reset_term(); |
| 1339 | #endif | 1394 | #endif |
diff --git a/runit/chpst.c b/runit/chpst.c index 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 | ||
| 10 | Define before inclusion (only those needed): | 10 | Define 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 | ||
| 20 | Defined by this header: | 20 | Defined 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 | ||
| 421 | PDCurses uses a 32-bit integer for its chtype: | 423 | PDCurses 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 | ||
| 428 | There are 256 color pairs (8 bits), 8 bits for modifiers, and 16 bits | 429 | There are 256 color pairs (8 bits), 8 bits for modifiers, and 16 bits |
| 429 | for character data. The modifiers are bold, underline, right-line, | 430 | for 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 | |||
| 53 | check() { | 53 | check() { |
| 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 |
| 56 | int main() {} | 56 | int main() { return 0; } |
| 57 | EOF | 57 | EOF |
| 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/selinux/sestatus.c b/selinux/sestatus.c index 098a4d189..cf664cc18 100644 --- a/selinux/sestatus.c +++ b/selinux/sestatus.c | |||
| @@ -131,13 +131,13 @@ static void display_verbose(void) | |||
| 131 | puts("\nFile contexts:"); | 131 | puts("\nFile contexts:"); |
| 132 | 132 | ||
| 133 | cterm = xmalloc_ttyname(0); | 133 | cterm = xmalloc_ttyname(0); |
| 134 | //FIXME: if cterm == NULL, we segfault!?? | ||
| 135 | puts(cterm); | ||
| 136 | if (cterm && lgetfilecon(cterm, &con) >= 0) { | 134 | if (cterm && lgetfilecon(cterm, &con) >= 0) { |
| 137 | printf(COL_FMT "%s\n", "Controlling term:", con); | 135 | printf(COL_FMT "%s\n", "Controlling term:", con); |
| 138 | if (ENABLE_FEATURE_CLEAN_UP) | 136 | if (ENABLE_FEATURE_CLEAN_UP) |
| 139 | freecon(con); | 137 | freecon(con); |
| 140 | } | 138 | } |
| 139 | if (ENABLE_FEATURE_CLEAN_UP) | ||
| 140 | free(cterm); | ||
| 141 | 141 | ||
| 142 | for (i = 0; fc[i] != NULL; i++) { | 142 | for (i = 0; fc[i] != NULL; i++) { |
| 143 | struct stat stbuf; | 143 | struct stat stbuf; |
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 | ||
| 173 | config FEATURE_SH_EMBEDDED_SCRIPTS | 174 | config FEATURE_SH_EMBEDDED_SCRIPTS |
| 174 | bool "Embed scripts in the binary" | 175 | bool "Embed scripts in the binary" |
diff --git a/shell/Kbuild.src b/shell/Kbuild.src index a287fce4e..6bba4989f 100644 --- a/shell/Kbuild.src +++ b/shell/Kbuild.src | |||
| @@ -9,4 +9,3 @@ lib-y:= | |||
| 9 | INSERT | 9 | INSERT |
| 10 | 10 | ||
| 11 | lib-$(CONFIG_FEATURE_SH_MATH) += math.o | 11 | lib-$(CONFIG_FEATURE_SH_MATH) += math.o |
| 12 | lib-$(CONFIG_FEATURE_PRNG_SHELL) += random.o | ||
diff --git a/shell/ash.c b/shell/ash.c index 3919118f0..12c95c338 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -384,10 +384,16 @@ union node; | |||
| 384 | struct strlist; | 384 | struct strlist; |
| 385 | struct job; | 385 | struct job; |
| 386 | 386 | ||
| 387 | #if defined(_WIN64) | ||
| 388 | # define ALIGN64 ALIGNED(16) | ||
| 389 | #else | ||
| 390 | # define ALIGN64 | ||
| 391 | #endif | ||
| 392 | |||
| 387 | struct forkshell { | 393 | struct forkshell { |
| 388 | /* filled by forkshell_copy() */ | 394 | /* filled by forkshell_copy() */ |
| 389 | struct globals_var *gvp; | ||
| 390 | struct globals_misc *gmp; | 395 | struct globals_misc *gmp; |
| 396 | struct globals_var *gvp; | ||
| 391 | struct tblentry **cmdtable; | 397 | struct tblentry **cmdtable; |
| 392 | #if ENABLE_ASH_ALIAS | 398 | #if ENABLE_ASH_ALIAS |
| 393 | struct alias **atab; | 399 | struct alias **atab; |
| @@ -401,7 +407,6 @@ struct forkshell { | |||
| 401 | unsigned njobs; | 407 | unsigned njobs; |
| 402 | struct job *curjob; | 408 | struct job *curjob; |
| 403 | #endif | 409 | #endif |
| 404 | /* struct parsefile *g_parsefile; */ | ||
| 405 | HANDLE hMapFile; | 410 | HANDLE hMapFile; |
| 406 | char *old_base; | 411 | char *old_base; |
| 407 | int size; | 412 | int size; |
| @@ -426,8 +431,8 @@ struct forkshell { | |||
| 426 | int fd[3]; | 431 | int fd[3]; |
| 427 | union node *n; | 432 | union node *n; |
| 428 | char **argv; | 433 | char **argv; |
| 429 | char *path; | 434 | const char *path; |
| 430 | }; | 435 | } ALIGN64; |
| 431 | 436 | ||
| 432 | enum { | 437 | enum { |
| 433 | FS_OPENHERE, | 438 | FS_OPENHERE, |
| @@ -461,7 +466,7 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes) | |||
| 461 | 466 | ||
| 462 | /* ============ Shell options */ | 467 | /* ============ Shell options */ |
| 463 | 468 | ||
| 464 | /* If you add/change options hare, update --help text too */ | 469 | /* If you add/change options here, update --help text too */ |
| 465 | static const char *const optletters_optnames[] ALIGN_PTR = { | 470 | static const char *const optletters_optnames[] ALIGN_PTR = { |
| 466 | "e" "errexit", | 471 | "e" "errexit", |
| 467 | "f" "noglob", | 472 | "f" "noglob", |
| @@ -534,6 +539,65 @@ static const char *const optletters_optnames[] ALIGN_PTR = { | |||
| 534 | enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; | 539 | enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; |
| 535 | 540 | ||
| 536 | 541 | ||
| 542 | /* ============ Parser data */ | ||
| 543 | |||
| 544 | struct strlist { | ||
| 545 | struct strlist *next; | ||
| 546 | char *text; | ||
| 547 | }; | ||
| 548 | |||
| 549 | struct alias; | ||
| 550 | |||
| 551 | struct strpush { | ||
| 552 | struct strpush *prev; /* preceding string on stack */ | ||
| 553 | char *prev_string; | ||
| 554 | int prev_left_in_line; | ||
| 555 | #if ENABLE_ASH_ALIAS | ||
| 556 | struct alias *ap; /* if push was associated with an alias */ | ||
| 557 | #endif | ||
| 558 | char *string; /* remember the string since it may change */ | ||
| 559 | |||
| 560 | /* Delay freeing so we can stop nested aliases. */ | ||
| 561 | struct strpush *spfree; | ||
| 562 | |||
| 563 | /* Remember last two characters for pungetc. */ | ||
| 564 | int lastc[2]; | ||
| 565 | |||
| 566 | /* Number of outstanding calls to pungetc. */ | ||
| 567 | int unget; | ||
| 568 | }; | ||
| 569 | |||
| 570 | /* | ||
| 571 | * The parsefile structure pointed to by the global variable parsefile | ||
| 572 | * contains information about the current file being read. | ||
| 573 | */ | ||
| 574 | struct parsefile { | ||
| 575 | struct parsefile *prev; /* preceding file on stack */ | ||
| 576 | int linno; /* current line */ | ||
| 577 | int pf_fd; /* file descriptor (or -1 if string) */ | ||
| 578 | int left_in_line; /* number of chars left in this line */ | ||
| 579 | int left_in_buffer; /* number of chars left in this buffer past the line */ | ||
| 580 | char *next_to_pgetc; /* next char in buffer */ | ||
| 581 | char *buf; /* input buffer */ | ||
| 582 | struct strpush *strpush; /* for pushing strings at this level */ | ||
| 583 | struct strpush basestrpush; /* so pushing one is fast */ | ||
| 584 | |||
| 585 | /* Delay freeing so we can stop nested aliases. */ | ||
| 586 | struct strpush *spfree; | ||
| 587 | |||
| 588 | /* Remember last two characters for pungetc. */ | ||
| 589 | int lastc[2]; | ||
| 590 | |||
| 591 | /* Number of outstanding calls to pungetc. */ | ||
| 592 | int unget; | ||
| 593 | |||
| 594 | #if ENABLE_PLATFORM_MINGW32 | ||
| 595 | /* True if a trailing CR from a previous read was left unprocessed. */ | ||
| 596 | int cr; | ||
| 597 | #endif | ||
| 598 | }; | ||
| 599 | |||
| 600 | |||
| 537 | /* ============ Misc data */ | 601 | /* ============ Misc data */ |
| 538 | 602 | ||
| 539 | #define msg_illnum "Illegal number: %s" | 603 | #define msg_illnum "Illegal number: %s" |
| @@ -558,6 +622,9 @@ struct globals_misc { | |||
| 558 | smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ | 622 | smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ |
| 559 | #endif | 623 | #endif |
| 560 | smallint inps4; /* Prevent PS4 nesting. */ | 624 | smallint inps4; /* Prevent PS4 nesting. */ |
| 625 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 626 | smallint vforked; | ||
| 627 | #endif | ||
| 561 | int savestatus; /* exit status of last command outside traps */ | 628 | int savestatus; /* exit status of last command outside traps */ |
| 562 | int rootpid; /* pid of main shell */ | 629 | int rootpid; /* pid of main shell */ |
| 563 | /* shell level: 0 for the main shell, 1 for its children, and so on */ | 630 | /* shell level: 0 for the main shell, 1 for its children, and so on */ |
| @@ -581,9 +648,6 @@ struct globals_misc { | |||
| 581 | char *physdir; // = nullstr; /* physical working directory */ | 648 | char *physdir; // = nullstr; /* physical working directory */ |
| 582 | 649 | ||
| 583 | char *arg0; /* value of $0 */ | 650 | char *arg0; /* value of $0 */ |
| 584 | #if ENABLE_PLATFORM_MINGW32 | ||
| 585 | char *commandname; | ||
| 586 | #endif | ||
| 587 | 651 | ||
| 588 | struct jmploc *exception_handler; | 652 | struct jmploc *exception_handler; |
| 589 | 653 | ||
| @@ -595,8 +659,10 @@ struct globals_misc { | |||
| 595 | */ | 659 | */ |
| 596 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ | 660 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ |
| 597 | #if !ENABLE_PLATFORM_MINGW32 | 661 | #if !ENABLE_PLATFORM_MINGW32 |
| 598 | volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ | 662 | volatile /*sig_atomic_t*/ smallint gotsigchld; /* 1 = got SIGCHLD */ |
| 599 | volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ | 663 | volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ |
| 664 | #else | ||
| 665 | volatile /*sig_atomic_t*/ smallint waitcmd_int; /* SIGINT in wait */ | ||
| 600 | #endif | 666 | #endif |
| 601 | smallint exception_type; /* kind of exception: */ | 667 | smallint exception_type; /* kind of exception: */ |
| 602 | #define EXINT 0 /* SIGINT received */ | 668 | #define EXINT 0 /* SIGINT received */ |
| @@ -669,8 +735,14 @@ struct globals_misc { | |||
| 669 | 735 | ||
| 670 | char **trap_ptr; /* used only by "trap hack" */ | 736 | char **trap_ptr; /* used only by "trap hack" */ |
| 671 | 737 | ||
| 738 | char *commandname; /* currently executing command */ | ||
| 739 | struct parsefile *g_parsefile; /* = &basepf, current input file */ | ||
| 740 | struct parsefile basepf; /* top level input file */ | ||
| 741 | |||
| 672 | /* Rarely referenced stuff */ | 742 | /* Rarely referenced stuff */ |
| 673 | 743 | ||
| 744 | struct jmploc main_handler; | ||
| 745 | |||
| 674 | /* Cached supplementary group array (for testing executable'ity of files) */ | 746 | /* Cached supplementary group array (for testing executable'ity of files) */ |
| 675 | struct cached_groupinfo groupinfo; | 747 | struct cached_groupinfo groupinfo; |
| 676 | 748 | ||
| @@ -685,6 +757,11 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
| 685 | #define back_exitstatus (G_misc.back_exitstatus ) | 757 | #define back_exitstatus (G_misc.back_exitstatus ) |
| 686 | #define job_warning (G_misc.job_warning) | 758 | #define job_warning (G_misc.job_warning) |
| 687 | #define inps4 (G_misc.inps4 ) | 759 | #define inps4 (G_misc.inps4 ) |
| 760 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 761 | #define vforked (G_misc.vforked ) | ||
| 762 | #else | ||
| 763 | #define vforked 0 | ||
| 764 | #endif | ||
| 688 | #define savestatus (G_misc.savestatus ) | 765 | #define savestatus (G_misc.savestatus ) |
| 689 | #define rootpid (G_misc.rootpid ) | 766 | #define rootpid (G_misc.rootpid ) |
| 690 | #define shlvl (G_misc.shlvl ) | 767 | #define shlvl (G_misc.shlvl ) |
| @@ -701,14 +778,14 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
| 701 | #define curdir (G_misc.curdir ) | 778 | #define curdir (G_misc.curdir ) |
| 702 | #define physdir (G_misc.physdir ) | 779 | #define physdir (G_misc.physdir ) |
| 703 | #define arg0 (G_misc.arg0 ) | 780 | #define arg0 (G_misc.arg0 ) |
| 704 | #if ENABLE_PLATFORM_MINGW32 | ||
| 705 | #define commandname (G_misc.commandname) | ||
| 706 | #endif | ||
| 707 | #define exception_handler (G_misc.exception_handler) | 781 | #define exception_handler (G_misc.exception_handler) |
| 708 | #define exception_type (G_misc.exception_type ) | 782 | #define exception_type (G_misc.exception_type ) |
| 709 | #define suppress_int (G_misc.suppress_int ) | 783 | #define suppress_int (G_misc.suppress_int ) |
| 710 | #define pending_int (G_misc.pending_int ) | 784 | #define pending_int (G_misc.pending_int ) |
| 711 | #define got_sigchld (G_misc.got_sigchld ) | 785 | #if ENABLE_PLATFORM_MINGW32 |
| 786 | #define waitcmd_int (G_misc.waitcmd_int ) | ||
| 787 | #endif | ||
| 788 | #define gotsigchld (G_misc.gotsigchld ) | ||
| 712 | #define pending_sig (G_misc.pending_sig ) | 789 | #define pending_sig (G_misc.pending_sig ) |
| 713 | #define nullstr (G_misc.nullstr ) | 790 | #define nullstr (G_misc.nullstr ) |
| 714 | #define optlist (G_misc.optlist ) | 791 | #define optlist (G_misc.optlist ) |
| @@ -717,6 +794,10 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
| 717 | #define may_have_traps (G_misc.may_have_traps ) | 794 | #define may_have_traps (G_misc.may_have_traps ) |
| 718 | #define trap (G_misc.trap ) | 795 | #define trap (G_misc.trap ) |
| 719 | #define trap_ptr (G_misc.trap_ptr ) | 796 | #define trap_ptr (G_misc.trap_ptr ) |
| 797 | #define commandname (G_misc.commandname) | ||
| 798 | #define g_parsefile (G_misc.g_parsefile ) | ||
| 799 | #define basepf (G_misc.basepf ) | ||
| 800 | #define main_handler (G_misc.main_handler ) | ||
| 720 | #define groupinfo (G_misc.groupinfo ) | 801 | #define groupinfo (G_misc.groupinfo ) |
| 721 | #define random_gen (G_misc.random_gen ) | 802 | #define random_gen (G_misc.random_gen ) |
| 722 | #define backgndpid (G_misc.backgndpid ) | 803 | #define backgndpid (G_misc.backgndpid ) |
| @@ -733,6 +814,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
| 733 | curdir = nullstr; \ | 814 | curdir = nullstr; \ |
| 734 | physdir = nullstr; \ | 815 | physdir = nullstr; \ |
| 735 | trap_ptr = trap; \ | 816 | trap_ptr = trap; \ |
| 817 | g_parsefile = &basepf; \ | ||
| 736 | groupinfo.euid = -1; \ | 818 | groupinfo.euid = -1; \ |
| 737 | groupinfo.egid = -1; \ | 819 | groupinfo.egid = -1; \ |
| 738 | } while (0) | 820 | } while (0) |
| @@ -780,73 +862,26 @@ var_end(const char *var) | |||
| 780 | return var; | 862 | return var; |
| 781 | } | 863 | } |
| 782 | 864 | ||
| 783 | 865 | #if !ENABLE_PLATFORM_MINGW32 | |
| 784 | /* ============ Parser data */ | 866 | /* Our signal logic never blocks individual signals |
| 785 | 867 | * using signal mask - only by setting SIG_IGN handler. | |
| 786 | /* | 868 | * Therefore just unmasking all of them instead of "restore old mask" |
| 787 | * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. | 869 | * approach is safe, modulo a case where the shell itself inherited |
| 788 | */ | 870 | * a _masked_ signal. |
| 789 | struct strlist { | ||
| 790 | struct strlist *next; | ||
| 791 | char *text; | ||
| 792 | }; | ||
| 793 | |||
| 794 | struct alias; | ||
| 795 | |||
| 796 | struct strpush { | ||
| 797 | struct strpush *prev; /* preceding string on stack */ | ||
| 798 | char *prev_string; | ||
| 799 | int prev_left_in_line; | ||
| 800 | #if ENABLE_ASH_ALIAS | ||
| 801 | struct alias *ap; /* if push was associated with an alias */ | ||
| 802 | #endif | ||
| 803 | char *string; /* remember the string since it may change */ | ||
| 804 | |||
| 805 | /* Delay freeing so we can stop nested aliases. */ | ||
| 806 | struct strpush *spfree; | ||
| 807 | |||
| 808 | /* Remember last two characters for pungetc. */ | ||
| 809 | int lastc[2]; | ||
| 810 | |||
| 811 | /* Number of outstanding calls to pungetc. */ | ||
| 812 | int unget; | ||
| 813 | }; | ||
| 814 | |||
| 815 | /* | ||
| 816 | * The parsefile structure pointed to by the global variable parsefile | ||
| 817 | * contains information about the current file being read. | ||
| 818 | */ | 871 | */ |
| 819 | struct parsefile { | 872 | static void |
| 820 | struct parsefile *prev; /* preceding file on stack */ | 873 | sigclearmask(void) |
| 821 | int linno; /* current line */ | 874 | { |
| 822 | int pf_fd; /* file descriptor (or -1 if string) */ | 875 | sigprocmask_allsigs(SIG_UNBLOCK); |
| 823 | int left_in_line; /* number of chars left in this line */ | 876 | } |
| 824 | int left_in_buffer; /* number of chars left in this buffer past the line */ | ||
| 825 | char *next_to_pgetc; /* next char in buffer */ | ||
| 826 | char *buf; /* input buffer */ | ||
| 827 | struct strpush *strpush; /* for pushing strings at this level */ | ||
| 828 | struct strpush basestrpush; /* so pushing one is fast */ | ||
| 829 | |||
| 830 | /* Delay freeing so we can stop nested aliases. */ | ||
| 831 | struct strpush *spfree; | ||
| 832 | |||
| 833 | /* Remember last two characters for pungetc. */ | ||
| 834 | int lastc[2]; | ||
| 835 | |||
| 836 | /* Number of outstanding calls to pungetc. */ | ||
| 837 | int unget; | ||
| 838 | |||
| 839 | #if ENABLE_PLATFORM_MINGW32 | ||
| 840 | /* True if a trailing CR from a previous read was left unprocessed. */ | ||
| 841 | int cr; | ||
| 842 | #endif | 877 | #endif |
| 843 | }; | ||
| 844 | 878 | ||
| 845 | static struct parsefile basepf; /* top level input file */ | 879 | /* Reset handler when entering a subshell */ |
| 846 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | 880 | static void |
| 847 | #if ENABLE_PLATFORM_POSIX | 881 | reset_exception_handler(void) |
| 848 | static char *commandname; /* currently executing command */ | 882 | { |
| 849 | #endif | 883 | exception_handler = &main_handler; |
| 884 | } | ||
| 850 | 885 | ||
| 851 | 886 | ||
| 852 | /* ============ Interrupts / exceptions */ | 887 | /* ============ Interrupts / exceptions */ |
| @@ -860,13 +895,13 @@ static void exitshell(void) NORETURN; | |||
| 860 | * more fun than worrying about efficiency and portability. :-)) | 895 | * more fun than worrying about efficiency and portability. :-)) |
| 861 | */ | 896 | */ |
| 862 | #if DEBUG_INTONOFF | 897 | #if DEBUG_INTONOFF |
| 863 | # define INT_OFF do { \ | 898 | # define INTOFF do { \ |
| 864 | TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \ | 899 | TRACE(("%s:%d INTOFF(%d)\n", __func__, __LINE__, suppress_int)); \ |
| 865 | suppress_int++; \ | 900 | suppress_int++; \ |
| 866 | barrier(); \ | 901 | barrier(); \ |
| 867 | } while (0) | 902 | } while (0) |
| 868 | #else | 903 | #else |
| 869 | # define INT_OFF do { \ | 904 | # define INTOFF do { \ |
| 870 | suppress_int++; \ | 905 | suppress_int++; \ |
| 871 | barrier(); \ | 906 | barrier(); \ |
| 872 | } while (0) | 907 | } while (0) |
| @@ -885,7 +920,11 @@ raise_exception(int e) | |||
| 885 | if (exception_handler == NULL) | 920 | if (exception_handler == NULL) |
| 886 | abort(); | 921 | abort(); |
| 887 | #endif | 922 | #endif |
| 888 | INT_OFF; | 923 | |
| 924 | if (vforked) | ||
| 925 | _exit(exitstatus); | ||
| 926 | |||
| 927 | INTOFF; | ||
| 889 | exception_type = e; | 928 | exception_type = e; |
| 890 | longjmp(exception_handler->loc, 1); | 929 | longjmp(exception_handler->loc, 1); |
| 891 | } | 930 | } |
| @@ -896,24 +935,25 @@ raise_exception(int e) | |||
| 896 | } while (0) | 935 | } while (0) |
| 897 | #endif | 936 | #endif |
| 898 | 937 | ||
| 938 | #if ENABLE_PLATFORM_MINGW32 | ||
| 939 | static void | ||
| 940 | write_ctrl_c(void) | ||
| 941 | { | ||
| 942 | console_write("^C", 2); | ||
| 943 | } | ||
| 944 | #endif | ||
| 945 | |||
| 899 | /* | 946 | /* |
| 900 | * Called when a SIGINT is received. (If the user specifies | 947 | * Called when a SIGINT is received. (If the user specifies |
| 901 | * that SIGINT is to be trapped or ignored using the trap builtin, then | 948 | * that SIGINT is to be trapped or ignored using the trap builtin, then |
| 902 | * this routine is not called.) suppress_int is nonzero when interrupts | 949 | * this routine is not called.) suppress_int is nonzero when interrupts |
| 903 | * are held using the INT_OFF macro. (The test for iflag is just | 950 | * are held using the INTOFF macro. (The test for iflag is just |
| 904 | * defensive programming.) | 951 | * defensive programming.) |
| 905 | */ | 952 | */ |
| 906 | static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN); | 953 | static void raise_interrupt(void) NORETURN; |
| 907 | static void | 954 | static void |
| 908 | raise_interrupt(void) | 955 | raise_interrupt(void) |
| 909 | { | 956 | { |
| 910 | #if ENABLE_PLATFORM_MINGW32 | ||
| 911 | /* Contrary to the comment above on Windows raise_interrupt() is | ||
| 912 | * called when SIGINT is trapped or ignored. We detect this here | ||
| 913 | * and return without doing anything. */ | ||
| 914 | if (trap[SIGINT]) | ||
| 915 | return; | ||
| 916 | #endif | ||
| 917 | pending_int = 0; | 957 | pending_int = 0; |
| 918 | #if !ENABLE_PLATFORM_MINGW32 | 958 | #if !ENABLE_PLATFORM_MINGW32 |
| 919 | /* Signal is not automatically unmasked after it is raised, | 959 | /* Signal is not automatically unmasked after it is raised, |
| @@ -929,12 +969,13 @@ raise_interrupt(void) | |||
| 929 | raise(SIGINT); | 969 | raise(SIGINT); |
| 930 | #else | 970 | #else |
| 931 | fflush_all(); | 971 | fflush_all(); |
| 972 | kill(-getpid(), SIGINT); | ||
| 932 | _exit(SIGINT << 24); | 973 | _exit(SIGINT << 24); |
| 933 | #endif | 974 | #endif |
| 934 | } | 975 | } |
| 935 | #if ENABLE_PLATFORM_MINGW32 | 976 | #if ENABLE_PLATFORM_MINGW32 |
| 936 | if (iflag) | 977 | if (iflag) |
| 937 | write(STDOUT_FILENO, "^C", 2); | 978 | write_ctrl_c(); |
| 938 | #endif | 979 | #endif |
| 939 | /* bash: ^C even on empty command line sets $? */ | 980 | /* bash: ^C even on empty command line sets $? */ |
| 940 | exitstatus = SIGINT + 128; | 981 | exitstatus = SIGINT + 128; |
| @@ -957,12 +998,12 @@ int_on(void) | |||
| 957 | barrier(); | 998 | barrier(); |
| 958 | } | 999 | } |
| 959 | #if DEBUG_INTONOFF | 1000 | #if DEBUG_INTONOFF |
| 960 | # define INT_ON do { \ | 1001 | # define INTON do { \ |
| 961 | TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \ | 1002 | TRACE(("%s:%d INTON(%d)\n", __func__, __LINE__, suppress_int-1)); \ |
| 962 | int_on(); \ | 1003 | int_on(); \ |
| 963 | } while (0) | 1004 | } while (0) |
| 964 | #else | 1005 | #else |
| 965 | # define INT_ON int_on() | 1006 | # define INTON int_on() |
| 966 | #endif | 1007 | #endif |
| 967 | static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void | 1008 | static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void |
| 968 | force_int_on(void) | 1009 | force_int_on(void) |
| @@ -973,7 +1014,7 @@ force_int_on(void) | |||
| 973 | raise_interrupt(); /* does not return */ | 1014 | raise_interrupt(); /* does not return */ |
| 974 | barrier(); | 1015 | barrier(); |
| 975 | } | 1016 | } |
| 976 | #define FORCE_INT_ON force_int_on() | 1017 | #define FORCEINTON force_int_on() |
| 977 | 1018 | ||
| 978 | #define SAVE_INT(v) ((v) = suppress_int) | 1019 | #define SAVE_INT(v) ((v) = suppress_int) |
| 979 | 1020 | ||
| @@ -991,27 +1032,27 @@ force_int_on(void) | |||
| 991 | static void | 1032 | static void |
| 992 | outstr(const char *p, FILE *file) | 1033 | outstr(const char *p, FILE *file) |
| 993 | { | 1034 | { |
| 994 | INT_OFF; | 1035 | INTOFF; |
| 995 | fputs(p, file); | 1036 | fputs(p, file); |
| 996 | INT_ON; | 1037 | INTON; |
| 997 | } | 1038 | } |
| 998 | 1039 | ||
| 999 | static void | 1040 | static void |
| 1000 | flush_stdout_stderr(void) | 1041 | flush_stdout_stderr(void) |
| 1001 | { | 1042 | { |
| 1002 | INT_OFF; | 1043 | INTOFF; |
| 1003 | fflush_all(); | 1044 | fflush_all(); |
| 1004 | INT_ON; | 1045 | INTON; |
| 1005 | } | 1046 | } |
| 1006 | 1047 | ||
| 1007 | /* Was called outcslow(c,FILE*), but c was always '\n' */ | 1048 | /* Was called outcslow(c,FILE*), but c was always '\n' */ |
| 1008 | static void | 1049 | static void |
| 1009 | newline_and_flush(FILE *dest) | 1050 | newline_and_flush(FILE *dest) |
| 1010 | { | 1051 | { |
| 1011 | INT_OFF; | 1052 | INTOFF; |
| 1012 | putc('\n', dest); | 1053 | putc('\n', dest); |
| 1013 | fflush(dest); | 1054 | fflush(dest); |
| 1014 | INT_ON; | 1055 | INTON; |
| 1015 | } | 1056 | } |
| 1016 | 1057 | ||
| 1017 | static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2))); | 1058 | static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2))); |
| @@ -1021,11 +1062,11 @@ out1fmt(const char *fmt, ...) | |||
| 1021 | va_list ap; | 1062 | va_list ap; |
| 1022 | int r; | 1063 | int r; |
| 1023 | 1064 | ||
| 1024 | INT_OFF; | 1065 | INTOFF; |
| 1025 | va_start(ap, fmt); | 1066 | va_start(ap, fmt); |
| 1026 | r = vprintf(fmt, ap); | 1067 | r = vprintf(fmt, ap); |
| 1027 | va_end(ap); | 1068 | va_end(ap); |
| 1028 | INT_ON; | 1069 | INTON; |
| 1029 | return r; | 1070 | return r; |
| 1030 | } | 1071 | } |
| 1031 | 1072 | ||
| @@ -1036,11 +1077,11 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...) | |||
| 1036 | va_list ap; | 1077 | va_list ap; |
| 1037 | int ret; | 1078 | int ret; |
| 1038 | 1079 | ||
| 1039 | INT_OFF; | 1080 | INTOFF; |
| 1040 | va_start(ap, fmt); | 1081 | va_start(ap, fmt); |
| 1041 | ret = vsnprintf(outbuf, length, fmt, ap); | 1082 | ret = vsnprintf(outbuf, length, fmt, ap); |
| 1042 | va_end(ap); | 1083 | va_end(ap); |
| 1043 | INT_ON; | 1084 | INTON; |
| 1044 | return ret > (int)length ? length : ret; | 1085 | return ret > (int)length ? length : ret; |
| 1045 | } | 1086 | } |
| 1046 | 1087 | ||
| @@ -1077,28 +1118,47 @@ out2str(const char *p) | |||
| 1077 | # define CTL_LAST CTLFROMPROC | 1118 | # define CTL_LAST CTLFROMPROC |
| 1078 | #endif | 1119 | #endif |
| 1079 | 1120 | ||
| 1080 | /* variable substitution byte (follows CTLVAR) */ | 1121 | /* ${VAR[ops]} encoding is CTLVAR,<type_byte>,"VARNAME=",<ops_encoded(details?)>,CTLENDVAR */ |
| 1081 | #define VSTYPE 0x0f /* type of variable substitution */ | 1122 | /* variable type byte (follows CTLVAR) */ |
| 1082 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ | 1123 | #define VSTYPE 0x0f /* type of variable substitution */ |
| 1083 | 1124 | #define VSNUL 0x10 /* colon: the op is one of :- :+ :? := */ | |
| 1084 | /* values of VSTYPE field */ | 1125 | /* values of VSTYPE field. The first 5 must be in this order, "}-+?=" string is used elsewhere to index into them */ |
| 1085 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | 1126 | #define VSNORMAL 0x1 /* $var or ${var} */ |
| 1086 | #define VSMINUS 0x2 /* ${var-text} */ | 1127 | #define VSMINUS 0x2 /* ${var[:]-text} */ |
| 1087 | #define VSPLUS 0x3 /* ${var+text} */ | 1128 | #define VSPLUS 0x3 /* ${var[:]+text} */ |
| 1088 | #define VSQUESTION 0x4 /* ${var?message} */ | 1129 | #define VSQUESTION 0x4 /* ${var[:]?message} */ |
| 1089 | #define VSASSIGN 0x5 /* ${var=text} */ | 1130 | #define VSASSIGN 0x5 /* ${var[:]=text} */ |
| 1090 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ | 1131 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ |
| 1091 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ | 1132 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ |
| 1092 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ | 1133 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ |
| 1093 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ | 1134 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ |
| 1094 | #define VSLENGTH 0xa /* ${#var} */ | 1135 | #define VSLENGTH 0xa /* ${#var} */ |
| 1095 | #if BASH_SUBSTR | 1136 | #if BASH_SUBSTR |
| 1096 | #define VSSUBSTR 0xc /* ${var:position:length} */ | 1137 | #define VSSUBSTR 0xb /* ${var:position:length} */ |
| 1097 | #endif | 1138 | #endif |
| 1098 | #if BASH_PATTERN_SUBST | 1139 | #if BASH_PATTERN_SUBST |
| 1099 | #define VSREPLACE 0xd /* ${var/pattern/replacement} */ | 1140 | #define VSREPLACE 0xc /* ${var/pattern/replacement} */ |
| 1100 | #define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ | 1141 | #define VSREPLACEALL 0xd /* ${var//pattern/replacement} */ |
| 1142 | #endif | ||
| 1143 | static const char vstype_suffix[][3] ALIGN1 = { | ||
| 1144 | [VSNORMAL - VSNORMAL] = "}", // $var or ${var} | ||
| 1145 | [VSMINUS - VSNORMAL] = "-", // ${var-text} | ||
| 1146 | [VSPLUS - VSNORMAL] = "+", // ${var+text} | ||
| 1147 | [VSQUESTION - VSNORMAL] = "?", // ${var?message} | ||
| 1148 | [VSASSIGN - VSNORMAL] = "=", // ${var=text} | ||
| 1149 | [VSTRIMRIGHT - VSNORMAL] = "%", // ${var%pattern} | ||
| 1150 | [VSTRIMRIGHTMAX - VSNORMAL] = "%%",// ${var%%pattern} | ||
| 1151 | [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern} | ||
| 1152 | [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern} | ||
| 1153 | [VSLENGTH - VSNORMAL] = "", // ${#var} | ||
| 1154 | #if BASH_SUBSTR | ||
| 1155 | [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length} | ||
| 1101 | #endif | 1156 | #endif |
| 1157 | #if BASH_PATTERN_SUBST | ||
| 1158 | [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement} | ||
| 1159 | [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement} | ||
| 1160 | #endif | ||
| 1161 | }; | ||
| 1102 | 1162 | ||
| 1103 | static const char dolatstr[] ALIGN1 = { | 1163 | static const char dolatstr[] ALIGN1 = { |
| 1104 | CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' | 1164 | CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' |
| @@ -1127,14 +1187,15 @@ static const char dolatstr[] ALIGN1 = { | |||
| 1127 | #endif | 1187 | #endif |
| 1128 | #define NCLOBBER 18 | 1188 | #define NCLOBBER 18 |
| 1129 | #define NFROM 19 | 1189 | #define NFROM 19 |
| 1130 | #define NFROMTO 20 | 1190 | #define NFROMSTR 20 |
| 1131 | #define NAPPEND 21 | 1191 | #define NFROMTO 21 |
| 1132 | #define NTOFD 22 | 1192 | #define NAPPEND 22 |
| 1133 | #define NFROMFD 23 | 1193 | #define NTOFD 23 |
| 1134 | #define NHERE 24 | 1194 | #define NFROMFD 24 |
| 1135 | #define NXHERE 25 | 1195 | #define NHERE 25 |
| 1136 | #define NNOT 26 | 1196 | #define NXHERE 26 |
| 1137 | #define N_NUMBER 27 | 1197 | #define NNOT 27 |
| 1198 | #define N_NUMBER 28 | ||
| 1138 | 1199 | ||
| 1139 | union node; | 1200 | union node; |
| 1140 | 1201 | ||
| @@ -1472,7 +1533,9 @@ sharg(union node *arg, FILE *fp) | |||
| 1472 | 1533 | ||
| 1473 | if (subtype & VSNUL) | 1534 | if (subtype & VSNUL) |
| 1474 | putc(':', fp); | 1535 | putc(':', fp); |
| 1475 | 1536 | #if 1 | |
| 1537 | fputs(vstype_suffix[(subtype & VSTYPE) - VSNORMAL], fp); | ||
| 1538 | #else | ||
| 1476 | switch (subtype & VSTYPE) { | 1539 | switch (subtype & VSTYPE) { |
| 1477 | case VSNORMAL: | 1540 | case VSNORMAL: |
| 1478 | putc('}', fp); | 1541 | putc('}', fp); |
| @@ -1508,6 +1571,7 @@ sharg(union node *arg, FILE *fp) | |||
| 1508 | default: | 1571 | default: |
| 1509 | out1fmt("<subtype %d>", subtype); | 1572 | out1fmt("<subtype %d>", subtype); |
| 1510 | } | 1573 | } |
| 1574 | #endif | ||
| 1511 | break; | 1575 | break; |
| 1512 | case CTLENDVAR: | 1576 | case CTLENDVAR: |
| 1513 | putc('}', fp); | 1577 | putc('}', fp); |
| @@ -1562,6 +1626,7 @@ shcmd(union node *cmd, FILE *fp) | |||
| 1562 | #endif | 1626 | #endif |
| 1563 | case NTOFD: s = ">&"; dftfd = 1; break; | 1627 | case NTOFD: s = ">&"; dftfd = 1; break; |
| 1564 | case NFROM: s = "<"; break; | 1628 | case NFROM: s = "<"; break; |
| 1629 | case NFROMSTR: s = "<<<"; break; | ||
| 1565 | case NFROMFD: s = "<&"; break; | 1630 | case NFROMFD: s = "<&"; break; |
| 1566 | case NFROMTO: s = "<>"; break; | 1631 | case NFROMTO: s = "<>"; break; |
| 1567 | default: s = "*error*"; break; | 1632 | default: s = "*error*"; break; |
| @@ -1648,13 +1713,14 @@ showtree(union node *n) | |||
| 1648 | static void | 1713 | static void |
| 1649 | ash_vmsg(const char *msg, va_list ap) | 1714 | ash_vmsg(const char *msg, va_list ap) |
| 1650 | { | 1715 | { |
| 1716 | //In dash, the order/format is different: | ||
| 1717 | // arg0: LINENO: [commandname:] MSG | ||
| 1718 | //If you fix it, change testsuite to match | ||
| 1651 | fprintf(stderr, "%s: ", arg0); | 1719 | fprintf(stderr, "%s: ", arg0); |
| 1652 | if (commandname) { | 1720 | if (commandname && strcmp(arg0, commandname) != 0) |
| 1653 | if (strcmp(arg0, commandname)) | 1721 | fprintf(stderr, "%s: ", commandname); |
| 1654 | fprintf(stderr, "%s: ", commandname); | 1722 | if (!iflag || g_parsefile->pf_fd > 0) |
| 1655 | if (!iflag || g_parsefile->pf_fd > 0) | 1723 | fprintf(stderr, "line %d: ", errlinno); |
| 1656 | fprintf(stderr, "line %d: ", errlinno); | ||
| 1657 | } | ||
| 1658 | vfprintf(stderr, msg, ap); | 1724 | vfprintf(stderr, msg, ap); |
| 1659 | newline_and_flush(stderr); | 1725 | newline_and_flush(stderr); |
| 1660 | } | 1726 | } |
| @@ -1683,6 +1749,7 @@ ash_vmsg_and_raise(int cond, const char *msg, va_list ap) | |||
| 1683 | /* NOTREACHED */ | 1749 | /* NOTREACHED */ |
| 1684 | } | 1750 | } |
| 1685 | 1751 | ||
| 1752 | /* This function is called sh_error() in dash */ | ||
| 1686 | static void ash_msg_and_raise_error(const char *, ...) NORETURN; | 1753 | static void ash_msg_and_raise_error(const char *, ...) NORETURN; |
| 1687 | static void | 1754 | static void |
| 1688 | ash_msg_and_raise_error(const char *msg, ...) | 1755 | ash_msg_and_raise_error(const char *msg, ...) |
| @@ -1737,17 +1804,60 @@ ash_msg(const char *fmt, ...) | |||
| 1737 | } | 1804 | } |
| 1738 | 1805 | ||
| 1739 | /* | 1806 | /* |
| 1807 | * Types of operations (passed to the errmsg routine). | ||
| 1808 | */ | ||
| 1809 | #define E_OPEN 01 /* opening a file */ | ||
| 1810 | #define E_CREAT 02 /* creating a file */ | ||
| 1811 | #define E_EXEC 04 /* executing a program */ | ||
| 1812 | /* | ||
| 1740 | * Return a string describing an error. The returned string may be a | 1813 | * Return a string describing an error. The returned string may be a |
| 1741 | * pointer to a static buffer that will be overwritten on the next call. | 1814 | * pointer to a static buffer that will be overwritten on the next call. |
| 1742 | * Action describes the operation that got the error. | 1815 | * Action describes the operation that got the error. |
| 1743 | */ | 1816 | */ |
| 1744 | static const char * | 1817 | static const char * |
| 1745 | errmsg(int e, const char *em) | 1818 | errmsg(int e, int action) |
| 1819 | { | ||
| 1820 | if (e != ENOENT && e != ENOTDIR) | ||
| 1821 | return strerror(e); | ||
| 1822 | |||
| 1823 | if (action & E_OPEN) | ||
| 1824 | return "no such file"; | ||
| 1825 | else if (action & E_CREAT) | ||
| 1826 | return "nonexistent directory"; | ||
| 1827 | else | ||
| 1828 | return "not found"; | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | static int sh_open_fail(const char *, int, int) NORETURN; | ||
| 1832 | static int sh_open_fail(const char *pathname, int flags, int e) | ||
| 1746 | { | 1833 | { |
| 1747 | if (e == ENOENT || e == ENOTDIR) { | 1834 | const char *word; |
| 1748 | return em; | 1835 | int action; |
| 1836 | |||
| 1837 | word = "open"; | ||
| 1838 | action = E_OPEN; | ||
| 1839 | if (flags & O_CREAT) { | ||
| 1840 | word = "create"; | ||
| 1841 | action = E_CREAT; | ||
| 1749 | } | 1842 | } |
| 1750 | return strerror(e); | 1843 | |
| 1844 | ash_msg_and_raise_error("can't %s %s: %s", word, pathname, errmsg(e, action)); | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | static int sh_open(const char *pathname, int flags, int mayfail) | ||
| 1848 | { | ||
| 1849 | int fd; | ||
| 1850 | int e; | ||
| 1851 | |||
| 1852 | do { | ||
| 1853 | fd = open(pathname, flags, 0666); | ||
| 1854 | e = errno; | ||
| 1855 | } while (fd < 0 && e == EINTR && !pending_sig); | ||
| 1856 | |||
| 1857 | if (mayfail || fd >= 0) | ||
| 1858 | return fd; | ||
| 1859 | |||
| 1860 | sh_open_fail(pathname, flags, e); | ||
| 1751 | } | 1861 | } |
| 1752 | 1862 | ||
| 1753 | 1863 | ||
| @@ -1821,7 +1931,6 @@ struct stackmark { | |||
| 1821 | size_t stacknleft; | 1931 | size_t stacknleft; |
| 1822 | }; | 1932 | }; |
| 1823 | 1933 | ||
| 1824 | |||
| 1825 | struct globals_memstack { | 1934 | struct globals_memstack { |
| 1826 | struct stack_block *g_stackp; // = &stackbase; | 1935 | struct stack_block *g_stackp; // = &stackbase; |
| 1827 | char *g_stacknxt; // = stackbase.space; | 1936 | char *g_stacknxt; // = stackbase.space; |
| @@ -1844,7 +1953,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack; | |||
| 1844 | sstrend = stackbase.space + MINSIZE; \ | 1953 | sstrend = stackbase.space + MINSIZE; \ |
| 1845 | } while (0) | 1954 | } while (0) |
| 1846 | 1955 | ||
| 1847 | |||
| 1848 | #define stackblock() ((void *)g_stacknxt) | 1956 | #define stackblock() ((void *)g_stacknxt) |
| 1849 | #define stackblocksize() g_stacknleft | 1957 | #define stackblocksize() g_stacknleft |
| 1850 | 1958 | ||
| @@ -1874,14 +1982,14 @@ stalloc(size_t nbytes) | |||
| 1874 | len = sizeof(struct stack_block) - MINSIZE + blocksize; | 1982 | len = sizeof(struct stack_block) - MINSIZE + blocksize; |
| 1875 | if (len < blocksize) | 1983 | if (len < blocksize) |
| 1876 | ash_msg_and_raise_error(bb_msg_memory_exhausted); | 1984 | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| 1877 | INT_OFF; | 1985 | INTOFF; |
| 1878 | sp = ckmalloc(len); | 1986 | sp = ckmalloc(len); |
| 1879 | sp->prev = g_stackp; | 1987 | sp->prev = g_stackp; |
| 1880 | g_stacknxt = sp->space; | 1988 | g_stacknxt = sp->space; |
| 1881 | g_stacknleft = blocksize; | 1989 | g_stacknleft = blocksize; |
| 1882 | sstrend = g_stacknxt + blocksize; | 1990 | sstrend = g_stacknxt + blocksize; |
| 1883 | g_stackp = sp; | 1991 | g_stackp = sp; |
| 1884 | INT_ON; | 1992 | INTON; |
| 1885 | } | 1993 | } |
| 1886 | p = g_stacknxt; | 1994 | p = g_stacknxt; |
| 1887 | g_stacknxt += aligned; | 1995 | g_stacknxt += aligned; |
| @@ -1947,7 +2055,7 @@ popstackmark(struct stackmark *mark) | |||
| 1947 | if (!mark->stackp) | 2055 | if (!mark->stackp) |
| 1948 | return; | 2056 | return; |
| 1949 | 2057 | ||
| 1950 | INT_OFF; | 2058 | INTOFF; |
| 1951 | while (g_stackp != mark->stackp) { | 2059 | while (g_stackp != mark->stackp) { |
| 1952 | sp = g_stackp; | 2060 | sp = g_stackp; |
| 1953 | g_stackp = sp->prev; | 2061 | g_stackp = sp->prev; |
| @@ -1956,7 +2064,7 @@ popstackmark(struct stackmark *mark) | |||
| 1956 | g_stacknxt = mark->stacknxt; | 2064 | g_stacknxt = mark->stacknxt; |
| 1957 | g_stacknleft = mark->stacknleft; | 2065 | g_stacknleft = mark->stacknleft; |
| 1958 | sstrend = mark->stacknxt + mark->stacknleft; | 2066 | sstrend = mark->stacknxt + mark->stacknleft; |
| 1959 | INT_ON; | 2067 | INTON; |
| 1960 | } | 2068 | } |
| 1961 | 2069 | ||
| 1962 | /* | 2070 | /* |
| @@ -1985,7 +2093,7 @@ growstackblock(size_t min) | |||
| 1985 | struct stack_block *prevstackp; | 2093 | struct stack_block *prevstackp; |
| 1986 | size_t grosslen; | 2094 | size_t grosslen; |
| 1987 | 2095 | ||
| 1988 | INT_OFF; | 2096 | INTOFF; |
| 1989 | sp = g_stackp; | 2097 | sp = g_stackp; |
| 1990 | prevstackp = sp->prev; | 2098 | prevstackp = sp->prev; |
| 1991 | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; | 2099 | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; |
| @@ -1995,7 +2103,7 @@ growstackblock(size_t min) | |||
| 1995 | g_stacknxt = sp->space; | 2103 | g_stacknxt = sp->space; |
| 1996 | g_stacknleft = newlen; | 2104 | g_stacknleft = newlen; |
| 1997 | sstrend = sp->space + newlen; | 2105 | sstrend = sp->space + newlen; |
| 1998 | INT_ON; | 2106 | INTON; |
| 1999 | } else { | 2107 | } else { |
| 2000 | char *oldspace = g_stacknxt; | 2108 | char *oldspace = g_stacknxt; |
| 2001 | size_t oldlen = g_stacknleft; | 2109 | size_t oldlen = g_stacknleft; |
| @@ -2361,7 +2469,6 @@ struct localvar { | |||
| 2361 | # define VIMPORT 0x400 /* variable was imported from environment */ | 2469 | # define VIMPORT 0x400 /* variable was imported from environment */ |
| 2362 | #endif | 2470 | #endif |
| 2363 | 2471 | ||
| 2364 | |||
| 2365 | /* Need to be before varinit_data[] */ | 2472 | /* Need to be before varinit_data[] */ |
| 2366 | #if ENABLE_LOCALE_SUPPORT | 2473 | #if ENABLE_LOCALE_SUPPORT |
| 2367 | static void FAST_FUNC | 2474 | static void FAST_FUNC |
| @@ -2856,7 +2963,7 @@ setvar(const char *name, const char *val, int flags) | |||
| 2856 | vallen = strlen(val); | 2963 | vallen = strlen(val); |
| 2857 | } | 2964 | } |
| 2858 | 2965 | ||
| 2859 | INT_OFF; | 2966 | INTOFF; |
| 2860 | nameeq = ckzalloc(namelen + vallen + 2); | 2967 | nameeq = ckzalloc(namelen + vallen + 2); |
| 2861 | p = mempcpy(nameeq, name, namelen); | 2968 | p = mempcpy(nameeq, name, namelen); |
| 2862 | if (val) { | 2969 | if (val) { |
| @@ -2864,7 +2971,7 @@ setvar(const char *name, const char *val, int flags) | |||
| 2864 | strcpy(p, val); | 2971 | strcpy(p, val); |
| 2865 | } | 2972 | } |
| 2866 | vp = setvareq(nameeq, flags | VNOSAVE); | 2973 | vp = setvareq(nameeq, flags | VNOSAVE); |
| 2867 | INT_ON; | 2974 | INTON; |
| 2868 | 2975 | ||
| 2869 | return vp; | 2976 | return vp; |
| 2870 | } | 2977 | } |
| @@ -3419,7 +3526,7 @@ setpwd(const char *val, int setold) | |||
| 3419 | if (setold) { | 3526 | if (setold) { |
| 3420 | setvar("OLDPWD", oldcur, VEXPORT); | 3527 | setvar("OLDPWD", oldcur, VEXPORT); |
| 3421 | } | 3528 | } |
| 3422 | INT_OFF; | 3529 | INTOFF; |
| 3423 | if (physdir != nullstr) { | 3530 | if (physdir != nullstr) { |
| 3424 | if (physdir != oldcur) | 3531 | if (physdir != oldcur) |
| 3425 | free(physdir); | 3532 | free(physdir); |
| @@ -3436,7 +3543,7 @@ setpwd(const char *val, int setold) | |||
| 3436 | free(oldcur); | 3543 | free(oldcur); |
| 3437 | } | 3544 | } |
| 3438 | curdir = dir; | 3545 | curdir = dir; |
| 3439 | INT_ON; | 3546 | INTON; |
| 3440 | setvar("PWD", dir, VEXPORT); | 3547 | setvar("PWD", dir, VEXPORT); |
| 3441 | } | 3548 | } |
| 3442 | 3549 | ||
| @@ -3454,7 +3561,7 @@ docd(const char *dest, int flags) | |||
| 3454 | 3561 | ||
| 3455 | TRACE(("docd(\"%s\", %d) called\n", dest, flags)); | 3562 | TRACE(("docd(\"%s\", %d) called\n", dest, flags)); |
| 3456 | 3563 | ||
| 3457 | INT_OFF; | 3564 | INTOFF; |
| 3458 | if (!(flags & CD_PHYSICAL)) { | 3565 | if (!(flags & CD_PHYSICAL)) { |
| 3459 | dir = updatepwd(dest); | 3566 | dir = updatepwd(dest); |
| 3460 | if (dir) | 3567 | if (dir) |
| @@ -3466,7 +3573,7 @@ docd(const char *dest, int flags) | |||
| 3466 | setpwd(dir, 1); | 3573 | setpwd(dir, 1); |
| 3467 | hashcd(); | 3574 | hashcd(); |
| 3468 | out: | 3575 | out: |
| 3469 | INT_ON; | 3576 | INTON; |
| 3470 | return err; | 3577 | return err; |
| 3471 | } | 3578 | } |
| 3472 | 3579 | ||
| @@ -3581,7 +3688,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 3581 | 3688 | ||
| 3582 | /* ============ ... */ | 3689 | /* ============ ... */ |
| 3583 | 3690 | ||
| 3584 | |||
| 3585 | #define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) | 3691 | #define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) |
| 3586 | 3692 | ||
| 3587 | /* Syntax classes */ | 3693 | /* Syntax classes */ |
| @@ -3988,13 +4094,11 @@ struct alias { | |||
| 3988 | int flag; | 4094 | int flag; |
| 3989 | }; | 4095 | }; |
| 3990 | 4096 | ||
| 3991 | |||
| 3992 | static struct alias **atab; // [ATABSIZE]; | 4097 | static struct alias **atab; // [ATABSIZE]; |
| 3993 | #define INIT_G_alias() do { \ | 4098 | #define INIT_G_alias() do { \ |
| 3994 | atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ | 4099 | atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ |
| 3995 | } while (0) | 4100 | } while (0) |
| 3996 | 4101 | ||
| 3997 | |||
| 3998 | static struct alias ** | 4102 | static struct alias ** |
| 3999 | __lookupalias(const char *name) | 4103 | __lookupalias(const char *name) |
| 4000 | { | 4104 | { |
| @@ -4056,7 +4160,7 @@ setalias(const char *name, const char *val) | |||
| 4056 | 4160 | ||
| 4057 | app = __lookupalias(name); | 4161 | app = __lookupalias(name); |
| 4058 | ap = *app; | 4162 | ap = *app; |
| 4059 | INT_OFF; | 4163 | INTOFF; |
| 4060 | if (ap) { | 4164 | if (ap) { |
| 4061 | if (!(ap->flag & ALIASINUSE)) { | 4165 | if (!(ap->flag & ALIASINUSE)) { |
| 4062 | free(ap->val); | 4166 | free(ap->val); |
| @@ -4072,7 +4176,7 @@ setalias(const char *name, const char *val) | |||
| 4072 | /*ap->next = NULL;*/ | 4176 | /*ap->next = NULL;*/ |
| 4073 | *app = ap; | 4177 | *app = ap; |
| 4074 | } | 4178 | } |
| 4075 | INT_ON; | 4179 | INTON; |
| 4076 | } | 4180 | } |
| 4077 | 4181 | ||
| 4078 | static int | 4182 | static int |
| @@ -4083,9 +4187,9 @@ unalias(const char *name) | |||
| 4083 | app = __lookupalias(name); | 4187 | app = __lookupalias(name); |
| 4084 | 4188 | ||
| 4085 | if (*app) { | 4189 | if (*app) { |
| 4086 | INT_OFF; | 4190 | INTOFF; |
| 4087 | *app = freealias(*app); | 4191 | *app = freealias(*app); |
| 4088 | INT_ON; | 4192 | INTON; |
| 4089 | return 0; | 4193 | return 0; |
| 4090 | } | 4194 | } |
| 4091 | 4195 | ||
| @@ -4098,7 +4202,7 @@ rmaliases(void) | |||
| 4098 | struct alias *ap, **app; | 4202 | struct alias *ap, **app; |
| 4099 | int i; | 4203 | int i; |
| 4100 | 4204 | ||
| 4101 | INT_OFF; | 4205 | INTOFF; |
| 4102 | for (i = 0; i < ATABSIZE; i++) { | 4206 | for (i = 0; i < ATABSIZE; i++) { |
| 4103 | app = &atab[i]; | 4207 | app = &atab[i]; |
| 4104 | for (ap = *app; ap; ap = *app) { | 4208 | for (ap = *app; ap; ap = *app) { |
| @@ -4108,7 +4212,7 @@ rmaliases(void) | |||
| 4108 | } | 4212 | } |
| 4109 | } | 4213 | } |
| 4110 | } | 4214 | } |
| 4111 | INT_ON; | 4215 | INTON; |
| 4112 | } | 4216 | } |
| 4113 | 4217 | ||
| 4114 | static void | 4218 | static void |
| @@ -4176,7 +4280,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 4176 | 4280 | ||
| 4177 | #endif /* ASH_ALIAS */ | 4281 | #endif /* ASH_ALIAS */ |
| 4178 | 4282 | ||
| 4179 | |||
| 4180 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ | 4283 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ |
| 4181 | #define FORK_FG 0 | 4284 | #define FORK_FG 0 |
| 4182 | #define FORK_BG 1 | 4285 | #define FORK_BG 1 |
| @@ -4230,20 +4333,19 @@ struct job { | |||
| 4230 | struct job *prev_job; /* previous job */ | 4333 | struct job *prev_job; /* previous job */ |
| 4231 | }; | 4334 | }; |
| 4232 | 4335 | ||
| 4233 | static struct job *makejob(/*union node *,*/ int); | ||
| 4234 | #if !ENABLE_PLATFORM_MINGW32 | 4336 | #if !ENABLE_PLATFORM_MINGW32 |
| 4235 | static int forkshell(struct job *, union node *, int); | 4337 | static int forkshell(struct job *, union node *, int); |
| 4236 | #endif | 4338 | #endif |
| 4237 | static int waitforjob(struct job *); | 4339 | static int waitforjob(struct job *); |
| 4238 | 4340 | ||
| 4239 | #if !JOBS && !JOBS_WIN32 | 4341 | #if !JOBS && !JOBS_WIN32 |
| 4240 | enum { doing_jobctl = 0 }; | 4342 | enum { jobctl = 0 }; |
| 4241 | #define setjobctl(on) do {} while (0) | 4343 | #define setjobctl(on) do {} while (0) |
| 4242 | #elif JOBS_WIN32 | 4344 | #elif JOBS_WIN32 |
| 4243 | static smallint doing_jobctl; //references:8 | 4345 | static smallint jobctl; //references:8 |
| 4244 | #define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0) | 4346 | #define setjobctl(on) do { if (rootshell) jobctl = on; } while (0) |
| 4245 | #else /* JOBS */ | 4347 | #else /* JOBS */ |
| 4246 | static smallint doing_jobctl; //references:8 | 4348 | static smallint jobctl; //references:8 |
| 4247 | static void setjobctl(int); | 4349 | static void setjobctl(int); |
| 4248 | #endif | 4350 | #endif |
| 4249 | 4351 | ||
| @@ -4259,22 +4361,30 @@ ignoresig(int signo) | |||
| 4259 | /* No, need to do it */ | 4361 | /* No, need to do it */ |
| 4260 | signal(signo, SIG_IGN); | 4362 | signal(signo, SIG_IGN); |
| 4261 | } | 4363 | } |
| 4262 | sigmode[signo - 1] = S_HARD_IGN; | 4364 | if (!vforked) |
| 4365 | sigmode[signo - 1] = S_HARD_IGN; | ||
| 4263 | } | 4366 | } |
| 4264 | 4367 | ||
| 4265 | /* | 4368 | /* |
| 4266 | * Only one usage site - in setsignal() | 4369 | * Only one usage site - in setsignal() |
| 4370 | * This function is called onsig() in dash | ||
| 4267 | */ | 4371 | */ |
| 4268 | static void | 4372 | static void |
| 4269 | signal_handler(int signo) | 4373 | signal_handler(int signo) |
| 4270 | { | 4374 | { |
| 4375 | // parent momentarily has vforked == 1 too, but it masks | ||
| 4376 | // all signals until after it resets vforked to 0. | ||
| 4377 | if (vforked) | ||
| 4378 | /* We are vfork child, DO NOT MODIFY ANY VARIABLES! */ | ||
| 4379 | return; | ||
| 4380 | |||
| 4271 | if (signo == SIGCHLD) { | 4381 | if (signo == SIGCHLD) { |
| 4272 | got_sigchld = 1; | 4382 | gotsigchld = 1; |
| 4273 | if (!trap[SIGCHLD]) | 4383 | if (!trap[SIGCHLD]) |
| 4274 | return; | 4384 | return; |
| 4275 | } | 4385 | } |
| 4276 | #if ENABLE_FEATURE_EDITING | 4386 | #if ENABLE_FEATURE_EDITING |
| 4277 | bb_got_signal = signo; /* for read_line_input: "we got a signal" */ | 4387 | bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */ |
| 4278 | #endif | 4388 | #endif |
| 4279 | gotsig[signo - 1] = 1; | 4389 | gotsig[signo - 1] = 1; |
| 4280 | pending_sig = signo; | 4390 | pending_sig = signo; |
| @@ -4295,10 +4405,13 @@ signal_handler(int signo) | |||
| 4295 | static void | 4405 | static void |
| 4296 | setsignal(int signo) | 4406 | setsignal(int signo) |
| 4297 | { | 4407 | { |
| 4408 | int lvforked; | ||
| 4298 | char *t; | 4409 | char *t; |
| 4299 | char cur_act, new_act; | 4410 | char cur_act, new_act; |
| 4300 | struct sigaction act; | 4411 | struct sigaction act; |
| 4301 | 4412 | ||
| 4413 | lvforked = vforked; | ||
| 4414 | |||
| 4302 | t = trap[signo]; | 4415 | t = trap[signo]; |
| 4303 | new_act = S_DFL; | 4416 | new_act = S_DFL; |
| 4304 | if (t != NULL) { /* trap for this sig is set */ | 4417 | if (t != NULL) { /* trap for this sig is set */ |
| @@ -4307,7 +4420,7 @@ setsignal(int signo) | |||
| 4307 | new_act = S_IGN; | 4420 | new_act = S_IGN; |
| 4308 | } | 4421 | } |
| 4309 | 4422 | ||
| 4310 | if (rootshell && new_act == S_DFL) { | 4423 | if (rootshell && new_act == S_DFL && !lvforked) { |
| 4311 | switch (signo) { | 4424 | switch (signo) { |
| 4312 | case SIGINT: | 4425 | case SIGINT: |
| 4313 | if (iflag || minusc || sflag == 0) | 4426 | if (iflag || minusc || sflag == 0) |
| @@ -4377,8 +4490,8 @@ setsignal(int signo) | |||
| 4377 | if (cur_act == S_HARD_IGN || cur_act == new_act) | 4490 | if (cur_act == S_HARD_IGN || cur_act == new_act) |
| 4378 | return; | 4491 | return; |
| 4379 | 4492 | ||
| 4380 | *t = new_act; | 4493 | if (!lvforked) |
| 4381 | 4494 | *t = new_act; | |
| 4382 | act.sa_handler = SIG_DFL; | 4495 | act.sa_handler = SIG_DFL; |
| 4383 | switch (new_act) { | 4496 | switch (new_act) { |
| 4384 | case S_CATCH: | 4497 | case S_CATCH: |
| @@ -4641,7 +4754,7 @@ freejob(struct job *jp) | |||
| 4641 | int i; | 4754 | int i; |
| 4642 | #endif | 4755 | #endif |
| 4643 | 4756 | ||
| 4644 | INT_OFF; | 4757 | INTOFF; |
| 4645 | #if ENABLE_PLATFORM_POSIX || JOBS_WIN32 | 4758 | #if ENABLE_PLATFORM_POSIX || JOBS_WIN32 |
| 4646 | for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { | 4759 | for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { |
| 4647 | if (ps->ps_cmd != nullstr) | 4760 | if (ps->ps_cmd != nullstr) |
| @@ -4652,14 +4765,29 @@ freejob(struct job *jp) | |||
| 4652 | free(jp->ps); | 4765 | free(jp->ps); |
| 4653 | jp->used = 0; | 4766 | jp->used = 0; |
| 4654 | set_curjob(jp, CUR_DELETE); | 4767 | set_curjob(jp, CUR_DELETE); |
| 4655 | INT_ON; | 4768 | INTON; |
| 4656 | } | 4769 | } |
| 4657 | 4770 | ||
| 4658 | #if JOBS | 4771 | #if JOBS |
| 4659 | static void | 4772 | static void |
| 4660 | xtcsetpgrp(int fd, pid_t pgrp) | 4773 | xtcsetpgrp(int fd, pid_t pgrp) |
| 4661 | { | 4774 | { |
| 4662 | if (tcsetpgrp(fd, pgrp)) | 4775 | int err; |
| 4776 | |||
| 4777 | sigblockall(NULL); | ||
| 4778 | err = tcsetpgrp(fd, pgrp); | ||
| 4779 | sigclearmask(); | ||
| 4780 | // Unmasking signals would cause any arrived signal to trigger, so why? | ||
| 4781 | // Generally yes, but there are exceptions. Such as: | ||
| 4782 | // """ | ||
| 4783 | // Attempts to use tcsetpgrp() from a process which is a member of | ||
| 4784 | // a background process group on a fd associated with its controlling | ||
| 4785 | // terminal shall cause the process group to be sent a SIGTTOU signal. | ||
| 4786 | // If the calling thread is blocking SIGTTOU signals or the process | ||
| 4787 | // is ignoring SIGTTOU signals, the process shall be allowed | ||
| 4788 | // to perform the operation, and no signal is sent.""" | ||
| 4789 | |||
| 4790 | if (err) | ||
| 4663 | ash_msg_and_raise_perror("can't set tty process group"); | 4791 | ash_msg_and_raise_perror("can't set tty process group"); |
| 4664 | } | 4792 | } |
| 4665 | 4793 | ||
| @@ -4678,11 +4806,11 @@ setjobctl(int on) | |||
| 4678 | int fd; | 4806 | int fd; |
| 4679 | int pgrp; | 4807 | int pgrp; |
| 4680 | 4808 | ||
| 4681 | if (on == doing_jobctl || rootshell == 0) | 4809 | if (on == jobctl || rootshell == 0) |
| 4682 | return; | 4810 | return; |
| 4683 | if (on) { | 4811 | if (on) { |
| 4684 | int ofd; | 4812 | int ofd; |
| 4685 | ofd = fd = open(_PATH_TTY, O_RDWR); | 4813 | ofd = fd = sh_open(_PATH_TTY, O_RDWR, 1); |
| 4686 | if (fd < 0) { | 4814 | if (fd < 0) { |
| 4687 | /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails. | 4815 | /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails. |
| 4688 | * That sometimes helps to acquire controlling tty. | 4816 | * That sometimes helps to acquire controlling tty. |
| @@ -4738,7 +4866,7 @@ setjobctl(int on) | |||
| 4738 | fd = -1; | 4866 | fd = -1; |
| 4739 | } | 4867 | } |
| 4740 | ttyfd = fd; | 4868 | ttyfd = fd; |
| 4741 | doing_jobctl = on; | 4869 | jobctl = on; |
| 4742 | } | 4870 | } |
| 4743 | #endif | 4871 | #endif |
| 4744 | 4872 | ||
| @@ -4827,7 +4955,7 @@ restartjob(struct job *jp, int mode) | |||
| 4827 | int status; | 4955 | int status; |
| 4828 | pid_t pgid; | 4956 | pid_t pgid; |
| 4829 | 4957 | ||
| 4830 | INT_OFF; | 4958 | INTOFF; |
| 4831 | if (jp->state == JOBDONE) | 4959 | if (jp->state == JOBDONE) |
| 4832 | goto out; | 4960 | goto out; |
| 4833 | jp->state = JOBRUNNING; | 4961 | jp->state = JOBRUNNING; |
| @@ -4847,7 +4975,7 @@ restartjob(struct job *jp, int mode) | |||
| 4847 | } while (--i); | 4975 | } while (--i); |
| 4848 | out: | 4976 | out: |
| 4849 | status = (mode == FORK_FG) ? waitforjob(jp) : 0; | 4977 | status = (mode == FORK_FG) ? waitforjob(jp) : 0; |
| 4850 | INT_ON; | 4978 | INTON; |
| 4851 | return status; | 4979 | return status; |
| 4852 | } | 4980 | } |
| 4853 | 4981 | ||
| @@ -4916,9 +5044,12 @@ static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) | |||
| 4916 | # if ENABLE_FEATURE_EDITING | 5044 | # if ENABLE_FEATURE_EDITING |
| 4917 | bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */ | 5045 | bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */ |
| 4918 | # endif | 5046 | # endif |
| 4919 | if (!suppress_int && !(rootshell && iflag)) | 5047 | waitcmd_int = -waitcmd_int; |
| 4920 | raise_interrupt(); | 5048 | if (!trap[SIGINT]) { |
| 4921 | pending_int = 1; | 5049 | if (!suppress_int && !(rootshell && iflag)) |
| 5050 | raise_interrupt(); | ||
| 5051 | pending_int = 1; | ||
| 5052 | } | ||
| 4922 | return TRUE; | 5053 | return TRUE; |
| 4923 | } | 5054 | } |
| 4924 | return FALSE; | 5055 | return FALSE; |
| @@ -4929,7 +5060,7 @@ static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) | |||
| 4929 | * They don't support waitpid(-1) | 5060 | * They don't support waitpid(-1) |
| 4930 | */ | 5061 | */ |
| 4931 | static pid_t | 5062 | static pid_t |
| 4932 | waitpid_child(int *status, int wait_flags) | 5063 | waitpid_child(int *status, DWORD blocking) |
| 4933 | { | 5064 | { |
| 4934 | struct job *jb; | 5065 | struct job *jb; |
| 4935 | int pid_nr = 0; | 5066 | int pid_nr = 0; |
| @@ -4955,24 +5086,26 @@ waitpid_child(int *status, int wait_flags) | |||
| 4955 | } | 5086 | } |
| 4956 | 5087 | ||
| 4957 | if (pid_nr) { | 5088 | if (pid_nr) { |
| 4958 | idx = WaitForMultipleObjects(pid_nr, proclist, FALSE, | 5089 | do { |
| 4959 | wait_flags & WNOHANG ? 0 : INFINITE); | 5090 | idx = WaitForMultipleObjects(pid_nr, proclist, FALSE, blocking); |
| 4960 | if (idx < pid_nr) { | 5091 | if (idx < pid_nr) { |
| 4961 | GetExitCodeProcess(proclist[idx], &win_status); | 5092 | GetExitCodeProcess(proclist[idx], &win_status); |
| 4962 | *status = exit_code_to_wait_status(win_status); | 5093 | *status = exit_code_to_wait_status(win_status); |
| 4963 | pid = GetProcessId(proclist[idx]); | 5094 | pid = GetProcessId(proclist[idx]); |
| 4964 | } | 5095 | break; |
| 5096 | } | ||
| 5097 | } while (blocking && !pending_int && waitcmd_int != 1); | ||
| 4965 | } | 5098 | } |
| 4966 | return pid; | 5099 | return pid; |
| 4967 | } | 5100 | } |
| 4968 | #define waitpid(p, s, f) waitpid_child(s, f) | ||
| 4969 | #endif | 5101 | #endif |
| 4970 | 5102 | ||
| 4971 | #define DOWAIT_NONBLOCK 0 | 5103 | /* Inside dowait(): */ |
| 4972 | #define DOWAIT_BLOCK 1 | 5104 | #define DOWAIT_NONBLOCK 0 /* waitpid() will use WNOHANG and won't wait for signals */ |
| 4973 | #define DOWAIT_BLOCK_OR_SIG 2 | 5105 | #define DOWAIT_BLOCK 1 /* waitpid() will NOT use WNOHANG */ |
| 5106 | #define DOWAIT_CHILD_OR_SIG 2 /* waitpid() will use WNOHANG and if got 0, will wait for signals, then loop back */ | ||
| 4974 | #if BASH_WAIT_N | 5107 | #if BASH_WAIT_N |
| 4975 | # define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ | 5108 | # define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ |
| 4976 | #endif | 5109 | #endif |
| 4977 | 5110 | ||
| 4978 | static int | 5111 | static int |
| @@ -4984,40 +5117,48 @@ waitproc(int block, int *status) | |||
| 4984 | int err; | 5117 | int err; |
| 4985 | 5118 | ||
| 4986 | #if JOBS | 5119 | #if JOBS |
| 4987 | if (doing_jobctl) | 5120 | if (jobctl) |
| 4988 | flags |= WUNTRACED; | 5121 | flags |= WUNTRACED; |
| 4989 | #endif | 5122 | #endif |
| 4990 | 5123 | ||
| 4991 | do { | 5124 | do { |
| 4992 | got_sigchld = 0; | 5125 | gotsigchld = 0; |
| 4993 | do | 5126 | do |
| 4994 | err = waitpid(-1, status, flags); | 5127 | err = waitpid(-1, status, flags); |
| 4995 | while (err < 0 && errno == EINTR); | 5128 | while (err < 0 && errno == EINTR); |
| 4996 | 5129 | ||
| 5130 | /* Return if error (for example, ECHILD); or if pid found; | ||
| 5131 | * or if "block" is DOWAIT_NONBLOCK (=0), in this case return -1. | ||
| 5132 | */ | ||
| 4997 | if (err || (err = -!block)) | 5133 | if (err || (err = -!block)) |
| 4998 | break; | 5134 | break; |
| 4999 | 5135 | ||
| 5000 | sigfillset(&oldmask); | 5136 | /* "block" is DOWAIT_CHILD_OR_SIG. All children are running |
| 5001 | sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */ | 5137 | * (waitpid(WNOHAG) above returned 0), wait for signals: |
| 5002 | while (!got_sigchld && !pending_sig) | 5138 | */ |
| 5003 | sigsuspend(&oldmask); | 5139 | |
| 5004 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
| 5005 | //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): | 5140 | //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): |
| 5006 | //while (!got_sigchld && !pending_sig) | 5141 | //while (!gotsigchld && !pending_sig) |
| 5007 | // pause(); | 5142 | // pause(); |
| 5008 | 5143 | ||
| 5009 | } while (got_sigchld); | 5144 | sigblockall(&oldmask); |
| 5145 | |||
| 5146 | while (!gotsigchld && !pending_sig) | ||
| 5147 | sigsuspend(&oldmask); | ||
| 5148 | |||
| 5149 | sigclearmask(); | ||
| 5150 | } while (gotsigchld); | ||
| 5151 | /* If we fall off the loop, err is 0, which means we got a !SIGCHLD signal */ | ||
| 5010 | 5152 | ||
| 5011 | return err; | 5153 | return err; |
| 5012 | #else | 5154 | #else |
| 5013 | int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; | 5155 | // Only DOWAIT_NONBLOCK is non-blocking, other values block. |
| 5014 | *status = 0; | 5156 | *status = 0; |
| 5015 | return waitpid(-1, status, flags); | 5157 | return waitpid_child(status, block != DOWAIT_NONBLOCK); |
| 5016 | #endif | 5158 | #endif |
| 5017 | } | 5159 | } |
| 5018 | 5160 | ||
| 5019 | static int | 5161 | static int waitone(int block, struct job *job) |
| 5020 | waitone(int block, struct job *job) | ||
| 5021 | { | 5162 | { |
| 5022 | int pid; | 5163 | int pid; |
| 5023 | int status; | 5164 | int status; |
| @@ -5030,25 +5171,25 @@ waitone(int block, struct job *job) | |||
| 5030 | 5171 | ||
| 5031 | TRACE(("dowait(0x%x) called\n", block)); | 5172 | TRACE(("dowait(0x%x) called\n", block)); |
| 5032 | 5173 | ||
| 5033 | /* It's wrong to call waitpid() outside of INT_OFF region: | 5174 | /* It's wrong to call waitpid() outside of INTOFF region: |
| 5034 | * signal can arrive just after syscall return and handler can | 5175 | * signal can arrive just after syscall return and handler can |
| 5035 | * longjmp away, losing stop/exit notification processing. | 5176 | * longjmp away, losing stop/exit notification processing. |
| 5036 | * Thus, for "jobs" builtin, and for waiting for a fg job, | 5177 | * Thus, for "jobs" builtin, and for waiting for a fg job, |
| 5037 | * we call waitpid() (blocking or non-blocking) inside INT_OFF. | 5178 | * we call waitpid() (blocking or non-blocking) inside INTOFF. |
| 5038 | * | 5179 | * |
| 5039 | * However, for "wait" builtin it is wrong to simply call waitpid() | 5180 | * However, for "wait" builtin it is wrong to simply call waitpid() |
| 5040 | * in INT_OFF region: "wait" needs to wait for any running job | 5181 | * in INTOFF region: "wait" needs to wait for any running job |
| 5041 | * to change state, but should exit on any trap too. | 5182 | * to change state, but should exit on any trap too. |
| 5042 | * In INT_OFF region, a signal just before syscall entry can set | 5183 | * In INTOFF region, a signal just before syscall entry can set |
| 5043 | * pending_sig variables, but we can't check them, and we would | 5184 | * pending_sig variables, but we can't check them, and we would |
| 5044 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. | 5185 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. |
| 5045 | * | 5186 | * |
| 5046 | * Because of this, we run inside INT_OFF, but use a special routine | 5187 | * Because of this, we run inside INTOFF, but use a special routine |
| 5047 | * which combines waitpid() and sigsuspend(). | 5188 | * which combines waitpid() and sigsuspend(). |
| 5048 | * This is the reason why we need to have a handler for SIGCHLD: | 5189 | * This is the reason why we need to have a handler for SIGCHLD: |
| 5049 | * SIG_DFL handler does not wake sigsuspend(). | 5190 | * SIG_DFL handler does not wake sigsuspend(). |
| 5050 | */ | 5191 | */ |
| 5051 | INT_OFF; | 5192 | INTOFF; |
| 5052 | pid = waitproc(block, &status); | 5193 | pid = waitproc(block, &status); |
| 5053 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); | 5194 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); |
| 5054 | if (pid <= 0) | 5195 | if (pid <= 0) |
| @@ -5056,36 +5197,34 @@ waitone(int block, struct job *job) | |||
| 5056 | 5197 | ||
| 5057 | for (jp = curjob; jp; jp = jp->prev_job) { | 5198 | for (jp = curjob; jp; jp = jp->prev_job) { |
| 5058 | int jobstate; | 5199 | int jobstate; |
| 5059 | struct procstat *ps; | 5200 | struct procstat *sp; |
| 5060 | struct procstat *psend; | 5201 | struct procstat *spend; |
| 5061 | if (jp->state == JOBDONE) | 5202 | if (jp->state == JOBDONE) |
| 5062 | continue; | 5203 | continue; |
| 5063 | jobstate = JOBDONE; | 5204 | jobstate = JOBDONE; |
| 5064 | ps = jp->ps; | 5205 | spend = jp->ps + jp->nprocs; |
| 5065 | psend = ps + jp->nprocs; | 5206 | sp = jp->ps; |
| 5066 | do { | 5207 | do { |
| 5067 | if (ps->ps_pid == pid) { | 5208 | if (sp->ps_pid == pid) { |
| 5068 | TRACE(("Job %d: changing status of proc %d " | 5209 | TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->ps_status, status)); |
| 5069 | "from 0x%x to 0x%x\n", | 5210 | sp->ps_status = status; |
| 5070 | jobno(jp), pid, ps->ps_status, status)); | ||
| 5071 | ps->ps_status = status; | ||
| 5072 | thisjob = jp; | 5211 | thisjob = jp; |
| 5073 | #if ENABLE_PLATFORM_MINGW32 | 5212 | #if ENABLE_PLATFORM_MINGW32 |
| 5074 | CloseHandle(ps->ps_proc); | 5213 | CloseHandle(sp->ps_proc); |
| 5075 | ps->ps_proc = NULL; | 5214 | sp->ps_proc = NULL; |
| 5076 | #endif | 5215 | #endif |
| 5077 | } | 5216 | } |
| 5078 | if (ps->ps_status == -1) | 5217 | if (sp->ps_status == -1) |
| 5079 | jobstate = JOBRUNNING; | 5218 | jobstate = JOBRUNNING; |
| 5080 | #if JOBS | 5219 | #if JOBS |
| 5081 | if (jobstate == JOBRUNNING) | 5220 | if (jobstate == JOBRUNNING) |
| 5082 | continue; | 5221 | continue; |
| 5083 | if (WIFSTOPPED(ps->ps_status)) { | 5222 | if (WIFSTOPPED(sp->ps_status)) { |
| 5084 | jp->stopstatus = ps->ps_status; | 5223 | jp->stopstatus = sp->ps_status; |
| 5085 | jobstate = JOBSTOPPED; | 5224 | jobstate = JOBSTOPPED; |
| 5086 | } | 5225 | } |
| 5087 | #endif | 5226 | #endif |
| 5088 | } while (++ps < psend); | 5227 | } while (++sp < spend); |
| 5089 | if (!thisjob) | 5228 | if (!thisjob) |
| 5090 | continue; | 5229 | continue; |
| 5091 | 5230 | ||
| @@ -5097,8 +5236,7 @@ waitone(int block, struct job *job) | |||
| 5097 | */ | 5236 | */ |
| 5098 | thisjob->changed = 1; | 5237 | thisjob->changed = 1; |
| 5099 | if (thisjob->state != jobstate) { | 5238 | if (thisjob->state != jobstate) { |
| 5100 | TRACE(("Job %d: changing state from %d to %d\n", | 5239 | TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, jobstate)); |
| 5101 | jobno(thisjob), thisjob->state, jobstate)); | ||
| 5102 | thisjob->state = jobstate; | 5240 | thisjob->state = jobstate; |
| 5103 | #if JOBS | 5241 | #if JOBS |
| 5104 | if (jobstate == JOBSTOPPED) | 5242 | if (jobstate == JOBSTOPPED) |
| @@ -5110,7 +5248,7 @@ waitone(int block, struct job *job) | |||
| 5110 | } | 5248 | } |
| 5111 | /* The process wasn't found in job list */ | 5249 | /* The process wasn't found in job list */ |
| 5112 | out: | 5250 | out: |
| 5113 | INT_ON; | 5251 | INTON; |
| 5114 | 5252 | ||
| 5115 | #if BASH_WAIT_N | 5253 | #if BASH_WAIT_N |
| 5116 | if (want_jobexitstatus) { | 5254 | if (want_jobexitstatus) { |
| @@ -5133,11 +5271,10 @@ waitone(int block, struct job *job) | |||
| 5133 | return pid; | 5271 | return pid; |
| 5134 | } | 5272 | } |
| 5135 | 5273 | ||
| 5136 | static int | 5274 | static int dowait(int block, struct job *jp) |
| 5137 | dowait(int block, struct job *jp) | ||
| 5138 | { | 5275 | { |
| 5139 | #if !ENABLE_PLATFORM_MINGW32 | 5276 | #if !ENABLE_PLATFORM_MINGW32 |
| 5140 | smallint gotchld = *(volatile smallint *)&got_sigchld; | 5277 | smallint gotchld = *(volatile smallint *)&gotsigchld; |
| 5141 | int rpid; | 5278 | int rpid; |
| 5142 | int pid; | 5279 | int pid; |
| 5143 | 5280 | ||
| @@ -5161,7 +5298,7 @@ dowait(int block, struct job *jp) | |||
| 5161 | #else | 5298 | #else |
| 5162 | int pid = 1; | 5299 | int pid = 1; |
| 5163 | 5300 | ||
| 5164 | while (jp ? jp->state == JOBRUNNING : pid > 0) | 5301 | while ((jp ? jp->state == JOBRUNNING : pid > 0) && waitcmd_int != 1) |
| 5165 | pid = waitone(block, jp); | 5302 | pid = waitone(block, jp); |
| 5166 | 5303 | ||
| 5167 | return pid; | 5304 | return pid; |
| @@ -5374,17 +5511,28 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
| 5374 | * with an exit status greater than 128, immediately after which | 5511 | * with an exit status greater than 128, immediately after which |
| 5375 | * the trap is executed." | 5512 | * the trap is executed." |
| 5376 | */ | 5513 | */ |
| 5514 | #if ENABLE_PLATFORM_MINGW32 | ||
| 5515 | waitcmd_int = -1; | ||
| 5516 | #endif | ||
| 5377 | #if BASH_WAIT_N | 5517 | #if BASH_WAIT_N |
| 5378 | status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL); | 5518 | status = dowait(DOWAIT_CHILD_OR_SIG | DOWAIT_JOBSTATUS, NULL); |
| 5379 | #else | 5519 | #else |
| 5380 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); | 5520 | dowait(DOWAIT_CHILD_OR_SIG, NULL); |
| 5381 | #endif | 5521 | #endif |
| 5382 | /* if child sends us a signal *and immediately exits*, | 5522 | /* if child sends us a signal *and immediately exits*, |
| 5383 | * dowait() returns pid > 0. Check this case, | 5523 | * dowait() returns pid > 0. Check this case, |
| 5384 | * not "if (dowait() < 0)"! | 5524 | * not "if (dowait() < 0)"! |
| 5385 | */ | 5525 | */ |
| 5526 | #if ENABLE_PLATFORM_MINGW32 | ||
| 5527 | if (waitcmd_int == 1) { | ||
| 5528 | write_ctrl_c(); | ||
| 5529 | return 128 | SIGINT; | ||
| 5530 | } | ||
| 5531 | waitcmd_int = 0; | ||
| 5532 | #else | ||
| 5386 | if (pending_sig) | 5533 | if (pending_sig) |
| 5387 | goto sigout; | 5534 | goto sigout; |
| 5535 | #endif | ||
| 5388 | #if BASH_WAIT_N | 5536 | #if BASH_WAIT_N |
| 5389 | if (one) { | 5537 | if (one) { |
| 5390 | /* wait -n waits for one _job_, not one _process_. | 5538 | /* wait -n waits for one _job_, not one _process_. |
| @@ -5418,7 +5566,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
| 5418 | job = getjob(*argv, 0); | 5566 | job = getjob(*argv, 0); |
| 5419 | } | 5567 | } |
| 5420 | /* loop until process terminated or stopped */ | 5568 | /* loop until process terminated or stopped */ |
| 5421 | dowait(DOWAIT_BLOCK_OR_SIG, job); | 5569 | dowait(DOWAIT_CHILD_OR_SIG, job); |
| 5422 | if (pending_sig) | 5570 | if (pending_sig) |
| 5423 | goto sigout; | 5571 | goto sigout; |
| 5424 | job->waited = 1; | 5572 | job->waited = 1; |
| @@ -5481,7 +5629,7 @@ growjobtab(void) | |||
| 5481 | * Called with interrupts off. | 5629 | * Called with interrupts off. |
| 5482 | */ | 5630 | */ |
| 5483 | static struct job * | 5631 | static struct job * |
| 5484 | makejob(/*union node *node,*/ int nprocs) | 5632 | makejob(int nprocs) |
| 5485 | { | 5633 | { |
| 5486 | int i; | 5634 | int i; |
| 5487 | struct job *jp; | 5635 | struct job *jp; |
| @@ -5496,7 +5644,7 @@ makejob(/*union node *node,*/ int nprocs) | |||
| 5496 | if (jp->state != JOBDONE || !jp->waited) | 5644 | if (jp->state != JOBDONE || !jp->waited) |
| 5497 | continue; | 5645 | continue; |
| 5498 | #if JOBS || JOBS_WIN32 | 5646 | #if JOBS || JOBS_WIN32 |
| 5499 | if (doing_jobctl) | 5647 | if (jobctl) |
| 5500 | continue; | 5648 | continue; |
| 5501 | #endif | 5649 | #endif |
| 5502 | freejob(jp); | 5650 | freejob(jp); |
| @@ -5505,8 +5653,8 @@ makejob(/*union node *node,*/ int nprocs) | |||
| 5505 | memset(jp, 0, sizeof(*jp)); | 5653 | memset(jp, 0, sizeof(*jp)); |
| 5506 | #if JOBS | 5654 | #if JOBS |
| 5507 | /* jp->jobctl is a bitfield. | 5655 | /* jp->jobctl is a bitfield. |
| 5508 | * "jp->jobctl |= doing_jobctl" likely to give awful code */ | 5656 | * "jp->jobctl |= jobctl" likely to give awful code */ |
| 5509 | if (doing_jobctl) | 5657 | if (jobctl) |
| 5510 | jp->jobctl = 1; | 5658 | jp->jobctl = 1; |
| 5511 | #endif | 5659 | #endif |
| 5512 | jp->prev_job = curjob; | 5660 | jp->prev_job = curjob; |
| @@ -5531,13 +5679,6 @@ static char *cmdnextc; | |||
| 5531 | static void | 5679 | static void |
| 5532 | cmdputs(const char *s) | 5680 | cmdputs(const char *s) |
| 5533 | { | 5681 | { |
| 5534 | static const char vstype[VSTYPE + 1][3] ALIGN1 = { | ||
| 5535 | "", "}", "-", "+", "?", "=", | ||
| 5536 | "%", "%%", "#", "##" | ||
| 5537 | IF_BASH_SUBSTR(, ":") | ||
| 5538 | IF_BASH_PATTERN_SUBST(, "/", "//") | ||
| 5539 | }; | ||
| 5540 | |||
| 5541 | const char *p, *str; | 5682 | const char *p, *str; |
| 5542 | char cc[2]; | 5683 | char cc[2]; |
| 5543 | char *nextc; | 5684 | char *nextc; |
| @@ -5600,32 +5741,34 @@ cmdputs(const char *s) | |||
| 5600 | case '=': | 5741 | case '=': |
| 5601 | if (subtype == 0) | 5742 | if (subtype == 0) |
| 5602 | break; | 5743 | break; |
| 5744 | /* We are in variable name */ | ||
| 5603 | if ((subtype & VSTYPE) != VSNORMAL) | 5745 | if ((subtype & VSTYPE) != VSNORMAL) |
| 5604 | quoted <<= 1; | 5746 | quoted <<= 1; |
| 5605 | str = vstype[subtype & VSTYPE]; | 5747 | str = vstype_suffix[(subtype & VSTYPE) - VSNORMAL]; |
| 5606 | if (subtype & VSNUL) | 5748 | if (!(subtype & VSNUL)) |
| 5607 | c = ':'; | 5749 | goto dostr; |
| 5608 | else | 5750 | c = ':'; |
| 5609 | goto checkstr; | ||
| 5610 | break; | 5751 | break; |
| 5611 | case '\'': | 5752 | case '$': |
| 5753 | /* Can happen inside quotes, or in variable name $$ */ | ||
| 5754 | if (subtype != 0) | ||
| 5755 | // Testcase: | ||
| 5756 | // $ true $$ & | ||
| 5757 | // $ <cr> | ||
| 5758 | // [1]+ Done true ${$} // shows ${\$} without "if (subtype)" check | ||
| 5759 | break; | ||
| 5760 | /* Not in variable name - show as \$ */ | ||
| 5761 | case '\'': /* These can only happen inside quotes */ | ||
| 5612 | case '\\': | 5762 | case '\\': |
| 5613 | case '"': | 5763 | case '"': |
| 5614 | case '$': | ||
| 5615 | /* These can only happen inside quotes */ | ||
| 5616 | cc[0] = c; | 5764 | cc[0] = c; |
| 5617 | str = cc; | 5765 | str = cc; |
| 5618 | //FIXME: | ||
| 5619 | // $ true $$ & | ||
| 5620 | // $ <cr> | ||
| 5621 | // [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok) | ||
| 5622 | c = '\\'; | 5766 | c = '\\'; |
| 5623 | break; | 5767 | break; |
| 5624 | default: | 5768 | default: |
| 5625 | break; | 5769 | break; |
| 5626 | } | 5770 | } |
| 5627 | USTPUTC(c, nextc); | 5771 | USTPUTC(c, nextc); |
| 5628 | checkstr: | ||
| 5629 | if (!str) | 5772 | if (!str) |
| 5630 | continue; | 5773 | continue; |
| 5631 | dostr: | 5774 | dostr: |
| @@ -5637,7 +5780,7 @@ cmdputs(const char *s) | |||
| 5637 | if (quoted & 1) { | 5780 | if (quoted & 1) { |
| 5638 | USTPUTC('"', nextc); | 5781 | USTPUTC('"', nextc); |
| 5639 | } | 5782 | } |
| 5640 | *nextc = 0; | 5783 | *nextc = '\0'; |
| 5641 | cmdnextc = nextc; | 5784 | cmdnextc = nextc; |
| 5642 | } | 5785 | } |
| 5643 | 5786 | ||
| @@ -5761,6 +5904,9 @@ cmdtxt(union node *n) | |||
| 5761 | case NXHERE: | 5904 | case NXHERE: |
| 5762 | p = "<<..."; | 5905 | p = "<<..."; |
| 5763 | goto dotail2; | 5906 | goto dotail2; |
| 5907 | case NFROMSTR: | ||
| 5908 | p = "<<<"; | ||
| 5909 | goto dotail2; | ||
| 5764 | case NCASE: | 5910 | case NCASE: |
| 5765 | cmdputs("case "); | 5911 | cmdputs("case "); |
| 5766 | cmdputs(n->ncase.expr->narg.text); | 5912 | cmdputs(n->ncase.expr->narg.text); |
| @@ -5848,7 +5994,7 @@ clear_traps(void) | |||
| 5848 | { | 5994 | { |
| 5849 | char **tp; | 5995 | char **tp; |
| 5850 | 5996 | ||
| 5851 | INT_OFF; | 5997 | INTOFF; |
| 5852 | for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) { | 5998 | for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) { |
| 5853 | if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ | 5999 | if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ |
| 5854 | if (trap_ptr == trap) | 6000 | if (trap_ptr == trap) |
| @@ -5860,7 +6006,7 @@ clear_traps(void) | |||
| 5860 | } | 6006 | } |
| 5861 | } | 6007 | } |
| 5862 | may_have_traps = 0; | 6008 | may_have_traps = 0; |
| 5863 | INT_ON; | 6009 | INTON; |
| 5864 | } | 6010 | } |
| 5865 | 6011 | ||
| 5866 | #if !ENABLE_PLATFORM_MINGW32 | 6012 | #if !ENABLE_PLATFORM_MINGW32 |
| @@ -5872,18 +6018,28 @@ static void closescript(void); | |||
| 5872 | static NOINLINE void | 6018 | static NOINLINE void |
| 5873 | forkchild(struct job *jp, union node *n, int mode) | 6019 | forkchild(struct job *jp, union node *n, int mode) |
| 5874 | { | 6020 | { |
| 6021 | int lvforked; | ||
| 5875 | int oldlvl; | 6022 | int oldlvl; |
| 5876 | 6023 | ||
| 5877 | TRACE(("Child shell %d\n", getpid())); | 6024 | TRACE(("Child shell %d\n", getpid())); |
| 5878 | oldlvl = shlvl; | 6025 | oldlvl = shlvl; |
| 5879 | shlvl++; | 6026 | lvforked = vforked; |
| 6027 | |||
| 6028 | if (!lvforked) { | ||
| 6029 | shlvl++; | ||
| 6030 | |||
| 6031 | closescript(); | ||
| 6032 | |||
| 6033 | #if JOBS | ||
| 6034 | /* do job control only in root shell */ | ||
| 6035 | jobctl = 0; | ||
| 6036 | #endif | ||
| 6037 | } | ||
| 5880 | 6038 | ||
| 5881 | /* man bash: "Non-builtin commands run by bash have signal handlers | 6039 | /* man bash: "Non-builtin commands run by bash have signal handlers |
| 5882 | * set to the values inherited by the shell from its parent". | 6040 | * set to the values inherited by the shell from its parent". |
| 5883 | * Do we do it correctly? */ | 6041 | * Do we do it correctly? */ |
| 5884 | 6042 | ||
| 5885 | closescript(); | ||
| 5886 | |||
| 5887 | if (n && n->type == NCMD /* is it single cmd? */ | 6043 | if (n && n->type == NCMD /* is it single cmd? */ |
| 5888 | /* && n->ncmd.args->type == NARG - always true? */ | 6044 | /* && n->ncmd.args->type == NARG - always true? */ |
| 5889 | && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 | 6045 | && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 |
| @@ -5929,10 +6085,9 @@ forkchild(struct job *jp, union node *n, int mode) | |||
| 5929 | trap_ptr = xmemdup(trap, sizeof(trap)); | 6085 | trap_ptr = xmemdup(trap, sizeof(trap)); |
| 5930 | /* Fall through into clearing traps */ | 6086 | /* Fall through into clearing traps */ |
| 5931 | } | 6087 | } |
| 5932 | clear_traps(); | 6088 | if (!lvforked) |
| 6089 | clear_traps(); | ||
| 5933 | #if JOBS | 6090 | #if JOBS |
| 5934 | /* do job control only in root shell */ | ||
| 5935 | doing_jobctl = 0; | ||
| 5936 | if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { | 6091 | if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { |
| 5937 | pid_t pgrp; | 6092 | pid_t pgrp; |
| 5938 | 6093 | ||
| @@ -5955,8 +6110,7 @@ forkchild(struct job *jp, union node *n, int mode) | |||
| 5955 | ignoresig(SIGQUIT); | 6110 | ignoresig(SIGQUIT); |
| 5956 | if (jp->nprocs == 0) { | 6111 | if (jp->nprocs == 0) { |
| 5957 | close(0); | 6112 | close(0); |
| 5958 | if (open(bb_dev_null, O_RDONLY) != 0) | 6113 | sh_open(_PATH_DEVNULL, O_RDONLY, 0); |
| 5959 | ash_msg_and_raise_perror("can't open '%s'", bb_dev_null); | ||
| 5960 | } | 6114 | } |
| 5961 | } | 6115 | } |
| 5962 | if (oldlvl == 0) { | 6116 | if (oldlvl == 0) { |
| @@ -5987,6 +6141,10 @@ forkchild(struct job *jp, union node *n, int mode) | |||
| 5987 | return; | 6141 | return; |
| 5988 | } | 6142 | } |
| 5989 | #endif | 6143 | #endif |
| 6144 | |||
| 6145 | if (lvforked) | ||
| 6146 | return; | ||
| 6147 | |||
| 5990 | for (jp = curjob; jp; jp = jp->prev_job) | 6148 | for (jp = curjob; jp; jp = jp->prev_job) |
| 5991 | freejob(jp); | 6149 | freejob(jp); |
| 5992 | } | 6150 | } |
| @@ -6005,8 +6163,17 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc) | |||
| 6005 | { | 6163 | { |
| 6006 | #if ENABLE_PLATFORM_MINGW32 | 6164 | #if ENABLE_PLATFORM_MINGW32 |
| 6007 | pid_t pid = GetProcessId(proc); | 6165 | pid_t pid = GetProcessId(proc); |
| 6166 | #else | ||
| 6167 | if (pid < 0) { | ||
| 6168 | TRACE(("Fork failed, errno=%d", errno)); | ||
| 6169 | if (jp) | ||
| 6170 | freejob(jp); | ||
| 6171 | ash_msg_and_raise_perror("can't fork"); | ||
| 6172 | /* NOTREACHED */ | ||
| 6173 | } | ||
| 6008 | #endif | 6174 | #endif |
| 6009 | TRACE(("In parent shell: child = %d\n", pid)); | 6175 | |
| 6176 | TRACE(("In parent shell: child = %d\n", pid)); | ||
| 6010 | if (!jp) /* jp is NULL when called by openhere() for heredoc support */ | 6177 | if (!jp) /* jp is NULL when called by openhere() for heredoc support */ |
| 6011 | return; | 6178 | return; |
| 6012 | #if JOBS | 6179 | #if JOBS |
| @@ -6040,7 +6207,7 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc) | |||
| 6040 | ps->ps_proc = proc; | 6207 | ps->ps_proc = proc; |
| 6041 | #endif | 6208 | #endif |
| 6042 | #if JOBS || JOBS_WIN32 | 6209 | #if JOBS || JOBS_WIN32 |
| 6043 | if (doing_jobctl && n) | 6210 | if (jobctl && n) |
| 6044 | ps->ps_cmd = commandtext(n); | 6211 | ps->ps_cmd = commandtext(n); |
| 6045 | #endif | 6212 | #endif |
| 6046 | } | 6213 | } |
| @@ -6055,20 +6222,43 @@ forkshell(struct job *jp, union node *n, int mode) | |||
| 6055 | 6222 | ||
| 6056 | TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); | 6223 | TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); |
| 6057 | pid = fork(); | 6224 | pid = fork(); |
| 6058 | if (pid < 0) { | ||
| 6059 | TRACE(("Fork failed, errno=%d", errno)); | ||
| 6060 | if (jp) | ||
| 6061 | freejob(jp); | ||
| 6062 | ash_msg_and_raise_perror("can't fork"); | ||
| 6063 | } | ||
| 6064 | if (pid == 0) { | 6225 | if (pid == 0) { |
| 6065 | CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ | 6226 | CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ |
| 6066 | forkchild(jp, n, mode); | 6227 | forkchild(jp, n, mode); |
| 6067 | } else { | 6228 | } else |
| 6068 | forkparent(jp, n, mode, pid); | 6229 | forkparent(jp, n, mode, pid); |
| 6069 | } | 6230 | |
| 6070 | return pid; | 6231 | return pid; |
| 6071 | } | 6232 | } |
| 6233 | |||
| 6234 | static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; | ||
| 6235 | |||
| 6236 | static struct job* | ||
| 6237 | vforkexec(union node *n, char **argv, const char *path, int idx) | ||
| 6238 | { | ||
| 6239 | struct job *jp; | ||
| 6240 | int pid; | ||
| 6241 | |||
| 6242 | jp = makejob(1); | ||
| 6243 | |||
| 6244 | sigblockall(NULL); | ||
| 6245 | vforked = 1; | ||
| 6246 | |||
| 6247 | pid = vfork(); | ||
| 6248 | |||
| 6249 | if (!pid) { | ||
| 6250 | forkchild(jp, n, FORK_FG); | ||
| 6251 | sigclearmask(); | ||
| 6252 | shellexec(argv[0], argv, path, idx); | ||
| 6253 | /* NOTREACHED */ | ||
| 6254 | } | ||
| 6255 | |||
| 6256 | vforked = 0; | ||
| 6257 | sigclearmask(); | ||
| 6258 | forkparent(jp, n, FORK_FG, pid); | ||
| 6259 | |||
| 6260 | return jp; | ||
| 6261 | } | ||
| 6072 | #endif | 6262 | #endif |
| 6073 | 6263 | ||
| 6074 | /* | 6264 | /* |
| @@ -6193,7 +6383,6 @@ stoppedjobs(void) | |||
| 6193 | } | 6383 | } |
| 6194 | #endif | 6384 | #endif |
| 6195 | 6385 | ||
| 6196 | |||
| 6197 | /* | 6386 | /* |
| 6198 | * Code for dealing with input/output redirection. | 6387 | * Code for dealing with input/output redirection. |
| 6199 | */ | 6388 | */ |
| @@ -6208,26 +6397,11 @@ stoppedjobs(void) | |||
| 6208 | * data to a pipe. If the document is short, we can stuff the data in | 6397 | * data to a pipe. If the document is short, we can stuff the data in |
| 6209 | * the pipe without forking. | 6398 | * the pipe without forking. |
| 6210 | */ | 6399 | */ |
| 6211 | /* openhere needs this forward reference */ | ||
| 6212 | static void expandhere(union node *arg); | ||
| 6213 | static int | 6400 | static int |
| 6214 | openhere(union node *redir) | 6401 | write2pipe(int pip[2], const char *p, size_t len) |
| 6215 | { | 6402 | { |
| 6216 | char *p; | ||
| 6217 | int pip[2]; | ||
| 6218 | size_t len = 0; | ||
| 6219 | IF_PLATFORM_MINGW32(struct forkshell fs); | 6403 | IF_PLATFORM_MINGW32(struct forkshell fs); |
| 6220 | 6404 | ||
| 6221 | if (pipe(pip) < 0) | ||
| 6222 | ash_msg_and_raise_perror("can't create pipe"); | ||
| 6223 | |||
| 6224 | p = redir->nhere.doc->narg.text; | ||
| 6225 | if (redir->type == NXHERE) { | ||
| 6226 | expandhere(redir->nhere.doc); | ||
| 6227 | p = stackblock(); | ||
| 6228 | } | ||
| 6229 | |||
| 6230 | len = strlen(p); | ||
| 6231 | if (len <= PIPE_BUF) { | 6405 | if (len <= PIPE_BUF) { |
| 6232 | xwrite(pip[1], p, len); | 6406 | xwrite(pip[1], p, len); |
| 6233 | goto out; | 6407 | goto out; |
| @@ -6258,89 +6432,113 @@ openhere(union node *redir) | |||
| 6258 | return pip[0]; | 6432 | return pip[0]; |
| 6259 | } | 6433 | } |
| 6260 | 6434 | ||
| 6435 | /* openhere needs this forward reference */ | ||
| 6436 | static void expandhere(union node *arg); | ||
| 6437 | static int | ||
| 6438 | openhere(union node *redir) | ||
| 6439 | { | ||
| 6440 | char *p; | ||
| 6441 | int pip[2]; | ||
| 6442 | |||
| 6443 | if (pipe(pip) < 0) | ||
| 6444 | ash_msg_and_raise_perror("can't create pipe"); | ||
| 6445 | |||
| 6446 | p = redir->nhere.doc->narg.text; | ||
| 6447 | if (redir->type == NXHERE) { | ||
| 6448 | expandhere(redir->nhere.doc); | ||
| 6449 | p = stackblock(); | ||
| 6450 | } | ||
| 6451 | |||
| 6452 | return write2pipe(pip, p, strlen(p)); | ||
| 6453 | } | ||
| 6454 | |||
| 6455 | static int | ||
| 6456 | openherestr(char *str) | ||
| 6457 | { | ||
| 6458 | int pip[2]; | ||
| 6459 | size_t len; | ||
| 6460 | |||
| 6461 | if (pipe(pip) < 0) | ||
| 6462 | ash_msg_and_raise_perror("can't create pipe"); | ||
| 6463 | |||
| 6464 | len = strlen(str); | ||
| 6465 | str[len] = '\n'; | ||
| 6466 | write2pipe(pip, str, len + 1); | ||
| 6467 | str[len] = '\0'; | ||
| 6468 | return pip[0]; | ||
| 6469 | } | ||
| 6470 | |||
| 6261 | static int | 6471 | static int |
| 6262 | openredirect(union node *redir) | 6472 | openredirect(union node *redir) |
| 6263 | { | 6473 | { |
| 6264 | struct stat sb; | 6474 | struct stat sb; |
| 6265 | char *fname; | 6475 | char *fname; |
| 6476 | int flags; | ||
| 6266 | int f; | 6477 | int f; |
| 6267 | 6478 | ||
| 6268 | switch (redir->nfile.type) { | 6479 | switch (redir->nfile.type) { |
| 6269 | /* Can't happen, our single caller does this itself */ | ||
| 6270 | // case NTOFD: | ||
| 6271 | // case NFROMFD: | ||
| 6272 | // return -1; | ||
| 6273 | case NHERE: | ||
| 6274 | case NXHERE: | ||
| 6275 | return openhere(redir); | ||
| 6276 | } | ||
| 6277 | |||
| 6278 | /* For N[X]HERE, reading redir->nfile.expfname would touch beyond | ||
| 6279 | * allocated space. Do it only when we know it is safe. | ||
| 6280 | */ | ||
| 6281 | fname = redir->nfile.expfname; | ||
| 6282 | |||
| 6283 | switch (redir->nfile.type) { | ||
| 6284 | default: | ||
| 6285 | #if DEBUG | ||
| 6286 | abort(); | ||
| 6287 | #endif | ||
| 6288 | case NFROM: | 6480 | case NFROM: |
| 6289 | f = open(fname, O_RDONLY); | 6481 | flags = O_RDONLY; |
| 6290 | if (f < 0) | 6482 | do_open: |
| 6291 | goto eopen; | 6483 | f = sh_open(redir->nfile.expfname, flags, 0); |
| 6484 | #if ENABLE_PLATFORM_MINGW32 | ||
| 6485 | if (redir->nfile.type == NAPPEND) | ||
| 6486 | lseek(f, 0, SEEK_END); | ||
| 6487 | #endif | ||
| 6292 | break; | 6488 | break; |
| 6293 | case NFROMTO: | 6489 | case NFROMSTR: |
| 6294 | f = open(fname, O_RDWR|O_CREAT, 0666); | 6490 | f = openherestr(redir->nfile.expfname); |
| 6295 | if (f < 0) | ||
| 6296 | goto ecreate; | ||
| 6297 | break; | 6491 | break; |
| 6492 | case NFROMTO: | ||
| 6493 | flags = O_RDWR|O_CREAT; | ||
| 6494 | goto do_open; | ||
| 6298 | case NTO: | 6495 | case NTO: |
| 6299 | #if BASH_REDIR_OUTPUT | 6496 | #if BASH_REDIR_OUTPUT |
| 6300 | case NTO2: | 6497 | case NTO2: |
| 6301 | #endif | 6498 | #endif |
| 6302 | /* Take care of noclobber mode. */ | 6499 | /* Take care of noclobber mode. */ |
| 6303 | if (Cflag) { | 6500 | if (Cflag) { |
| 6501 | fname = redir->nfile.expfname; | ||
| 6304 | if (stat(fname, &sb) < 0) { | 6502 | if (stat(fname, &sb) < 0) { |
| 6305 | f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); | 6503 | flags = O_WRONLY|O_CREAT|O_EXCL; |
| 6306 | if (f < 0) | 6504 | goto do_open; |
| 6307 | goto ecreate; | 6505 | } |
| 6308 | } else if (!S_ISREG(sb.st_mode)) { | 6506 | |
| 6309 | f = open(fname, O_WRONLY, 0666); | 6507 | if (S_ISREG(sb.st_mode)) |
| 6310 | if (f < 0) | 6508 | goto ecreate; |
| 6311 | goto ecreate; | 6509 | |
| 6312 | if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) { | 6510 | f = sh_open(fname, O_WRONLY, 0); |
| 6313 | close(f); | 6511 | if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) { |
| 6314 | errno = EEXIST; | 6512 | close(f); |
| 6315 | goto ecreate; | ||
| 6316 | } | ||
| 6317 | } else { | ||
| 6318 | errno = EEXIST; | ||
| 6319 | goto ecreate; | 6513 | goto ecreate; |
| 6320 | } | 6514 | } |
| 6321 | break; | 6515 | break; |
| 6322 | } | 6516 | } |
| 6323 | /* FALLTHROUGH */ | 6517 | /* FALLTHROUGH */ |
| 6324 | case NCLOBBER: | 6518 | case NCLOBBER: |
| 6325 | f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666); | 6519 | flags = O_WRONLY|O_CREAT|O_TRUNC; |
| 6326 | if (f < 0) | 6520 | goto do_open; |
| 6327 | goto ecreate; | ||
| 6328 | break; | ||
| 6329 | case NAPPEND: | 6521 | case NAPPEND: |
| 6330 | f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); | 6522 | flags = O_WRONLY|O_CREAT|O_APPEND; |
| 6331 | if (f < 0) | 6523 | goto do_open; |
| 6332 | goto ecreate; | 6524 | /* Can't happen, our single caller does this itself */ |
| 6333 | #if ENABLE_PLATFORM_MINGW32 | 6525 | // case NTOFD: |
| 6334 | lseek(f, 0, SEEK_END); | 6526 | // case NFROMFD: |
| 6527 | // return -1; | ||
| 6528 | default: | ||
| 6529 | #ifdef DEBUG | ||
| 6530 | abort(); | ||
| 6335 | #endif | 6531 | #endif |
| 6532 | /* Fall through to eliminate warning. */ | ||
| 6533 | case NHERE: | ||
| 6534 | case NXHERE: | ||
| 6535 | f = openhere(redir); | ||
| 6336 | break; | 6536 | break; |
| 6337 | } | 6537 | } |
| 6338 | 6538 | ||
| 6339 | return f; | 6539 | return f; |
| 6340 | ecreate: | 6540 | ecreate: |
| 6341 | ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory")); | 6541 | sh_open_fail(fname, O_CREAT, EEXIST); |
| 6342 | eopen: | ||
| 6343 | ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file")); | ||
| 6344 | } | 6542 | } |
| 6345 | 6543 | ||
| 6346 | /* | 6544 | /* |
| @@ -6593,7 +6791,7 @@ redirect(union node *redir, int flags) | |||
| 6593 | return; | 6791 | return; |
| 6594 | 6792 | ||
| 6595 | sv = NULL; | 6793 | sv = NULL; |
| 6596 | INT_OFF; | 6794 | INTOFF; |
| 6597 | if (flags & REDIR_PUSH) | 6795 | if (flags & REDIR_PUSH) |
| 6598 | sv = redirlist; | 6796 | sv = redirlist; |
| 6599 | do { | 6797 | do { |
| @@ -6658,7 +6856,7 @@ redirect(union node *redir, int flags) | |||
| 6658 | #endif | 6856 | #endif |
| 6659 | } | 6857 | } |
| 6660 | } while ((redir = redir->nfile.next) != NULL); | 6858 | } while ((redir = redir->nfile.next) != NULL); |
| 6661 | INT_ON; | 6859 | INTON; |
| 6662 | 6860 | ||
| 6663 | //dash:#define REDIR_SAVEFD2 03 /* set preverrout */ | 6861 | //dash:#define REDIR_SAVEFD2 03 /* set preverrout */ |
| 6664 | #define REDIR_SAVEFD2 0 | 6862 | #define REDIR_SAVEFD2 0 |
| @@ -6746,7 +6944,7 @@ popredir(int drop) | |||
| 6746 | 6944 | ||
| 6747 | if (redirlist == NULL) | 6945 | if (redirlist == NULL) |
| 6748 | return; | 6946 | return; |
| 6749 | INT_OFF; | 6947 | INTOFF; |
| 6750 | rp = redirlist; | 6948 | rp = redirlist; |
| 6751 | for (i = 0; i < rp->pair_count; i++) { | 6949 | for (i = 0; i < rp->pair_count; i++) { |
| 6752 | int fd = rp->two_fd[i].orig_fd; | 6950 | int fd = rp->two_fd[i].orig_fd; |
| @@ -6766,7 +6964,7 @@ popredir(int drop) | |||
| 6766 | } | 6964 | } |
| 6767 | redirlist = rp->next; | 6965 | redirlist = rp->next; |
| 6768 | free(rp); | 6966 | free(rp); |
| 6769 | INT_ON; | 6967 | INTON; |
| 6770 | } | 6968 | } |
| 6771 | 6969 | ||
| 6772 | static void | 6970 | static void |
| @@ -6793,11 +6991,11 @@ ash_arith(const char *s) | |||
| 6793 | math_state.setvar = setvar0; | 6991 | math_state.setvar = setvar0; |
| 6794 | //math_state.endofname = endofname; | 6992 | //math_state.endofname = endofname; |
| 6795 | 6993 | ||
| 6796 | INT_OFF; | 6994 | INTOFF; |
| 6797 | result = arith(&math_state, s); | 6995 | result = arith(&math_state, s); |
| 6798 | if (math_state.errmsg) | 6996 | if (math_state.errmsg) |
| 6799 | ash_msg_and_raise_error(math_state.errmsg); | 6997 | ash_msg_and_raise_error(math_state.errmsg); |
| 6800 | INT_ON; | 6998 | INTON; |
| 6801 | 6999 | ||
| 6802 | return result; | 7000 | return result; |
| 6803 | } | 7001 | } |
| @@ -7018,7 +7216,7 @@ ifsfree(void) | |||
| 7018 | if (!p) | 7216 | if (!p) |
| 7019 | goto out; | 7217 | goto out; |
| 7020 | 7218 | ||
| 7021 | INT_OFF; | 7219 | INTOFF; |
| 7022 | do { | 7220 | do { |
| 7023 | struct ifsregion *ifsp; | 7221 | struct ifsregion *ifsp; |
| 7024 | ifsp = p->next; | 7222 | ifsp = p->next; |
| @@ -7026,7 +7224,7 @@ ifsfree(void) | |||
| 7026 | p = ifsp; | 7224 | p = ifsp; |
| 7027 | } while (p); | 7225 | } while (p); |
| 7028 | ifsfirst.next = NULL; | 7226 | ifsfirst.next = NULL; |
| 7029 | INT_ON; | 7227 | INTON; |
| 7030 | out: | 7228 | out: |
| 7031 | ifslastp = NULL; | 7229 | ifslastp = NULL; |
| 7032 | } | 7230 | } |
| @@ -7241,11 +7439,11 @@ recordregion(int start, int end, int nulonly) | |||
| 7241 | if (ifslastp == NULL) { | 7439 | if (ifslastp == NULL) { |
| 7242 | ifsp = &ifsfirst; | 7440 | ifsp = &ifsfirst; |
| 7243 | } else { | 7441 | } else { |
| 7244 | INT_OFF; | 7442 | INTOFF; |
| 7245 | ifsp = ckzalloc(sizeof(*ifsp)); | 7443 | ifsp = ckzalloc(sizeof(*ifsp)); |
| 7246 | /*ifsp->next = NULL; - ckzalloc did it */ | 7444 | /*ifsp->next = NULL; - ckzalloc did it */ |
| 7247 | ifslastp->next = ifsp; | 7445 | ifslastp->next = ifsp; |
| 7248 | INT_ON; | 7446 | INTON; |
| 7249 | } | 7447 | } |
| 7250 | ifslastp = ifsp; | 7448 | ifslastp = ifsp; |
| 7251 | ifslastp->begoff = start; | 7449 | ifslastp->begoff = start; |
| @@ -7262,11 +7460,11 @@ removerecordregions(int endoff) | |||
| 7262 | if (ifsfirst.endoff > endoff) { | 7460 | if (ifsfirst.endoff > endoff) { |
| 7263 | while (ifsfirst.next) { | 7461 | while (ifsfirst.next) { |
| 7264 | struct ifsregion *ifsp; | 7462 | struct ifsregion *ifsp; |
| 7265 | INT_OFF; | 7463 | INTOFF; |
| 7266 | ifsp = ifsfirst.next->next; | 7464 | ifsp = ifsfirst.next->next; |
| 7267 | free(ifsfirst.next); | 7465 | free(ifsfirst.next); |
| 7268 | ifsfirst.next = ifsp; | 7466 | ifsfirst.next = ifsp; |
| 7269 | INT_ON; | 7467 | INTON; |
| 7270 | } | 7468 | } |
| 7271 | if (ifsfirst.begoff > endoff) { | 7469 | if (ifsfirst.begoff > endoff) { |
| 7272 | ifslastp = NULL; | 7470 | ifslastp = NULL; |
| @@ -7282,11 +7480,11 @@ removerecordregions(int endoff) | |||
| 7282 | ifslastp = ifslastp->next; | 7480 | ifslastp = ifslastp->next; |
| 7283 | while (ifslastp->next) { | 7481 | while (ifslastp->next) { |
| 7284 | struct ifsregion *ifsp; | 7482 | struct ifsregion *ifsp; |
| 7285 | INT_OFF; | 7483 | INTOFF; |
| 7286 | ifsp = ifslastp->next->next; | 7484 | ifsp = ifslastp->next->next; |
| 7287 | free(ifslastp->next); | 7485 | free(ifslastp->next); |
| 7288 | ifslastp->next = ifsp; | 7486 | ifslastp->next = ifsp; |
| 7289 | INT_ON; | 7487 | INTON; |
| 7290 | } | 7488 | } |
| 7291 | if (ifslastp->endoff > endoff) | 7489 | if (ifslastp->endoff > endoff) |
| 7292 | ifslastp->endoff = endoff; | 7490 | ifslastp->endoff = endoff; |
| @@ -7399,7 +7597,7 @@ evalbackcmd(union node *n, struct backcmd *result | |||
| 7399 | if (pipe(pip) < 0) | 7597 | if (pipe(pip) < 0) |
| 7400 | ash_msg_and_raise_perror("can't create pipe"); | 7598 | ash_msg_and_raise_perror("can't create pipe"); |
| 7401 | /* process substitution uses NULL job, like openhere() */ | 7599 | /* process substitution uses NULL job, like openhere() */ |
| 7402 | jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; | 7600 | jp = (ctl == CTLBACKQ) ? makejob(1) : NULL; |
| 7403 | #if ENABLE_PLATFORM_MINGW32 | 7601 | #if ENABLE_PLATFORM_MINGW32 |
| 7404 | memset(&fs, 0, sizeof(fs)); | 7602 | memset(&fs, 0, sizeof(fs)); |
| 7405 | fs.fpid = FS_EVALBACKCMD; | 7603 | fs.fpid = FS_EVALBACKCMD; |
| @@ -7411,7 +7609,8 @@ evalbackcmd(union node *n, struct backcmd *result | |||
| 7411 | #else | 7609 | #else |
| 7412 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | 7610 | if (forkshell(jp, n, FORK_NOJOB) == 0) { |
| 7413 | /* child */ | 7611 | /* child */ |
| 7414 | FORCE_INT_ON; | 7612 | reset_exception_handler(); |
| 7613 | FORCEINTON; | ||
| 7415 | close(pip[ip]); | 7614 | close(pip[ip]); |
| 7416 | /* ic is index of child end of pipe *and* fd to connect it to */ | 7615 | /* ic is index of child end of pipe *and* fd to connect it to */ |
| 7417 | if (pip[ic] != ic) { | 7616 | if (pip[ic] != ic) { |
| @@ -7473,7 +7672,7 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl)) | |||
| 7473 | if (flag & EXP_DISCARD) | 7672 | if (flag & EXP_DISCARD) |
| 7474 | goto out; | 7673 | goto out; |
| 7475 | 7674 | ||
| 7476 | INT_OFF; | 7675 | INTOFF; |
| 7477 | startloc = expdest - (char *)stackblock(); | 7676 | startloc = expdest - (char *)stackblock(); |
| 7478 | pushstackmark(&smark, startloc); | 7677 | pushstackmark(&smark, startloc); |
| 7479 | evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl)); | 7678 | evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl)); |
| @@ -7507,7 +7706,7 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl)) | |||
| 7507 | back_exitstatus = waitforjob(in.jp); | 7706 | back_exitstatus = waitforjob(in.jp); |
| 7508 | } | 7707 | } |
| 7509 | done: | 7708 | done: |
| 7510 | INT_ON; | 7709 | INTON; |
| 7511 | 7710 | ||
| 7512 | /* Eat all trailing newlines */ | 7711 | /* Eat all trailing newlines */ |
| 7513 | dest = expdest; | 7712 | dest = expdest; |
| @@ -8611,7 +8810,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
| 8611 | if (!hasmeta(str->text)) | 8810 | if (!hasmeta(str->text)) |
| 8612 | goto nometa; | 8811 | goto nometa; |
| 8613 | 8812 | ||
| 8614 | INT_OFF; | 8813 | INTOFF; |
| 8615 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 8814 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
| 8616 | // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match | 8815 | // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match |
| 8617 | // GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) | 8816 | // GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) |
| @@ -8642,12 +8841,12 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
| 8642 | #endif | 8841 | #endif |
| 8643 | addglob(&pglob); | 8842 | addglob(&pglob); |
| 8644 | globfree(&pglob); | 8843 | globfree(&pglob); |
| 8645 | INT_ON; | 8844 | INTON; |
| 8646 | break; | 8845 | break; |
| 8647 | case GLOB_NOMATCH: | 8846 | case GLOB_NOMATCH: |
| 8648 | //nometa2: | 8847 | //nometa2: |
| 8649 | globfree(&pglob); | 8848 | globfree(&pglob); |
| 8650 | INT_ON; | 8849 | INTON; |
| 8651 | nometa: | 8850 | nometa: |
| 8652 | *exparg.lastp = str; | 8851 | *exparg.lastp = str; |
| 8653 | rmescapes(str->text, 0, NULL); | 8852 | rmescapes(str->text, 0, NULL); |
| @@ -8655,7 +8854,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
| 8655 | break; | 8854 | break; |
| 8656 | default: /* GLOB_NOSPACE */ | 8855 | default: /* GLOB_NOSPACE */ |
| 8657 | globfree(&pglob); | 8856 | globfree(&pglob); |
| 8658 | INT_ON; | 8857 | INTON; |
| 8659 | ash_msg_and_raise_error(bb_msg_memory_exhausted); | 8858 | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| 8660 | } | 8859 | } |
| 8661 | str = str->next; | 8860 | str = str->next; |
| @@ -8915,7 +9114,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
| 8915 | goto nometa; | 9114 | goto nometa; |
| 8916 | savelastp = exparg.lastp; | 9115 | savelastp = exparg.lastp; |
| 8917 | 9116 | ||
| 8918 | INT_OFF; | 9117 | INTOFF; |
| 8919 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 9118 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
| 8920 | len = strlen(p); | 9119 | len = strlen(p); |
| 8921 | exp.dir_max = len + PATH_MAX; | 9120 | exp.dir_max = len + PATH_MAX; |
| @@ -8925,7 +9124,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
| 8925 | free(exp.dir); | 9124 | free(exp.dir); |
| 8926 | if (p != str->text) | 9125 | if (p != str->text) |
| 8927 | free(p); | 9126 | free(p); |
| 8928 | INT_ON; | 9127 | INTON; |
| 8929 | if (exparg.lastp == savelastp) { | 9128 | if (exparg.lastp == savelastp) { |
| 8930 | /* | 9129 | /* |
| 8931 | * no matches | 9130 | * no matches |
| @@ -9128,7 +9327,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
| 9128 | argv[0] = (char *)"Which"; | 9327 | argv[0] = (char *)"Which"; |
| 9129 | } | 9328 | } |
| 9130 | # else | 9329 | # else |
| 9131 | if (APPLET_IS_NOEXEC(applet_no)) { | 9330 | if (!vforked && APPLET_IS_NOEXEC(applet_no)) { |
| 9132 | # endif | 9331 | # endif |
| 9133 | #if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT) | 9332 | #if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT) |
| 9134 | /* If building for UCRT move this up into shellexec() to | 9333 | /* If building for UCRT move this up into shellexec() to |
| @@ -9232,9 +9431,11 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
| 9232 | * have to change the find_command routine as well. | 9431 | * have to change the find_command routine as well. |
| 9233 | * argv[-1] must exist and be writable! See tryexec() for why. | 9432 | * argv[-1] must exist and be writable! See tryexec() for why. |
| 9234 | */ | 9433 | */ |
| 9434 | #if ENABLE_PLATFORM_MINGW32 | ||
| 9235 | static struct builtincmd *find_builtin(const char *name); | 9435 | static struct builtincmd *find_builtin(const char *name); |
| 9236 | static void shellexec(char *prog, char **argv, const char *path, int idx, | 9436 | static void shellexec(char *prog, char **argv, const char *path, int idx, |
| 9237 | int noexec) NORETURN; | 9437 | int noexec) NORETURN; |
| 9438 | #endif | ||
| 9238 | static void shellexec(char *prog, char **argv, const char *path, int idx, | 9439 | static void shellexec(char *prog, char **argv, const char *path, int idx, |
| 9239 | int noexec) | 9440 | int noexec) |
| 9240 | { | 9441 | { |
| @@ -9325,7 +9526,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx, | |||
| 9325 | exitstatus = exerrno; | 9526 | exitstatus = exerrno; |
| 9326 | TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", | 9527 | TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", |
| 9327 | prog, e, suppress_int)); | 9528 | prog, e, suppress_int)); |
| 9328 | ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found")); | 9529 | ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, E_EXEC)); |
| 9329 | /* NOTREACHED */ | 9530 | /* NOTREACHED */ |
| 9330 | } | 9531 | } |
| 9331 | 9532 | ||
| @@ -9358,7 +9559,7 @@ clearcmdentry(void) | |||
| 9358 | struct tblentry **pp; | 9559 | struct tblentry **pp; |
| 9359 | struct tblentry *cmdp; | 9560 | struct tblentry *cmdp; |
| 9360 | 9561 | ||
| 9361 | INT_OFF; | 9562 | INTOFF; |
| 9362 | for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { | 9563 | for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { |
| 9363 | pp = tblp; | 9564 | pp = tblp; |
| 9364 | while ((cmdp = *pp) != NULL) { | 9565 | while ((cmdp = *pp) != NULL) { |
| @@ -9375,7 +9576,7 @@ clearcmdentry(void) | |||
| 9375 | } | 9576 | } |
| 9376 | } | 9577 | } |
| 9377 | } | 9578 | } |
| 9378 | INT_ON; | 9579 | INTON; |
| 9379 | } | 9580 | } |
| 9380 | 9581 | ||
| 9381 | /* | 9582 | /* |
| @@ -9429,13 +9630,13 @@ delete_cmd_entry(void) | |||
| 9429 | { | 9630 | { |
| 9430 | struct tblentry *cmdp; | 9631 | struct tblentry *cmdp; |
| 9431 | 9632 | ||
| 9432 | INT_OFF; | 9633 | INTOFF; |
| 9433 | cmdp = *lastcmdentry; | 9634 | cmdp = *lastcmdentry; |
| 9434 | *lastcmdentry = cmdp->next; | 9635 | *lastcmdentry = cmdp->next; |
| 9435 | if (cmdp->cmdtype == CMDFUNCTION) | 9636 | if (cmdp->cmdtype == CMDFUNCTION) |
| 9436 | freefunc(cmdp->param.func); | 9637 | freefunc(cmdp->param.func); |
| 9437 | free(cmdp); | 9638 | free(cmdp); |
| 9438 | INT_ON; | 9639 | INTON; |
| 9439 | } | 9640 | } |
| 9440 | 9641 | ||
| 9441 | /* | 9642 | /* |
| @@ -9903,7 +10104,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 9903 | } | 10104 | } |
| 9904 | #endif | 10105 | #endif |
| 9905 | 10106 | ||
| 9906 | |||
| 9907 | /*static int funcblocksize; // size of structures in function */ | 10107 | /*static int funcblocksize; // size of structures in function */ |
| 9908 | /*static int funcstringsize; // size of strings in node */ | 10108 | /*static int funcstringsize; // size of strings in node */ |
| 9909 | static void *funcblock; /* block to allocate function from */ | 10109 | static void *funcblock; /* block to allocate function from */ |
| @@ -10026,6 +10226,7 @@ calcsize(int funcblocksize, union node *n) | |||
| 10026 | #endif | 10226 | #endif |
| 10027 | case NCLOBBER: | 10227 | case NCLOBBER: |
| 10028 | case NFROM: | 10228 | case NFROM: |
| 10229 | case NFROMSTR: | ||
| 10029 | case NFROMTO: | 10230 | case NFROMTO: |
| 10030 | case NAPPEND: | 10231 | case NAPPEND: |
| 10031 | funcblocksize = calcsize(funcblocksize, n->nfile.fname); | 10232 | funcblocksize = calcsize(funcblocksize, n->nfile.fname); |
| @@ -10242,6 +10443,7 @@ copynode(union node *n) | |||
| 10242 | #endif | 10443 | #endif |
| 10243 | case NCLOBBER: | 10444 | case NCLOBBER: |
| 10244 | case NFROM: | 10445 | case NFROM: |
| 10446 | case NFROMSTR: | ||
| 10245 | case NFROMTO: | 10447 | case NFROMTO: |
| 10246 | case NAPPEND: | 10448 | case NAPPEND: |
| 10247 | new->nfile.fname = copynode(n->nfile.fname); | 10449 | new->nfile.fname = copynode(n->nfile.fname); |
| @@ -10304,11 +10506,11 @@ defun(union node *func) | |||
| 10304 | { | 10506 | { |
| 10305 | struct cmdentry entry; | 10507 | struct cmdentry entry; |
| 10306 | 10508 | ||
| 10307 | INT_OFF; | 10509 | INTOFF; |
| 10308 | entry.cmdtype = CMDFUNCTION; | 10510 | entry.cmdtype = CMDFUNCTION; |
| 10309 | entry.u.func = copyfunc(func); | 10511 | entry.u.func = copyfunc(func); |
| 10310 | addcmdentry(func->ndefun.text, &entry); | 10512 | addcmdentry(func->ndefun.text, &entry); |
| 10311 | INT_ON; | 10513 | INTON; |
| 10312 | } | 10514 | } |
| 10313 | 10515 | ||
| 10314 | /* Reasons for skipping commands (see comment on breakcmd routine) */ | 10516 | /* Reasons for skipping commands (see comment on breakcmd routine) */ |
| @@ -10392,8 +10594,10 @@ dotrap(void) | |||
| 10392 | int status, last_status; | 10594 | int status, last_status; |
| 10393 | char *p; | 10595 | char *p; |
| 10394 | 10596 | ||
| 10395 | if (!pending_int) | 10597 | if (!pending_int && waitcmd_int != 1) { |
| 10598 | waitcmd_int = 0; | ||
| 10396 | return; | 10599 | return; |
| 10600 | } | ||
| 10397 | 10601 | ||
| 10398 | status = savestatus; | 10602 | status = savestatus; |
| 10399 | last_status = status; | 10603 | last_status = status; |
| @@ -10401,7 +10605,7 @@ dotrap(void) | |||
| 10401 | status = exitstatus; | 10605 | status = exitstatus; |
| 10402 | savestatus = status; | 10606 | savestatus = status; |
| 10403 | } | 10607 | } |
| 10404 | pending_int = 0; | 10608 | pending_int = waitcmd_int = 0; |
| 10405 | barrier(); | 10609 | barrier(); |
| 10406 | 10610 | ||
| 10407 | TRACE(("dotrap entered\n")); | 10611 | TRACE(("dotrap entered\n")); |
| @@ -10470,7 +10674,7 @@ evaltree(union node *n, int flags) | |||
| 10470 | #endif | 10674 | #endif |
| 10471 | case NNOT: | 10675 | case NNOT: |
| 10472 | status = !evaltree(n->nnot.com, EV_TESTED); | 10676 | status = !evaltree(n->nnot.com, EV_TESTED); |
| 10473 | goto setstatus; | 10677 | break; |
| 10474 | case NREDIR: | 10678 | case NREDIR: |
| 10475 | errlinno = lineno = n->nredir.linno; | 10679 | errlinno = lineno = n->nredir.linno; |
| 10476 | expredir(n->nredir.redirect); | 10680 | expredir(n->nredir.redirect); |
| @@ -10481,7 +10685,7 @@ evaltree(union node *n, int flags) | |||
| 10481 | } | 10685 | } |
| 10482 | if (n->nredir.redirect) | 10686 | if (n->nredir.redirect) |
| 10483 | popredir(/*drop:*/ 0); | 10687 | popredir(/*drop:*/ 0); |
| 10484 | goto setstatus; | 10688 | break; |
| 10485 | case NCMD: | 10689 | case NCMD: |
| 10486 | evalfn = evalcommand; | 10690 | evalfn = evalcommand; |
| 10487 | checkexit: | 10691 | checkexit: |
| @@ -10525,7 +10729,7 @@ evaltree(union node *n, int flags) | |||
| 10525 | evalfn = evaltree; | 10729 | evalfn = evaltree; |
| 10526 | calleval: | 10730 | calleval: |
| 10527 | status = evalfn(n, flags); | 10731 | status = evalfn(n, flags); |
| 10528 | goto setstatus; | 10732 | break; |
| 10529 | } | 10733 | } |
| 10530 | case NIF: | 10734 | case NIF: |
| 10531 | status = evaltree(n->nif.test, EV_TESTED); | 10735 | status = evaltree(n->nif.test, EV_TESTED); |
| @@ -10539,17 +10743,18 @@ evaltree(union node *n, int flags) | |||
| 10539 | goto evaln; | 10743 | goto evaln; |
| 10540 | } | 10744 | } |
| 10541 | status = 0; | 10745 | status = 0; |
| 10542 | goto setstatus; | 10746 | break; |
| 10543 | case NDEFUN: | 10747 | case NDEFUN: |
| 10544 | defun(n); | 10748 | defun(n); |
| 10545 | /* Not necessary. To test it: | 10749 | /* Not necessary. To test it: |
| 10546 | * "false; f() { qwerty; }; echo $?" should print 0. | 10750 | * "false; f() { qwerty; }; echo $?" should print 0. |
| 10547 | */ | 10751 | */ |
| 10548 | /* status = 0; */ | 10752 | /* status = 0; */ |
| 10549 | setstatus: | ||
| 10550 | exitstatus = status; | ||
| 10551 | break; | 10753 | break; |
| 10552 | } | 10754 | } |
| 10755 | |||
| 10756 | exitstatus = status; | ||
| 10757 | |||
| 10553 | out: | 10758 | out: |
| 10554 | /* Order of checks below is important: | 10759 | /* Order of checks below is important: |
| 10555 | * signal handlers trigger before exit caused by "set -e". | 10760 | * signal handlers trigger before exit caused by "set -e". |
| @@ -10718,6 +10923,7 @@ evalsubshell(union node *n, int flags) | |||
| 10718 | 10923 | ||
| 10719 | #if ENABLE_PLATFORM_MINGW32 | 10924 | #if ENABLE_PLATFORM_MINGW32 |
| 10720 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) { | 10925 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) { |
| 10926 | reset_exception_handler(); | ||
| 10721 | expredir(n->nredir.redirect); | 10927 | expredir(n->nredir.redirect); |
| 10722 | redirect(n->nredir.redirect, 0); | 10928 | redirect(n->nredir.redirect, 0); |
| 10723 | evaltreenr(n->nredir.n, flags); | 10929 | evaltreenr(n->nredir.n, flags); |
| @@ -10728,10 +10934,10 @@ evalsubshell(union node *n, int flags) | |||
| 10728 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) | 10934 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) |
| 10729 | goto nofork; | 10935 | goto nofork; |
| 10730 | #endif | 10936 | #endif |
| 10731 | INT_OFF; | 10937 | INTOFF; |
| 10732 | if (backgnd == FORK_FG) | 10938 | if (backgnd == FORK_FG) |
| 10733 | get_tty_state(); | 10939 | get_tty_state(); |
| 10734 | jp = makejob(/*n,*/ 1); | 10940 | jp = makejob(1); |
| 10735 | #if ENABLE_PLATFORM_MINGW32 | 10941 | #if ENABLE_PLATFORM_MINGW32 |
| 10736 | memset(&fs, 0, sizeof(fs)); | 10942 | memset(&fs, 0, sizeof(fs)); |
| 10737 | fs.fpid = FS_EVALSUBSHELL; | 10943 | fs.fpid = FS_EVALSUBSHELL; |
| @@ -10741,11 +10947,12 @@ evalsubshell(union node *n, int flags) | |||
| 10741 | #else | 10947 | #else |
| 10742 | if (forkshell(jp, n, backgnd) == 0) { | 10948 | if (forkshell(jp, n, backgnd) == 0) { |
| 10743 | /* child */ | 10949 | /* child */ |
| 10744 | INT_ON; | 10950 | INTON; |
| 10745 | flags |= EV_EXIT; | 10951 | flags |= EV_EXIT; |
| 10746 | if (backgnd) | 10952 | if (backgnd) |
| 10747 | flags &= ~EV_TESTED; | 10953 | flags &= ~EV_TESTED; |
| 10748 | nofork: | 10954 | nofork: |
| 10955 | reset_exception_handler(); | ||
| 10749 | redirect(n->nredir.redirect, 0); | 10956 | redirect(n->nredir.redirect, 0); |
| 10750 | evaltreenr(n->nredir.n, flags); | 10957 | evaltreenr(n->nredir.n, flags); |
| 10751 | /* never returns */ | 10958 | /* never returns */ |
| @@ -10755,7 +10962,7 @@ evalsubshell(union node *n, int flags) | |||
| 10755 | status = 0; | 10962 | status = 0; |
| 10756 | if (backgnd == FORK_FG) | 10963 | if (backgnd == FORK_FG) |
| 10757 | status = waitforjob(jp); | 10964 | status = waitforjob(jp); |
| 10758 | INT_ON; | 10965 | INTON; |
| 10759 | return status; | 10966 | return status; |
| 10760 | } | 10967 | } |
| 10761 | 10968 | ||
| @@ -10776,6 +10983,7 @@ expredir(union node *n) | |||
| 10776 | switch (redir->type) { | 10983 | switch (redir->type) { |
| 10777 | case NFROMTO: | 10984 | case NFROMTO: |
| 10778 | case NFROM: | 10985 | case NFROM: |
| 10986 | case NFROMSTR: | ||
| 10779 | case NTO: | 10987 | case NTO: |
| 10780 | #if BASH_REDIR_OUTPUT | 10988 | #if BASH_REDIR_OUTPUT |
| 10781 | case NTO2: | 10989 | case NTO2: |
| @@ -10844,10 +11052,10 @@ evalpipe(union node *n, int flags) | |||
| 10844 | for (lp = n->npipe.cmdlist; lp; lp = lp->next) | 11052 | for (lp = n->npipe.cmdlist; lp; lp = lp->next) |
| 10845 | pipelen++; | 11053 | pipelen++; |
| 10846 | flags |= EV_EXIT; | 11054 | flags |= EV_EXIT; |
| 10847 | INT_OFF; | 11055 | INTOFF; |
| 10848 | if (n->npipe.pipe_backgnd == 0) | 11056 | if (n->npipe.pipe_backgnd == 0) |
| 10849 | get_tty_state(); | 11057 | get_tty_state(); |
| 10850 | jp = makejob(/*n,*/ pipelen); | 11058 | jp = makejob(pipelen); |
| 10851 | prevfd = -1; | 11059 | prevfd = -1; |
| 10852 | for (lp = n->npipe.cmdlist; lp; lp = lp->next) { | 11060 | for (lp = n->npipe.cmdlist; lp; lp = lp->next) { |
| 10853 | prehash(lp->n); | 11061 | prehash(lp->n); |
| @@ -10870,7 +11078,8 @@ evalpipe(union node *n, int flags) | |||
| 10870 | #else | 11078 | #else |
| 10871 | if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { | 11079 | if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { |
| 10872 | /* child */ | 11080 | /* child */ |
| 10873 | INT_ON; | 11081 | reset_exception_handler(); |
| 11082 | INTON; | ||
| 10874 | if (pip[1] >= 0) { | 11083 | if (pip[1] >= 0) { |
| 10875 | close(pip[0]); | 11084 | close(pip[0]); |
| 10876 | } | 11085 | } |
| @@ -10898,7 +11107,7 @@ evalpipe(union node *n, int flags) | |||
| 10898 | status = waitforjob(jp); | 11107 | status = waitforjob(jp); |
| 10899 | TRACE(("evalpipe: job done exit status %d\n", status)); | 11108 | TRACE(("evalpipe: job done exit status %d\n", status)); |
| 10900 | } | 11109 | } |
| 10901 | INT_ON; | 11110 | INTON; |
| 10902 | 11111 | ||
| 10903 | return status; | 11112 | return status; |
| 10904 | } | 11113 | } |
| @@ -11002,7 +11211,7 @@ poplocalvars(int keep) | |||
| 11002 | int var_type; | 11211 | int var_type; |
| 11003 | #endif | 11212 | #endif |
| 11004 | 11213 | ||
| 11005 | INT_OFF; | 11214 | INTOFF; |
| 11006 | ll = localvar_stack; | 11215 | ll = localvar_stack; |
| 11007 | localvar_stack = ll->next; | 11216 | localvar_stack = ll->next; |
| 11008 | 11217 | ||
| @@ -11057,7 +11266,7 @@ poplocalvars(int keep) | |||
| 11057 | } | 11266 | } |
| 11058 | free(lvp); | 11267 | free(lvp); |
| 11059 | } | 11268 | } |
| 11060 | INT_ON; | 11269 | INTON; |
| 11061 | } | 11270 | } |
| 11062 | 11271 | ||
| 11063 | /* | 11272 | /* |
| @@ -11073,12 +11282,12 @@ pushlocalvars(int push) | |||
| 11073 | if (!push) | 11282 | if (!push) |
| 11074 | goto out; | 11283 | goto out; |
| 11075 | 11284 | ||
| 11076 | INT_OFF; | 11285 | INTOFF; |
| 11077 | ll = ckzalloc(sizeof(*ll)); | 11286 | ll = ckzalloc(sizeof(*ll)); |
| 11078 | /*ll->lv = NULL; - zalloc did it */ | 11287 | /*ll->lv = NULL; - zalloc did it */ |
| 11079 | ll->next = top; | 11288 | ll->next = top; |
| 11080 | localvar_stack = ll; | 11289 | localvar_stack = ll; |
| 11081 | INT_ON; | 11290 | INTON; |
| 11082 | out: | 11291 | out: |
| 11083 | return top; | 11292 | return top; |
| 11084 | } | 11293 | } |
| @@ -11115,13 +11324,13 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
| 11115 | if (e) { | 11324 | if (e) { |
| 11116 | goto funcdone; | 11325 | goto funcdone; |
| 11117 | } | 11326 | } |
| 11118 | INT_OFF; | 11327 | INTOFF; |
| 11119 | exception_handler = &jmploc; | 11328 | exception_handler = &jmploc; |
| 11120 | shellparam.malloced = 0; | 11329 | shellparam.malloced = 0; |
| 11121 | func->count++; | 11330 | func->count++; |
| 11122 | funcname = func->n.ndefun.text; | 11331 | funcname = func->n.ndefun.text; |
| 11123 | funcline = func->n.ndefun.linno; | 11332 | funcline = func->n.ndefun.linno; |
| 11124 | INT_ON; | 11333 | INTON; |
| 11125 | shellparam.nparam = argc - 1; | 11334 | shellparam.nparam = argc - 1; |
| 11126 | shellparam.p = argv + 1; | 11335 | shellparam.p = argv + 1; |
| 11127 | #if ENABLE_ASH_GETOPTS | 11336 | #if ENABLE_ASH_GETOPTS |
| @@ -11130,7 +11339,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
| 11130 | #endif | 11339 | #endif |
| 11131 | evaltree(func->n.ndefun.body, flags & EV_TESTED); | 11340 | evaltree(func->n.ndefun.body, flags & EV_TESTED); |
| 11132 | funcdone: | 11341 | funcdone: |
| 11133 | INT_OFF; | 11342 | INTOFF; |
| 11134 | funcname = savefuncname; | 11343 | funcname = savefuncname; |
| 11135 | if (savetrap) { | 11344 | if (savetrap) { |
| 11136 | if (!trap[NTRAP_ERR]) | 11345 | if (!trap[NTRAP_ERR]) |
| @@ -11144,7 +11353,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
| 11144 | freeparam(&shellparam); | 11353 | freeparam(&shellparam); |
| 11145 | shellparam = saveparam; | 11354 | shellparam = saveparam; |
| 11146 | exception_handler = savehandler; | 11355 | exception_handler = savehandler; |
| 11147 | INT_ON; | 11356 | INTON; |
| 11148 | evalskip &= ~(SKIPFUNC | SKIPFUNCDEF); | 11357 | evalskip &= ~(SKIPFUNC | SKIPFUNCDEF); |
| 11149 | return e; | 11358 | return e; |
| 11150 | } | 11359 | } |
| @@ -11163,7 +11372,7 @@ mklocal(char *name, int flags) | |||
| 11163 | struct var *vp; | 11372 | struct var *vp; |
| 11164 | char *eq = strchr(name, '='); | 11373 | char *eq = strchr(name, '='); |
| 11165 | 11374 | ||
| 11166 | INT_OFF; | 11375 | INTOFF; |
| 11167 | /* Cater for duplicate "local". Examples: | 11376 | /* Cater for duplicate "local". Examples: |
| 11168 | * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x | 11377 | * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x |
| 11169 | * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x | 11378 | * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x |
| @@ -11214,7 +11423,7 @@ mklocal(char *name, int flags) | |||
| 11214 | lvp->next = localvar_stack->lv; | 11423 | lvp->next = localvar_stack->lv; |
| 11215 | localvar_stack->lv = lvp; | 11424 | localvar_stack->lv = lvp; |
| 11216 | ret: | 11425 | ret: |
| 11217 | INT_ON; | 11426 | INTON; |
| 11218 | } | 11427 | } |
| 11219 | 11428 | ||
| 11220 | /* | 11429 | /* |
| @@ -11772,7 +11981,7 @@ evalcommand(union node *cmd, int flags) | |||
| 11772 | char *sv_argv0; | 11981 | char *sv_argv0; |
| 11773 | #endif | 11982 | #endif |
| 11774 | 11983 | ||
| 11775 | INT_OFF; | 11984 | INTOFF; |
| 11776 | sv_environ = environ; | 11985 | sv_environ = environ; |
| 11777 | environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL); | 11986 | environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL); |
| 11778 | /* | 11987 | /* |
| @@ -11802,10 +12011,11 @@ evalcommand(union node *cmd, int flags) | |||
| 11802 | * our signals to SA_RESTART? | 12011 | * our signals to SA_RESTART? |
| 11803 | */ | 12012 | */ |
| 11804 | /*clearerr(stdout);*/ | 12013 | /*clearerr(stdout);*/ |
| 11805 | INT_ON; | 12014 | INTON; |
| 11806 | break; | 12015 | break; |
| 11807 | } | 12016 | } |
| 11808 | #endif | 12017 | #endif |
| 12018 | /* Fork off a child process if necessary. */ | ||
| 11809 | /* Can we avoid forking? For example, very last command | 12019 | /* Can we avoid forking? For example, very last command |
| 11810 | * in a script or a subshell does not need forking, | 12020 | * in a script or a subshell does not need forking, |
| 11811 | * we can just exec it. | 12021 | * we can just exec it. |
| @@ -11815,7 +12025,7 @@ evalcommand(union node *cmd, int flags) | |||
| 11815 | /* No, forking off a child is necessary */ | 12025 | /* No, forking off a child is necessary */ |
| 11816 | struct forkshell fs; | 12026 | struct forkshell fs; |
| 11817 | 12027 | ||
| 11818 | INT_OFF; | 12028 | INTOFF; |
| 11819 | memset(&fs, 0, sizeof(fs)); | 12029 | memset(&fs, 0, sizeof(fs)); |
| 11820 | fs.fpid = FS_SHELLEXEC; | 12030 | fs.fpid = FS_SHELLEXEC; |
| 11821 | fs.argv = argv; | 12031 | fs.argv = argv; |
| @@ -11828,16 +12038,10 @@ evalcommand(union node *cmd, int flags) | |||
| 11828 | #else | 12038 | #else |
| 11829 | if (!(flags & EV_EXIT) || may_have_traps) { | 12039 | if (!(flags & EV_EXIT) || may_have_traps) { |
| 11830 | /* No, forking off a child is necessary */ | 12040 | /* No, forking off a child is necessary */ |
| 11831 | INT_OFF; | 12041 | INTOFF; |
| 11832 | get_tty_state(); | 12042 | get_tty_state(); |
| 11833 | jp = makejob(/*cmd,*/ 1); | 12043 | jp = vforkexec(cmd, argv, path, cmdentry.u.index); |
| 11834 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 12044 | break; |
| 11835 | /* parent */ | ||
| 11836 | break; | ||
| 11837 | } | ||
| 11838 | /* child */ | ||
| 11839 | FORCE_INT_ON; | ||
| 11840 | /* fall through to exec'ing external program */ | ||
| 11841 | } | 12045 | } |
| 11842 | #endif | 12046 | #endif |
| 11843 | shellexec(argv[0], argv, path, cmdentry.u.index, FALSE); | 12047 | shellexec(argv[0], argv, path, cmdentry.u.index, FALSE); |
| @@ -11861,7 +12065,7 @@ evalcommand(union node *cmd, int flags) | |||
| 11861 | status = waitforjob(jp); | 12065 | status = waitforjob(jp); |
| 11862 | if (jp) | 12066 | if (jp) |
| 11863 | TRACE(("forked child exited with %d\n", status)); | 12067 | TRACE(("forked child exited with %d\n", status)); |
| 11864 | FORCE_INT_ON; | 12068 | FORCEINTON; |
| 11865 | 12069 | ||
| 11866 | out: | 12070 | out: |
| 11867 | if (cmd->ncmd.redirect) | 12071 | if (cmd->ncmd.redirect) |
| @@ -11919,7 +12123,6 @@ goodname(const char *p) | |||
| 11919 | return endofname(p)[0] == '\0'; | 12123 | return endofname(p)[0] == '\0'; |
| 11920 | } | 12124 | } |
| 11921 | 12125 | ||
| 11922 | |||
| 11923 | /* | 12126 | /* |
| 11924 | * Search for a command. This is called before we fork so that the | 12127 | * 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 | 12128 | * location of the command will be available in the parent as well as |
| @@ -11999,7 +12202,7 @@ pushstring(char *s, struct alias *ap) | |||
| 11999 | int len; | 12202 | int len; |
| 12000 | 12203 | ||
| 12001 | len = strlen(s); | 12204 | len = strlen(s); |
| 12002 | INT_OFF; | 12205 | INTOFF; |
| 12003 | if (g_parsefile->strpush || g_parsefile->spfree) { | 12206 | if (g_parsefile->strpush || g_parsefile->spfree) { |
| 12004 | sp = ckzalloc(sizeof(*sp)); | 12207 | sp = ckzalloc(sizeof(*sp)); |
| 12005 | sp->prev = g_parsefile->strpush; | 12208 | sp->prev = g_parsefile->strpush; |
| @@ -12023,14 +12226,14 @@ pushstring(char *s, struct alias *ap) | |||
| 12023 | g_parsefile->left_in_line = len; | 12226 | g_parsefile->left_in_line = len; |
| 12024 | g_parsefile->unget = 0; | 12227 | g_parsefile->unget = 0; |
| 12025 | g_parsefile->spfree = NULL; | 12228 | g_parsefile->spfree = NULL; |
| 12026 | INT_ON; | 12229 | INTON; |
| 12027 | } | 12230 | } |
| 12028 | 12231 | ||
| 12029 | static void popstring(void) | 12232 | static void popstring(void) |
| 12030 | { | 12233 | { |
| 12031 | struct strpush *sp = g_parsefile->strpush; | 12234 | struct strpush *sp = g_parsefile->strpush; |
| 12032 | 12235 | ||
| 12033 | INT_OFF; | 12236 | INTOFF; |
| 12034 | #if ENABLE_ASH_ALIAS | 12237 | #if ENABLE_ASH_ALIAS |
| 12035 | if (sp->ap) { | 12238 | if (sp->ap) { |
| 12036 | if (g_parsefile->next_to_pgetc[-1] == ' ' | 12239 | if (g_parsefile->next_to_pgetc[-1] == ' ' |
| @@ -12049,7 +12252,7 @@ static void popstring(void) | |||
| 12049 | memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); | 12252 | memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); |
| 12050 | g_parsefile->strpush = sp->prev; | 12253 | g_parsefile->strpush = sp->prev; |
| 12051 | g_parsefile->spfree = sp; | 12254 | g_parsefile->spfree = sp; |
| 12052 | INT_ON; | 12255 | INTON; |
| 12053 | } | 12256 | } |
| 12054 | 12257 | ||
| 12055 | #if ENABLE_PLATFORM_MINGW32 | 12258 | #if ENABLE_PLATFORM_MINGW32 |
| @@ -12146,11 +12349,11 @@ preadfd(void) | |||
| 12146 | * #(bash 5.0.17 exits after first "T", looks like a bug) | 12349 | * #(bash 5.0.17 exits after first "T", looks like a bug) |
| 12147 | */ | 12350 | */ |
| 12148 | bb_got_signal = 0; | 12351 | bb_got_signal = 0; |
| 12149 | INT_OFF; /* no longjmp'ing out of read_line_input please */ | 12352 | INTOFF; /* no longjmp'ing out of read_line_input please */ |
| 12150 | nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ); | 12353 | nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ); |
| 12151 | if (bb_got_signal == SIGINT) | 12354 | if (bb_got_signal == SIGINT) |
| 12152 | write(STDOUT_FILENO, "^C\n", 3); | 12355 | write(STDOUT_FILENO, "^C\n", 3); |
| 12153 | INT_ON; /* here non-blocked SIGINT will longjmp */ | 12356 | INTON; /* here non-blocked SIGINT will longjmp */ |
| 12154 | if (nr == 0) { | 12357 | if (nr == 0) { |
| 12155 | /* ^C pressed, "convert" to SIGINT */ | 12358 | /* ^C pressed, "convert" to SIGINT */ |
| 12156 | # if !ENABLE_PLATFORM_MINGW32 | 12359 | # if !ENABLE_PLATFORM_MINGW32 |
| @@ -12160,7 +12363,6 @@ preadfd(void) | |||
| 12160 | * is SIG_IGNed on startup, it stays SIG_IGNed) | 12363 | * is SIG_IGNed on startup, it stays SIG_IGNed) |
| 12161 | */ | 12364 | */ |
| 12162 | # else | 12365 | # else |
| 12163 | raise_interrupt(); | ||
| 12164 | write(STDOUT_FILENO, "^C\n", 3); | 12366 | write(STDOUT_FILENO, "^C\n", 3); |
| 12165 | # endif | 12367 | # endif |
| 12166 | if (trap[SIGINT]) { | 12368 | if (trap[SIGINT]) { |
| @@ -12173,6 +12375,10 @@ preadfd(void) | |||
| 12173 | buf[1] = '\0'; | 12375 | buf[1] = '\0'; |
| 12174 | return 1; | 12376 | return 1; |
| 12175 | } | 12377 | } |
| 12378 | # if ENABLE_PLATFORM_MINGW32 | ||
| 12379 | else | ||
| 12380 | raise_interrupt(); | ||
| 12381 | # endif | ||
| 12176 | exitstatus = 128 + SIGINT; | 12382 | exitstatus = 128 + SIGINT; |
| 12177 | /* bash behavior on ^C + ignored SIGINT: */ | 12383 | /* bash behavior on ^C + ignored SIGINT: */ |
| 12178 | goto again; | 12384 | goto again; |
| @@ -12316,7 +12522,7 @@ nlnoprompt(void) | |||
| 12316 | 12522 | ||
| 12317 | static void freestrings(struct strpush *sp) | 12523 | static void freestrings(struct strpush *sp) |
| 12318 | { | 12524 | { |
| 12319 | INT_OFF; | 12525 | INTOFF; |
| 12320 | do { | 12526 | do { |
| 12321 | struct strpush *psp; | 12527 | struct strpush *psp; |
| 12322 | #if ENABLE_ASH_ALIAS | 12528 | #if ENABLE_ASH_ALIAS |
| @@ -12335,7 +12541,7 @@ static void freestrings(struct strpush *sp) | |||
| 12335 | } while (sp); | 12541 | } while (sp); |
| 12336 | 12542 | ||
| 12337 | g_parsefile->spfree = NULL; | 12543 | g_parsefile->spfree = NULL; |
| 12338 | INT_ON; | 12544 | INTON; |
| 12339 | } | 12545 | } |
| 12340 | 12546 | ||
| 12341 | static int __pgetc(void) | 12547 | static int __pgetc(void) |
| @@ -12468,7 +12674,7 @@ popfile(void) | |||
| 12468 | if (pf == &basepf) | 12674 | if (pf == &basepf) |
| 12469 | return; | 12675 | return; |
| 12470 | 12676 | ||
| 12471 | INT_OFF; | 12677 | INTOFF; |
| 12472 | if (pf->pf_fd >= 0) | 12678 | if (pf->pf_fd >= 0) |
| 12473 | close(pf->pf_fd); | 12679 | close(pf->pf_fd); |
| 12474 | free(pf->buf); | 12680 | free(pf->buf); |
| @@ -12480,7 +12686,7 @@ popfile(void) | |||
| 12480 | } | 12686 | } |
| 12481 | g_parsefile = pf->prev; | 12687 | g_parsefile = pf->prev; |
| 12482 | free(pf); | 12688 | free(pf); |
| 12483 | INT_ON; | 12689 | INTON; |
| 12484 | } | 12690 | } |
| 12485 | 12691 | ||
| 12486 | static void | 12692 | static void |
| @@ -12543,14 +12749,10 @@ setinputfile(const char *fname, int flags) | |||
| 12543 | { | 12749 | { |
| 12544 | int fd; | 12750 | int fd; |
| 12545 | 12751 | ||
| 12546 | INT_OFF; | 12752 | INTOFF; |
| 12547 | fd = open(fname, O_RDONLY | O_CLOEXEC); | 12753 | fd = sh_open(fname, O_RDONLY, flags & INPUT_NOFILE_OK); |
| 12548 | if (fd < 0) { | 12754 | if (fd < 0) |
| 12549 | if (flags & INPUT_NOFILE_OK) | 12755 | goto out; |
| 12550 | goto out; | ||
| 12551 | exitstatus = 127; | ||
| 12552 | ash_msg_and_raise_perror("can't open '%s'", fname); | ||
| 12553 | } | ||
| 12554 | if (fd < 10) | 12756 | if (fd < 10) |
| 12555 | fd = savefd(fd); | 12757 | fd = savefd(fd); |
| 12556 | else if (O_CLOEXEC == 0) /* old libc */ | 12758 | else if (O_CLOEXEC == 0) /* old libc */ |
| @@ -12558,7 +12760,7 @@ setinputfile(const char *fname, int flags) | |||
| 12558 | 12760 | ||
| 12559 | setinputfd(fd, flags & INPUT_PUSH_FILE); | 12761 | setinputfd(fd, flags & INPUT_PUSH_FILE); |
| 12560 | out: | 12762 | out: |
| 12561 | INT_ON; | 12763 | INTON; |
| 12562 | return fd; | 12764 | return fd; |
| 12563 | } | 12765 | } |
| 12564 | 12766 | ||
| @@ -12568,13 +12770,13 @@ setinputfile(const char *fname, int flags) | |||
| 12568 | static void | 12770 | static void |
| 12569 | setinputstring(char *string) | 12771 | setinputstring(char *string) |
| 12570 | { | 12772 | { |
| 12571 | INT_OFF; | 12773 | INTOFF; |
| 12572 | pushfile(); | 12774 | pushfile(); |
| 12573 | g_parsefile->next_to_pgetc = string; | 12775 | g_parsefile->next_to_pgetc = string; |
| 12574 | g_parsefile->left_in_line = strlen(string); | 12776 | g_parsefile->left_in_line = strlen(string); |
| 12575 | g_parsefile->buf = NULL; | 12777 | g_parsefile->buf = NULL; |
| 12576 | g_parsefile->linno = lineno; | 12778 | g_parsefile->linno = lineno; |
| 12577 | INT_ON; | 12779 | INTON; |
| 12578 | } | 12780 | } |
| 12579 | 12781 | ||
| 12580 | 12782 | ||
| @@ -12870,7 +13072,7 @@ shiftcmd(int argc UNUSED_PARAM, char **argv) | |||
| 12870 | n = number(argv[1]); | 13072 | n = number(argv[1]); |
| 12871 | if (n > shellparam.nparam) | 13073 | if (n > shellparam.nparam) |
| 12872 | return 1; | 13074 | return 1; |
| 12873 | INT_OFF; | 13075 | INTOFF; |
| 12874 | shellparam.nparam -= n; | 13076 | shellparam.nparam -= n; |
| 12875 | for (ap1 = shellparam.p; --n >= 0; ap1++) { | 13077 | for (ap1 = shellparam.p; --n >= 0; ap1++) { |
| 12876 | if (shellparam.malloced) | 13078 | if (shellparam.malloced) |
| @@ -12883,7 +13085,7 @@ shiftcmd(int argc UNUSED_PARAM, char **argv) | |||
| 12883 | shellparam.optind = 1; | 13085 | shellparam.optind = 1; |
| 12884 | shellparam.optoff = -1; | 13086 | shellparam.optoff = -1; |
| 12885 | #endif | 13087 | #endif |
| 12886 | INT_ON; | 13088 | INTON; |
| 12887 | return 0; | 13089 | return 0; |
| 12888 | } | 13090 | } |
| 12889 | 13091 | ||
| @@ -12937,7 +13139,7 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 12937 | if (!argv[1]) | 13139 | if (!argv[1]) |
| 12938 | return showvars(nullstr, 0, VUNSET); | 13140 | return showvars(nullstr, 0, VUNSET); |
| 12939 | 13141 | ||
| 12940 | INT_OFF; | 13142 | INTOFF; |
| 12941 | retval = options(/*login_sh:*/ NULL); | 13143 | retval = options(/*login_sh:*/ NULL); |
| 12942 | if (retval == 0) { /* if no parse error... */ | 13144 | if (retval == 0) { /* if no parse error... */ |
| 12943 | optschanged(); | 13145 | optschanged(); |
| @@ -12945,7 +13147,7 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 12945 | setparam(argptr); | 13147 | setparam(argptr); |
| 12946 | } | 13148 | } |
| 12947 | } | 13149 | } |
| 12948 | INT_ON; | 13150 | INTON; |
| 12949 | return retval; | 13151 | return retval; |
| 12950 | } | 13152 | } |
| 12951 | 13153 | ||
| @@ -13355,7 +13557,7 @@ fixredir(union node *n, const char *text, int err) | |||
| 13355 | * silently truncate results to word width. | 13557 | * silently truncate results to word width. |
| 13356 | */ | 13558 | */ |
| 13357 | if (err) | 13559 | if (err) |
| 13358 | raise_error_syntax("bad fd number"); | 13560 | ash_msg_and_raise_error("bad fd number"); |
| 13359 | n->ndup.vname = makename(); | 13561 | n->ndup.vname = makename(); |
| 13360 | } | 13562 | } |
| 13361 | } | 13563 | } |
| @@ -13788,12 +13990,12 @@ decode_dollar_squote(void) | |||
| 13788 | #endif | 13990 | #endif |
| 13789 | 13991 | ||
| 13790 | /* Used by expandstr to get here-doc like behaviour. */ | 13992 | /* Used by expandstr to get here-doc like behaviour. */ |
| 13791 | #define FAKEEOFMARK ((char*)(uintptr_t)1) | 13993 | #define FAKEEOFMARK ((struct heredoc*)(uintptr_t)1) |
| 13792 | 13994 | ||
| 13793 | static ALWAYS_INLINE int | 13995 | static ALWAYS_INLINE int |
| 13794 | realeofmark(const char *eofmark) | 13996 | realeofmark(struct heredoc *here) |
| 13795 | { | 13997 | { |
| 13796 | return eofmark && eofmark != FAKEEOFMARK; | 13998 | return here && here != FAKEEOFMARK; |
| 13797 | } | 13999 | } |
| 13798 | 14000 | ||
| 13799 | /* | 14001 | /* |
| @@ -13815,7 +14017,7 @@ realeofmark(const char *eofmark) | |||
| 13815 | #define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} | 14017 | #define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} |
| 13816 | #define PARSEARITH() {goto parsearith; parsearith_return:;} | 14018 | #define PARSEARITH() {goto parsearith; parsearith_return:;} |
| 13817 | static int | 14019 | static int |
| 13818 | readtoken1(int c, int syntax, char *eofmark, int striptabs) | 14020 | readtoken1(int c, int syntax, struct heredoc *eofmark) |
| 13819 | { | 14021 | { |
| 13820 | /* NB: syntax parameter fits into smallint */ | 14022 | /* NB: syntax parameter fits into smallint */ |
| 13821 | /* c parameter is an unsigned char or PEOF */ | 14023 | /* c parameter is an unsigned char or PEOF */ |
| @@ -14067,23 +14269,30 @@ checkend: { | |||
| 14067 | int markloc; | 14269 | int markloc; |
| 14068 | char *p; | 14270 | char *p; |
| 14069 | 14271 | ||
| 14070 | if (striptabs) { | 14272 | if (eofmark->striptabs) { |
| 14071 | while (c == '\t') | 14273 | while (c == '\t') |
| 14072 | c = pgetc(); | 14274 | if (eofmark->here->type == NHERE) |
| 14275 | c = pgetc(); /* dash always does pgetc() */ | ||
| 14276 | else /* NXHERE */ | ||
| 14277 | c = pgetc_eatbnl(); | ||
| 14278 | /* (see heredoc_bkslash_newline3a.tests) */ | ||
| 14073 | } | 14279 | } |
| 14074 | 14280 | ||
| 14075 | markloc = out - (char *)stackblock(); | 14281 | markloc = out - (char *)stackblock(); |
| 14076 | for (p = eofmark; STPUTC(c, out), *p; p++) { | 14282 | for (p = eofmark->eofmark; STPUTC(c, out), *p; p++) { |
| 14077 | if (c != *p) | 14283 | if (c != *p) |
| 14078 | goto more_heredoc; | 14284 | goto more_heredoc; |
| 14079 | /* FIXME: fails for backslash-newlined terminator: | 14285 | /* dash still has this not fixed (as of 2025-08) |
| 14080 | * cat <<EOF | 14286 | * cat <<EOF |
| 14081 | * ... | 14287 | * ... |
| 14082 | * EO\ | 14288 | * EO\ |
| 14083 | * F | 14289 | * F |
| 14084 | * (see heredoc_bkslash_newline2.tests) | 14290 | * (see heredoc_bkslash_newline2.tests) |
| 14085 | */ | 14291 | */ |
| 14086 | c = pgetc(); | 14292 | if (eofmark->here->type == NHERE) |
| 14293 | c = pgetc(); /* dash always does pgetc() */ | ||
| 14294 | else /* NXHERE */ | ||
| 14295 | c = pgetc_eatbnl(); | ||
| 14087 | } | 14296 | } |
| 14088 | 14297 | ||
| 14089 | if (c == '\n' || c == PEOF) { | 14298 | if (c == '\n' || c == PEOF) { |
| @@ -14093,7 +14302,6 @@ checkend: { | |||
| 14093 | needprompt = doprompt; | 14302 | needprompt = doprompt; |
| 14094 | } else { | 14303 | } else { |
| 14095 | int len_here; | 14304 | int len_here; |
| 14096 | |||
| 14097 | more_heredoc: | 14305 | more_heredoc: |
| 14098 | p = (char *)stackblock() + markloc + 1; | 14306 | p = (char *)stackblock() + markloc + 1; |
| 14099 | len_here = out - p; | 14307 | len_here = out - p; |
| @@ -14156,6 +14364,11 @@ parseredir: { | |||
| 14156 | c = pgetc_eatbnl(); | 14364 | c = pgetc_eatbnl(); |
| 14157 | switch (c) { | 14365 | switch (c) { |
| 14158 | case '<': | 14366 | case '<': |
| 14367 | c = pgetc_eatbnl(); | ||
| 14368 | if (c == '<') { | ||
| 14369 | np->type = NFROMSTR; | ||
| 14370 | break; | ||
| 14371 | } | ||
| 14159 | if (sizeof(struct nfile) != sizeof(struct nhere)) { | 14372 | if (sizeof(struct nfile) != sizeof(struct nhere)) { |
| 14160 | np = stzalloc(sizeof(struct nhere)); | 14373 | np = stzalloc(sizeof(struct nhere)); |
| 14161 | /*np->nfile.fd = 0; - stzalloc did it */ | 14374 | /*np->nfile.fd = 0; - stzalloc did it */ |
| @@ -14163,7 +14376,6 @@ parseredir: { | |||
| 14163 | np->type = NHERE; | 14376 | np->type = NHERE; |
| 14164 | heredoc = stzalloc(sizeof(struct heredoc)); | 14377 | heredoc = stzalloc(sizeof(struct heredoc)); |
| 14165 | heredoc->here = np; | 14378 | heredoc->here = np; |
| 14166 | c = pgetc_eatbnl(); | ||
| 14167 | if (c == '-') { | 14379 | if (c == '-') { |
| 14168 | heredoc->striptabs = 1; | 14380 | heredoc->striptabs = 1; |
| 14169 | } else { | 14381 | } else { |
| @@ -14602,7 +14814,7 @@ xxreadtoken(void) | |||
| 14602 | } | 14814 | } |
| 14603 | } /* for (;;) */ | 14815 | } /* for (;;) */ |
| 14604 | 14816 | ||
| 14605 | return readtoken1(c, BASESYNTAX, (char *) NULL, 0); | 14817 | return readtoken1(c, BASESYNTAX, NULL); |
| 14606 | } | 14818 | } |
| 14607 | #else /* old xxreadtoken */ | 14819 | #else /* old xxreadtoken */ |
| 14608 | #define RETURN(token) return lasttoken = token | 14820 | #define RETURN(token) return lasttoken = token |
| @@ -14653,7 +14865,7 @@ xxreadtoken(void) | |||
| 14653 | } | 14865 | } |
| 14654 | break; | 14866 | break; |
| 14655 | } | 14867 | } |
| 14656 | return readtoken1(c, BASESYNTAX, (char *)NULL, 0); | 14868 | return readtoken1(c, BASESYNTAX, NULL); |
| 14657 | #undef RETURN | 14869 | #undef RETURN |
| 14658 | } | 14870 | } |
| 14659 | #endif /* old xxreadtoken */ | 14871 | #endif /* old xxreadtoken */ |
| @@ -14759,9 +14971,9 @@ parseheredoc(void) | |||
| 14759 | tokpushback = 0; | 14971 | tokpushback = 0; |
| 14760 | setprompt_if(needprompt, 2); | 14972 | setprompt_if(needprompt, 2); |
| 14761 | if (here->here->type == NHERE) | 14973 | if (here->here->type == NHERE) |
| 14762 | readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs); | 14974 | readtoken1(pgetc(), SQSYNTAX, here); |
| 14763 | else | 14975 | else |
| 14764 | readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs); | 14976 | readtoken1(pgetc_eatbnl(), DQSYNTAX, here); |
| 14765 | n = stzalloc(sizeof(struct narg)); | 14977 | n = stzalloc(sizeof(struct narg)); |
| 14766 | n->narg.type = NARG; | 14978 | n->narg.type = NARG; |
| 14767 | /*n->narg.next = NULL; - stzalloc did it */ | 14979 | /*n->narg.next = NULL; - stzalloc did it */ |
| @@ -14772,7 +14984,6 @@ parseheredoc(void) | |||
| 14772 | } | 14984 | } |
| 14773 | } | 14985 | } |
| 14774 | 14986 | ||
| 14775 | |||
| 14776 | static const char * | 14987 | static const char * |
| 14777 | expandstr(const char *ps, int syntax_type) | 14988 | expandstr(const char *ps, int syntax_type) |
| 14778 | { | 14989 | { |
| @@ -14805,8 +15016,7 @@ expandstr(const char *ps, int syntax_type) | |||
| 14805 | * PS1='$(date "+%H:%M:%S) > ' | 15016 | * PS1='$(date "+%H:%M:%S) > ' |
| 14806 | */ | 15017 | */ |
| 14807 | exception_handler = &jmploc; | 15018 | exception_handler = &jmploc; |
| 14808 | readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0); | 15019 | readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK); |
| 14809 | |||
| 14810 | n.narg.type = NARG; | 15020 | n.narg.type = NARG; |
| 14811 | n.narg.next = NULL; | 15021 | n.narg.next = NULL; |
| 14812 | n.narg.text = wordtext; | 15022 | n.narg.text = wordtext; |
| @@ -14943,7 +15153,7 @@ cmdloop(int top) | |||
| 14943 | 15153 | ||
| 14944 | setstackmark(&smark); | 15154 | setstackmark(&smark); |
| 14945 | #if JOBS || JOBS_WIN32 | 15155 | #if JOBS || JOBS_WIN32 |
| 14946 | if (doing_jobctl) | 15156 | if (jobctl) |
| 14947 | showjobs(SHOW_CHANGED|SHOW_STDERR); | 15157 | showjobs(SHOW_CHANGED|SHOW_STDERR); |
| 14948 | #endif | 15158 | #endif |
| 14949 | inter = 0; | 15159 | inter = 0; |
| @@ -15355,11 +15565,11 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
| 15355 | entry->u.index = idx; | 15565 | entry->u.index = idx; |
| 15356 | return; | 15566 | return; |
| 15357 | } | 15567 | } |
| 15358 | INT_OFF; | 15568 | INTOFF; |
| 15359 | cmdp = cmdlookup(name, 1); | 15569 | cmdp = cmdlookup(name, 1); |
| 15360 | cmdp->cmdtype = CMDNORMAL; | 15570 | cmdp->cmdtype = CMDNORMAL; |
| 15361 | cmdp->param.index = idx; | 15571 | cmdp->param.index = idx; |
| 15362 | INT_ON; | 15572 | INTON; |
| 15363 | goto success; | 15573 | goto success; |
| 15364 | } | 15574 | } |
| 15365 | 15575 | ||
| @@ -15379,7 +15589,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
| 15379 | return; | 15589 | return; |
| 15380 | } | 15590 | } |
| 15381 | #endif | 15591 | #endif |
| 15382 | ash_msg("%s: %s", name, errmsg(e, "not found")); | 15592 | ash_msg("%s: %s", name, errmsg(e, E_EXEC)); |
| 15383 | } | 15593 | } |
| 15384 | fail: | 15594 | fail: |
| 15385 | entry->cmdtype = CMDUNKNOWN; | 15595 | entry->cmdtype = CMDUNKNOWN; |
| @@ -15391,18 +15601,17 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
| 15391 | entry->u.cmd = bcmd; | 15601 | entry->u.cmd = bcmd; |
| 15392 | return; | 15602 | return; |
| 15393 | } | 15603 | } |
| 15394 | INT_OFF; | 15604 | INTOFF; |
| 15395 | cmdp = cmdlookup(name, 1); | 15605 | cmdp = cmdlookup(name, 1); |
| 15396 | cmdp->cmdtype = CMDBUILTIN; | 15606 | cmdp->cmdtype = CMDBUILTIN; |
| 15397 | cmdp->param.cmd = bcmd; | 15607 | cmdp->param.cmd = bcmd; |
| 15398 | INT_ON; | 15608 | INTON; |
| 15399 | success: | 15609 | success: |
| 15400 | cmdp->rehash = 0; | 15610 | cmdp->rehash = 0; |
| 15401 | entry->cmdtype = cmdp->cmdtype; | 15611 | entry->cmdtype = cmdp->cmdtype; |
| 15402 | entry->u = cmdp->param; | 15612 | entry->u = cmdp->param; |
| 15403 | } | 15613 | } |
| 15404 | 15614 | ||
| 15405 | |||
| 15406 | /* | 15615 | /* |
| 15407 | * The trap builtin. | 15616 | * The trap builtin. |
| 15408 | */ | 15617 | */ |
| @@ -15458,7 +15667,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 15458 | exitcode = 1; | 15667 | exitcode = 1; |
| 15459 | goto next; | 15668 | goto next; |
| 15460 | } | 15669 | } |
| 15461 | INT_OFF; | 15670 | INTOFF; |
| 15462 | if (action) { | 15671 | if (action) { |
| 15463 | if (LONE_DASH(action)) | 15672 | if (LONE_DASH(action)) |
| 15464 | action = NULL; | 15673 | action = NULL; |
| @@ -15495,7 +15704,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 15495 | if (signo != 0 && signo < NSIG) | 15704 | if (signo != 0 && signo < NSIG) |
| 15496 | setsignal(signo); | 15705 | setsignal(signo); |
| 15497 | #endif | 15706 | #endif |
| 15498 | INT_ON; | 15707 | INTON; |
| 15499 | next: | 15708 | next: |
| 15500 | ap++; | 15709 | ap++; |
| 15501 | } | 15710 | } |
| @@ -15808,10 +16017,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 15808 | * to jump out of it. | 16017 | * to jump out of it. |
| 15809 | */ | 16018 | */ |
| 15810 | again: | 16019 | again: |
| 15811 | INT_OFF; | 16020 | INTOFF; |
| 15812 | r = shell_builtin_read(¶ms); | 16021 | r = shell_builtin_read(¶ms); |
| 15813 | INT_ON; | 16022 | INTON; |
| 15814 | 16023 | ||
| 16024 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 15815 | if ((uintptr_t)r == 1 && errno == EINTR) { | 16025 | if ((uintptr_t)r == 1 && errno == EINTR) { |
| 15816 | /* To get SIGCHLD: sleep 1 & read x; echo $x | 16026 | /* To get SIGCHLD: sleep 1 & read x; echo $x |
| 15817 | * Correct behavior is to not exit "read" | 16027 | * Correct behavior is to not exit "read" |
| @@ -15820,11 +16030,18 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 15820 | goto again; | 16030 | goto again; |
| 15821 | } | 16031 | } |
| 15822 | 16032 | ||
| 15823 | #if ENABLE_PLATFORM_MINGW32 | 16033 | if ((uintptr_t)r == 2) /* -t SEC timeout? */ |
| 16034 | /* bash: "The exit status is greater than 128 if the timeout is exceeded." */ | ||
| 16035 | /* The actual value observed with bash 5.2.15: */ | ||
| 16036 | return 128 + SIGALRM; | ||
| 16037 | #else /* ENABLE_PLATFORM_MINGW32 */ | ||
| 15824 | if ((uintptr_t)r == 2) { | 16038 | if ((uintptr_t)r == 2) { |
| 16039 | /* Timeout, return 128 + SIGALRM */ | ||
| 16040 | return 142; | ||
| 16041 | } else if ((uintptr_t)r == 3) { | ||
| 15825 | /* ^C pressed, propagate event */ | 16042 | /* ^C pressed, propagate event */ |
| 15826 | if (trap[SIGINT]) { | 16043 | if (trap[SIGINT]) { |
| 15827 | write(STDOUT_FILENO, "^C", 2); | 16044 | write_ctrl_c(); |
| 15828 | pending_int = 1; | 16045 | pending_int = 1; |
| 15829 | dotrap(); | 16046 | dotrap(); |
| 15830 | if (!(rootshell && iflag)) | 16047 | if (!(rootshell && iflag)) |
| @@ -15859,10 +16076,10 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 15859 | symbolic_mode = 1; | 16076 | symbolic_mode = 1; |
| 15860 | } | 16077 | } |
| 15861 | 16078 | ||
| 15862 | INT_OFF; | 16079 | INTOFF; |
| 15863 | mask = umask(0); | 16080 | mask = umask(0); |
| 15864 | umask(mask); | 16081 | umask(mask); |
| 15865 | INT_ON; | 16082 | INTON; |
| 15866 | 16083 | ||
| 15867 | if (*argptr == NULL) { | 16084 | if (*argptr == NULL) { |
| 15868 | if (symbolic_mode) { | 16085 | if (symbolic_mode) { |
| @@ -15969,8 +16186,25 @@ exitshell(void) | |||
| 15969 | char *p; | 16186 | char *p; |
| 15970 | 16187 | ||
| 15971 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 16188 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
| 15972 | save_history(line_input_state); /* may be NULL */ | 16189 | if (line_input_state) { |
| 16190 | const char *hp; | ||
| 16191 | # if ENABLE_FEATURE_SH_HISTFILESIZE | ||
| 16192 | // in bash: | ||
| 16193 | // HISTFILESIZE controls the on-disk history file size (in lines, 0=no history): | ||
| 16194 | // "When this variable is assigned a value, the history file is truncated, if necessary" | ||
| 16195 | // but we do it only at exit, not on assignment: | ||
| 16196 | /* Use HISTFILESIZE to limit file size */ | ||
| 16197 | hp = lookupvar("HISTFILESIZE"); | ||
| 16198 | if (hp) | ||
| 16199 | line_input_state->max_history = size_from_HISTFILESIZE(hp); | ||
| 16200 | # endif | ||
| 16201 | /* HISTFILE: "If unset, the command history is not saved when a shell exits." */ | ||
| 16202 | hp = lookupvar("HISTFILE"); | ||
| 16203 | line_input_state->hist_file = hp; | ||
| 16204 | save_history(line_input_state); /* no-op if hist_file is NULL or "" */ | ||
| 16205 | } | ||
| 15973 | #endif | 16206 | #endif |
| 16207 | |||
| 15974 | savestatus = exitstatus; | 16208 | savestatus = exitstatus; |
| 15975 | TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); | 16209 | TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); |
| 15976 | if (setjmp(loc.loc)) | 16210 | if (setjmp(loc.loc)) |
| @@ -16020,11 +16254,13 @@ init(void) | |||
| 16020 | { | 16254 | { |
| 16021 | #if ENABLE_PLATFORM_MINGW32 | 16255 | #if ENABLE_PLATFORM_MINGW32 |
| 16022 | int import = 0; | 16256 | int import = 0; |
| 16023 | #else | 16257 | #endif |
| 16258 | |||
| 16024 | /* we will never free this */ | 16259 | /* we will never free this */ |
| 16025 | basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); | 16260 | basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); |
| 16026 | basepf.linno = 1; | 16261 | basepf.linno = 1; |
| 16027 | 16262 | ||
| 16263 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 16028 | sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ | 16264 | sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ |
| 16029 | setsignal(SIGCHLD); | 16265 | setsignal(SIGCHLD); |
| 16030 | #endif | 16266 | #endif |
| @@ -16144,7 +16380,6 @@ init(void) | |||
| 16144 | } | 16380 | } |
| 16145 | } | 16381 | } |
| 16146 | 16382 | ||
| 16147 | |||
| 16148 | //usage:#define ash_trivial_usage | 16383 | //usage:#define ash_trivial_usage |
| 16149 | //usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" | 16384 | //usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" |
| 16150 | //////// comes from ^^^^^^^^^^optletters | 16385 | //////// comes from ^^^^^^^^^^optletters |
| @@ -16155,18 +16390,15 @@ init(void) | |||
| 16155 | * Process the shell command line arguments. | 16390 | * Process the shell command line arguments. |
| 16156 | */ | 16391 | */ |
| 16157 | static int | 16392 | static int |
| 16158 | procargs(char **argv) | 16393 | procargs(char **xargv) |
| 16159 | { | 16394 | { |
| 16160 | int i; | 16395 | int i; |
| 16161 | const char *xminusc; | ||
| 16162 | char **xargv; | ||
| 16163 | int login_sh; | 16396 | int login_sh; |
| 16164 | 16397 | ||
| 16165 | xargv = argv; | ||
| 16166 | #if ENABLE_PLATFORM_MINGW32 | 16398 | #if ENABLE_PLATFORM_MINGW32 |
| 16167 | login_sh = applet_name[0] == 'l'; | 16399 | login_sh = applet_name[0] == 'l'; |
| 16168 | #else | 16400 | #else |
| 16169 | login_sh = xargv[0] && xargv[0][0] == '-'; | 16401 | login_sh = /*xargv[0] &&*/ xargv[0][0] == '-'; |
| 16170 | #endif | 16402 | #endif |
| 16171 | #if NUM_SCRIPTS > 0 | 16403 | #if NUM_SCRIPTS > 0 |
| 16172 | if (minusc) | 16404 | if (minusc) |
| @@ -16183,9 +16415,8 @@ procargs(char **argv) | |||
| 16183 | raise_exception(EXERROR); /* does not return */ | 16415 | raise_exception(EXERROR); /* does not return */ |
| 16184 | } | 16416 | } |
| 16185 | xargv = argptr; | 16417 | xargv = argptr; |
| 16186 | xminusc = minusc; | ||
| 16187 | if (*xargv == NULL) { | 16418 | if (*xargv == NULL) { |
| 16188 | if (xminusc) | 16419 | if (minusc) |
| 16189 | ash_msg_and_raise_error(bb_msg_requires_arg, "-c"); | 16420 | ash_msg_and_raise_error(bb_msg_requires_arg, "-c"); |
| 16190 | sflag = 1; | 16421 | sflag = 1; |
| 16191 | } | 16422 | } |
| @@ -16207,7 +16438,7 @@ procargs(char **argv) | |||
| 16207 | debug = 1; | 16438 | debug = 1; |
| 16208 | #endif | 16439 | #endif |
| 16209 | /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */ | 16440 | /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */ |
| 16210 | if (xminusc) { | 16441 | if (minusc) { |
| 16211 | minusc = *xargv++; | 16442 | minusc = *xargv++; |
| 16212 | if (*xargv) | 16443 | if (*xargv) |
| 16213 | goto setarg0; | 16444 | goto setarg0; |
| @@ -16218,7 +16449,6 @@ procargs(char **argv) | |||
| 16218 | #endif | 16449 | #endif |
| 16219 | setarg0: | 16450 | setarg0: |
| 16220 | arg0 = *xargv++; | 16451 | arg0 = *xargv++; |
| 16221 | commandname = arg0; | ||
| 16222 | } | 16452 | } |
| 16223 | 16453 | ||
| 16224 | shellparam.p = xargv; | 16454 | shellparam.p = xargv; |
| @@ -16283,17 +16513,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 16283 | /* note: 'argc' is used only if embedded scripts are enabled */ | 16513 | /* note: 'argc' is used only if embedded scripts are enabled */ |
| 16284 | { | 16514 | { |
| 16285 | volatile smallint state; | 16515 | volatile smallint state; |
| 16286 | struct jmploc jmploc; | ||
| 16287 | struct stackmark smark; | 16516 | struct stackmark smark; |
| 16288 | int login_sh; | 16517 | int login_sh; |
| 16289 | 16518 | ||
| 16290 | #if ENABLE_PLATFORM_MINGW32 | 16519 | #if ENABLE_PLATFORM_MINGW32 |
| 16291 | INIT_G_memstack(); | 16520 | INIT_G_memstack(); |
| 16292 | 16521 | ||
| 16293 | /* from init() */ | ||
| 16294 | basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); | ||
| 16295 | basepf.linno = 1; | ||
| 16296 | |||
| 16297 | if (argc == 3 && !strcmp(argv[1], "--fs")) { | 16522 | if (argc == 3 && !strcmp(argv[1], "--fs")) { |
| 16298 | forkshell_init(argv[2]); | 16523 | forkshell_init(argv[2]); |
| 16299 | /* only reached in case of error */ | 16524 | /* only reached in case of error */ |
| @@ -16317,7 +16542,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 16317 | #endif | 16542 | #endif |
| 16318 | 16543 | ||
| 16319 | state = 0; | 16544 | state = 0; |
| 16320 | if (setjmp(jmploc.loc)) { | 16545 | if (setjmp(main_handler.loc)) { |
| 16321 | smallint e; | 16546 | smallint e; |
| 16322 | smallint s; | 16547 | smallint s; |
| 16323 | 16548 | ||
| @@ -16336,7 +16561,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 16336 | } | 16561 | } |
| 16337 | 16562 | ||
| 16338 | popstackmark(&smark); | 16563 | popstackmark(&smark); |
| 16339 | FORCE_INT_ON; /* enable interrupts */ | 16564 | FORCEINTON; /* enable interrupts */ |
| 16340 | if (s == 1) | 16565 | if (s == 1) |
| 16341 | goto state1; | 16566 | goto state1; |
| 16342 | if (s == 2) | 16567 | if (s == 2) |
| @@ -16345,7 +16570,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 16345 | goto state3; | 16570 | goto state3; |
| 16346 | goto state4; | 16571 | goto state4; |
| 16347 | } | 16572 | } |
| 16348 | exception_handler = &jmploc; | 16573 | exception_handler = &main_handler; |
| 16349 | rootpid = getpid(); | 16574 | rootpid = getpid(); |
| 16350 | 16575 | ||
| 16351 | init(); | 16576 | init(); |
| @@ -16463,18 +16688,23 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 16463 | if (!hp) { | 16688 | if (!hp) { |
| 16464 | hp = lookupvar("HOME"); | 16689 | hp = lookupvar("HOME"); |
| 16465 | if (hp) { | 16690 | if (hp) { |
| 16466 | INT_OFF; | 16691 | INTOFF; |
| 16467 | hp = concat_path_file(hp, ".ash_history"); | 16692 | hp = concat_path_file(hp, ".ash_history"); |
| 16468 | setvar0("HISTFILE", hp); | 16693 | setvar0("HISTFILE", hp); |
| 16469 | free((char*)hp); | 16694 | free((char*)hp); |
| 16470 | INT_ON; | 16695 | INTON; |
| 16471 | hp = lookupvar("HISTFILE"); | 16696 | hp = lookupvar("HISTFILE"); |
| 16472 | } | 16697 | } |
| 16473 | } | 16698 | } |
| 16474 | if (hp) | 16699 | if (hp) |
| 16475 | line_input_state->hist_file = xstrdup(hp); | 16700 | line_input_state->hist_file = xstrdup(hp); |
| 16476 | # if ENABLE_FEATURE_SH_HISTFILESIZE | 16701 | # if ENABLE_FEATURE_SH_HISTFILESIZE |
| 16477 | hp = lookupvar("HISTFILESIZE"); | 16702 | hp = lookupvar("HISTSIZE"); |
| 16703 | /* Using HISTFILESIZE above to limit max_history would be WRONG: | ||
| 16704 | * users may set HISTFILESIZE=0 in their profile scripts | ||
| 16705 | * to prevent _saving_ of history files, but still want to have | ||
| 16706 | * non-zero history limit for in-memory list. | ||
| 16707 | */ | ||
| 16478 | line_input_state->max_history = size_from_HISTFILESIZE(hp); | 16708 | line_input_state->max_history = size_from_HISTFILESIZE(hp); |
| 16479 | # endif | 16709 | # endif |
| 16480 | } | 16710 | } |
| @@ -16537,7 +16767,7 @@ forkshell_evalbackcmd(struct forkshell *fs) | |||
| 16537 | pip[ip] = fs->fd[ip]; | 16767 | pip[ip] = fs->fd[ip]; |
| 16538 | pip[ic] = fs->fd[ic]; | 16768 | pip[ic] = fs->fd[ic]; |
| 16539 | 16769 | ||
| 16540 | FORCE_INT_ON; | 16770 | FORCEINTON; |
| 16541 | close(pip[ip]); | 16771 | close(pip[ip]); |
| 16542 | if (pip[ic] != ic) { | 16772 | if (pip[ic] != ic) { |
| 16543 | /*close(ic);*/ | 16773 | /*close(ic);*/ |
| @@ -16557,7 +16787,7 @@ forkshell_evalsubshell(struct forkshell *fs) | |||
| 16557 | int flags = fs->flags; | 16787 | int flags = fs->flags; |
| 16558 | 16788 | ||
| 16559 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); | 16789 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); |
| 16560 | INT_ON; | 16790 | INTON; |
| 16561 | flags |= EV_EXIT; | 16791 | flags |= EV_EXIT; |
| 16562 | if (fs->mode) | 16792 | if (fs->mode) |
| 16563 | flags &= ~EV_TESTED; | 16793 | flags &= ~EV_TESTED; |
| @@ -16576,7 +16806,7 @@ forkshell_evalpipe(struct forkshell *fs) | |||
| 16576 | int pip[2] = {fs->fd[0], fs->fd[1]}; | 16806 | int pip[2] = {fs->fd[0], fs->fd[1]}; |
| 16577 | 16807 | ||
| 16578 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); | 16808 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); |
| 16579 | INT_ON; | 16809 | INTON; |
| 16580 | if (pip[1] >= 0) { | 16810 | if (pip[1] >= 0) { |
| 16581 | close(pip[0]); | 16811 | close(pip[0]); |
| 16582 | } | 16812 | } |
| @@ -16596,9 +16826,9 @@ forkshell_shellexec(struct forkshell *fs) | |||
| 16596 | { | 16826 | { |
| 16597 | int idx = fs->fd[0]; | 16827 | int idx = fs->fd[0]; |
| 16598 | char **argv = fs->argv; | 16828 | char **argv = fs->argv; |
| 16599 | char *path = fs->path; | 16829 | const char *path = fs->path; |
| 16600 | 16830 | ||
| 16601 | FORCE_INT_ON; | 16831 | FORCEINTON; |
| 16602 | shellexec(argv[0], argv, path, idx, TRUE); | 16832 | shellexec(argv[0], argv, path, idx, TRUE); |
| 16603 | } | 16833 | } |
| 16604 | 16834 | ||
| @@ -17148,6 +17378,8 @@ globals_misc_size(struct datasize ds) | |||
| 17148 | #undef physdir | 17378 | #undef physdir |
| 17149 | #undef arg0 | 17379 | #undef arg0 |
| 17150 | #undef commandname | 17380 | #undef commandname |
| 17381 | #undef g_parsefile | ||
| 17382 | #undef basepf | ||
| 17151 | #undef nullstr | 17383 | #undef nullstr |
| 17152 | #undef trap | 17384 | #undef trap |
| 17153 | static struct globals_misc * | 17385 | static struct globals_misc * |
| @@ -17176,6 +17408,8 @@ globals_misc_copy(void) | |||
| 17176 | new->trap[i] = nodeckstrdup(p->trap[i]); | 17408 | new->trap[i] = nodeckstrdup(p->trap[i]); |
| 17177 | SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE); | 17409 | SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE); |
| 17178 | } | 17410 | } |
| 17411 | new->g_parsefile = NULL; | ||
| 17412 | memset(&new->basepf, 0, sizeof(struct parsefile)); | ||
| 17179 | return new; | 17413 | return new; |
| 17180 | } | 17414 | } |
| 17181 | 17415 | ||
| @@ -17188,8 +17422,8 @@ forkshell_size(struct forkshell *fs) | |||
| 17188 | if (fs->fpid == FS_OPENHERE) | 17422 | if (fs->fpid == FS_OPENHERE) |
| 17189 | return ds; | 17423 | return ds; |
| 17190 | 17424 | ||
| 17191 | ds = globals_var_size(ds); | ||
| 17192 | ds = globals_misc_size(ds); | 17425 | ds = globals_misc_size(ds); |
| 17426 | ds = globals_var_size(ds); | ||
| 17193 | ds = cmdtable_size(ds); | 17427 | ds = cmdtable_size(ds); |
| 17194 | 17428 | ||
| 17195 | ds.funcblocksize = calcsize(ds.funcblocksize, fs->n); | 17429 | ds.funcblocksize = calcsize(ds.funcblocksize, fs->n); |
| @@ -17221,11 +17455,11 @@ forkshell_copy(struct forkshell *fs, struct forkshell *new) | |||
| 17221 | if (fs->fpid == FS_OPENHERE) | 17455 | if (fs->fpid == FS_OPENHERE) |
| 17222 | return; | 17456 | return; |
| 17223 | 17457 | ||
| 17224 | new->gvp = globals_var_copy(); | ||
| 17225 | new->gmp = globals_misc_copy(); | 17458 | new->gmp = globals_misc_copy(); |
| 17459 | new->gvp = globals_var_copy(); | ||
| 17226 | new->cmdtable = cmdtable_copy(); | 17460 | new->cmdtable = cmdtable_copy(); |
| 17227 | SAVE_PTR(new->gvp, "gvp", NO_FREE); | ||
| 17228 | SAVE_PTR(new->gmp, "gmp", NO_FREE); | 17461 | SAVE_PTR(new->gmp, "gmp", NO_FREE); |
| 17462 | SAVE_PTR(new->gvp, "gvp", NO_FREE); | ||
| 17229 | SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE); | 17463 | SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE); |
| 17230 | 17464 | ||
| 17231 | new->n = copynode(fs->n); | 17465 | new->n = copynode(fs->n); |
| @@ -17262,7 +17496,7 @@ forkshell_copy(struct forkshell *fs, struct forkshell *new) | |||
| 17262 | 17496 | ||
| 17263 | #if FORKSHELL_DEBUG | 17497 | #if FORKSHELL_DEBUG |
| 17264 | #define NUM_BLOCKS FUNCSTRING | 17498 | #define NUM_BLOCKS FUNCSTRING |
| 17265 | enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING}; | 17499 | enum {GMP, GVP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING}; |
| 17266 | 17500 | ||
| 17267 | /* fp0 and notes can each be NULL */ | 17501 | /* fp0 and notes can each be NULL */ |
| 17268 | static void | 17502 | static void |
| @@ -17327,8 +17561,8 @@ forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes) | |||
| 17327 | lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB]; | 17561 | lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB]; |
| 17328 | lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV]; | 17562 | lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV]; |
| 17329 | lptr[CMDTABLE] = (char *)fs->cmdtable; | 17563 | lptr[CMDTABLE] = (char *)fs->cmdtable; |
| 17330 | lptr[GMP] = (char *)fs->gmp; | ||
| 17331 | lptr[GVP] = (char *)fs->gvp; | 17564 | lptr[GVP] = (char *)fs->gvp; |
| 17565 | lptr[GMP] = (char *)fs->gmp; | ||
| 17332 | 17566 | ||
| 17333 | fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize); | 17567 | fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize); |
| 17334 | total = 0; | 17568 | total = 0; |
| @@ -17470,7 +17704,6 @@ forkshell_init(const char *idstr) | |||
| 17470 | int i; | 17704 | int i; |
| 17471 | char **ptr; | 17705 | char **ptr; |
| 17472 | char *lrelocate; | 17706 | char *lrelocate; |
| 17473 | struct jmploc jmploc; | ||
| 17474 | 17707 | ||
| 17475 | if (sscanf(idstr, "%p", &map_handle) != 1) | 17708 | if (sscanf(idstr, "%p", &map_handle) != 1) |
| 17476 | return; | 17709 | return; |
| @@ -17508,9 +17741,14 @@ forkshell_init(const char *idstr) | |||
| 17508 | } | 17741 | } |
| 17509 | fs->gmp->trap_ptr = fs->gmp->trap; | 17742 | fs->gmp->trap_ptr = fs->gmp->trap; |
| 17510 | 17743 | ||
| 17744 | /* from init() */ | ||
| 17745 | fs->gmp->basepf.next_to_pgetc = fs->gmp->basepf.buf = ckzalloc(IBUFSIZ); | ||
| 17746 | fs->gmp->basepf.linno = 1; | ||
| 17747 | fs->gmp->g_parsefile = &fs->gmp->basepf; | ||
| 17748 | |||
| 17511 | /* Set global variables */ | 17749 | /* Set global variables */ |
| 17512 | ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp); | ||
| 17513 | ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp); | 17750 | ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp); |
| 17751 | ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp); | ||
| 17514 | cmdtable = fs->cmdtable; | 17752 | cmdtable = fs->cmdtable; |
| 17515 | #if ENABLE_ASH_ALIAS | 17753 | #if ENABLE_ASH_ALIAS |
| 17516 | atab = fs->atab; /* will be NULL for FS_SHELLEXEC */ | 17754 | atab = fs->atab; /* will be NULL for FS_SHELLEXEC */ |
| @@ -17533,11 +17771,11 @@ forkshell_init(const char *idstr) | |||
| 17533 | 17771 | ||
| 17534 | reinitvar(); | 17772 | reinitvar(); |
| 17535 | 17773 | ||
| 17536 | if (setjmp(jmploc.loc)) { | 17774 | if (setjmp(main_handler.loc)) { |
| 17537 | exitreset(); | 17775 | exitreset(); |
| 17538 | exitshell(); | 17776 | exitshell(); |
| 17539 | } | 17777 | } |
| 17540 | exception_handler = &jmploc; | 17778 | exception_handler = &main_handler; |
| 17541 | 17779 | ||
| 17542 | shlvl++; | 17780 | shlvl++; |
| 17543 | if (fs->mode == FORK_BG) { | 17781 | if (fs->mode == FORK_BG) { |
| @@ -17566,7 +17804,7 @@ forkshell_init(const char *idstr) | |||
| 17566 | clear_traps(); | 17804 | clear_traps(); |
| 17567 | #if JOBS_WIN32 | 17805 | #if JOBS_WIN32 |
| 17568 | /* do job control only in root shell */ | 17806 | /* do job control only in root shell */ |
| 17569 | doing_jobctl = 0; | 17807 | jobctl = 0; |
| 17570 | 17808 | ||
| 17571 | if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args && | 17809 | if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args && |
| 17572 | strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) { | 17810 | strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) { |
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests new file mode 100755 index 000000000..eb2223031 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | EO\ | ||
| 4 | F | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests new file mode 100755 index 000000000..de21132d1 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests new file mode 100755 index 000000000..da3860804 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/ash_test/ash-heredoc/herestring1.right b/shell/ash_test/ash-heredoc/herestring1.right new file mode 100644 index 000000000..555937daa --- /dev/null +++ b/shell/ash_test/ash-heredoc/herestring1.right | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | one | ||
| 2 | _two | ||
| 3 | \_three | ||
| 4 | \_four | ||
| 5 | \_two | ||
| 6 | \_three | ||
| 7 | \\_four | ||
| 8 | two_newlines | ||
| 9 | |||
| 10 | /bin/c* | ||
| 11 | star_* | ||
| 12 | star_* | ||
| 13 | star_* | ||
| 14 | star_\* | ||
| 15 | star_* | ||
| 16 | star_\* | ||
| 17 | line1 | ||
| 18 | line2 | ||
| 19 | line3 | ||
| 20 | line1 | ||
| 21 | line2 | ||
| 22 | line3 | ||
| 23 | 512 | ||
| 24 | 256 | ||
| 25 | 128 | ||
| 26 | 64 | ||
| 27 | 32 | ||
| 28 | 16 | ||
| 29 | 8 | ||
| 30 | 4 | ||
| 31 | 2 | ||
| 32 | v-$a-\t-\-\"-\x-`-\--\z-\*-\?- | ||
| 33 | v-$a-\t-\-"-\x-`-\--\z-\*-\?- | ||
diff --git a/shell/ash_test/ash-heredoc/herestring1.tests b/shell/ash_test/ash-heredoc/herestring1.tests new file mode 100755 index 000000000..fb7bd0dda --- /dev/null +++ b/shell/ash_test/ash-heredoc/herestring1.tests | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | cat <<<one | ||
| 2 | cat <<<\_two | ||
| 3 | cat <<<"\_three" | ||
| 4 | cat <<<'\_four' | ||
| 5 | cat <<<\\_two | ||
| 6 | cat <<<"\\_three" | ||
| 7 | cat <<<'\\_four' | ||
| 8 | |||
| 9 | cat <<<$'two_newlines\n' | ||
| 10 | |||
| 11 | cat <<</bin/c* | ||
| 12 | |||
| 13 | cat <<<star_* | ||
| 14 | cat <<<star_\* | ||
| 15 | cat <<<"star_*" | ||
| 16 | cat <<<"star_\*" | ||
| 17 | cat <<<'star_*' | ||
| 18 | cat <<<'star_\*' | ||
| 19 | |||
| 20 | var=$'line1 | ||
| 21 | line2 | ||
| 22 | line3' | ||
| 23 | |||
| 24 | cat <<<$var | ||
| 25 | |||
| 26 | cat <<<"$var" | ||
| 27 | |||
| 28 | i=10 | ||
| 29 | until test $((--i)) = 0; do | ||
| 30 | cat <<<$((2**i)) | ||
| 31 | done | ||
| 32 | |||
| 33 | a=qwerty | ||
| 34 | cat <<<`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'` | ||
| 35 | cat <<<"`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`" | ||
diff --git a/shell/ash_test/ash-misc/assignment2.right b/shell/ash_test/ash-misc/assignment2.right index 179c71c5a..246694462 100644 --- a/shell/ash_test/ash-misc/assignment2.right +++ b/shell/ash_test/ash-misc/assignment2.right | |||
| @@ -1,2 +1,4 @@ | |||
| 1 | ./assignment2.tests: line 2: a=b: not found | 1 | ./assignment2.tests: line 2: a=b: not found |
| 2 | 127 | 2 | 127 |
| 3 | ./assignment2.tests: line 4: ab=c: not found | ||
| 4 | 127 | ||
diff --git a/shell/ash_test/ash-misc/assignment2.tests b/shell/ash_test/ash-misc/assignment2.tests index f6938434c..a93a48d09 100755 --- a/shell/ash_test/ash-misc/assignment2.tests +++ b/shell/ash_test/ash-misc/assignment2.tests | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | # This must not be interpreted as an assignment | 1 | # This must not be interpreted as an assignment |
| 2 | a''=b true | 2 | a''=b true |
| 3 | echo $? | 3 | echo $? |
| 4 | a'b'=c true | ||
| 5 | echo $? | ||
diff --git a/shell/ash_test/ash-misc/func6.right b/shell/ash_test/ash-misc/func6.right new file mode 100644 index 000000000..01e79c32a --- /dev/null +++ b/shell/ash_test/ash-misc/func6.right | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | 1 | ||
| 2 | 2 | ||
| 3 | 3 | ||
diff --git a/shell/ash_test/ash-misc/func6.tests b/shell/ash_test/ash-misc/func6.tests new file mode 100755 index 000000000..5f1699c42 --- /dev/null +++ b/shell/ash_test/ash-misc/func6.tests | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | { f() { echo $1; } } | ||
| 2 | f 1 | ||
| 3 | |||
| 4 | { f() ( echo $1; )} | ||
| 5 | f 2 | ||
| 6 | |||
| 7 | { f()(echo $1)} | ||
| 8 | f 3 | ||
diff --git a/shell/ash_test/ash-misc/func7.right b/shell/ash_test/ash-misc/func7.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/ash_test/ash-misc/func7.right | |||
| @@ -0,0 +1 @@ | |||
| Ok:0 | |||
diff --git a/shell/ash_test/ash-misc/func7.tests b/shell/ash_test/ash-misc/func7.tests new file mode 100755 index 000000000..f5e03b6e1 --- /dev/null +++ b/shell/ash_test/ash-misc/func7.tests | |||
| @@ -0,0 +1 @@ | |||
| if f() { echo Ok:$?; } then f; fi | |||
diff --git a/shell/ash_test/ash-read/read_ifs2.right b/shell/ash_test/ash-read/read_ifs2.right new file mode 100644 index 000000000..797137dae --- /dev/null +++ b/shell/ash_test/ash-read/read_ifs2.right | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | |X|Y:Z:| | ||
| 2 | |X|Y:Z| | ||
| 3 | |X|Y| | ||
| 4 | |X|Y| | ||
| 5 | |X|| | ||
| 6 | |X|| | ||
| 7 | ||| | ||
| 8 | Whitespace 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 @@ | |||
| 1 | echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 2 | echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 3 | echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 4 | echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 5 | echo "X:" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 6 | echo "X" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 7 | echo "" | (IFS=": " read x y; echo "|$x|$y|") | ||
| 8 | echo Whitespace should be trimmed too: | ||
| 9 | echo "X:Y : " | (IFS=": " read x y; echo "|$x|$y|") | ||
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right index 04126cbe6..3eedae275 100644 --- a/shell/ash_test/ash-read/read_t.right +++ b/shell/ash_test/ash-read/read_t.right | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | >< | 1 | >te:142< |
| 2 | >< | 2 | >:142< |
| 3 | >test< | 3 | >test:0< |
| 4 | >test< | 4 | >test:0< |
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests index d65f1aeaa..9fbeec517 100755 --- a/shell/ash_test/ash-read/read_t.tests +++ b/shell/ash_test/ash-read/read_t.tests | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | # bash 3.2 outputs: | 1 | # bash 5.2 outputs: |
| 2 | 2 | ||
| 3 | # >< | 3 | # >te:142< |
| 4 | { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") | 4 | { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<") |
| 5 | # >< | 5 | # >:142< |
| 6 | { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") | 6 | { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<") |
| 7 | # >test< | 7 | # >test:0< |
| 8 | { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") | 8 | { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<") |
| 9 | # >test< | 9 | # >test:0< |
| 10 | { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") | 10 | { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<") |
diff --git a/shell/ash_test/ash-redir/redir_EINTR1.right b/shell/ash_test/ash-redir/redir_EINTR1.right new file mode 100644 index 000000000..287d91f67 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_EINTR1.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | Hello | ||
| 2 | Done:0 | ||
diff --git a/shell/ash_test/ash-redir/redir_EINTR1.tests b/shell/ash_test/ash-redir/redir_EINTR1.tests new file mode 100755 index 000000000..cede4d4d8 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_EINTR1.tests | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | rm -f test.fifo | ||
| 2 | mkfifo test.fifo | ||
| 3 | |||
| 4 | (sleep 1; kill -chld $$) & | ||
| 5 | (sleep 2; echo Hello >test.fifo) & | ||
| 6 | |||
| 7 | # We get open("test.fifo") interrupted by SIGCHLD from the first subshell. | ||
| 8 | # The shell MUST retry the open (no printing of error messages). | ||
| 9 | # Then, the second subshell opens fifo for writing and open unblocks and succeeds. | ||
| 10 | cat <test.fifo | ||
| 11 | |||
| 12 | echo "Done:$?" | ||
| 13 | rm -f test.fifo | ||
diff --git a/shell/ash_test/ash-redir/redir_EINTR2.right b/shell/ash_test/ash-redir/redir_EINTR2.right new file mode 100644 index 000000000..287d91f67 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_EINTR2.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | Hello | ||
| 2 | Done:0 | ||
diff --git a/shell/ash_test/ash-redir/redir_EINTR2.tests b/shell/ash_test/ash-redir/redir_EINTR2.tests new file mode 100755 index 000000000..3d343c7ea --- /dev/null +++ b/shell/ash_test/ash-redir/redir_EINTR2.tests | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | rm -f test.fifo | ||
| 2 | mkfifo test.fifo | ||
| 3 | |||
| 4 | (sleep 1; kill -chld $$) & | ||
| 5 | (sleep 2; echo Hello >test.fifo) & | ||
| 6 | |||
| 7 | # We get open("test.fifo") interrupted by SIGCHLD from the first subshell. | ||
| 8 | # The shell MUST retry the open (no printing of error messages). | ||
| 9 | # Then, the second subshell opens fifo for writing and open unblocks and succeeds. | ||
| 10 | read HELLO <test.fifo | ||
| 11 | echo "$HELLO" | ||
| 12 | |||
| 13 | echo "Done:$?" | ||
| 14 | rm -f test.fifo | ||
diff --git a/shell/ash_test/ash-vars/var_backslash1.right b/shell/ash_test/ash-vars/var_backslash1.right new file mode 100644 index 000000000..3f2c21289 --- /dev/null +++ b/shell/ash_test/ash-vars/var_backslash1.right | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | a is '\*bc' | ||
| 2 | b is '\' | ||
| 3 | c is '*' | ||
| 4 | ${a##?*} removes everything: || - matches one char, then all | ||
| 5 | ${a##?"*"} removes \*: |bc| - matches one char, then * | ||
| 6 | ${a##\*} removes nothing: |\*bc| - first char is not * | ||
| 7 | ${a##\\*} removes everything: || - matches \, then all | ||
| 8 | ${a##\\\*} removes \*: |bc| - matches \, then * | ||
| 9 | ${a##?$c} removes everything: || - matches one char, then all | ||
| 10 | ${a##?"$c"} removes \*: |bc| - matches one char, then * | ||
| 11 | ${a##\\$c} removes everything: || - matches \, then all | ||
| 12 | ${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ | ||
| 13 | ${a##\\"$c"} removes \*: |bc| - matches \, then * | ||
| 14 | ${a##$b} removes \: |*bc| - matches \ | ||
| 15 | ${a##"$b"} removes \: |*bc| - matches \ | ||
| 16 | |||
| 17 | Single quote tests: | ||
| 18 | ${a##?'*'} removes \*: |bc| - matches one char, then * | ||
| 19 | ${a##'\'*} removes everything: || - matches \, then all | ||
| 20 | ${a##'\'\*} removes \*: |bc| - matches \, then * | ||
| 21 | ${a##'\*'} removes \*: |bc| - matches \, then * | ||
| 22 | ${a##'\'$c} removes everything: || - matches \, then all | ||
| 23 | ${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $ | ||
| 24 | ${a##'\'"$c"} removes \*: |bc| - matches \, then * | ||
| 25 | |||
| 26 | ${a##"$b"?} removes \*: |bc| - matches \, then one char | ||
| 27 | ${a##"$b"*} removes everything: || - matches \, then all | ||
| 28 | ${a##"$b""?"} removes nothing: |\*bc| - second char is not ? | ||
| 29 | ${a##"$b""*"} removes \*: |bc| - matches \, then * | ||
| 30 | ${a##"$b"\*} removes \*: |bc| - matches \, then * | ||
| 31 | ${a##"$b"$c} removes everything:|| - matches \, then all | ||
| 32 | ${a##"$b""$c"} removes \*: |bc| - matches \, then * | ||
| 33 | ${a##"$b?"} removes nothing: |\*bc| - second char is not ? | ||
| 34 | ${a##"$b*"} removes \*: |bc| - matches \, then * | ||
| 35 | ${a##"$b$c"} removes \*: |bc| - matches \, then * | ||
diff --git a/shell/ash_test/ash-vars/var_backslash1.tests b/shell/ash_test/ash-vars/var_backslash1.tests new file mode 100755 index 000000000..015a6107b --- /dev/null +++ b/shell/ash_test/ash-vars/var_backslash1.tests | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | a='\*bc' | ||
| 2 | b='\' | ||
| 3 | c='*' | ||
| 4 | echo "a is '$a'" | ||
| 5 | echo "b is '$b'" | ||
| 6 | echo "c is '$c'" | ||
| 7 | echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' | ||
| 8 | echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' | ||
| 9 | echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' | ||
| 10 | echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' | ||
| 11 | echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' | ||
| 12 | echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' | ||
| 13 | echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' | ||
| 14 | echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' | ||
| 15 | echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' | ||
| 16 | echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' | ||
| 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' | ||
| 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' | ||
| 19 | echo | ||
| 20 | sq="'" | ||
| 21 | echo 'Single quote tests:' | ||
| 22 | echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *' | ||
| 23 | echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all' | ||
| 24 | echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *' | ||
| 25 | echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *' | ||
| 26 | echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all' | ||
| 27 | echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $' | ||
| 28 | echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *' | ||
| 29 | echo | ||
| 30 | ## In bash, this isn't working as expected | ||
| 31 | #echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| | ||
| 32 | #echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| | ||
| 33 | #echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| | ||
| 34 | #echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| | ||
| 35 | ## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: | ||
| 36 | ## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) | ||
| 37 | ## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| | ||
| 38 | #echo | ||
| 39 | echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' | ||
| 40 | echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' | ||
| 41 | echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| | ||
| 42 | echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' | ||
| 43 | echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' | ||
| 44 | echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' | ||
| 45 | echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' | ||
| 46 | echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| | ||
| 47 | echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || | ||
| 48 | echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' | ||
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c index c86308d3b..f0f41984d 100644 --- a/shell/ash_test/printenv.c +++ b/shell/ash_test/printenv.c | |||
| @@ -31,9 +31,7 @@ | |||
| 31 | extern char **environ; | 31 | extern char **environ; |
| 32 | 32 | ||
| 33 | int | 33 | int |
| 34 | main (argc, argv) | 34 | main (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 | ||
| 30 | void strprint(); | 30 | void strprint(char *); |
| 31 | 31 | ||
| 32 | int main(int argc, char **argv) | 32 | int main(int argc, char **argv) |
| 33 | { | 33 | { |
diff --git a/shell/hush.c b/shell/hush.c index 04dda0734..da0db7948 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -44,23 +44,20 @@ | |||
| 44 | * make complex ${var%...} constructs support optional | 44 | * make complex ${var%...} constructs support optional |
| 45 | * make here documents optional | 45 | * make here documents optional |
| 46 | * special variables (done: PWD, PPID, RANDOM) | 46 | * special variables (done: PWD, PPID, RANDOM) |
| 47 | * follow IFS rules more precisely, including update semantics | ||
| 48 | * tilde expansion | 47 | * tilde expansion |
| 49 | * aliases | ||
| 50 | * "command" missing features: | 48 | * "command" missing features: |
| 51 | * command -p CMD: run CMD using default $PATH | 49 | * command -p CMD: run CMD using default $PATH |
| 52 | * (can use this to override standalone shell as well?) | 50 | * (can use this to override standalone shell as well?) |
| 53 | * command BLTIN: disables special-ness (e.g. errors do not abort) | 51 | * command BLTIN: disables special-ness (e.g. errors do not abort) |
| 54 | * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard) | 52 | * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard) |
| 55 | * builtins mandated by standards we don't support: | 53 | * builtins mandated by standards we don't support: |
| 56 | * [un]alias, fc: | ||
| 57 | * fc -l[nr] [BEG] [END]: list range of commands in history | 54 | * fc -l[nr] [BEG] [END]: list range of commands in history |
| 58 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | 55 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands |
| 59 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | 56 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP |
| 60 | * | 57 | * |
| 61 | * Bash compat TODO: | 58 | * Bash compat TODO: |
| 62 | * redirection of stdout+stderr: &> and >& | 59 | * redirection of stdout+stderr: &> and >& |
| 63 | * reserved words: function select | 60 | * reserved words: select |
| 64 | * advanced test: [[ ]] | 61 | * advanced test: [[ ]] |
| 65 | * process substitution: <(list) and >(list) | 62 | * process substitution: <(list) and >(list) |
| 66 | * let EXPR [EXPR...] | 63 | * let EXPR [EXPR...] |
| @@ -102,7 +99,7 @@ | |||
| 102 | //config: | 99 | //config: |
| 103 | //config: It will compile and work on no-mmu systems. | 100 | //config: It will compile and work on no-mmu systems. |
| 104 | //config: | 101 | //config: |
| 105 | //config: It does not handle select, aliases, tilde expansion, | 102 | //config: It does not handle select, tilde expansion, |
| 106 | //config: &>file and >&file redirection of stdout+stderr. | 103 | //config: &>file and >&file redirection of stdout+stderr. |
| 107 | //config: | 104 | //config: |
| 108 | // This option is visible (has a description) to make it possible to select | 105 | // This option is visible (has a description) to make it possible to select |
| @@ -115,6 +112,11 @@ | |||
| 115 | //config:# It's only needed to get "nice" menuconfig indenting. | 112 | //config:# It's only needed to get "nice" menuconfig indenting. |
| 116 | //config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH | 113 | //config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH |
| 117 | //config: | 114 | //config: |
| 115 | //config:config HUSH_NEED_FOR_SPEED | ||
| 116 | //config: bool "Faster, but larger code" | ||
| 117 | //config: default y | ||
| 118 | //config: depends on SHELL_HUSH | ||
| 119 | //config: | ||
| 118 | //config:config HUSH_BASH_COMPAT | 120 | //config:config HUSH_BASH_COMPAT |
| 119 | //config: bool "bash-compatible extensions" | 121 | //config: bool "bash-compatible extensions" |
| 120 | //config: default y | 122 | //config: default y |
| @@ -189,6 +191,13 @@ | |||
| 189 | //config: help | 191 | //config: help |
| 190 | //config: Enable case ... esac statement. +400 bytes. | 192 | //config: Enable case ... esac statement. +400 bytes. |
| 191 | //config: | 193 | //config: |
| 194 | //config:config HUSH_ALIAS | ||
| 195 | //config: bool "Support aliases" | ||
| 196 | //config: default y | ||
| 197 | //config: depends on SHELL_HUSH | ||
| 198 | //config: help | ||
| 199 | //config: Enable aliases. | ||
| 200 | //config: | ||
| 192 | //config:config HUSH_FUNCTIONS | 201 | //config:config HUSH_FUNCTIONS |
| 193 | //config: bool "Support funcname() { commands; } syntax" | 202 | //config: bool "Support funcname() { commands; } syntax" |
| 194 | //config: default y | 203 | //config: default y |
| @@ -196,6 +205,13 @@ | |||
| 196 | //config: help | 205 | //config: help |
| 197 | //config: Enable support for shell functions. +800 bytes. | 206 | //config: Enable support for shell functions. +800 bytes. |
| 198 | //config: | 207 | //config: |
| 208 | //config:config HUSH_FUNCTION_KEYWORD | ||
| 209 | //config: bool "Support function keyword" | ||
| 210 | //config: default y | ||
| 211 | //config: depends on HUSH_FUNCTIONS | ||
| 212 | //config: help | ||
| 213 | //config: Support "function FUNCNAME { CMD; }" syntax. | ||
| 214 | //config: | ||
| 199 | //config:config HUSH_LOCAL | 215 | //config:config HUSH_LOCAL |
| 200 | //config: bool "local builtin" | 216 | //config: bool "local builtin" |
| 201 | //config: default y | 217 | //config: default y |
| @@ -392,6 +408,8 @@ | |||
| 392 | 408 | ||
| 393 | /* Build knobs */ | 409 | /* Build knobs */ |
| 394 | #define LEAK_HUNTING 0 | 410 | #define LEAK_HUNTING 0 |
| 411 | #define LEAK_PRINTF(...) fdprintf(__VA_ARGS__) | ||
| 412 | //#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0) | ||
| 395 | #define BUILD_AS_NOMMU 0 | 413 | #define BUILD_AS_NOMMU 0 |
| 396 | /* Enable/disable sanity checks. Ok to enable in production, | 414 | /* Enable/disable sanity checks. Ok to enable in production, |
| 397 | * only adds a bit of bloat. Set to >1 to get non-production level verbosity. | 415 | * only adds a bit of bloat. Set to >1 to get non-production level verbosity. |
| @@ -551,25 +569,22 @@ typedef struct o_string { | |||
| 551 | smallint has_empty_slot; | 569 | smallint has_empty_slot; |
| 552 | smallint ended_in_ifs; | 570 | smallint ended_in_ifs; |
| 553 | } o_string; | 571 | } o_string; |
| 572 | #define IS_NULL_WORD(str) \ | ||
| 573 | ((str).length == 0 && !(str).has_quoted_part) | ||
| 554 | enum { | 574 | enum { |
| 555 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ | 575 | /* Protect all newly added chars against globbing by prepending \ to *, ?, [, -, \ */ |
| 556 | EXP_FLAG_GLOB = 0x2, | 576 | EXP_FLAG_GLOBPROTECT_CHARS = 0x1, |
| 557 | /* Protect newly added chars against globbing | 577 | /* Protect quoted vars against globbing */ |
| 558 | * by prepending \ to *, ?, [, \ */ | 578 | EXP_FLAG_GLOBPROTECT_VARS = 0x2, |
| 559 | EXP_FLAG_ESC_GLOB_CHARS = 0x1, | 579 | /* On word-split, perform globbing (one word may become many) */ |
| 580 | EXP_FLAG_DO_GLOBBING = 0x4, | ||
| 581 | /* Do not word-split */ | ||
| 582 | EXP_FLAG_SINGLEWORD = 0x80, | ||
| 583 | /* ^^^^ EXP_FLAG_SINGLEWORD must be 0x80 */ | ||
| 560 | }; | 584 | }; |
| 561 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 585 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
| 562 | #define NULL_O_STRING { NULL } | 586 | #define NULL_O_STRING { NULL } |
| 563 | 587 | ||
| 564 | #ifndef debug_printf_parse | ||
| 565 | static const char *const assignment_flag[] ALIGN_PTR = { | ||
| 566 | "MAYBE_ASSIGNMENT", | ||
| 567 | "DEFINITELY_ASSIGNMENT", | ||
| 568 | "NOT_ASSIGNMENT", | ||
| 569 | "WORD_IS_KEYWORD", | ||
| 570 | }; | ||
| 571 | #endif | ||
| 572 | |||
| 573 | /* We almost can use standard FILE api, but we need an ability to move | 588 | /* We almost can use standard FILE api, but we need an ability to move |
| 574 | * its fd when redirects coincide with it. No api exists for that | 589 | * its fd when redirects coincide with it. No api exists for that |
| 575 | * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902). | 590 | * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902). |
| @@ -587,22 +602,28 @@ typedef struct HFILE { | |||
| 587 | 602 | ||
| 588 | typedef struct in_str { | 603 | typedef struct in_str { |
| 589 | const char *p; | 604 | const char *p; |
| 605 | #if ENABLE_HUSH_ALIAS | ||
| 606 | /* During alias expansion, p is saved in saved_ibuf, and replaced by alias expansion */ | ||
| 607 | const char *saved_ibuf; | ||
| 608 | char *albuf; /* malloced */ | ||
| 609 | #endif | ||
| 590 | int peek_buf[2]; | 610 | int peek_buf[2]; |
| 591 | int last_char; | 611 | int last_char; |
| 592 | HFILE *file; | 612 | HFILE *file; |
| 593 | } in_str; | 613 | } in_str; |
| 594 | 614 | ||
| 595 | /* The descrip member of this structure is only used to make | 615 | /* The descrip3 member of this structure is only used to make |
| 596 | * debugging output pretty */ | 616 | * debugging output pretty */ |
| 597 | static const struct { | 617 | static const struct { |
| 598 | int32_t mode; | 618 | int32_t mode; |
| 599 | signed char default_fd; | 619 | signed char default_fd; |
| 600 | char descrip[3]; | 620 | char descrip3[3]; |
| 601 | } redir_table[] ALIGN4 = { | 621 | } redir_table[] ALIGN4 = { |
| 602 | { O_RDONLY, 0, "<" }, | 622 | { O_RDONLY, 0, "<" }, |
| 603 | { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, | 623 | { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, |
| 604 | { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, | 624 | { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, |
| 605 | { O_CREAT|O_RDWR, 1, "<>" }, | 625 | { O_CREAT|O_RDWR, 1, "<>" }, |
| 626 | { O_RDONLY, 0, "<<<" }, | ||
| 606 | { O_RDONLY, 0, "<<" }, | 627 | { O_RDONLY, 0, "<<" }, |
| 607 | /* Should not be needed. Bogus default_fd helps in debugging */ | 628 | /* Should not be needed. Bogus default_fd helps in debugging */ |
| 608 | /* { O_RDONLY, 77, "<<" }, */ | 629 | /* { O_RDONLY, 77, "<<" }, */ |
| @@ -622,12 +643,13 @@ struct redir_struct { | |||
| 622 | */ | 643 | */ |
| 623 | }; | 644 | }; |
| 624 | typedef enum redir_type { | 645 | typedef enum redir_type { |
| 625 | REDIRECT_INPUT = 0, | 646 | REDIRECT_INPUT = 0, |
| 626 | REDIRECT_OVERWRITE = 1, | 647 | REDIRECT_OVERWRITE = 1, |
| 627 | REDIRECT_APPEND = 2, | 648 | REDIRECT_APPEND = 2, |
| 628 | REDIRECT_IO = 3, | 649 | REDIRECT_IO = 3, |
| 629 | REDIRECT_HEREDOC = 4, | 650 | REDIRECT_HERESTRING = 4, |
| 630 | REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */ | 651 | REDIRECT_HEREDOC = 5, |
| 652 | REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */ | ||
| 631 | 653 | ||
| 632 | REDIRFD_CLOSE = -3, | 654 | REDIRFD_CLOSE = -3, |
| 633 | REDIRFD_SYNTAX_ERR = -2, | 655 | REDIRFD_SYNTAX_ERR = -2, |
| @@ -656,8 +678,12 @@ struct command { | |||
| 656 | # define CMD_SINGLEWORD_NOGLOB 3 | 678 | # define CMD_SINGLEWORD_NOGLOB 3 |
| 657 | #endif | 679 | #endif |
| 658 | #if ENABLE_HUSH_FUNCTIONS | 680 | #if ENABLE_HUSH_FUNCTIONS |
| 659 | # define CMD_FUNCDEF 4 | 681 | # if ENABLE_HUSH_FUNCTION_KEYWORD |
| 682 | # define CMD_FUNCTION_KWORD 4 | ||
| 683 | # endif | ||
| 684 | # define CMD_FUNCDEF 5 | ||
| 660 | #endif | 685 | #endif |
| 686 | /* ^^^ if you change this, update CMDTYPE[] array too */ | ||
| 661 | 687 | ||
| 662 | smalluint cmd_exitcode; | 688 | smalluint cmd_exitcode; |
| 663 | /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ | 689 | /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ |
| @@ -693,7 +719,9 @@ struct command { | |||
| 693 | }; | 719 | }; |
| 694 | /* Is there anything in this command at all? */ | 720 | /* Is there anything in this command at all? */ |
| 695 | #define IS_NULL_CMD(cmd) \ | 721 | #define IS_NULL_CMD(cmd) \ |
| 696 | (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects) | 722 | (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects \ |
| 723 | /* maybe? IF_HUSH_FUNCTION_KEYWORD(&& !(cmd)->cmd_type) */ \ | ||
| 724 | ) | ||
| 697 | 725 | ||
| 698 | struct pipe { | 726 | struct pipe { |
| 699 | struct pipe *next; | 727 | struct pipe *next; |
| @@ -734,13 +762,9 @@ struct parse_context { | |||
| 734 | #if !BB_MMU | 762 | #if !BB_MMU |
| 735 | o_string as_string; | 763 | o_string as_string; |
| 736 | #endif | 764 | #endif |
| 737 | smallint is_assignment; /* 0:maybe, 1:yes, 2:no, 3:keyword */ | 765 | smallint is_assignment; |
| 738 | #if HAS_KEYWORDS | 766 | #if HAS_KEYWORDS |
| 739 | smallint ctx_res_w; | 767 | smallint ctx_res_w; |
| 740 | smallint ctx_inverted; /* "! cmd | cmd" */ | ||
| 741 | #if ENABLE_HUSH_CASE | ||
| 742 | smallint ctx_dsemicolon; /* ";;" seen */ | ||
| 743 | #endif | ||
| 744 | /* bitmask of FLAG_xxx, for figuring out valid reserved words */ | 768 | /* bitmask of FLAG_xxx, for figuring out valid reserved words */ |
| 745 | int old_flag; | 769 | int old_flag; |
| 746 | /* group we are enclosed in: | 770 | /* group we are enclosed in: |
| @@ -759,9 +783,14 @@ enum { | |||
| 759 | MAYBE_ASSIGNMENT = 0, | 783 | MAYBE_ASSIGNMENT = 0, |
| 760 | DEFINITELY_ASSIGNMENT = 1, | 784 | DEFINITELY_ASSIGNMENT = 1, |
| 761 | NOT_ASSIGNMENT = 2, | 785 | NOT_ASSIGNMENT = 2, |
| 762 | /* Not an assignment, but next word may be: "if v=xyz cmd;" */ | ||
| 763 | WORD_IS_KEYWORD = 3, | ||
| 764 | }; | 786 | }; |
| 787 | #ifndef debug_printf_parse | ||
| 788 | static const char *const assignment_flag[] ALIGN_PTR = { | ||
| 789 | "MAYBE_ASSIGNMENT", | ||
| 790 | "DEFINITELY_ASSIGNMENT", | ||
| 791 | "NOT_ASSIGNMENT", | ||
| 792 | }; | ||
| 793 | #endif | ||
| 765 | 794 | ||
| 766 | /* On program start, environ points to initial environment. | 795 | /* On program start, environ points to initial environment. |
| 767 | * putenv adds new pointers into it, unsetenv removes them. | 796 | * putenv adds new pointers into it, unsetenv removes them. |
| @@ -795,6 +824,14 @@ struct function { | |||
| 795 | }; | 824 | }; |
| 796 | #endif | 825 | #endif |
| 797 | 826 | ||
| 827 | #if ENABLE_HUSH_ALIAS | ||
| 828 | struct alias { | ||
| 829 | struct alias *next; | ||
| 830 | char *str; | ||
| 831 | smallint dont_recurse; | ||
| 832 | }; | ||
| 833 | #endif | ||
| 834 | |||
| 798 | /* set -/+o OPT support. (TODO: make it optional) | 835 | /* set -/+o OPT support. (TODO: make it optional) |
| 799 | * bash supports the following opts: | 836 | * bash supports the following opts: |
| 800 | * allexport off | 837 | * allexport off |
| @@ -916,6 +953,7 @@ struct globals { | |||
| 916 | #endif | 953 | #endif |
| 917 | /* set by signal handler if SIGINT is received _and_ its trap is not set */ | 954 | /* set by signal handler if SIGINT is received _and_ its trap is not set */ |
| 918 | smallint flag_SIGINT; | 955 | smallint flag_SIGINT; |
| 956 | smallint flag_startup_done; | ||
| 919 | #if ENABLE_HUSH_LOOPS | 957 | #if ENABLE_HUSH_LOOPS |
| 920 | smallint flag_break_continue; | 958 | smallint flag_break_continue; |
| 921 | #endif | 959 | #endif |
| @@ -930,6 +968,12 @@ struct globals { | |||
| 930 | # define G_flag_return_in_progress 0 | 968 | # define G_flag_return_in_progress 0 |
| 931 | #endif | 969 | #endif |
| 932 | smallint exiting; /* used to prevent EXIT trap recursion */ | 970 | smallint exiting; /* used to prevent EXIT trap recursion */ |
| 971 | #if !BB_MMU | ||
| 972 | smallint reexeced_on_NOMMU; | ||
| 973 | # define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU) | ||
| 974 | #else | ||
| 975 | # define G_reexeced_on_NOMMU 0 | ||
| 976 | #endif | ||
| 933 | /* These support $? */ | 977 | /* These support $? */ |
| 934 | smalluint last_exitcode; | 978 | smalluint last_exitcode; |
| 935 | smalluint expand_exitcode; | 979 | smalluint expand_exitcode; |
| @@ -970,6 +1014,9 @@ struct globals { | |||
| 970 | # endif | 1014 | # endif |
| 971 | struct function *top_func; | 1015 | struct function *top_func; |
| 972 | #endif | 1016 | #endif |
| 1017 | #if ENABLE_HUSH_ALIAS | ||
| 1018 | struct alias *top_alias; | ||
| 1019 | #endif | ||
| 973 | /* Signal and trap handling */ | 1020 | /* Signal and trap handling */ |
| 974 | #if ENABLE_HUSH_FAST | 1021 | #if ENABLE_HUSH_FAST |
| 975 | unsigned count_SIGCHLD; | 1022 | unsigned count_SIGCHLD; |
| @@ -1072,6 +1119,10 @@ static int builtin_jobs(char **argv) FAST_FUNC; | |||
| 1072 | #if ENABLE_HUSH_GETOPTS | 1119 | #if ENABLE_HUSH_GETOPTS |
| 1073 | static int builtin_getopts(char **argv) FAST_FUNC; | 1120 | static int builtin_getopts(char **argv) FAST_FUNC; |
| 1074 | #endif | 1121 | #endif |
| 1122 | #if ENABLE_HUSH_ALIAS | ||
| 1123 | static int builtin_alias(char **argv) FAST_FUNC; | ||
| 1124 | static int builtin_unalias(char **argv) FAST_FUNC; | ||
| 1125 | #endif | ||
| 1075 | #if ENABLE_HUSH_HELP | 1126 | #if ENABLE_HUSH_HELP |
| 1076 | static int builtin_help(char **argv) FAST_FUNC; | 1127 | static int builtin_help(char **argv) FAST_FUNC; |
| 1077 | #endif | 1128 | #endif |
| @@ -1149,6 +1200,9 @@ struct built_in_command { | |||
| 1149 | static const struct built_in_command bltins1[] ALIGN_PTR = { | 1200 | static const struct built_in_command bltins1[] ALIGN_PTR = { |
| 1150 | BLTIN("." , builtin_source , "Run commands in file"), | 1201 | BLTIN("." , builtin_source , "Run commands in file"), |
| 1151 | BLTIN(":" , builtin_true , NULL), | 1202 | BLTIN(":" , builtin_true , NULL), |
| 1203 | #if ENABLE_HUSH_ALIAS | ||
| 1204 | BLTIN("alias" , builtin_alias , "Define or display aliases"), | ||
| 1205 | #endif | ||
| 1152 | #if ENABLE_HUSH_JOB | 1206 | #if ENABLE_HUSH_JOB |
| 1153 | BLTIN("bg" , builtin_fg_bg , "Resume job in background"), | 1207 | BLTIN("bg" , builtin_fg_bg , "Resume job in background"), |
| 1154 | #endif | 1208 | #endif |
| @@ -1200,7 +1254,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = { | |||
| 1200 | BLTIN("return" , builtin_return , "Return from function"), | 1254 | BLTIN("return" , builtin_return , "Return from function"), |
| 1201 | #endif | 1255 | #endif |
| 1202 | #if ENABLE_HUSH_SET | 1256 | #if ENABLE_HUSH_SET |
| 1203 | BLTIN("set" , builtin_set , "Set positional parameters"), | 1257 | BLTIN("set" , builtin_set , "Set options or positional parameters"), |
| 1204 | #endif | 1258 | #endif |
| 1205 | BLTIN("shift" , builtin_shift , "Shift positional parameters"), | 1259 | BLTIN("shift" , builtin_shift , "Shift positional parameters"), |
| 1206 | #if BASH_SOURCE | 1260 | #if BASH_SOURCE |
| @@ -1210,7 +1264,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = { | |||
| 1210 | BLTIN("times" , builtin_times , NULL), | 1264 | BLTIN("times" , builtin_times , NULL), |
| 1211 | #endif | 1265 | #endif |
| 1212 | #if ENABLE_HUSH_TRAP | 1266 | #if ENABLE_HUSH_TRAP |
| 1213 | BLTIN("trap" , builtin_trap , "Trap signals"), | 1267 | BLTIN("trap" , builtin_trap , "Define or display signal handlers"), |
| 1214 | #endif | 1268 | #endif |
| 1215 | BLTIN("true" , builtin_true , NULL), | 1269 | BLTIN("true" , builtin_true , NULL), |
| 1216 | #if ENABLE_HUSH_TYPE | 1270 | #if ENABLE_HUSH_TYPE |
| @@ -1222,6 +1276,9 @@ static const struct built_in_command bltins1[] ALIGN_PTR = { | |||
| 1222 | #if ENABLE_HUSH_UMASK | 1276 | #if ENABLE_HUSH_UMASK |
| 1223 | BLTIN("umask" , builtin_umask , "Set file creation mask"), | 1277 | BLTIN("umask" , builtin_umask , "Set file creation mask"), |
| 1224 | #endif | 1278 | #endif |
| 1279 | #if ENABLE_HUSH_ALIAS | ||
| 1280 | BLTIN("unalias" , builtin_unalias , "Delete aliases"), | ||
| 1281 | #endif | ||
| 1225 | #if ENABLE_HUSH_UNSET | 1282 | #if ENABLE_HUSH_UNSET |
| 1226 | BLTIN("unset" , builtin_unset , "Unset variables"), | 1283 | BLTIN("unset" , builtin_unset , "Unset variables"), |
| 1227 | #endif | 1284 | #endif |
| @@ -1352,30 +1409,67 @@ static void debug_print_strings(const char *prefix, char **vv) | |||
| 1352 | static void *xxmalloc(int lineno, size_t size) | 1409 | static void *xxmalloc(int lineno, size_t size) |
| 1353 | { | 1410 | { |
| 1354 | void *ptr = xmalloc((size + 0xff) & ~0xff); | 1411 | void *ptr = xmalloc((size + 0xff) & ~0xff); |
| 1355 | fdprintf(2, "line %d: malloc %p\n", lineno, ptr); | 1412 | LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr); |
| 1413 | return ptr; | ||
| 1414 | } | ||
| 1415 | static void *xxzalloc(int lineno, size_t size) | ||
| 1416 | { | ||
| 1417 | void *ptr = xzalloc((size + 0xff) & ~0xff); | ||
| 1418 | LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr); | ||
| 1356 | return ptr; | 1419 | return ptr; |
| 1357 | } | 1420 | } |
| 1358 | static void *xxrealloc(int lineno, void *ptr, size_t size) | 1421 | static void *xxrealloc(int lineno, void *ptr, size_t size) |
| 1359 | { | 1422 | { |
| 1423 | char *p = ptr; | ||
| 1360 | ptr = xrealloc(ptr, (size + 0xff) & ~0xff); | 1424 | ptr = xrealloc(ptr, (size + 0xff) & ~0xff); |
| 1361 | fdprintf(2, "line %d: realloc %p\n", lineno, ptr); | 1425 | if (p != ptr) |
| 1426 | LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr); | ||
| 1427 | return ptr; | ||
| 1428 | } | ||
| 1429 | static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr) | ||
| 1430 | { | ||
| 1431 | char *p = ptr; | ||
| 1432 | ptr = xrealloc_getcwd_or_warn(ptr); | ||
| 1433 | if (p != ptr) | ||
| 1434 | LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr); | ||
| 1362 | return ptr; | 1435 | return ptr; |
| 1363 | } | 1436 | } |
| 1364 | static char *xxstrdup(int lineno, const char *str) | 1437 | static char *xxstrdup(int lineno, const char *str) |
| 1365 | { | 1438 | { |
| 1366 | char *ptr = xstrdup(str); | 1439 | char *ptr = xstrdup(str); |
| 1367 | fdprintf(2, "line %d: strdup %p\n", lineno, ptr); | 1440 | LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr); |
| 1441 | return ptr; | ||
| 1442 | } | ||
| 1443 | static char *xxstrndup(int lineno, const char *str, size_t n) | ||
| 1444 | { | ||
| 1445 | char *ptr = xstrndup(str, n); | ||
| 1446 | LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr); | ||
| 1447 | return ptr; | ||
| 1448 | } | ||
| 1449 | static char *xxasprintf(int lineno, const char *f, ...) | ||
| 1450 | { | ||
| 1451 | char *ptr; | ||
| 1452 | va_list args; | ||
| 1453 | va_start(args, f); | ||
| 1454 | if (vasprintf(&ptr, f, args) < 0) | ||
| 1455 | bb_die_memory_exhausted(); | ||
| 1456 | va_end(args); | ||
| 1457 | LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr); | ||
| 1368 | return ptr; | 1458 | return ptr; |
| 1369 | } | 1459 | } |
| 1370 | static void xxfree(void *ptr) | 1460 | static void xxfree(void *ptr) |
| 1371 | { | 1461 | { |
| 1372 | fdprintf(2, "free %p\n", ptr); | 1462 | LEAK_PRINTF(2, "free %p\n", ptr); |
| 1373 | free(ptr); | 1463 | free(ptr); |
| 1374 | } | 1464 | } |
| 1375 | # define xmalloc(s) xxmalloc(__LINE__, s) | 1465 | # define xmalloc(s) xxmalloc(__LINE__, s) |
| 1376 | # define xrealloc(p, s) xxrealloc(__LINE__, p, s) | 1466 | # define xzalloc(s) xxzalloc(__LINE__, s) |
| 1377 | # define xstrdup(s) xxstrdup(__LINE__, s) | 1467 | # define xrealloc(p, s) xxrealloc(__LINE__, p, s) |
| 1378 | # define free(p) xxfree(p) | 1468 | # define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p) |
| 1469 | # define xstrdup(s) xxstrdup(__LINE__, s) | ||
| 1470 | # define xstrndup(s, n) xxstrndup(__LINE__, s, n) | ||
| 1471 | # define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__) | ||
| 1472 | # define free(p) xxfree(p) | ||
| 1379 | #endif | 1473 | #endif |
| 1380 | 1474 | ||
| 1381 | /* | 1475 | /* |
| @@ -1391,11 +1485,14 @@ static void xxfree(void *ptr) | |||
| 1391 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) | 1485 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) |
| 1392 | # define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s) | 1486 | # define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s) |
| 1393 | # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) | 1487 | # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) |
| 1488 | # define syntax_error_unexpected_str(lineno, s) syntax_error_unexpected_str(s) | ||
| 1394 | #endif | 1489 | #endif |
| 1395 | 1490 | ||
| 1396 | static void die_if_script(void) | 1491 | static void die_if_script(void) |
| 1397 | { | 1492 | { |
| 1398 | if (!G_interactive_fd) { | 1493 | if (G.flag_startup_done /* when not yet set, allows "hush -l" to not die on errors in /etc/profile */ |
| 1494 | && !G_interactive_fd | ||
| 1495 | ) { | ||
| 1399 | if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */ | 1496 | if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */ |
| 1400 | xfunc_error_retval = G.last_exitcode; | 1497 | xfunc_error_retval = G.last_exitcode; |
| 1401 | xfunc_die(); | 1498 | xfunc_die(); |
| @@ -1438,24 +1535,27 @@ static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) | |||
| 1438 | // die_if_script(); | 1535 | // die_if_script(); |
| 1439 | } | 1536 | } |
| 1440 | 1537 | ||
| 1441 | static void syntax_error_unterm_ch(unsigned lineno, char ch) | 1538 | static void syntax_error_unterm_ch(unsigned lineno, int ch) |
| 1442 | { | 1539 | { |
| 1443 | char msg[2] = { ch, '\0' }; | 1540 | char msg[2] = { ch, '\0' }; |
| 1444 | syntax_error_unterm_str(lineno, msg); | 1541 | syntax_error_unterm_str(lineno, ch == EOF ? "EOF" : msg); |
| 1445 | } | 1542 | } |
| 1446 | 1543 | ||
| 1447 | static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | 1544 | static void syntax_error_unexpected_str(unsigned lineno UNUSED_PARAM, const char *s) |
| 1448 | { | 1545 | { |
| 1449 | char msg[2]; | ||
| 1450 | msg[0] = ch; | ||
| 1451 | msg[1] = '\0'; | ||
| 1452 | #if HUSH_DEBUG >= 2 | 1546 | #if HUSH_DEBUG >= 2 |
| 1453 | bb_error_msg("hush.c:%u", lineno); | 1547 | bb_error_msg("hush.c:%u", lineno); |
| 1454 | #endif | 1548 | #endif |
| 1455 | bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); | 1549 | bb_error_msg("syntax error: unexpected %s", s); |
| 1456 | die_if_script(); | 1550 | die_if_script(); |
| 1457 | } | 1551 | } |
| 1458 | 1552 | ||
| 1553 | static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | ||
| 1554 | { | ||
| 1555 | char msg[2] = { ch, '\0' }; | ||
| 1556 | syntax_error_unexpected_str(lineno, ch == EOF ? "EOF" : msg); | ||
| 1557 | } | ||
| 1558 | |||
| 1459 | #if HUSH_DEBUG < 2 | 1559 | #if HUSH_DEBUG < 2 |
| 1460 | # undef msg_and_die_if_script | 1560 | # undef msg_and_die_if_script |
| 1461 | # undef syntax_error | 1561 | # undef syntax_error |
| @@ -1463,6 +1563,7 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | |||
| 1463 | # undef syntax_error_unterm_ch | 1563 | # undef syntax_error_unterm_ch |
| 1464 | # undef syntax_error_unterm_str | 1564 | # undef syntax_error_unterm_str |
| 1465 | # undef syntax_error_unexpected_ch | 1565 | # undef syntax_error_unexpected_ch |
| 1566 | # undef syntax_error_unexpected_str | ||
| 1466 | #else | 1567 | #else |
| 1467 | # define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__) | 1568 | # define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__) |
| 1468 | # define syntax_error(msg) syntax_error(__LINE__, msg) | 1569 | # define syntax_error(msg) syntax_error(__LINE__, msg) |
| @@ -1470,6 +1571,7 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | |||
| 1470 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) | 1571 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) |
| 1471 | # define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s) | 1572 | # define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s) |
| 1472 | # define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch) | 1573 | # define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch) |
| 1574 | # define syntax_error_unexpected_str(s) syntax_error_unexpected_str(__LINE__, s) | ||
| 1473 | #endif | 1575 | #endif |
| 1474 | 1576 | ||
| 1475 | /* | 1577 | /* |
| @@ -1619,6 +1721,21 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) | |||
| 1619 | return newfd; | 1721 | return newfd; |
| 1620 | } | 1722 | } |
| 1621 | 1723 | ||
| 1724 | #if ENABLE_HUSH_ALIAS | ||
| 1725 | static void enable_all_aliases(void) | ||
| 1726 | { | ||
| 1727 | if (G_interactive_fd) { | ||
| 1728 | struct alias *alias = G.top_alias; | ||
| 1729 | while (alias) { | ||
| 1730 | alias->dont_recurse = 0; | ||
| 1731 | alias = alias->next; | ||
| 1732 | } | ||
| 1733 | } | ||
| 1734 | } | ||
| 1735 | #else | ||
| 1736 | #define enable_all_aliases() ((void)0) | ||
| 1737 | #endif | ||
| 1738 | |||
| 1622 | /* | 1739 | /* |
| 1623 | * Manipulating HFILEs | 1740 | * Manipulating HFILEs |
| 1624 | */ | 1741 | */ |
| @@ -1929,7 +2046,7 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
| 1929 | * "trap - SIGxxx": | 2046 | * "trap - SIGxxx": |
| 1930 | * if sig is in special_sig_mask, set handler back to: | 2047 | * if sig is in special_sig_mask, set handler back to: |
| 1931 | * record_pending_signo, or to IGN if it's a tty stop signal | 2048 | * record_pending_signo, or to IGN if it's a tty stop signal |
| 1932 | * if sig is in fatal_sig_mask, set handler back to sigexit. | 2049 | * if sig is in fatal_sig_mask, set handler back to restore_ttypgrp_and_killsig_or__exit. |
| 1933 | * else: set handler back to SIG_DFL | 2050 | * else: set handler back to SIG_DFL |
| 1934 | * "trap 'cmd' SIGxxx": | 2051 | * "trap 'cmd' SIGxxx": |
| 1935 | * set handler to record_pending_signo. | 2052 | * set handler to record_pending_signo. |
| @@ -1973,7 +2090,7 @@ static void record_pending_signo(int sig) | |||
| 1973 | || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) | 2090 | || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) |
| 1974 | /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ | 2091 | /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ |
| 1975 | ) { | 2092 | ) { |
| 1976 | bb_got_signal = sig; /* for read_line_input: "we got a signal" */ | 2093 | bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */ |
| 1977 | } | 2094 | } |
| 1978 | #endif | 2095 | #endif |
| 1979 | #if ENABLE_HUSH_FAST | 2096 | #if ENABLE_HUSH_FAST |
| @@ -2002,19 +2119,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler) | |||
| 2002 | return old_sa.sa_handler; | 2119 | return old_sa.sa_handler; |
| 2003 | } | 2120 | } |
| 2004 | 2121 | ||
| 2005 | static void hush_exit(int exitcode) NORETURN; | ||
| 2006 | |||
| 2007 | static void restore_ttypgrp_and__exit(void) NORETURN; | ||
| 2008 | static void restore_ttypgrp_and__exit(void) | ||
| 2009 | { | ||
| 2010 | /* xfunc has failed! die die die */ | ||
| 2011 | /* no EXIT traps, this is an escape hatch! */ | ||
| 2012 | G.exiting = 1; | ||
| 2013 | hush_exit(xfunc_error_retval); | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | #if ENABLE_HUSH_JOB | ||
| 2017 | |||
| 2018 | /* Needed only on some libc: | 2122 | /* Needed only on some libc: |
| 2019 | * It was observed that on exit(), fgetc'ed buffered data | 2123 | * It was observed that on exit(), fgetc'ed buffered data |
| 2020 | * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). | 2124 | * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). |
| @@ -2028,26 +2132,20 @@ static void restore_ttypgrp_and__exit(void) | |||
| 2028 | * and in `cmd` handling. | 2132 | * and in `cmd` handling. |
| 2029 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): | 2133 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): |
| 2030 | */ | 2134 | */ |
| 2031 | static void fflush_and__exit(void) NORETURN; | 2135 | static NORETURN void fflush_and__exit(void) |
| 2032 | static void fflush_and__exit(void) | ||
| 2033 | { | 2136 | { |
| 2034 | fflush_all(); | 2137 | fflush_all(); |
| 2035 | _exit(xfunc_error_retval); | 2138 | _exit(xfunc_error_retval); |
| 2036 | } | 2139 | } |
| 2037 | 2140 | ||
| 2038 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ | 2141 | #if ENABLE_HUSH_JOB |
| 2039 | # define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit) | ||
| 2040 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ | ||
| 2041 | # define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit) | ||
| 2042 | |||
| 2043 | /* Restores tty foreground process group, and exits. | 2142 | /* Restores tty foreground process group, and exits. |
| 2044 | * May be called as signal handler for fatal signal | 2143 | * May be called as signal handler for fatal signal |
| 2045 | * (will resend signal to itself, producing correct exit state) | 2144 | * (will resend signal to itself, producing correct exit state) |
| 2046 | * or called directly with -EXITCODE. | 2145 | * or called directly with -EXITCODE. |
| 2047 | * We also call it if xfunc is exiting. | 2146 | * We also call it if xfunc is exiting. |
| 2048 | */ | 2147 | */ |
| 2049 | static void sigexit(int sig) NORETURN; | 2148 | static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig) |
| 2050 | static void sigexit(int sig) | ||
| 2051 | { | 2149 | { |
| 2052 | /* Careful: we can end up here after [v]fork. Do not restore | 2150 | /* Careful: we can end up here after [v]fork. Do not restore |
| 2053 | * tty pgrp then, only top-level shell process does that */ | 2151 | * tty pgrp then, only top-level shell process does that */ |
| @@ -2065,6 +2163,19 @@ static void sigexit(int sig) | |||
| 2065 | 2163 | ||
| 2066 | kill_myself_with_sig(sig); /* does not return */ | 2164 | kill_myself_with_sig(sig); /* does not return */ |
| 2067 | } | 2165 | } |
| 2166 | |||
| 2167 | static NORETURN void fflush_restore_ttypgrp_and__exit(void) | ||
| 2168 | { | ||
| 2169 | /* xfunc has failed! die die die */ | ||
| 2170 | fflush_all(); | ||
| 2171 | restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval); | ||
| 2172 | } | ||
| 2173 | |||
| 2174 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ | ||
| 2175 | # define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit) | ||
| 2176 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ | ||
| 2177 | # define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit) | ||
| 2178 | |||
| 2068 | #else | 2179 | #else |
| 2069 | 2180 | ||
| 2070 | # define disable_restore_tty_pgrp_on_exit() ((void)0) | 2181 | # define disable_restore_tty_pgrp_on_exit() ((void)0) |
| @@ -2081,7 +2192,7 @@ static sighandler_t pick_sighandler(unsigned sig) | |||
| 2081 | #if ENABLE_HUSH_JOB | 2192 | #if ENABLE_HUSH_JOB |
| 2082 | /* is sig fatal? */ | 2193 | /* is sig fatal? */ |
| 2083 | if (G_fatal_sig_mask & sigmask) | 2194 | if (G_fatal_sig_mask & sigmask) |
| 2084 | handler = sigexit; | 2195 | handler = restore_ttypgrp_and_killsig_or__exit; |
| 2085 | else | 2196 | else |
| 2086 | #endif | 2197 | #endif |
| 2087 | /* sig has special handling? */ | 2198 | /* sig has special handling? */ |
| @@ -2099,11 +2210,33 @@ static sighandler_t pick_sighandler(unsigned sig) | |||
| 2099 | return handler; | 2210 | return handler; |
| 2100 | } | 2211 | } |
| 2101 | 2212 | ||
| 2102 | /* Restores tty foreground process group, and exits. */ | 2213 | static const char* FAST_FUNC get_local_var_value(const char *name); |
| 2103 | static void hush_exit(int exitcode) | 2214 | |
| 2215 | /* Self-explanatory. | ||
| 2216 | * Restores tty foreground process group too. | ||
| 2217 | */ | ||
| 2218 | static NORETURN void save_history_run_exit_trap_and_exit(int exitcode) | ||
| 2104 | { | 2219 | { |
| 2105 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 2220 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
| 2106 | save_history(G.line_input_state); /* may be NULL */ | 2221 | if (G.line_input_state |
| 2222 | && getpid() == G.root_pid /* exits in subshells do not save history */ | ||
| 2223 | ) { | ||
| 2224 | const char *hp; | ||
| 2225 | # if ENABLE_FEATURE_SH_HISTFILESIZE | ||
| 2226 | // in bash: | ||
| 2227 | // HISTFILESIZE controls the on-disk history file size (in lines, 0=no history): | ||
| 2228 | // "When this variable is assigned a value, the history file is truncated, if necessary" | ||
| 2229 | // but we do it only at exit, not on every assignment: | ||
| 2230 | /* Use HISTFILESIZE to limit file size */ | ||
| 2231 | hp = get_local_var_value("HISTFILESIZE"); | ||
| 2232 | if (hp) | ||
| 2233 | G.line_input_state->max_history = size_from_HISTFILESIZE(hp); | ||
| 2234 | # endif | ||
| 2235 | /* HISTFILE: "If unset, the command history is not saved when a shell exits." */ | ||
| 2236 | hp = get_local_var_value("HISTFILE"); | ||
| 2237 | G.line_input_state->hist_file = hp; | ||
| 2238 | save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */ | ||
| 2239 | } | ||
| 2107 | #endif | 2240 | #endif |
| 2108 | 2241 | ||
| 2109 | fflush_all(); | 2242 | fflush_all(); |
| @@ -2137,7 +2270,7 @@ static void hush_exit(int exitcode) | |||
| 2137 | 2270 | ||
| 2138 | fflush_all(); | 2271 | fflush_all(); |
| 2139 | #if ENABLE_HUSH_JOB | 2272 | #if ENABLE_HUSH_JOB |
| 2140 | sigexit(- (exitcode & 0xff)); | 2273 | restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff)); |
| 2141 | #else | 2274 | #else |
| 2142 | _exit(exitcode); | 2275 | _exit(exitcode); |
| 2143 | #endif | 2276 | #endif |
| @@ -2222,7 +2355,7 @@ static int check_and_run_traps(void) | |||
| 2222 | } | 2355 | } |
| 2223 | } | 2356 | } |
| 2224 | /* this restores tty pgrp, then kills us with SIGHUP */ | 2357 | /* this restores tty pgrp, then kills us with SIGHUP */ |
| 2225 | sigexit(SIGHUP); | 2358 | restore_ttypgrp_and_killsig_or__exit(SIGHUP); |
| 2226 | } | 2359 | } |
| 2227 | #endif | 2360 | #endif |
| 2228 | #if ENABLE_HUSH_FAST | 2361 | #if ENABLE_HUSH_FAST |
| @@ -2375,7 +2508,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2375 | if (cur->flg_read_only) { | 2508 | if (cur->flg_read_only) { |
| 2376 | bb_error_msg("%s: readonly variable", str); | 2509 | bb_error_msg("%s: readonly variable", str); |
| 2377 | free(str); | 2510 | free(str); |
| 2378 | //NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1, | 2511 | //NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $? to 1, |
| 2379 | //but export per se succeeds (does put the var in env). We don't mimic that. | 2512 | //but export per se succeeds (does put the var in env). We don't mimic that. |
| 2380 | return -1; | 2513 | return -1; |
| 2381 | } | 2514 | } |
| @@ -2433,10 +2566,10 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2433 | 2566 | ||
| 2434 | /* Replace the value in the found "struct variable" */ | 2567 | /* Replace the value in the found "struct variable" */ |
| 2435 | if (cur->max_len != 0) { | 2568 | if (cur->max_len != 0) { |
| 2436 | if (cur->max_len >= strnlen(str, cur->max_len + 1)) { | 2569 | /* This one is from startup env, try to reuse space */ |
| 2437 | /* This one is from startup env, reuse space */ | 2570 | int new_len = stpncpy(cur->varstr, str, cur->max_len + 1) - cur->varstr; |
| 2438 | debug_printf_env("reusing startup env for '%s'\n", str); | 2571 | if (new_len <= cur->max_len) { |
| 2439 | strcpy(cur->varstr, str); | 2572 | debug_printf_env("reused startup env for '%s'\n", str); |
| 2440 | goto free_and_exp; | 2573 | goto free_and_exp; |
| 2441 | } | 2574 | } |
| 2442 | /* Can't reuse */ | 2575 | /* Can't reuse */ |
| @@ -2453,7 +2586,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2453 | goto set_str_and_exp; | 2586 | goto set_str_and_exp; |
| 2454 | } | 2587 | } |
| 2455 | 2588 | ||
| 2456 | /* Not found or shadowed - create new variable struct */ | 2589 | /* Not found, or found but shadowed - create new variable struct */ |
| 2457 | debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl); | 2590 | debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl); |
| 2458 | cur = xzalloc(sizeof(*cur)); | 2591 | cur = xzalloc(sizeof(*cur)); |
| 2459 | cur->var_nest_level = local_lvl; | 2592 | cur->var_nest_level = local_lvl; |
| @@ -2669,20 +2802,16 @@ static const char *setup_prompt_string(void) | |||
| 2669 | debug_printf("prompt_str '%s'\n", prompt_str); | 2802 | debug_printf("prompt_str '%s'\n", prompt_str); |
| 2670 | return prompt_str; | 2803 | return prompt_str; |
| 2671 | } | 2804 | } |
| 2672 | static int get_user_input(struct in_str *i) | 2805 | static int show_prompt_and_get_stdin(struct in_str *i) |
| 2673 | { | 2806 | { |
| 2674 | # if ENABLE_FEATURE_EDITING | 2807 | # if ENABLE_FEATURE_EDITING |
| 2675 | /* In EDITING case, this function reads next input line, | 2808 | /* In EDITING case, this function reads next input line, |
| 2676 | * saves it in i->p, then returns 1st char of it. | 2809 | * saves it in i->p, then returns 1st char of it. |
| 2677 | */ | 2810 | */ |
| 2678 | int r; | ||
| 2679 | const char *prompt_str; | ||
| 2680 | |||
| 2681 | prompt_str = setup_prompt_string(); | ||
| 2682 | for (;;) { | 2811 | for (;;) { |
| 2683 | reinit_unicode_for_hush(); | 2812 | int r; |
| 2684 | G.flag_SIGINT = 0; | ||
| 2685 | 2813 | ||
| 2814 | G.flag_SIGINT = 0; | ||
| 2686 | bb_got_signal = 0; | 2815 | bb_got_signal = 0; |
| 2687 | if (!sigisemptyset(&G.pending_set)) { | 2816 | if (!sigisemptyset(&G.pending_set)) { |
| 2688 | /* Whoops, already got a signal, do not call read_line_input */ | 2817 | /* Whoops, already got a signal, do not call read_line_input */ |
| @@ -2702,6 +2831,8 @@ static int get_user_input(struct in_str *i) | |||
| 2702 | * #^^^ prints "T", prints prompt, repeats | 2831 | * #^^^ prints "T", prints prompt, repeats |
| 2703 | * #(bash 5.0.17 exits after first "T", looks like a bug) | 2832 | * #(bash 5.0.17 exits after first "T", looks like a bug) |
| 2704 | */ | 2833 | */ |
| 2834 | const char *prompt_str = setup_prompt_string(); | ||
| 2835 | reinit_unicode_for_hush(); | ||
| 2705 | r = read_line_input(G.line_input_state, prompt_str, | 2836 | r = read_line_input(G.line_input_state, prompt_str, |
| 2706 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 | 2837 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 |
| 2707 | ); | 2838 | ); |
| @@ -2732,7 +2863,7 @@ static int get_user_input(struct in_str *i) | |||
| 2732 | /* it was a signal: go back, read another input line */ | 2863 | /* it was a signal: go back, read another input line */ |
| 2733 | } | 2864 | } |
| 2734 | i->p = G.user_input_buf; | 2865 | i->p = G.user_input_buf; |
| 2735 | return (unsigned char)*i->p++; | 2866 | return (unsigned char)*i->p++; /* can't be NUL */ |
| 2736 | # else | 2867 | # else |
| 2737 | /* In !EDITING case, this function gets called for every char. | 2868 | /* In !EDITING case, this function gets called for every char. |
| 2738 | * Buffering happens deeper in the call chain, in hfgetc(i->file). | 2869 | * Buffering happens deeper in the call chain, in hfgetc(i->file). |
| @@ -2773,13 +2904,13 @@ static int get_user_input(struct in_str *i) | |||
| 2773 | } | 2904 | } |
| 2774 | /* This is the magic location that prints prompts | 2905 | /* This is the magic location that prints prompts |
| 2775 | * and gets data back from the user */ | 2906 | * and gets data back from the user */ |
| 2776 | static int fgetc_interactive(struct in_str *i) | 2907 | static int i_getch_interactive(struct in_str *i) |
| 2777 | { | 2908 | { |
| 2778 | int ch; | 2909 | int ch; |
| 2779 | /* If it's interactive stdin, get new line. */ | 2910 | /* If it's interactive stdin, get new line. */ |
| 2780 | if (G_interactive_fd && i->file == G.HFILE_stdin) { | 2911 | if (G_interactive_fd && i->file == G.HFILE_stdin) { |
| 2781 | /* Returns first char (or EOF), the rest is in i->p[] */ | 2912 | /* Returns first char (or EOF), the rest is in i->p[] */ |
| 2782 | ch = get_user_input(i); | 2913 | ch = show_prompt_and_get_stdin(i); |
| 2783 | G.promptmode = 1; /* PS2 */ | 2914 | G.promptmode = 1; /* PS2 */ |
| 2784 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 2915 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
| 2785 | } else { | 2916 | } else { |
| @@ -2789,7 +2920,7 @@ static int fgetc_interactive(struct in_str *i) | |||
| 2789 | return ch; | 2920 | return ch; |
| 2790 | } | 2921 | } |
| 2791 | #else /* !INTERACTIVE */ | 2922 | #else /* !INTERACTIVE */ |
| 2792 | static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) | 2923 | static ALWAYS_INLINE int i_getch_interactive(struct in_str *i) |
| 2793 | { | 2924 | { |
| 2794 | int ch; | 2925 | int ch; |
| 2795 | do ch = hfgetc(i->file); while (ch == '\0'); | 2926 | do ch = hfgetc(i->file); while (ch == '\0'); |
| @@ -2797,37 +2928,88 @@ static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) | |||
| 2797 | } | 2928 | } |
| 2798 | #endif /* !INTERACTIVE */ | 2929 | #endif /* !INTERACTIVE */ |
| 2799 | 2930 | ||
| 2931 | #if ENABLE_HUSH_ALIAS | ||
| 2932 | static ALWAYS_INLINE int i_has_alias_buffer(struct in_str *i) | ||
| 2933 | { | ||
| 2934 | return i->saved_ibuf && i->p && *i->p; | ||
| 2935 | } | ||
| 2936 | static void i_prepend_to_alias_buffer(struct in_str *i, char *prepend, char ch) | ||
| 2937 | { | ||
| 2938 | if (i->saved_ibuf) { | ||
| 2939 | /* Nested alias expansion example: | ||
| 2940 | * alias a='b c'; alias b='echo A:' | ||
| 2941 | * a | ||
| 2942 | * ^^^ runs "echo A: c" | ||
| 2943 | */ | ||
| 2944 | char *old = i->albuf; | ||
| 2945 | //bb_error_msg("before'%s' p'%s'", i->albuf, i->p); | ||
| 2946 | i->albuf = xasprintf("%s%c%s", prepend, ch, i->p); | ||
| 2947 | i->p = i->albuf; | ||
| 2948 | //bb_error_msg("after'%s' p'%s'", i->albuf, i->p); | ||
| 2949 | free(old); | ||
| 2950 | return; | ||
| 2951 | } | ||
| 2952 | i->saved_ibuf = i->p; | ||
| 2953 | i->p = i->albuf = xasprintf("%s%c", prepend, ch); | ||
| 2954 | //bb_error_msg("albuf'%s'", i->albuf); | ||
| 2955 | } | ||
| 2956 | static void i_free_alias_buffer(struct in_str *i) | ||
| 2957 | { | ||
| 2958 | if (i->saved_ibuf) { | ||
| 2959 | /* We are here if alias expansion has ended just now */ | ||
| 2960 | free(i->albuf); | ||
| 2961 | i->p = i->saved_ibuf; | ||
| 2962 | i->saved_ibuf = NULL; | ||
| 2963 | /* We re-enable aliases only if expansion has finished, not on command boundaries. | ||
| 2964 | * Example: | ||
| 2965 | * alias a="nice&&a" | ||
| 2966 | * a;a | ||
| 2967 | * This should run "nice" and then "can't execute 'a': No such file or directory", | ||
| 2968 | * then should run "nice" again and then "can't execute 'a': No such file or directory" again | ||
| 2969 | * because the second "a" in alias definition must not expand (to prevent infinite expansion), | ||
| 2970 | * but the "a" after ; must expand (there is no danger of infinite expansion). | ||
| 2971 | * alias a="nice&&nice" | ||
| 2972 | * a;a&&a | ||
| 2973 | * should execute "nice" six times. | ||
| 2974 | */ | ||
| 2975 | debug_printf_parse("end of alias\n"); | ||
| 2976 | enable_all_aliases(); | ||
| 2977 | } | ||
| 2978 | } | ||
| 2979 | #else | ||
| 2980 | # define i_has_alias_buffer(i) 0 | ||
| 2981 | # define i_free_alias_buffer(i) ((void)0) | ||
| 2982 | #endif | ||
| 2983 | |||
| 2800 | static int i_getch(struct in_str *i) | 2984 | static int i_getch(struct in_str *i) |
| 2801 | { | 2985 | { |
| 2802 | int ch; | 2986 | int ch; |
| 2803 | 2987 | ||
| 2804 | if (!i->file) { | 2988 | IF_HUSH_ALIAS(again:) |
| 2805 | /* string-based in_str */ | 2989 | if (i->p) { |
| 2990 | /* string-based in_str, or line editing buffer, or alias buffer */ | ||
| 2806 | ch = (unsigned char)*i->p; | 2991 | ch = (unsigned char)*i->p; |
| 2807 | if (ch != '\0') { | 2992 | if (ch != '\0') { |
| 2808 | i->p++; | 2993 | i->p++; |
| 2809 | i->last_char = ch; | 2994 | goto out; |
| 2810 | #if ENABLE_HUSH_LINENO_VAR | 2995 | } |
| 2811 | if (ch == '\n') { | 2996 | #if ENABLE_HUSH_ALIAS |
| 2812 | G.parse_lineno++; | 2997 | if (i->saved_ibuf) { |
| 2813 | debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); | 2998 | i_free_alias_buffer(i); |
| 2814 | } | 2999 | goto again; |
| 3000 | } | ||
| 2815 | #endif | 3001 | #endif |
| 2816 | return ch; | 3002 | /* If string-based in_str, end-of-string is EOF */ |
| 3003 | if (!i->file) { | ||
| 3004 | debug_printf("i_getch: got EOF from string\n"); | ||
| 3005 | return EOF; | ||
| 2817 | } | 3006 | } |
| 2818 | return EOF; | ||
| 2819 | } | 3007 | } |
| 2820 | 3008 | ||
| 2821 | /* FILE-based in_str */ | 3009 | /* FILE-based in_str */ |
| 2822 | 3010 | ||
| 2823 | #if ENABLE_FEATURE_EDITING | 3011 | /* Use what i_peek / i_peek2 saved (if anything) */ |
| 2824 | /* This can be stdin, check line editing char[] buffer */ | 3012 | /* peek_buf[] is an int array, not char - can contain EOF */ |
| 2825 | if (i->p && *i->p != '\0') { | ||
| 2826 | ch = (unsigned char)*i->p++; | ||
| 2827 | goto out; | ||
| 2828 | } | ||
| 2829 | #endif | ||
| 2830 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
| 2831 | ch = i->peek_buf[0]; | 3013 | ch = i->peek_buf[0]; |
| 2832 | if (ch != 0) { | 3014 | if (ch != 0) { |
| 2833 | int ch2 = i->peek_buf[1]; | 3015 | int ch2 = i->peek_buf[1]; |
| @@ -2838,70 +3020,71 @@ static int i_getch(struct in_str *i) | |||
| 2838 | goto out; | 3020 | goto out; |
| 2839 | } | 3021 | } |
| 2840 | 3022 | ||
| 2841 | ch = fgetc_interactive(i); | 3023 | ch = i_getch_interactive(i); |
| 2842 | out: | 3024 | out: |
| 2843 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 3025 | debug_printf("i_getch: got '%c' %d\n", ch, ch); |
| 2844 | i->last_char = ch; | ||
| 2845 | #if ENABLE_HUSH_LINENO_VAR | 3026 | #if ENABLE_HUSH_LINENO_VAR |
| 2846 | if (ch == '\n') { | 3027 | if (ch == '\n') { |
| 2847 | G.parse_lineno++; | 3028 | G.parse_lineno++; |
| 2848 | debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); | 3029 | debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); |
| 2849 | } | 3030 | } |
| 2850 | #endif | 3031 | #endif |
| 3032 | i->last_char = ch; | ||
| 2851 | return ch; | 3033 | return ch; |
| 2852 | } | 3034 | } |
| 2853 | 3035 | ||
| 3036 | /* Called often, has optimizations to make it faster: | ||
| 3037 | * = May return NUL instead of EOF. | ||
| 3038 | * It's ok because use cases are "got '&', peek next ch to see whether it is '&'" | ||
| 3039 | * = Must not be called after '\n' (it would cause unexpected line editing prompt). | ||
| 3040 | */ | ||
| 2854 | static int i_peek(struct in_str *i) | 3041 | static int i_peek(struct in_str *i) |
| 2855 | { | 3042 | { |
| 2856 | int ch; | 3043 | int ch; |
| 2857 | 3044 | ||
| 2858 | if (!i->file) { | 3045 | if (i->p) { |
| 2859 | /* string-based in_str */ | 3046 | /* string-based in_str, or line editing buffer, or alias buffer */ |
| 2860 | /* Doesn't report EOF on NUL. None of the callers care. */ | 3047 | #if ENABLE_HUSH_ALIAS |
| 3048 | if (*i->p == '\0' && i->saved_ibuf) { | ||
| 3049 | /* corner case: "an_alias&&..." expansion will have | ||
| 3050 | * i->p = "<alias_expansion>&", i->saved_ibuf = "&..." | ||
| 3051 | * and at the end of it, we must not return NUL, | ||
| 3052 | * this would logically split && into & & during parsing. | ||
| 3053 | */ | ||
| 3054 | return (unsigned char)*i->saved_ibuf; | ||
| 3055 | } | ||
| 3056 | #endif | ||
| 2861 | return (unsigned char)*i->p; | 3057 | return (unsigned char)*i->p; |
| 2862 | } | 3058 | } |
| 2863 | 3059 | ||
| 2864 | /* FILE-based in_str */ | 3060 | /* Now we know it is a file-based in_str. */ |
| 2865 | 3061 | ||
| 2866 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE | ||
| 2867 | /* This can be stdin, check line editing char[] buffer */ | ||
| 2868 | if (i->p && *i->p != '\0') | ||
| 2869 | return (unsigned char)*i->p; | ||
| 2870 | #endif | ||
| 2871 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | 3062 | /* peek_buf[] is an int array, not char. Can contain EOF. */ |
| 2872 | ch = i->peek_buf[0]; | 3063 | ch = i->peek_buf[0]; |
| 2873 | if (ch != 0) | 3064 | if (ch == 0) { |
| 2874 | return ch; | 3065 | /* We did not read it yet, get it now */ |
| 3066 | do ch = hfgetc(i->file); while (ch == '\0'); | ||
| 3067 | i->peek_buf[0] = ch; | ||
| 3068 | } | ||
| 2875 | 3069 | ||
| 2876 | /* Need to get a new char */ | ||
| 2877 | ch = fgetc_interactive(i); | ||
| 2878 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | 3070 | debug_printf("file_peek: got '%c' %d\n", ch, ch); |
| 2879 | |||
| 2880 | /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */ | ||
| 2881 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE | ||
| 2882 | if (i->p) { | ||
| 2883 | i->p -= 1; | ||
| 2884 | return ch; | ||
| 2885 | } | ||
| 2886 | #endif | ||
| 2887 | i->peek_buf[0] = ch; | ||
| 2888 | /*i->peek_buf[1] = 0; - already is */ | ||
| 2889 | return ch; | 3071 | return ch; |
| 2890 | } | 3072 | } |
| 2891 | 3073 | ||
| 2892 | /* Only ever called if i_peek() was called, and did not return EOF. | 3074 | /* Called if i_peek() was called, and saw an ordinary char |
| 2893 | * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, | 3075 | * (not EOF, not NUL, not end-of-line). |
| 2894 | * not end-of-line. Therefore we never need to read a new editing line here. | 3076 | * Therefore we never need to read a new editing line here. |
| 2895 | */ | 3077 | */ |
| 2896 | static int i_peek2(struct in_str *i) | 3078 | static int i_peek2(struct in_str *i) |
| 2897 | { | 3079 | { |
| 2898 | int ch; | 3080 | int ch; |
| 2899 | 3081 | ||
| 2900 | /* There are two cases when i->p[] buffer exists. | 3082 | /* There are three cases when i->p[] buffer exists. |
| 3083 | * (0) alias expansion in progress. | ||
| 2901 | * (1) it's a string in_str. | 3084 | * (1) it's a string in_str. |
| 2902 | * (2) It's a file, and we have a saved line editing buffer. | 3085 | * (2) It's a file, and we have a saved line editing buffer. |
| 2903 | * In both cases, we know that i->p[0] exists and not NUL, and | 3086 | * In all cases, we know that i->p[0] exists and not NUL. |
| 2904 | * the peek2 result is in i->p[1]. | 3087 | * The peek2 result is in i->p[1]. |
| 2905 | */ | 3088 | */ |
| 2906 | if (i->p) | 3089 | if (i->p) |
| 2907 | return (unsigned char)i->p[1]; | 3090 | return (unsigned char)i->p[1]; |
| @@ -3003,7 +3186,7 @@ static void o_grow_by(o_string *o, int len) | |||
| 3003 | } | 3186 | } |
| 3004 | } | 3187 | } |
| 3005 | 3188 | ||
| 3006 | static void o_addchr(o_string *o, int ch) | 3189 | static ALWAYS_INLINE void INLINED_o_addchr(o_string *o, int ch) |
| 3007 | { | 3190 | { |
| 3008 | debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); | 3191 | debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); |
| 3009 | if (o->length < o->maxlen) { | 3192 | if (o->length < o->maxlen) { |
| @@ -3017,6 +3200,10 @@ static void o_addchr(o_string *o, int ch) | |||
| 3017 | o_grow_by(o, 1); | 3200 | o_grow_by(o, 1); |
| 3018 | goto add; | 3201 | goto add; |
| 3019 | } | 3202 | } |
| 3203 | static void o_addchr(o_string *o, int ch) | ||
| 3204 | { | ||
| 3205 | INLINED_o_addchr(o, ch); | ||
| 3206 | } | ||
| 3020 | 3207 | ||
| 3021 | #if 0 | 3208 | #if 0 |
| 3022 | /* Valid only if we know o_string is not empty */ | 3209 | /* Valid only if we know o_string is not empty */ |
| @@ -3127,7 +3314,7 @@ static void o_addqchr(o_string *o, int ch) | |||
| 3127 | static void o_addQchr(o_string *o, int ch) | 3314 | static void o_addQchr(o_string *o, int ch) |
| 3128 | { | 3315 | { |
| 3129 | int sz = 1; | 3316 | int sz = 1; |
| 3130 | if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) | 3317 | if ((o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) |
| 3131 | && strchr("*?[-\\" MAYBE_BRACES, ch) | 3318 | && strchr("*?[-\\" MAYBE_BRACES, ch) |
| 3132 | ) { | 3319 | ) { |
| 3133 | sz++; | 3320 | sz++; |
| @@ -3170,7 +3357,7 @@ static void o_addqblock(o_string *o, const char *str, int len) | |||
| 3170 | 3357 | ||
| 3171 | static void o_addQblock(o_string *o, const char *str, int len) | 3358 | static void o_addQblock(o_string *o, const char *str, int len) |
| 3172 | { | 3359 | { |
| 3173 | if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) { | 3360 | if (!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)) { |
| 3174 | o_addblock(o, str, len); | 3361 | o_addblock(o, str, len); |
| 3175 | return; | 3362 | return; |
| 3176 | } | 3363 | } |
| @@ -3182,6 +3369,11 @@ static void o_addQstr(o_string *o, const char *str) | |||
| 3182 | o_addQblock(o, str, strlen(str)); | 3369 | o_addQblock(o, str, strlen(str)); |
| 3183 | } | 3370 | } |
| 3184 | 3371 | ||
| 3372 | static void o_addqstr(o_string *o, const char *str) | ||
| 3373 | { | ||
| 3374 | o_addqblock(o, str, strlen(str)); | ||
| 3375 | } | ||
| 3376 | |||
| 3185 | /* A special kind of o_string for $VAR and `cmd` expansion. | 3377 | /* A special kind of o_string for $VAR and `cmd` expansion. |
| 3186 | * It contains char* list[] at the beginning, which is grown in 16 element | 3378 | * It contains char* list[] at the beginning, which is grown in 16 element |
| 3187 | * increments. Actual string data starts at the next multiple of 16 * (char*). | 3379 | * increments. Actual string data starts at the next multiple of 16 * (char*). |
| @@ -3200,11 +3392,11 @@ static void debug_print_list(const char *prefix, o_string *o, int n) | |||
| 3200 | int i = 0; | 3392 | int i = 0; |
| 3201 | 3393 | ||
| 3202 | indent(); | 3394 | indent(); |
| 3203 | fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", | 3395 | fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d do_glob:%d has_quoted:%d globprotect:%d\n", |
| 3204 | prefix, list, n, string_start, o->length, o->maxlen, | 3396 | prefix, list, n, string_start, o->length, o->maxlen, |
| 3205 | !!(o->o_expflags & EXP_FLAG_GLOB), | 3397 | !!(o->o_expflags & EXP_FLAG_DO_GLOBBING), |
| 3206 | o->has_quoted_part, | 3398 | o->has_quoted_part, |
| 3207 | !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 3399 | !!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); |
| 3208 | while (i < n) { | 3400 | while (i < n) { |
| 3209 | indent(); | 3401 | indent(); |
| 3210 | fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i], | 3402 | fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i], |
| @@ -3327,7 +3519,19 @@ static int glob_needed(const char *s) | |||
| 3327 | s += 2; | 3519 | s += 2; |
| 3328 | continue; | 3520 | continue; |
| 3329 | } | 3521 | } |
| 3330 | if (*s == '*' || *s == '[' || *s == '?' || *s == '{') | 3522 | if (*s == '*' || *s == '?') |
| 3523 | return 1; | ||
| 3524 | /* Only force glob if "..[..].." detected. | ||
| 3525 | * Not merely "[", "[[", "][" etc. | ||
| 3526 | * Optimization to avoid glob() | ||
| 3527 | * on "[ COND ]" and "[[ COND ]]": | ||
| 3528 | * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' | ||
| 3529 | * shouldn't be doing 50000 stat("["). | ||
| 3530 | * (Can do it for "{" too, but it's not a common case). | ||
| 3531 | */ | ||
| 3532 | if (*s == '[' && strchr(s+1, ']')) | ||
| 3533 | return 1; | ||
| 3534 | if (*s == '{' /* && strchr(s+1, '}')*/) | ||
| 3331 | return 1; | 3535 | return 1; |
| 3332 | s++; | 3536 | s++; |
| 3333 | } | 3537 | } |
| @@ -3518,7 +3722,16 @@ static int glob_needed(const char *s) | |||
| 3518 | s += 2; | 3722 | s += 2; |
| 3519 | continue; | 3723 | continue; |
| 3520 | } | 3724 | } |
| 3521 | if (*s == '*' || *s == '[' || *s == '?') | 3725 | if (*s == '*' || *s == '?') |
| 3726 | return 1; | ||
| 3727 | /* Only force glob if "..[..].." detected. | ||
| 3728 | * Not merely "[", "[[", "][" etc. | ||
| 3729 | * Optimization to avoid glob() | ||
| 3730 | * on "[ COND ]" and "[[ COND ]]": | ||
| 3731 | * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' | ||
| 3732 | * shouldn't be doing 50000 stat("["). | ||
| 3733 | */ | ||
| 3734 | if (*s == '[' && strchr(s+1, ']')) | ||
| 3522 | return 1; | 3735 | return 1; |
| 3523 | s++; | 3736 | s++; |
| 3524 | } | 3737 | } |
| @@ -3585,11 +3798,11 @@ static int perform_glob(o_string *o, int n) | |||
| 3585 | 3798 | ||
| 3586 | #endif /* !HUSH_BRACE_EXPANSION */ | 3799 | #endif /* !HUSH_BRACE_EXPANSION */ |
| 3587 | 3800 | ||
| 3588 | /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. | 3801 | /* If o->o_expflags & EXP_FLAG_DO_GLOBBING, glob the string so far remembered. |
| 3589 | * Otherwise, just finish current list[] and start new */ | 3802 | * Otherwise, just finish current list[] and start new */ |
| 3590 | static int o_save_ptr(o_string *o, int n) | 3803 | static int o_save_ptr(o_string *o, int n) |
| 3591 | { | 3804 | { |
| 3592 | if (o->o_expflags & EXP_FLAG_GLOB) { | 3805 | if (o->o_expflags & EXP_FLAG_DO_GLOBBING) { |
| 3593 | /* If o->has_empty_slot, list[n] was already globbed | 3806 | /* If o->has_empty_slot, list[n] was already globbed |
| 3594 | * (if it was requested back then when it was filled) | 3807 | * (if it was requested back then when it was filled) |
| 3595 | * so don't do that again! */ | 3808 | * so don't do that again! */ |
| @@ -3665,8 +3878,8 @@ static struct pipe *free_pipe(struct pipe *pi) | |||
| 3665 | //command->group_as_string = NULL; | 3878 | //command->group_as_string = NULL; |
| 3666 | #endif | 3879 | #endif |
| 3667 | for (r = command->redirects; r; r = rnext) { | 3880 | for (r = command->redirects; r; r = rnext) { |
| 3668 | debug_printf_clean(" redirect %d%s", | 3881 | debug_printf_clean(" redirect %d%.3s", |
| 3669 | r->rd_fd, redir_table[r->rd_type].descrip); | 3882 | r->rd_fd, redir_table[r->rd_type].descrip3); |
| 3670 | /* guard against the case >$FOO, where foo is unset or blank */ | 3883 | /* guard against the case >$FOO, where foo is unset or blank */ |
| 3671 | if (r->rd_filename) { | 3884 | if (r->rd_filename) { |
| 3672 | debug_printf_clean(" fname:'%s'\n", r->rd_filename); | 3885 | debug_printf_clean(" fname:'%s'\n", r->rd_filename); |
| @@ -3735,7 +3948,7 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
| 3735 | # endif | 3948 | # endif |
| 3736 | # if ENABLE_HUSH_CASE | 3949 | # if ENABLE_HUSH_CASE |
| 3737 | [RES_CASE ] = "CASE" , | 3950 | [RES_CASE ] = "CASE" , |
| 3738 | [RES_CASE_IN ] = "CASE_IN" , | 3951 | [RES_CASE_IN] = "CASE_IN", |
| 3739 | [RES_MATCH] = "MATCH", | 3952 | [RES_MATCH] = "MATCH", |
| 3740 | [RES_CASE_BODY] = "CASE_BODY", | 3953 | [RES_CASE_BODY] = "CASE_BODY", |
| 3741 | [RES_ESAC ] = "ESAC" , | 3954 | [RES_ESAC ] = "ESAC" , |
| @@ -3744,11 +3957,13 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
| 3744 | [RES_SNTX ] = "SNTX" , | 3957 | [RES_SNTX ] = "SNTX" , |
| 3745 | }; | 3958 | }; |
| 3746 | static const char *const CMDTYPE[] ALIGN_PTR = { | 3959 | static const char *const CMDTYPE[] ALIGN_PTR = { |
| 3747 | "{}", | 3960 | "{}", //CMD_NORMAL |
| 3748 | "()", | 3961 | "()", //CMD_SUBSHELL |
| 3749 | "[noglob]", | 3962 | "[test2]", //CMD_TEST2_SINGLEWORD_NOGLOB |
| 3963 | "[noglob]", //CMD_SINGLEWORD_NOGLOB | ||
| 3750 | # if ENABLE_HUSH_FUNCTIONS | 3964 | # if ENABLE_HUSH_FUNCTIONS |
| 3751 | "func()", | 3965 | "func()", //CMD_FUNCTION_KWORD |
| 3966 | "funcdef", //CMD_FUNCDEF | ||
| 3752 | # endif | 3967 | # endif |
| 3753 | }; | 3968 | }; |
| 3754 | 3969 | ||
| @@ -3812,9 +4027,11 @@ static struct pipe *new_pipe(void) | |||
| 3812 | return pi; | 4027 | return pi; |
| 3813 | } | 4028 | } |
| 3814 | 4029 | ||
| 3815 | /* Command (member of a pipe) is complete, or we start a new pipe | 4030 | /* Parsing of command (member of a pipe) is completed. |
| 3816 | * if ctx->command is NULL. | 4031 | * If it's not null, a new empty command structure is added |
| 3817 | * No errors possible here. | 4032 | * to the current pipe, and ctx->command is set to it. |
| 4033 | * Return the current number of already parsed commands in the pipe. | ||
| 4034 | * No errors are possible here. | ||
| 3818 | */ | 4035 | */ |
| 3819 | static int done_command(struct parse_context *ctx) | 4036 | static int done_command(struct parse_context *ctx) |
| 3820 | { | 4037 | { |
| @@ -3830,17 +4047,16 @@ static int done_command(struct parse_context *ctx) | |||
| 3830 | ctx->pending_redirect = NULL; | 4047 | ctx->pending_redirect = NULL; |
| 3831 | } | 4048 | } |
| 3832 | #endif | 4049 | #endif |
| 3833 | |||
| 3834 | if (command) { | 4050 | if (command) { |
| 3835 | if (IS_NULL_CMD(command)) { | 4051 | if (IS_NULL_CMD(command)) { |
| 3836 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | 4052 | debug_printf_parse("done_command: skipping null cmd, num_cmds:%d\n", pi->num_cmds); |
| 3837 | goto clear_and_ret; | 4053 | goto clear_and_ret; |
| 3838 | } | 4054 | } |
| 3839 | pi->num_cmds++; | 4055 | pi->num_cmds++; |
| 3840 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); | 4056 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); |
| 3841 | //debug_print_tree(ctx->list_head, 20); | 4057 | //debug_print_tree(ctx->list_head, 20); |
| 3842 | } else { | 4058 | } else { |
| 3843 | debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); | 4059 | debug_printf_parse("done_command: initializing, num_cmds:%d\n", pi->num_cmds); |
| 3844 | } | 4060 | } |
| 3845 | 4061 | ||
| 3846 | /* Only real trickiness here is that the uncommitted | 4062 | /* Only real trickiness here is that the uncommitted |
| @@ -3851,21 +4067,36 @@ static int done_command(struct parse_context *ctx) | |||
| 3851 | memset(command, 0, sizeof(*command)); | 4067 | memset(command, 0, sizeof(*command)); |
| 3852 | #if ENABLE_HUSH_LINENO_VAR | 4068 | #if ENABLE_HUSH_LINENO_VAR |
| 3853 | command->lineno = G.parse_lineno; | 4069 | command->lineno = G.parse_lineno; |
| 3854 | debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno); | 4070 | debug_printf_parse("command->lineno=G.parse_lineno (%u)\n", G.parse_lineno); |
| 3855 | #endif | 4071 | #endif |
| 3856 | return pi->num_cmds; /* used only for 0/nonzero check */ | 4072 | return pi->num_cmds; |
| 3857 | } | 4073 | } |
| 3858 | 4074 | ||
| 3859 | static void done_pipe(struct parse_context *ctx, pipe_style type) | 4075 | /* Parsing of a pipe is completed. |
| 4076 | * Finish parsing current command via done_command(). | ||
| 4077 | * (If the pipe is not empty, but done_command() did not change the number | ||
| 4078 | * of commands in pipe, return value is 1. Used for catching syntax errors) | ||
| 4079 | * Then append a new pipe with one empty command. | ||
| 4080 | */ | ||
| 4081 | static int done_pipe(struct parse_context *ctx, pipe_style type) | ||
| 3860 | { | 4082 | { |
| 3861 | int not_null; | 4083 | int num_cmds; |
| 4084 | int oldnum; | ||
| 4085 | int last_cmd_is_null; | ||
| 3862 | 4086 | ||
| 3863 | debug_printf_parse("done_pipe entered, followup %d\n", type); | 4087 | debug_printf_parse("done_pipe entered, followup %d\n", type); |
| 3864 | /* Close previous command */ | 4088 | /* Close previous command */ |
| 3865 | not_null = done_command(ctx); | 4089 | oldnum = ctx->pipe->num_cmds; |
| 4090 | num_cmds = done_command(ctx); | ||
| 4091 | |||
| 4092 | /* This is true if this was a non-empty pipe, | ||
| 4093 | * but done_command didn't add a new member to it. | ||
| 4094 | * Usually it is a syntax error. | ||
| 4095 | * Examples: "date | | ...", "date | ; ..." | ||
| 4096 | */ | ||
| 4097 | last_cmd_is_null = (oldnum != 0 && num_cmds == oldnum); | ||
| 4098 | |||
| 3866 | #if HAS_KEYWORDS | 4099 | #if HAS_KEYWORDS |
| 3867 | ctx->pipe->pi_inverted = ctx->ctx_inverted; | ||
| 3868 | ctx->ctx_inverted = 0; | ||
| 3869 | ctx->pipe->res_word = ctx->ctx_res_w; | 4100 | ctx->pipe->res_word = ctx->ctx_res_w; |
| 3870 | #endif | 4101 | #endif |
| 3871 | if (type == PIPE_BG && ctx->list_head != ctx->pipe) { | 4102 | if (type == PIPE_BG && ctx->list_head != ctx->pipe) { |
| @@ -3904,7 +4135,7 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 3904 | ctx->list_head = ctx->pipe = pi; | 4135 | ctx->list_head = ctx->pipe = pi; |
| 3905 | /* for cases like "cmd && &", do not be tricked by last command | 4136 | /* for cases like "cmd && &", do not be tricked by last command |
| 3906 | * being null - the entire {...} & is NOT null! */ | 4137 | * being null - the entire {...} & is NOT null! */ |
| 3907 | not_null = 1; | 4138 | num_cmds = 1; |
| 3908 | } else { | 4139 | } else { |
| 3909 | no_conv: | 4140 | no_conv: |
| 3910 | ctx->pipe->followup = type; | 4141 | ctx->pipe->followup = type; |
| @@ -3912,8 +4143,8 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 3912 | 4143 | ||
| 3913 | /* Without this check, even just <enter> on command line generates | 4144 | /* Without this check, even just <enter> on command line generates |
| 3914 | * tree of three NOPs (!). Which is harmless but annoying. | 4145 | * tree of three NOPs (!). Which is harmless but annoying. |
| 3915 | * IOW: it is safe to do it unconditionally. */ | 4146 | * IOW: it is safe to do the following unconditionally. */ |
| 3916 | if (not_null | 4147 | if (num_cmds != 0 |
| 3917 | #if ENABLE_HUSH_IF | 4148 | #if ENABLE_HUSH_IF |
| 3918 | || ctx->ctx_res_w == RES_FI | 4149 | || ctx->ctx_res_w == RES_FI |
| 3919 | #endif | 4150 | #endif |
| @@ -3928,37 +4159,39 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 3928 | ) { | 4159 | ) { |
| 3929 | struct pipe *new_p; | 4160 | struct pipe *new_p; |
| 3930 | debug_printf_parse("done_pipe: adding new pipe: " | 4161 | debug_printf_parse("done_pipe: adding new pipe: " |
| 3931 | "not_null:%d ctx->ctx_res_w:%d\n", | 4162 | "num_cmds:%d ctx->ctx_res_w:%d\n", |
| 3932 | not_null, ctx->ctx_res_w); | 4163 | num_cmds, ctx->ctx_res_w); |
| 3933 | new_p = new_pipe(); | 4164 | new_p = new_pipe(); |
| 3934 | ctx->pipe->next = new_p; | 4165 | ctx->pipe->next = new_p; |
| 3935 | ctx->pipe = new_p; | 4166 | ctx->pipe = new_p; |
| 3936 | /* RES_THEN, RES_DO etc are "sticky" - | 4167 | /* RES_THEN, RES_DO etc are "sticky" - |
| 3937 | * they remain set for pipes inside if/while. | 4168 | * they remain set for pipes inside if/while. |
| 3938 | * This is used to control execution. | 4169 | * This is used to control execution. |
| 3939 | * RES_FOR and RES_IN are NOT sticky (needed to support | ||
| 3940 | * cases where variable or value happens to match a keyword): | ||
| 3941 | */ | 4170 | */ |
| 3942 | #if ENABLE_HUSH_LOOPS | 4171 | #if ENABLE_HUSH_LOOPS |
| 4172 | /* RES_FOR and RES_IN are NOT sticky (needed to support | ||
| 4173 | * cases where variable or value happens to match a keyword): | ||
| 4174 | */ | ||
| 3943 | if (ctx->ctx_res_w == RES_FOR | 4175 | if (ctx->ctx_res_w == RES_FOR |
| 3944 | || ctx->ctx_res_w == RES_IN) | 4176 | || ctx->ctx_res_w == RES_IN) |
| 3945 | ctx->ctx_res_w = RES_NONE; | 4177 | ctx->ctx_res_w = RES_NONE; |
| 3946 | #endif | 4178 | #endif |
| 3947 | #if ENABLE_HUSH_CASE | 4179 | #if ENABLE_HUSH_CASE |
| 3948 | if (ctx->ctx_res_w == RES_MATCH) | ||
| 3949 | ctx->ctx_res_w = RES_CASE_BODY; | ||
| 3950 | if (ctx->ctx_res_w == RES_CASE) | 4180 | if (ctx->ctx_res_w == RES_CASE) |
| 3951 | ctx->ctx_res_w = RES_CASE_IN; | 4181 | ctx->ctx_res_w = RES_CASE_IN; |
| 4182 | if (ctx->ctx_res_w == RES_MATCH) | ||
| 4183 | ctx->ctx_res_w = RES_CASE_BODY; | ||
| 3952 | #endif | 4184 | #endif |
| 3953 | ctx->command = NULL; /* trick done_command below */ | ||
| 3954 | /* Create the memory for command, roughly: | 4185 | /* Create the memory for command, roughly: |
| 3955 | * ctx->pipe->cmds = new struct command; | 4186 | * ctx->pipe->cmds = new struct command; |
| 3956 | * ctx->command = &ctx->pipe->cmds[0]; | 4187 | * ctx->command = &ctx->pipe->cmds[0]; |
| 3957 | */ | 4188 | */ |
| 4189 | ctx->command = NULL; | ||
| 3958 | done_command(ctx); | 4190 | done_command(ctx); |
| 3959 | //debug_print_tree(ctx->list_head, 10); | 4191 | //debug_print_tree(ctx->list_head, 10); |
| 3960 | } | 4192 | } |
| 3961 | debug_printf_parse("done_pipe return\n"); | 4193 | debug_printf_parse("done_pipe return: last_cmd_is_null:%d\n", last_cmd_is_null); |
| 4194 | return last_cmd_is_null; | ||
| 3962 | } | 4195 | } |
| 3963 | 4196 | ||
| 3964 | static void initialize_context(struct parse_context *ctx) | 4197 | static void initialize_context(struct parse_context *ctx) |
| @@ -3972,6 +4205,10 @@ static void initialize_context(struct parse_context *ctx) | |||
| 3972 | * ctx->command = &ctx->pipe->cmds[0]; | 4205 | * ctx->command = &ctx->pipe->cmds[0]; |
| 3973 | */ | 4206 | */ |
| 3974 | done_command(ctx); | 4207 | done_command(ctx); |
| 4208 | /* If very first arg is "" or '', ctx.word.data may end up NULL. | ||
| 4209 | * Prevent this: | ||
| 4210 | */ | ||
| 4211 | ctx->word.data = xzalloc(1); /* start as "", not as NULL */ | ||
| 3975 | } | 4212 | } |
| 3976 | 4213 | ||
| 3977 | /* If a reserved word is found and processed, parse context is modified | 4214 | /* If a reserved word is found and processed, parse context is modified |
| @@ -4008,39 +4245,39 @@ enum { | |||
| 4008 | FLAG_START = (1 << RES_XXXX ), | 4245 | FLAG_START = (1 << RES_XXXX ), |
| 4009 | }; | 4246 | }; |
| 4010 | 4247 | ||
| 4011 | static const struct reserved_combo* match_reserved_word(o_string *word) | 4248 | /* Mostly a list of accepted follow-up reserved words. |
| 4012 | { | 4249 | * FLAG_END means we are done with the sequence, and are ready |
| 4013 | /* Mostly a list of accepted follow-up reserved words. | 4250 | * to turn the compound list into a command. |
| 4014 | * FLAG_END means we are done with the sequence, and are ready | 4251 | * FLAG_START means the word must start a new compound list. |
| 4015 | * to turn the compound list into a command. | 4252 | */ |
| 4016 | * FLAG_START means the word must start a new compound list. | 4253 | static const struct reserved_combo reserved_list[] ALIGN4 = { |
| 4017 | */ | ||
| 4018 | static const struct reserved_combo reserved_list[] ALIGN4 = { | ||
| 4019 | # if ENABLE_HUSH_IF | 4254 | # if ENABLE_HUSH_IF |
| 4020 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, | 4255 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
| 4021 | { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, | 4256 | { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, |
| 4022 | { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | 4257 | { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
| 4023 | { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, | 4258 | { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, |
| 4024 | { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, | 4259 | { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, |
| 4025 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, | 4260 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
| 4026 | # endif | 4261 | # endif |
| 4027 | # if ENABLE_HUSH_LOOPS | 4262 | # if ENABLE_HUSH_LOOPS |
| 4028 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, | 4263 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
| 4029 | { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, | 4264 | { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
| 4030 | { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, | 4265 | { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
| 4031 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, | 4266 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
| 4032 | { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, | 4267 | { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, |
| 4033 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, | 4268 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
| 4034 | # endif | 4269 | # endif |
| 4035 | # if ENABLE_HUSH_CASE | 4270 | # if ENABLE_HUSH_CASE |
| 4036 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, | 4271 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
| 4037 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, | 4272 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
| 4038 | # endif | 4273 | # endif |
| 4039 | }; | 4274 | }; |
| 4275 | static const struct reserved_combo* match_reserved_word(const char *word) | ||
| 4276 | { | ||
| 4040 | const struct reserved_combo *r; | 4277 | const struct reserved_combo *r; |
| 4041 | 4278 | ||
| 4042 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { | 4279 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { |
| 4043 | if (strcmp(word->data, r->literal) == 0) | 4280 | if (strcmp(word, r->literal) == 0) |
| 4044 | return r; | 4281 | return r; |
| 4045 | } | 4282 | } |
| 4046 | return NULL; | 4283 | return NULL; |
| @@ -4051,16 +4288,36 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx) | |||
| 4051 | { | 4288 | { |
| 4052 | # if ENABLE_HUSH_CASE | 4289 | # if ENABLE_HUSH_CASE |
| 4053 | static const struct reserved_combo reserved_match = { | 4290 | static const struct reserved_combo reserved_match = { |
| 4054 | "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC | 4291 | "", RES_MATCH, NOT_ASSIGNMENT, FLAG_MATCH | FLAG_ESAC |
| 4055 | }; | 4292 | }; |
| 4056 | # endif | 4293 | # endif |
| 4057 | const struct reserved_combo *r; | 4294 | const struct reserved_combo *r; |
| 4058 | 4295 | ||
| 4059 | if (ctx->word.has_quoted_part) | 4296 | # if ENABLE_HUSH_FUNCTION_KEYWORD |
| 4060 | return 0; | 4297 | /* This is ~60 bytes smaller than adding "function" to reserved_list[] */ |
| 4061 | r = match_reserved_word(&ctx->word); | 4298 | if (strcmp(ctx->word.data, "function") == 0) { |
| 4299 | ctx->command->cmd_type = CMD_FUNCTION_KWORD; | ||
| 4300 | /* Return something harmless !NULL */ | ||
| 4301 | return &reserved_list[0]; | ||
| 4302 | } | ||
| 4303 | # endif | ||
| 4304 | |||
| 4305 | r = match_reserved_word(ctx->word.data); | ||
| 4062 | if (!r) | 4306 | if (!r) |
| 4063 | return r; /* NULL */ | 4307 | return r; /* NULL */ |
| 4308 | # if ENABLE_HUSH_CASE /* "case" syntax has a curveball */ | ||
| 4309 | if (ctx->ctx_res_w == RES_MATCH | ||
| 4310 | && r->res != RES_ESAC | ||
| 4311 | ) { | ||
| 4312 | /* We are at WORD in ";; WORD" or "case .. in WORD". | ||
| 4313 | * Here, only "esac" is a keyword. | ||
| 4314 | * Else WORD is a case pattern, can be keyword-like: | ||
| 4315 | * if) echo got_if;; | ||
| 4316 | * is allowed. | ||
| 4317 | */ | ||
| 4318 | return NULL; | ||
| 4319 | } | ||
| 4320 | # endif | ||
| 4064 | 4321 | ||
| 4065 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | 4322 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
| 4066 | # if ENABLE_HUSH_CASE | 4323 | # if ENABLE_HUSH_CASE |
| @@ -4070,11 +4327,13 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx) | |||
| 4070 | } else | 4327 | } else |
| 4071 | # endif | 4328 | # endif |
| 4072 | if (r->flag == 0) { /* '!' */ | 4329 | if (r->flag == 0) { /* '!' */ |
| 4073 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ | 4330 | if (ctx->pipe->num_cmds != 0 /* bash disallows: nice | ! cat */ |
| 4074 | syntax_error("! ! command"); | 4331 | /* || ctx->pipe->pi_inverted - bash used to disallow "! ! true" bash 5.2.15 allows it */ |
| 4332 | ) { | ||
| 4333 | syntax_error_unexpected_ch('!'); | ||
| 4075 | ctx->ctx_res_w = RES_SNTX; | 4334 | ctx->ctx_res_w = RES_SNTX; |
| 4076 | } | 4335 | } |
| 4077 | ctx->ctx_inverted = 1; | 4336 | ctx->pipe->pi_inverted = 1 - ctx->pipe->pi_inverted; |
| 4078 | return r; | 4337 | return r; |
| 4079 | } | 4338 | } |
| 4080 | if (r->flag & FLAG_START) { | 4339 | if (r->flag & FLAG_START) { |
| @@ -4141,8 +4400,11 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx) | |||
| 4141 | } | 4400 | } |
| 4142 | #endif /* HAS_KEYWORDS */ | 4401 | #endif /* HAS_KEYWORDS */ |
| 4143 | 4402 | ||
| 4144 | /* Word is complete, look at it and update parsing context. | 4403 | /* Parsing of a word is complete. |
| 4145 | * Normal return is 0. Syntax errors return 1. | 4404 | * Look at it and update current command: |
| 4405 | * update current command's argv/cmd_type/etc, fill in redirect name and type, | ||
| 4406 | * check reserved-ness and assignment-ness, etc... | ||
| 4407 | * Normal return is 0. Syntax errors print error message and return 1. | ||
| 4146 | * Note: on return, word is reset, but not o_free'd! | 4408 | * Note: on return, word is reset, but not o_free'd! |
| 4147 | */ | 4409 | */ |
| 4148 | static int done_word(struct parse_context *ctx) | 4410 | static int done_word(struct parse_context *ctx) |
| @@ -4150,8 +4412,8 @@ static int done_word(struct parse_context *ctx) | |||
| 4150 | struct command *command = ctx->command; | 4412 | struct command *command = ctx->command; |
| 4151 | 4413 | ||
| 4152 | debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command); | 4414 | debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command); |
| 4153 | if (ctx->word.length == 0 && !ctx->word.has_quoted_part) { | 4415 | if (IS_NULL_WORD(ctx->word)) { |
| 4154 | debug_printf_parse("done_word return 0: true null, ignored\n"); | 4416 | debug_printf_parse("done_word return 0: no word, ignored\n"); |
| 4155 | return 0; | 4417 | return 0; |
| 4156 | } | 4418 | } |
| 4157 | 4419 | ||
| @@ -4178,7 +4440,7 @@ static int done_word(struct parse_context *ctx) | |||
| 4178 | // as written: | 4440 | // as written: |
| 4179 | // <<EOF$t | 4441 | // <<EOF$t |
| 4180 | // <<EOF$((1)) | 4442 | // <<EOF$((1)) |
| 4181 | // <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug] | 4443 | // <<EOF`true` [bash 4.3.43 bug: this case also makes heredoc "quoted", a-la <<"EOF". Fixed by 5.2.15] |
| 4182 | 4444 | ||
| 4183 | ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data); | 4445 | ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data); |
| 4184 | /* Cater for >\file case: | 4446 | /* Cater for >\file case: |
| @@ -4195,124 +4457,124 @@ static int done_word(struct parse_context *ctx) | |||
| 4195 | } | 4457 | } |
| 4196 | debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data); | 4458 | debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data); |
| 4197 | ctx->pending_redirect = NULL; | 4459 | ctx->pending_redirect = NULL; |
| 4198 | } else { | 4460 | goto ret; |
| 4461 | } | ||
| 4462 | |||
| 4199 | #if HAS_KEYWORDS | 4463 | #if HAS_KEYWORDS |
| 4200 | # if ENABLE_HUSH_CASE | ||
| 4201 | if (ctx->ctx_dsemicolon | ||
| 4202 | && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */ | ||
| 4203 | ) { | ||
| 4204 | /* already done when ctx_dsemicolon was set to 1: */ | ||
| 4205 | /* ctx->ctx_res_w = RES_MATCH; */ | ||
| 4206 | ctx->ctx_dsemicolon = 0; | ||
| 4207 | } else | ||
| 4208 | # endif | ||
| 4209 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | 4464 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
| 4210 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB | 4465 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB |
| 4211 | && strcmp(ctx->word.data, "]]") == 0 | 4466 | && !ctx->word.has_quoted_part |
| 4212 | ) { | 4467 | && strcmp(ctx->word.data, "]]") == 0 |
| 4213 | /* allow "[[ ]] >file" etc */ | 4468 | ) { |
| 4214 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4469 | /* End test2-specific parsing rules */ |
| 4215 | } else | 4470 | /* Allow "[[ ]] >file" etc (> is a redirect symbol again) */ |
| 4471 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | ||
| 4472 | } else | ||
| 4216 | # endif | 4473 | # endif |
| 4217 | if (!command->argv /* if it's the first word... */ | 4474 | /* Is it a place where keyword can appear? */ |
| 4475 | //FIXME: this is wrong, it allows invalid syntax: { echo t; } if true; then echo YES; fi | ||
| 4476 | if ((!command->argv /* if it's the first word of command... */ | ||
| 4477 | # if ENABLE_HUSH_FUNCTIONS | ||
| 4478 | || command->cmd_type == CMD_FUNCDEF /* ^^^ or after FUNC() {} */ | ||
| 4479 | # endif | ||
| 4480 | ) | ||
| 4481 | && !ctx->word.has_quoted_part /* ""WORD never matches any keywords */ | ||
| 4482 | && !command->redirects /* no redirects yet... disallows: </dev/null if true; then... */ | ||
| 4218 | # if ENABLE_HUSH_LOOPS | 4483 | # if ENABLE_HUSH_LOOPS |
| 4219 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | 4484 | && ctx->ctx_res_w != RES_FOR /* not after "for" or "in" */ |
| 4220 | && ctx->ctx_res_w != RES_IN | 4485 | && ctx->ctx_res_w != RES_IN |
| 4221 | # endif | 4486 | # endif |
| 4222 | # if ENABLE_HUSH_CASE | 4487 | # if ENABLE_HUSH_CASE |
| 4223 | && ctx->ctx_res_w != RES_CASE | 4488 | && ctx->ctx_res_w != RES_CASE /* not after "case" */ |
| 4224 | # endif | 4489 | # endif |
| 4225 | ) { | 4490 | ) { |
| 4226 | const struct reserved_combo *reserved; | 4491 | const struct reserved_combo *reserved; |
| 4227 | reserved = reserved_word(ctx); | 4492 | reserved = reserved_word(ctx); |
| 4228 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); | 4493 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); |
| 4229 | if (reserved) { | 4494 | if (reserved) { |
| 4230 | # if ENABLE_HUSH_LINENO_VAR | 4495 | # if ENABLE_HUSH_LINENO_VAR |
| 4231 | /* Case: | 4496 | /* Case: |
| 4232 | * "while ...; do | 4497 | * while ...; do |
| 4233 | * cmd ..." | 4498 | * CMD |
| 4234 | * If we don't close the pipe _now_, immediately after "do", lineno logic | 4499 | * If we don't close the pipe _now_, immediately after "do", lineno logic |
| 4235 | * sees "cmd" as starting at "do" - i.e., at the previous line. | 4500 | * sees CMD as starting at "do" - i.e., at the previous line. |
| 4236 | */ | 4501 | */ |
| 4237 | if (0 | 4502 | if (0 |
| 4238 | IF_HUSH_IF(|| reserved->res == RES_THEN) | 4503 | IF_HUSH_IF(|| reserved->res == RES_THEN) |
| 4239 | IF_HUSH_IF(|| reserved->res == RES_ELIF) | 4504 | IF_HUSH_IF(|| reserved->res == RES_ELIF) |
| 4240 | IF_HUSH_IF(|| reserved->res == RES_ELSE) | 4505 | IF_HUSH_IF(|| reserved->res == RES_ELSE) |
| 4241 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) | 4506 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) |
| 4242 | ) { | 4507 | ) { |
| 4243 | done_pipe(ctx, PIPE_SEQ); | 4508 | done_pipe(ctx, PIPE_SEQ); |
| 4244 | } | ||
| 4245 | # endif | ||
| 4246 | o_reset_to_empty_unquoted(&ctx->word); | ||
| 4247 | debug_printf_parse("done_word return %d\n", | ||
| 4248 | (ctx->ctx_res_w == RES_SNTX)); | ||
| 4249 | return (ctx->ctx_res_w == RES_SNTX); | ||
| 4250 | } | 4509 | } |
| 4510 | # endif | ||
| 4511 | o_reset_to_empty_unquoted(&ctx->word); | ||
| 4512 | debug_printf_parse("done_word return %d\n", | ||
| 4513 | (ctx->ctx_res_w == RES_SNTX)); | ||
| 4514 | return (ctx->ctx_res_w == RES_SNTX); | ||
| 4515 | } | ||
| 4251 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | 4516 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
| 4252 | if (strcmp(ctx->word.data, "[[") == 0) { | 4517 | if (strcmp(ctx->word.data, "[[") == 0) { |
| 4253 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; | 4518 | /* Inside [[ ]], parsing rules are different */ |
| 4254 | } else | 4519 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; |
| 4520 | } else | ||
| 4255 | # endif | 4521 | # endif |
| 4256 | # if defined(CMD_SINGLEWORD_NOGLOB) | 4522 | # if defined(CMD_SINGLEWORD_NOGLOB) |
| 4257 | if (0 | 4523 | if (0 |
| 4258 | /* In bash, local/export/readonly are special, args | 4524 | /* In bash, local/export/readonly are special, args |
| 4259 | * are assignments and therefore expansion of them | 4525 | * are assignments and therefore expansion of them |
| 4260 | * should be "one-word" expansion: | 4526 | * should be "one-word" expansion: |
| 4261 | * $ export i=`echo 'a b'` # one arg: "i=a b" | 4527 | * $ export i=`echo 'a b'` # one arg: "i=a b" |
| 4262 | * compare with: | 4528 | * compare with: |
| 4263 | * $ ls i=`echo 'a b'` # two args: "i=a" and "b" | 4529 | * $ ls i=`echo 'a b'` # two args: "i=a" and "b" |
| 4264 | * ls: cannot access i=a: No such file or directory | 4530 | * ls: cannot access i=a: No such file or directory |
| 4265 | * ls: cannot access b: No such file or directory | 4531 | * ls: cannot access b: No such file or directory |
| 4266 | * Note: bash 3.2.33(1) does this only if export word | 4532 | * Note: bash 3.2.33(1) does this only if export word |
| 4267 | * itself is not quoted: | 4533 | * itself is not quoted: |
| 4268 | * $ export i=`echo 'aaa bbb'`; echo "$i" | 4534 | * $ export i=`echo 'aaa bbb'`; echo "$i" |
| 4269 | * aaa bbb | 4535 | * aaa bbb |
| 4270 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" | 4536 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" |
| 4271 | * aaa | 4537 | * aaa |
| 4272 | */ | 4538 | */ |
| 4273 | IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0) | 4539 | IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0) |
| 4274 | IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0) | 4540 | IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0) |
| 4275 | IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0) | 4541 | IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0) |
| 4276 | ) { | 4542 | ) { |
| 4277 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4543 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; |
| 4278 | } | 4544 | } |
| 4279 | # else | 4545 | # else |
| 4280 | { /* empty block to pair "if ... else" */ } | 4546 | { /* empty block to pair "if ... else" */ } |
| 4281 | # endif | 4547 | # endif |
| 4282 | } | 4548 | } |
| 4283 | #endif /* HAS_KEYWORDS */ | 4549 | #endif /* HAS_KEYWORDS */ |
| 4284 | 4550 | ||
| 4285 | if (command->group) { | 4551 | if (command->group) { |
| 4286 | /* "{ echo foo; } echo bar" - bad */ | 4552 | /* "{ echo foo; } echo bar" - bad */ |
| 4287 | syntax_error_at(ctx->word.data); | 4553 | syntax_error_at(ctx->word.data); |
| 4288 | debug_printf_parse("done_word return 1: syntax error, " | 4554 | debug_printf_parse("done_word return 1: syntax error, " |
| 4289 | "groups and arglists don't mix\n"); | 4555 | "groups and arglists don't mix\n"); |
| 4290 | return 1; | 4556 | return 1; |
| 4291 | } | 4557 | } |
| 4292 | 4558 | ||
| 4293 | /* If this word wasn't an assignment, next ones definitely | 4559 | /* If this word wasn't an assignment, next ones definitely |
| 4294 | * can't be assignments. Even if they look like ones. */ | 4560 | * can't be assignments. Even if they look like ones. */ |
| 4295 | if (ctx->is_assignment != DEFINITELY_ASSIGNMENT | 4561 | if (ctx->is_assignment != DEFINITELY_ASSIGNMENT) { |
| 4296 | && ctx->is_assignment != WORD_IS_KEYWORD | 4562 | ctx->is_assignment = NOT_ASSIGNMENT; |
| 4297 | ) { | 4563 | } else { |
| 4298 | ctx->is_assignment = NOT_ASSIGNMENT; | 4564 | /* This was an assignment word, next one maybe will be too */ |
| 4299 | } else { | 4565 | command->assignment_cnt++; |
| 4300 | if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) { | 4566 | debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); |
| 4301 | command->assignment_cnt++; | 4567 | ctx->is_assignment = MAYBE_ASSIGNMENT; |
| 4302 | debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); | ||
| 4303 | } | ||
| 4304 | debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]); | ||
| 4305 | ctx->is_assignment = MAYBE_ASSIGNMENT; | ||
| 4306 | } | ||
| 4307 | debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]); | ||
| 4308 | command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data)); | ||
| 4309 | debug_print_strings("word appended to argv", command->argv); | ||
| 4310 | } | 4568 | } |
| 4569 | debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]); | ||
| 4570 | |||
| 4571 | command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data)); | ||
| 4572 | debug_print_strings("word appended to argv", command->argv); | ||
| 4311 | 4573 | ||
| 4312 | #if ENABLE_HUSH_LOOPS | 4574 | #if ENABLE_HUSH_LOOPS |
| 4313 | if (ctx->ctx_res_w == RES_FOR) { | 4575 | if (ctx->ctx_res_w == RES_FOR) { |
| 4314 | if (ctx->word.has_quoted_part | 4576 | if (ctx->word.has_quoted_part |
| 4315 | || endofname(command->argv[0])[0] != '\0' | 4577 | || endofname(ctx->word.data)[0] != '\0' |
| 4316 | ) { | 4578 | ) { |
| 4317 | /* bash says just "not a valid identifier" */ | 4579 | /* bash says just "not a valid identifier" */ |
| 4318 | syntax_error("bad for loop variable"); | 4580 | syntax_error("bad for loop variable"); |
| @@ -4327,13 +4589,16 @@ static int done_word(struct parse_context *ctx) | |||
| 4327 | #endif | 4589 | #endif |
| 4328 | #if ENABLE_HUSH_CASE | 4590 | #if ENABLE_HUSH_CASE |
| 4329 | /* Force CASE to have just one word */ | 4591 | /* Force CASE to have just one word */ |
| 4330 | if (ctx->ctx_res_w == RES_CASE) { | 4592 | if (ctx->ctx_res_w == RES_CASE) |
| 4331 | done_pipe(ctx, PIPE_SEQ); | 4593 | done_pipe(ctx, PIPE_SEQ); |
| 4332 | } | 4594 | //TODO syntax check? |
| 4595 | // if (ctx->ctx_res_w == RES_MATCH) | ||
| 4596 | // eat all following spaces and tabs (but not newlines), | ||
| 4597 | // check that next char is | or ) | ||
| 4333 | #endif | 4598 | #endif |
| 4334 | 4599 | ||
| 4600 | ret: | ||
| 4335 | o_reset_to_empty_unquoted(&ctx->word); | 4601 | o_reset_to_empty_unquoted(&ctx->word); |
| 4336 | |||
| 4337 | debug_printf_parse("done_word return 0\n"); | 4602 | debug_printf_parse("done_word return 0\n"); |
| 4338 | return 0; | 4603 | return 0; |
| 4339 | } | 4604 | } |
| @@ -4368,7 +4633,7 @@ static int parse_redir_right_fd(o_string *as_string, struct in_str *input) | |||
| 4368 | } | 4633 | } |
| 4369 | d = 0; | 4634 | d = 0; |
| 4370 | ok = 0; | 4635 | ok = 0; |
| 4371 | while (ch != EOF && isdigit(ch)) { | 4636 | while (/*ch != EOF &&*/ isdigit(ch)) { |
| 4372 | d = d*10 + (ch-'0'); | 4637 | d = d*10 + (ch-'0'); |
| 4373 | ok = 1; | 4638 | ok = 1; |
| 4374 | ch = i_getch(input); | 4639 | ch = i_getch(input); |
| @@ -4396,31 +4661,31 @@ static int parse_redirect(struct parse_context *ctx, | |||
| 4396 | int dup_num; | 4661 | int dup_num; |
| 4397 | 4662 | ||
| 4398 | dup_num = REDIRFD_TO_FILE; | 4663 | dup_num = REDIRFD_TO_FILE; |
| 4399 | if (style != REDIRECT_HEREDOC) { | 4664 | if (style != REDIRECT_HEREDOC && style != REDIRECT_HERESTRING) { |
| 4400 | /* Check for a '>&1' type redirect */ | 4665 | /* Check for a '>&1' type redirect */ |
| 4401 | dup_num = parse_redir_right_fd(&ctx->as_string, input); | 4666 | dup_num = parse_redir_right_fd(&ctx->as_string, input); |
| 4402 | if (dup_num == REDIRFD_SYNTAX_ERR) | 4667 | if (dup_num == REDIRFD_SYNTAX_ERR) |
| 4403 | return 1; | 4668 | return 1; |
| 4404 | } else { | 4669 | if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { |
| 4405 | int ch = i_peek_and_eat_bkslash_nl(input); | 4670 | int ch = i_peek_and_eat_bkslash_nl(input); |
| 4406 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ | 4671 | if (ch == '|') { |
| 4407 | if (dup_num) { /* <<-... */ | 4672 | /* >|FILE redirect ("clobbering" >). |
| 4408 | ch = i_getch(input); | 4673 | * Since we do not support "set -o noclobber" yet, |
| 4409 | nommu_addchr(&ctx->as_string, ch); | 4674 | * >| and > are the same for now. Just eat |. |
| 4410 | ch = i_peek(input); | 4675 | */ |
| 4676 | ch = i_getch(input); | ||
| 4677 | nommu_addchr(&ctx->as_string, ch); | ||
| 4678 | } | ||
| 4411 | } | 4679 | } |
| 4412 | } | 4680 | } else if (style == REDIRECT_HEREDOC) { |
| 4413 | |||
| 4414 | if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { | ||
| 4415 | int ch = i_peek_and_eat_bkslash_nl(input); | 4681 | int ch = i_peek_and_eat_bkslash_nl(input); |
| 4416 | if (ch == '|') { | 4682 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ |
| 4417 | /* >|FILE redirect ("clobbering" >). | 4683 | if (dup_num) { /* "<<-HEREDOC"? */ |
| 4418 | * Since we do not support "set -o noclobber" yet, | ||
| 4419 | * >| and > are the same for now. Just eat |. | ||
| 4420 | */ | ||
| 4421 | ch = i_getch(input); | 4684 | ch = i_getch(input); |
| 4422 | nommu_addchr(&ctx->as_string, ch); | 4685 | nommu_addchr(&ctx->as_string, ch); |
| 4423 | } | 4686 | } |
| 4687 | } else { /* REDIRECT_HERESTRING */ | ||
| 4688 | dup_num = 0; /* make sure no bits like HEREDOC_QUOTED are set */ | ||
| 4424 | } | 4689 | } |
| 4425 | 4690 | ||
| 4426 | /* Create a new redir_struct and append it to the linked list */ | 4691 | /* Create a new redir_struct and append it to the linked list */ |
| @@ -4434,11 +4699,14 @@ static int parse_redirect(struct parse_context *ctx, | |||
| 4434 | redir->rd_type = style; | 4699 | redir->rd_type = style; |
| 4435 | redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; | 4700 | redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; |
| 4436 | 4701 | ||
| 4437 | debug_printf_parse("redirect type %d %s\n", redir->rd_fd, | 4702 | debug_printf_parse("redirect type %d %.3s\n", redir->rd_fd, |
| 4438 | redir_table[style].descrip); | 4703 | redir_table[style].descrip3); |
| 4439 | 4704 | ||
| 4440 | redir->rd_dup = dup_num; | 4705 | redir->rd_dup = dup_num; |
| 4441 | if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) { | 4706 | if (style != REDIRECT_HEREDOC |
| 4707 | && style != REDIRECT_HERESTRING | ||
| 4708 | && dup_num != REDIRFD_TO_FILE | ||
| 4709 | ) { | ||
| 4442 | /* Erik had a check here that the file descriptor in question | 4710 | /* Erik had a check here that the file descriptor in question |
| 4443 | * is legit; I postpone that to "run time" | 4711 | * is legit; I postpone that to "run time" |
| 4444 | * A "-" representation of "close me" shows up as a -3 here */ | 4712 | * A "-" representation of "close me" shows up as a -3 here */ |
| @@ -4447,7 +4715,7 @@ static int parse_redirect(struct parse_context *ctx, | |||
| 4447 | } else { | 4715 | } else { |
| 4448 | #if 0 /* Instead we emit error message at run time */ | 4716 | #if 0 /* Instead we emit error message at run time */ |
| 4449 | if (ctx->pending_redirect) { | 4717 | if (ctx->pending_redirect) { |
| 4450 | /* For example, "cmd > <file" */ | 4718 | /* For example, "CMD > <FILE" */ |
| 4451 | syntax_error("invalid redirect"); | 4719 | syntax_error("invalid redirect"); |
| 4452 | } | 4720 | } |
| 4453 | #endif | 4721 | #endif |
| @@ -4462,10 +4730,10 @@ static int parse_redirect(struct parse_context *ctx, | |||
| 4462 | * supposed to tell which file descriptor to redirect. This routine | 4730 | * supposed to tell which file descriptor to redirect. This routine |
| 4463 | * looks for such preceding numbers. In an ideal world this routine | 4731 | * looks for such preceding numbers. In an ideal world this routine |
| 4464 | * needs to handle all the following classes of redirects... | 4732 | * needs to handle all the following classes of redirects... |
| 4465 | * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo | 4733 | * echo 2>FILE # redirects fd 2 to FILE, nothing passed to echo |
| 4466 | * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo | 4734 | * echo 49>FILE # redirects fd 49 to FILE, nothing passed to echo |
| 4467 | * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo | 4735 | * echo -2>FILE # redirects fd 1 to FILE, "-2" passed to echo |
| 4468 | * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo | 4736 | * echo 49x>FILE # redirects fd 1 to FILE, "49x" passed to echo |
| 4469 | * | 4737 | * |
| 4470 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | 4738 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html |
| 4471 | * "2.7 Redirection | 4739 | * "2.7 Redirection |
| @@ -4502,7 +4770,7 @@ static char *fetch_till_str(o_string *as_string, | |||
| 4502 | { | 4770 | { |
| 4503 | o_string heredoc = NULL_O_STRING; | 4771 | o_string heredoc = NULL_O_STRING; |
| 4504 | unsigned past_EOL; | 4772 | unsigned past_EOL; |
| 4505 | int prev = 0; /* not \ */ | 4773 | int prev = 0; /* not '\' */ |
| 4506 | int ch; | 4774 | int ch; |
| 4507 | 4775 | ||
| 4508 | /* Starting with "" is necessary for this case: | 4776 | /* Starting with "" is necessary for this case: |
| @@ -4538,7 +4806,15 @@ static char *fetch_till_str(o_string *as_string, | |||
| 4538 | past_EOL = heredoc.length; | 4806 | past_EOL = heredoc.length; |
| 4539 | /* Get 1st char of next line, possibly skipping leading tabs */ | 4807 | /* Get 1st char of next line, possibly skipping leading tabs */ |
| 4540 | do { | 4808 | do { |
| 4541 | ch = i_getch(input); | 4809 | if (heredoc_flags & HEREDOC_QUOTED) |
| 4810 | ch = i_getch(input); | ||
| 4811 | else { /* see heredoc_bkslash_newline3a.tests: | ||
| 4812 | * cat <<-EOF | ||
| 4813 | * <tab>\ | ||
| 4814 | * <tab>EOF | ||
| 4815 | */ | ||
| 4816 | ch = i_getch_and_eat_bkslash_nl(input); | ||
| 4817 | } | ||
| 4542 | if (ch != EOF) | 4818 | if (ch != EOF) |
| 4543 | nommu_addchr(as_string, ch); | 4819 | nommu_addchr(as_string, ch); |
| 4544 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); | 4820 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); |
| @@ -4564,7 +4840,7 @@ static char *fetch_till_str(o_string *as_string, | |||
| 4564 | prev = 0; /* not '\' */ | 4840 | prev = 0; /* not '\' */ |
| 4565 | continue; | 4841 | continue; |
| 4566 | } | 4842 | } |
| 4567 | } | 4843 | } /* if (\n or EOF) */ |
| 4568 | if (ch == EOF) { | 4844 | if (ch == EOF) { |
| 4569 | o_free(&heredoc); | 4845 | o_free(&heredoc); |
| 4570 | return NULL; /* error */ | 4846 | return NULL; /* error */ |
| @@ -4607,6 +4883,11 @@ static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, | |||
| 4607 | 4883 | ||
| 4608 | redir->rd_type = REDIRECT_HEREDOC2; | 4884 | redir->rd_type = REDIRECT_HEREDOC2; |
| 4609 | /* redir->rd_dup is (ab)used to indicate <<- */ | 4885 | /* redir->rd_dup is (ab)used to indicate <<- */ |
| 4886 | if (!redir->rd_filename) { | ||
| 4887 | /* examples: "echo <<", "echo <<<", "echo <<>" */ | ||
| 4888 | syntax_error("missing here document terminator"); | ||
| 4889 | return -1; | ||
| 4890 | } | ||
| 4610 | p = fetch_till_str(as_string, input, | 4891 | p = fetch_till_str(as_string, input, |
| 4611 | redir->rd_filename, redir->rd_dup); | 4892 | redir->rd_filename, redir->rd_dup); |
| 4612 | if (!p) { | 4893 | if (!p) { |
| @@ -4643,8 +4924,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 4643 | struct in_str *input, | 4924 | struct in_str *input, |
| 4644 | int end_trigger); | 4925 | int end_trigger); |
| 4645 | 4926 | ||
| 4646 | /* Returns number of heredocs not yet consumed, | 4927 | /* Returns number of heredocs not yet consumed, or -1 on error. |
| 4647 | * or -1 on error. | ||
| 4648 | */ | 4928 | */ |
| 4649 | static int parse_group(struct parse_context *ctx, | 4929 | static int parse_group(struct parse_context *ctx, |
| 4650 | struct in_str *input, int ch) | 4930 | struct in_str *input, int ch) |
| @@ -4664,25 +4944,36 @@ static int parse_group(struct parse_context *ctx, | |||
| 4664 | 4944 | ||
| 4665 | debug_printf_parse("parse_group entered\n"); | 4945 | debug_printf_parse("parse_group entered\n"); |
| 4666 | #if ENABLE_HUSH_FUNCTIONS | 4946 | #if ENABLE_HUSH_FUNCTIONS |
| 4667 | if (ch == '(' && !ctx->word.has_quoted_part) { | 4947 | if ((ch == '(' |
| 4948 | # if ENABLE_HUSH_FUNCTION_KEYWORD | ||
| 4949 | || command->cmd_type == CMD_FUNCTION_KWORD /* "function WORD" */ | ||
| 4950 | # endif | ||
| 4951 | ) | ||
| 4952 | && !ctx->word.has_quoted_part | ||
| 4953 | ) { | ||
| 4668 | if (ctx->word.length) | 4954 | if (ctx->word.length) |
| 4669 | if (done_word(ctx)) | 4955 | if (done_word(ctx)) |
| 4670 | return -1; | 4956 | return -1; |
| 4671 | if (!command->argv) | 4957 | if (!command->argv) |
| 4672 | goto skip; /* (... */ | 4958 | goto skip; /* (... */ |
| 4673 | if (command->argv[1]) { /* word word ... (... */ | 4959 | if (command->argv[1]) { /* word word ... (... */ |
| 4674 | syntax_error_unexpected_ch('('); | 4960 | if (ch == '(') |
| 4961 | syntax_error_unexpected_ch('('); | ||
| 4962 | else | ||
| 4963 | syntax_error("expected funcdef"); | ||
| 4675 | return -1; | 4964 | return -1; |
| 4676 | } | 4965 | } |
| 4677 | /* it is "word(..." or "word (..." */ | 4966 | if (ch == '(') { |
| 4678 | do | 4967 | /* it is "word(..." or "word (..." */ |
| 4679 | ch = i_getch(input); | 4968 | do |
| 4680 | while (ch == ' ' || ch == '\t'); | 4969 | ch = i_getch(input); |
| 4681 | if (ch != ')') { | 4970 | while (ch == ' ' || ch == '\t'); |
| 4682 | syntax_error_unexpected_ch(ch); | 4971 | if (ch != ')') { |
| 4683 | return -1; | 4972 | syntax_error_unexpected_ch(ch); |
| 4973 | return -1; | ||
| 4974 | } | ||
| 4975 | nommu_addchr(&ctx->as_string, ch); | ||
| 4684 | } | 4976 | } |
| 4685 | nommu_addchr(&ctx->as_string, ch); | ||
| 4686 | do | 4977 | do |
| 4687 | ch = i_getch(input); | 4978 | ch = i_getch(input); |
| 4688 | while (ch == ' ' || ch == '\t' || ch == '\n'); | 4979 | while (ch == ' ' || ch == '\t' || ch == '\n'); |
| @@ -4703,13 +4994,10 @@ static int parse_group(struct parse_context *ctx, | |||
| 4703 | 4994 | ||
| 4704 | #if 0 /* Prevented by caller */ | 4995 | #if 0 /* Prevented by caller */ |
| 4705 | if (command->argv /* word [word]{... */ | 4996 | if (command->argv /* word [word]{... */ |
| 4706 | || ctx->word.length /* word{... */ | 4997 | || !IS_NULL_WORD(ctx->word) /* word{... or ""{... */ |
| 4707 | || ctx->word.has_quoted_part /* ""{... */ | ||
| 4708 | ) { | 4998 | ) { |
| 4709 | syntax_error(NULL); | ||
| 4710 | debug_printf_parse("parse_group return -1: " | 4999 | debug_printf_parse("parse_group return -1: " |
| 4711 | "syntax error, groups and arglists don't mix\n"); | 5000 | "syntax error, groups and arglists don't mix\n"); |
| 4712 | return -1; | ||
| 4713 | } | 5001 | } |
| 4714 | #endif | 5002 | #endif |
| 4715 | 5003 | ||
| @@ -4987,16 +5275,16 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
| 4987 | # if BB_MMU | 5275 | # if BB_MMU |
| 4988 | #define parse_dollar_squote(as_string, dest, input) \ | 5276 | #define parse_dollar_squote(as_string, dest, input) \ |
| 4989 | parse_dollar_squote(dest, input) | 5277 | parse_dollar_squote(dest, input) |
| 4990 | #define as_string NULL | ||
| 4991 | # endif | 5278 | # endif |
| 4992 | static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input) | 5279 | static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input) |
| 4993 | { | 5280 | { |
| 4994 | int start; | 5281 | int start; |
| 4995 | int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ | 5282 | int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ |
| 4996 | debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch); | 5283 | |
| 4997 | if (ch != '\'') | 5284 | if (ch != '\'') |
| 4998 | return 0; | 5285 | return 0; |
| 4999 | 5286 | ||
| 5287 | debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch); | ||
| 5000 | dest->has_quoted_part = 1; | 5288 | dest->has_quoted_part = 1; |
| 5001 | start = dest->length; | 5289 | start = dest->length; |
| 5002 | 5290 | ||
| @@ -5071,7 +5359,6 @@ static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_st | |||
| 5071 | } | 5359 | } |
| 5072 | 5360 | ||
| 5073 | return 1; | 5361 | return 1; |
| 5074 | # undef as_string | ||
| 5075 | } | 5362 | } |
| 5076 | #else | 5363 | #else |
| 5077 | # define parse_dollar_squote(as_string, dest, input) 0 | 5364 | # define parse_dollar_squote(as_string, dest, input) 0 |
| @@ -5349,7 +5636,6 @@ static int parse_dollar(o_string *as_string, | |||
| 5349 | #if BB_MMU | 5636 | #if BB_MMU |
| 5350 | #define encode_string(as_string, dest, input, dquote_end) \ | 5637 | #define encode_string(as_string, dest, input, dquote_end) \ |
| 5351 | encode_string(dest, input, dquote_end) | 5638 | encode_string(dest, input, dquote_end) |
| 5352 | #define as_string NULL | ||
| 5353 | #endif | 5639 | #endif |
| 5354 | static int encode_string(o_string *as_string, | 5640 | static int encode_string(o_string *as_string, |
| 5355 | o_string *dest, | 5641 | o_string *dest, |
| @@ -5376,8 +5662,8 @@ static int encode_string(o_string *as_string, | |||
| 5376 | if (ch != '\n') { | 5662 | if (ch != '\n') { |
| 5377 | next = i_peek(input); | 5663 | next = i_peek(input); |
| 5378 | } | 5664 | } |
| 5379 | debug_printf_parse("\" ch=%c (%d) escape=%d\n", | 5665 | debug_printf_parse("\" ch:%c (%d) globprotect:%d\n", |
| 5380 | ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 5666 | ch, ch, !!(dest->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); |
| 5381 | if (ch == '\\') { | 5667 | if (ch == '\\') { |
| 5382 | if (next == EOF) { | 5668 | if (next == EOF) { |
| 5383 | /* Testcase: in interactive shell a file with | 5669 | /* Testcase: in interactive shell a file with |
| @@ -5405,8 +5691,6 @@ static int encode_string(o_string *as_string, | |||
| 5405 | goto again; | 5691 | goto again; |
| 5406 | } | 5692 | } |
| 5407 | if (ch == '$') { | 5693 | if (ch == '$') { |
| 5408 | //if (parse_dollar_squote(as_string, dest, input)) | ||
| 5409 | // goto again; | ||
| 5410 | if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) { | 5694 | if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) { |
| 5411 | debug_printf_parse("encode_string return 0: " | 5695 | debug_printf_parse("encode_string return 0: " |
| 5412 | "parse_dollar returned 0 (error)\n"); | 5696 | "parse_dollar returned 0 (error)\n"); |
| @@ -5433,9 +5717,113 @@ static int encode_string(o_string *as_string, | |||
| 5433 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 5717 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
| 5434 | } | 5718 | } |
| 5435 | goto again; | 5719 | goto again; |
| 5436 | #undef as_string | ||
| 5437 | } | 5720 | } |
| 5438 | 5721 | ||
| 5722 | #if ENABLE_HUSH_ALIAS | ||
| 5723 | static char* end_of_alias_name(const char *name) | ||
| 5724 | { | ||
| 5725 | while (*name) { | ||
| 5726 | if (!isalnum(*name) | ||
| 5727 | // Uncommented chars are allowed in alias names. | ||
| 5728 | // Commented out with // are disallowed in bash: space, "$&'();<=>\`| | ||
| 5729 | // Commented out with //bb are allowed in bash, but disallowed in hush: !#*-/?[]{}~ | ||
| 5730 | // (do you really want alias named '?' to be allowed?) | ||
| 5731 | // && *name != ' ' // 20 | ||
| 5732 | //bb && *name != '!' // 21 | ||
| 5733 | // && *name != '"' // 22 | ||
| 5734 | //bb && *name != '#' // 23 | ||
| 5735 | // && *name != '$' // 24 | ||
| 5736 | && *name != '%' // 25 | ||
| 5737 | // && *name != '&' // 26 | ||
| 5738 | // && *name != '\'' // 27 | ||
| 5739 | // && *name != '(' // 28 | ||
| 5740 | // && *name != ')' // 29 | ||
| 5741 | //bb && *name != '*' // 2a | ||
| 5742 | && *name != '+' // 2b | ||
| 5743 | && *name != ',' // 2c | ||
| 5744 | //bb && *name != '-' // 2d bash _can_ set it: "alias -- -=STR" (and it lists it as "alias -- -='STR'" in "alias" output!) | ||
| 5745 | && *name != '.' // 2e seen Fedora defining alias "l." | ||
| 5746 | //bb && *name != '/' // 2f | ||
| 5747 | && *name != ':' // 3a | ||
| 5748 | // && *name != ';' // 3b | ||
| 5749 | // && *name != '<' // 3c | ||
| 5750 | // && *name != '=' // 3d | ||
| 5751 | // && *name != '>' // 3e | ||
| 5752 | //bb && *name != '?' // 3f | ||
| 5753 | && *name != '@' // 40 | ||
| 5754 | //bb && *name != '[' // 5b | ||
| 5755 | // && *name != '\\' // 5c | ||
| 5756 | //bb && *name != ']' // 5d | ||
| 5757 | && *name != '^' // 5e | ||
| 5758 | && *name != '_' // 5f | ||
| 5759 | // && *name != '`' // 60 | ||
| 5760 | //bb && *name != '{' // 7b | ||
| 5761 | // && *name != '|' // 7c | ||
| 5762 | //bb && *name != '}' // 7d | ||
| 5763 | //bb && *name != '~' // 7e | ||
| 5764 | ) { | ||
| 5765 | break; /* disallowed char, stop */ | ||
| 5766 | } | ||
| 5767 | name++; | ||
| 5768 | } | ||
| 5769 | return (char*)name; | ||
| 5770 | } | ||
| 5771 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | ||
| 5772 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | ||
| 5773 | |||
| 5774 | static struct alias **find_alias_slot(const char *name, const char *eq) | ||
| 5775 | { | ||
| 5776 | unsigned len; | ||
| 5777 | struct alias *alias; | ||
| 5778 | struct alias **aliaspp; | ||
| 5779 | |||
| 5780 | len = eq - name; | ||
| 5781 | aliaspp = &G.top_alias; | ||
| 5782 | while ((alias = *aliaspp) != NULL) { | ||
| 5783 | //bb_error_msg("alias->str'%s' name'%.*s'", alias->str, len, name); | ||
| 5784 | if (strncmp(name, alias->str, len) == 0 | ||
| 5785 | && alias->str[len] == '=' | ||
| 5786 | ) { | ||
| 5787 | //bb_error_msg("match!"); | ||
| 5788 | break; | ||
| 5789 | } | ||
| 5790 | aliaspp = &alias->next; | ||
| 5791 | } | ||
| 5792 | return aliaspp; | ||
| 5793 | } | ||
| 5794 | |||
| 5795 | static ALWAYS_INLINE const struct alias *find_alias(const char *name) | ||
| 5796 | { | ||
| 5797 | //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); | ||
| 5798 | return *find_alias_slot(name, strchr(name, '\0')); | ||
| 5799 | } | ||
| 5800 | |||
| 5801 | static const struct alias *word_matches_alias(struct parse_context *ctx) | ||
| 5802 | { | ||
| 5803 | if (ctx->ctx_res_w != RES_CASE_BODY | ||
| 5804 | /* && !ctx.command->argv - caller checked this */ | ||
| 5805 | && !ctx->word.has_quoted_part | ||
| 5806 | && ctx->word.data[0] != '\0' /* optimization */ | ||
| 5807 | ) { | ||
| 5808 | const char *word = ctx->word.data; | ||
| 5809 | const char *end = end_of_alias_name(word); | ||
| 5810 | if (*end == '\0') { | ||
| 5811 | struct alias *alias; | ||
| 5812 | |||
| 5813 | //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); | ||
| 5814 | alias = *find_alias_slot(word, end); | ||
| 5815 | if (alias && !alias->dont_recurse) { | ||
| 5816 | alias->dont_recurse = 1; | ||
| 5817 | o_reset_to_empty_unquoted(&ctx->word); | ||
| 5818 | return alias; | ||
| 5819 | } | ||
| 5820 | } | ||
| 5821 | } | ||
| 5822 | return NULL; | ||
| 5823 | } | ||
| 5824 | |||
| 5825 | #endif /* ENABLE_HUSH_ALIAS */ | ||
| 5826 | |||
| 5439 | /* | 5827 | /* |
| 5440 | * Scan input until EOF or end_trigger char. | 5828 | * Scan input until EOF or end_trigger char. |
| 5441 | * Return a list of pipes to execute, or NULL on EOF | 5829 | * Return a list of pipes to execute, or NULL on EOF |
| @@ -5449,6 +5837,8 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5449 | struct in_str *input, | 5837 | struct in_str *input, |
| 5450 | int end_trigger) | 5838 | int end_trigger) |
| 5451 | { | 5839 | { |
| 5840 | IF_HUSH_ALIAS(const struct alias *alias;) | ||
| 5841 | struct pipe *pi; | ||
| 5452 | struct parse_context ctx; | 5842 | struct parse_context ctx; |
| 5453 | int heredoc_cnt; | 5843 | int heredoc_cnt; |
| 5454 | 5844 | ||
| @@ -5459,13 +5849,9 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5459 | end_trigger ? end_trigger : 'X'); | 5849 | end_trigger ? end_trigger : 'X'); |
| 5460 | debug_enter(); | 5850 | debug_enter(); |
| 5461 | 5851 | ||
| 5852 | enable_all_aliases(); | ||
| 5462 | initialize_context(&ctx); | 5853 | initialize_context(&ctx); |
| 5463 | 5854 | ||
| 5464 | /* If very first arg is "" or '', ctx.word.data may end up NULL. | ||
| 5465 | * Preventing this: | ||
| 5466 | */ | ||
| 5467 | ctx.word.data = xzalloc(1); /* start as "", not as NULL */ | ||
| 5468 | |||
| 5469 | /* We used to separate words on $IFS here. This was wrong. | 5855 | /* We used to separate words on $IFS here. This was wrong. |
| 5470 | * $IFS is used only for word splitting when $var is expanded, | 5856 | * $IFS is used only for word splitting when $var is expanded, |
| 5471 | * here we should use blank chars as separators, not $IFS | 5857 | * here we should use blank chars as separators, not $IFS |
| @@ -5473,74 +5859,29 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5473 | 5859 | ||
| 5474 | heredoc_cnt = 0; | 5860 | heredoc_cnt = 0; |
| 5475 | while (1) { | 5861 | while (1) { |
| 5476 | const char *is_blank; | ||
| 5477 | const char *is_special; | ||
| 5478 | int ch; | 5862 | int ch; |
| 5479 | int next; | 5863 | int next; |
| 5480 | int redir_fd; | 5864 | int redir_fd; |
| 5481 | redir_type redir_style; | 5865 | redir_type redir_style; |
| 5482 | 5866 | ||
| 5483 | ch = i_getch(input); | 5867 | ch = i_getch(input); |
| 5484 | debug_printf_parse(": ch=%c (%d) escape=%d\n", | 5868 | debug_printf_parse(": ch:%c (%d) globprotect:%d\n", |
| 5485 | ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 5869 | ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); |
| 5486 | if (ch == EOF) { | 5870 | # if ENABLE_HUSH_NEED_FOR_SPEED |
| 5487 | struct pipe *pi; | 5871 | if ((ch >= '.' && ch <= ':') /* ASCII "./0123456789:" */ |
| 5488 | 5872 | /* can't include preceding "+,-" above: "-" needs glob-escaping (example?) */ | |
| 5489 | if (heredoc_cnt) { | 5873 | || (ch >= '@' && ch <= 'Z') /* ASCII "@A..Z" */ |
| 5490 | syntax_error_unterm_str("here document"); | 5874 | || (ch >= 'a' && ch <= 'z') /* ASCII "a..Z" */ |
| 5491 | goto parse_error_exitcode1; | 5875 | /* can't include preceding "^_`" above because of "`". Pity. "_" is relatively common */ |
| 5492 | } | 5876 | ) { |
| 5493 | if (end_trigger == ')') { | 5877 | /* These are never special and just go into the current word */ |
| 5494 | syntax_error_unterm_ch('('); | 5878 | /* ~5% faster parsing of typical shell scripts */ |
| 5495 | goto parse_error_exitcode1; | 5879 | INLINED_o_addchr(&ctx.word, ch); |
| 5496 | } | 5880 | continue; |
| 5497 | if (end_trigger == '}') { | ||
| 5498 | syntax_error_unterm_ch('{'); | ||
| 5499 | goto parse_error_exitcode1; | ||
| 5500 | } | ||
| 5501 | |||
| 5502 | if (done_word(&ctx)) { | ||
| 5503 | goto parse_error_exitcode1; | ||
| 5504 | } | ||
| 5505 | o_free_and_set_NULL(&ctx.word); | ||
| 5506 | done_pipe(&ctx, PIPE_SEQ); | ||
| 5507 | |||
| 5508 | /* Do we sit inside of any if's, loops or case's? */ | ||
| 5509 | if (HAS_KEYWORDS | ||
| 5510 | IF_HAS_KEYWORDS(&& (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0)) | ||
| 5511 | ) { | ||
| 5512 | syntax_error_unterm_str("compound statement"); | ||
| 5513 | goto parse_error_exitcode1; | ||
| 5514 | } | ||
| 5515 | |||
| 5516 | pi = ctx.list_head; | ||
| 5517 | /* If we got nothing... */ | ||
| 5518 | /* (this makes bare "&" cmd a no-op. | ||
| 5519 | * bash says: "syntax error near unexpected token '&'") */ | ||
| 5520 | if (pi->num_cmds == 0 | ||
| 5521 | IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE) | ||
| 5522 | ) { | ||
| 5523 | free_pipe_list(pi); | ||
| 5524 | pi = NULL; | ||
| 5525 | } | ||
| 5526 | #if !BB_MMU | ||
| 5527 | debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data); | ||
| 5528 | if (pstring) | ||
| 5529 | *pstring = ctx.as_string.data; | ||
| 5530 | else | ||
| 5531 | o_free(&ctx.as_string); | ||
| 5532 | #endif | ||
| 5533 | // heredoc_cnt must be 0 here anyway | ||
| 5534 | //if (heredoc_cnt_ptr) | ||
| 5535 | // *heredoc_cnt_ptr = heredoc_cnt; | ||
| 5536 | debug_leave(); | ||
| 5537 | debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); | ||
| 5538 | debug_printf_parse("parse_stream return %p: EOF\n", pi); | ||
| 5539 | return pi; | ||
| 5540 | } | 5881 | } |
| 5541 | 5882 | #endif | |
| 5542 | /* Handle "'" and "\" first, as they won't play nice with | 5883 | /* Handle "'" and "\" first, as they won't play nice with |
| 5543 | * i_peek_and_eat_bkslash_nl() anyway: | 5884 | * i_peek_and_eat_bkslash_nl(): |
| 5544 | * echo z\\ | 5885 | * echo z\\ |
| 5545 | * and | 5886 | * and |
| 5546 | * echo '\ | 5887 | * echo '\ |
| @@ -5561,9 +5902,9 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5561 | if (ch == EOF) { | 5902 | if (ch == EOF) { |
| 5562 | /* Testcase: eval 'echo Ok\' */ | 5903 | /* Testcase: eval 'echo Ok\' */ |
| 5563 | /* bash-4.3.43 was removing backslash, | 5904 | /* bash-4.3.43 was removing backslash, |
| 5564 | * but 4.4.19 retains it, most other shells too | 5905 | * but 4.4.19 retains it, most other shells retain too |
| 5565 | */ | 5906 | */ |
| 5566 | continue; /* get next char */ | 5907 | break; |
| 5567 | } | 5908 | } |
| 5568 | /* Example: echo Hello \2>file | 5909 | /* Example: echo Hello \2>file |
| 5569 | * we need to know that word 2 is quoted | 5910 | * we need to know that word 2 is quoted |
| @@ -5573,14 +5914,25 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5573 | o_addchr(&ctx.word, ch); | 5914 | o_addchr(&ctx.word, ch); |
| 5574 | continue; /* get next char */ | 5915 | continue; /* get next char */ |
| 5575 | } | 5916 | } |
| 5917 | if (ch == EOF) | ||
| 5918 | break; | ||
| 5576 | nommu_addchr(&ctx.as_string, ch); | 5919 | nommu_addchr(&ctx.as_string, ch); |
| 5577 | if (ch == '\'') { | 5920 | if (ch == '\'') { |
| 5578 | ctx.word.has_quoted_part = 1; | 5921 | ctx.word.has_quoted_part = 1; |
| 5579 | next = i_getch(input); | 5922 | ch = i_getch(input); |
| 5580 | if (next == '\'' && !ctx.pending_redirect) | 5923 | if (ch == '\'' && !ctx.pending_redirect/*why?*/) { |
| 5581 | goto insert_empty_quoted_str_marker; | 5924 | insert_empty_quoted_str_marker: |
| 5582 | 5925 | nommu_addchr(&ctx.as_string, ch); | |
| 5583 | ch = next; | 5926 | //Just inserting nothing doesn't work: consider |
| 5927 | // CMD $EMPTYVAR | ||
| 5928 | // CMD '' | ||
| 5929 | //At execution time both will expand argv[1] to empty string | ||
| 5930 | //and thus the argument will "vanish". | ||
| 5931 | //But for second CMD, it should not vanish! | ||
| 5932 | o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL); | ||
| 5933 | o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL); | ||
| 5934 | continue; /* get next char */ | ||
| 5935 | } | ||
| 5584 | while (1) { | 5936 | while (1) { |
| 5585 | if (ch == EOF) { | 5937 | if (ch == EOF) { |
| 5586 | syntax_error_unterm_ch('\''); | 5938 | syntax_error_unterm_ch('\''); |
| @@ -5600,135 +5952,173 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5600 | continue; /* get next char */ | 5952 | continue; /* get next char */ |
| 5601 | } | 5953 | } |
| 5602 | 5954 | ||
| 5603 | next = '\0'; | 5955 | if (ch == ' ' || ch == '\t') { |
| 5604 | if (ch != '\n') | 5956 | #if ENABLE_HUSH_ALIAS |
| 5605 | next = i_peek_and_eat_bkslash_nl(input); | 5957 | /* Check for alias expansion (only for first word of command) */ |
| 5606 | 5958 | if (G_interactive_fd && !ctx.command->argv) { | |
| 5607 | is_special = "{}<>&|();#" /* special outside of "str" */ | 5959 | alias = word_matches_alias(&ctx); |
| 5608 | "$\"" IF_HUSH_TICK("`") /* always special */ | 5960 | if (alias) { |
| 5609 | SPECIAL_VAR_SYMBOL_STR; | 5961 | add_to_albuf_and_get_next_char: |
| 5610 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | 5962 | i_prepend_to_alias_buffer(input, strchr(alias->str, '=') + 1, ch); |
| 5611 | if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { | 5963 | continue; /* get next char (which will be from albuf) */ |
| 5612 | /* In [[ ]], {}<>&|() are not special */ | 5964 | } |
| 5613 | is_special += 8; | ||
| 5614 | } else | ||
| 5615 | #endif | ||
| 5616 | /* Are { and } special here? */ | ||
| 5617 | if (ctx.command->argv /* word [word]{... - non-special */ | ||
| 5618 | || ctx.word.length /* word{... - non-special */ | ||
| 5619 | || ctx.word.has_quoted_part /* ""{... - non-special */ | ||
| 5620 | || (next != ';' /* }; - special */ | ||
| 5621 | && next != ')' /* }) - special */ | ||
| 5622 | && next != '(' /* {( - special */ | ||
| 5623 | && next != '&' /* }& and }&& ... - special */ | ||
| 5624 | && next != '|' /* }|| ... - special */ | ||
| 5625 | && !strchr(defifs, next) /* {word - non-special */ | ||
| 5626 | ) | ||
| 5627 | ) { | ||
| 5628 | /* They are not special, skip "{}" */ | ||
| 5629 | is_special += 2; | ||
| 5630 | } | ||
| 5631 | is_special = strchr(is_special, ch); | ||
| 5632 | is_blank = strchr(defifs, ch); | ||
| 5633 | |||
| 5634 | if (!is_special && !is_blank) { /* ordinary char */ | ||
| 5635 | ordinary_char: | ||
| 5636 | o_addQchr(&ctx.word, ch); | ||
| 5637 | if ((ctx.is_assignment == MAYBE_ASSIGNMENT | ||
| 5638 | || ctx.is_assignment == WORD_IS_KEYWORD) | ||
| 5639 | && ch == '=' | ||
| 5640 | && endofname(ctx.word.data)[0] == '=' | ||
| 5641 | ) { | ||
| 5642 | ctx.is_assignment = DEFINITELY_ASSIGNMENT; | ||
| 5643 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | ||
| 5644 | } | 5965 | } |
| 5645 | continue; | 5966 | #endif |
| 5646 | } | ||
| 5647 | 5967 | ||
| 5648 | if (is_blank) { | ||
| 5649 | #if ENABLE_HUSH_LINENO_VAR | 5968 | #if ENABLE_HUSH_LINENO_VAR |
| 5650 | /* Case: | 5969 | /* "while ...; do<whitespace><newline> |
| 5651 | * "while ...; do<whitespace><newline> | 5970 | * CMD" |
| 5652 | * cmd ..." | 5971 | * would think that CMD starts in <whitespace> - |
| 5653 | * would think that "cmd" starts in <whitespace> - | ||
| 5654 | * i.e., at the previous line. | 5972 | * i.e., at the previous line. |
| 5655 | * We need to skip all whitespace before newlines. | 5973 | * Need to skip whitespace up to next newline (and eat it) |
| 5974 | * or not-whitespace (and do not eat it). | ||
| 5656 | */ | 5975 | */ |
| 5657 | while (ch != '\n') { | 5976 | for (;;) { |
| 5658 | next = i_peek(input); | 5977 | next = i_peek(input); |
| 5659 | if (next != ' ' && next != '\t' && next != '\n') | 5978 | if (next != ' ' && next != '\t' && next != '\n') |
| 5660 | break; /* next char is not ws */ | 5979 | break; /* next char is not ws */ |
| 5661 | ch = i_getch(input); | 5980 | ch = i_getch(input); |
| 5981 | if (ch == '\n') | ||
| 5982 | goto ch_is_newline; | ||
| 5662 | } | 5983 | } |
| 5663 | /* ch == last eaten whitespace char */ | ||
| 5664 | #endif | 5984 | #endif |
| 5665 | if (done_word(&ctx)) { | 5985 | if (done_word(&ctx)) |
| 5666 | goto parse_error_exitcode1; | 5986 | goto parse_error_exitcode1; |
| 5987 | #if ENABLE_HUSH_FUNCTION_KEYWORD | ||
| 5988 | if (ctx.command->cmd_type == CMD_FUNCTION_KWORD | ||
| 5989 | && ctx.command->argv /* "function WORD" */ | ||
| 5990 | ) | ||
| 5991 | goto parse_group; | ||
| 5992 | #endif | ||
| 5993 | continue; /* get next char */ | ||
| 5994 | } | ||
| 5995 | |||
| 5996 | if (ch == '\n') { | ||
| 5997 | IF_HUSH_LINENO_VAR(ch_is_newline:) | ||
| 5998 | #if ENABLE_HUSH_ALIAS | ||
| 5999 | /* Check for alias expansion (only for first word of command) */ | ||
| 6000 | if (G_interactive_fd && !ctx.command->argv) { | ||
| 6001 | alias = word_matches_alias(&ctx); | ||
| 6002 | if (alias) | ||
| 6003 | goto add_to_albuf_and_get_next_char; | ||
| 5667 | } | 6004 | } |
| 5668 | if (ch == '\n') { | 6005 | #endif |
| 5669 | /* Is this a case when newline is simply ignored? | 6006 | if (done_word(&ctx)) |
| 5670 | * Some examples: | 6007 | goto parse_error_exitcode1; |
| 5671 | * "cmd | <newline> cmd ..." | 6008 | /* Is this a case when newline is simply ignored? |
| 5672 | * "case ... in <newline> word) ..." | 6009 | * Some examples: |
| 6010 | * "CMD | <newline> CMD ..." | ||
| 6011 | * "case ... in <newline> PATTERN) ..." | ||
| 6012 | */ | ||
| 6013 | if (IS_NULL_CMD(ctx.command) | ||
| 6014 | && heredoc_cnt == 0 | ||
| 6015 | ) { | ||
| 6016 | /* This newline can be ignored. But... | ||
| 6017 | * Without check #1, interactive shell | ||
| 6018 | * ignores even bare <newline>, | ||
| 6019 | * and shows the continuation prompt: | ||
| 6020 | * ps1$ <enter> | ||
| 6021 | * ps2> _ <=== wrong, should be ps1 | ||
| 6022 | * Without check #2, "CMD & <newline>" | ||
| 6023 | * is similarly mistreated. | ||
| 6024 | * (BTW, this makes "CMD & CMD" | ||
| 6025 | * and "CMD && CMD" non-orthogonal. | ||
| 6026 | * Really, ask yourself, why | ||
| 6027 | * "CMD && <newline>" doesn't start | ||
| 6028 | * CMD but waits for more input? | ||
| 6029 | * The only reason is that it might be | ||
| 6030 | * a "CMD1 && <nl> CMD2 &" construct: | ||
| 6031 | * CMD1 may need to run in BG). | ||
| 5673 | */ | 6032 | */ |
| 5674 | if (IS_NULL_CMD(ctx.command) | 6033 | pi = ctx.list_head; |
| 5675 | && ctx.word.length == 0 | 6034 | if (pi->num_cmds != 0 /* check #1 */ |
| 5676 | && !ctx.word.has_quoted_part | 6035 | && pi->followup != PIPE_BG /* check #2 */ |
| 5677 | && heredoc_cnt == 0 | ||
| 5678 | ) { | 6036 | ) { |
| 5679 | /* This newline can be ignored. But... | 6037 | debug_printf_parse("newline is treated as ws\n"); |
| 5680 | * Without check #1, interactive shell | 6038 | continue; /* ignore newline */ |
| 5681 | * ignores even bare <newline>, | ||
| 5682 | * and shows the continuation prompt: | ||
| 5683 | * ps1_prompt$ <enter> | ||
| 5684 | * ps2> _ <=== wrong, should be ps1 | ||
| 5685 | * Without check #2, "cmd & <newline>" | ||
| 5686 | * is similarly mistreated. | ||
| 5687 | * (BTW, this makes "cmd & cmd" | ||
| 5688 | * and "cmd && cmd" non-orthogonal. | ||
| 5689 | * Really, ask yourself, why | ||
| 5690 | * "cmd && <newline>" doesn't start | ||
| 5691 | * cmd but waits for more input? | ||
| 5692 | * The only reason is that it might be | ||
| 5693 | * a "cmd1 && <nl> cmd2 &" construct, | ||
| 5694 | * cmd1 may need to run in BG). | ||
| 5695 | */ | ||
| 5696 | struct pipe *pi = ctx.list_head; | ||
| 5697 | if (pi->num_cmds != 0 /* check #1 */ | ||
| 5698 | && pi->followup != PIPE_BG /* check #2 */ | ||
| 5699 | ) { | ||
| 5700 | continue; | ||
| 5701 | } | ||
| 5702 | } | 6039 | } |
| 5703 | /* Treat newline as a command separator. */ | 6040 | } |
| 5704 | done_pipe(&ctx, PIPE_SEQ); | 6041 | #if ENABLE_HUSH_FUNCTION_KEYWORD |
| 5705 | debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt); | 6042 | if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) { |
| 5706 | if (heredoc_cnt) { | 6043 | if (!ctx.command->argv) { |
| 5707 | heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); | 6044 | /* Testcase: sh -c $'function\n' */ |
| 5708 | if (heredoc_cnt != 0) | 6045 | syntax_error("expected funcdef"); |
| 5709 | goto parse_error_exitcode1; | 6046 | goto parse_error_exitcode1; |
| 5710 | } | 6047 | } |
| 5711 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 6048 | /* "function WORD" */ |
| 5712 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 6049 | goto parse_group; |
| 5713 | ch = ';'; | 6050 | } |
| 5714 | /* note: if (is_blank) continue; | 6051 | #endif |
| 5715 | * will still trigger for us */ | 6052 | /* Treat newline as a command separator */ |
| 6053 | done_pipe(&ctx, PIPE_SEQ); | ||
| 6054 | debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt); | ||
| 6055 | if (heredoc_cnt) { | ||
| 6056 | heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); | ||
| 6057 | if (heredoc_cnt != 0) | ||
| 6058 | goto parse_error_exitcode1; | ||
| 6059 | } | ||
| 6060 | ctx.is_assignment = MAYBE_ASSIGNMENT; | ||
| 6061 | debug_printf_parse("newline is treated as ';', ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | ||
| 6062 | next = '\0'; | ||
| 6063 | ch = ';'; | ||
| 6064 | } else { | ||
| 6065 | const char *is_special; | ||
| 6066 | |||
| 6067 | next = i_peek_and_eat_bkslash_nl(input); | ||
| 6068 | is_special = "{}<>&|();#" /* special outside of "str" */ | ||
| 6069 | "$\"" IF_HUSH_TICK("`") /* always special */ | ||
| 6070 | SPECIAL_VAR_SYMBOL_STR; | ||
| 6071 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
| 6072 | if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { | ||
| 6073 | /* In [[ ]], {}<>&|() are not special */ | ||
| 6074 | is_special += 8; | ||
| 6075 | } else | ||
| 6076 | #endif | ||
| 6077 | /* Are { and } special here? */ | ||
| 6078 | if ((ctx.command->argv /* WORD [WORD]{... - non-special */ | ||
| 6079 | #if ENABLE_HUSH_FUNCTIONS | ||
| 6080 | && ctx.command->cmd_type != CMD_FUNCDEF /* ^^^ unless FUNC() {} */ | ||
| 6081 | #endif | ||
| 6082 | ) | ||
| 6083 | || !IS_NULL_WORD(ctx.word) /* WORD{... ""{... - non-special */ | ||
| 6084 | || (next != ';' /* }; - special */ | ||
| 6085 | && next != ')' /* }) - special */ | ||
| 6086 | && next != '(' /* {( - special */ | ||
| 6087 | && next != '&' /* }& and }&& ... - special */ | ||
| 6088 | && next != '|' /* }|| ... - special */ | ||
| 6089 | && !strchr(defifs, next) /* {WORD - non-special */ | ||
| 6090 | ) | ||
| 6091 | ) { | ||
| 6092 | /* They are not special, skip "{}" */ | ||
| 6093 | is_special += 2; | ||
| 6094 | } | ||
| 6095 | if (!strchr(is_special, ch)) { /* ordinary char? */ | ||
| 6096 | ordinary_char: | ||
| 6097 | o_addQchr(&ctx.word, ch); | ||
| 6098 | if (ctx.is_assignment == MAYBE_ASSIGNMENT | ||
| 6099 | && ch == '=' | ||
| 6100 | && !ctx.word.has_quoted_part /* a''=b a'b'c=d: not assignments */ | ||
| 6101 | && endofname(ctx.word.data)[0] == '=' | ||
| 6102 | ) { | ||
| 6103 | ctx.is_assignment = DEFINITELY_ASSIGNMENT; | ||
| 6104 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | ||
| 6105 | } | ||
| 6106 | continue; /* get next char */ | ||
| 5716 | } | 6107 | } |
| 5717 | } | 6108 | } |
| 5718 | 6109 | ||
| 5719 | /* "cmd}" or "cmd }..." without semicolon or &: | 6110 | /* "CMD}" or "CMD }..." without semicolon or &: |
| 5720 | * } is an ordinary char in this case, even inside { cmd; } | 6111 | * } is an ordinary char in this case, even inside { CMD; } |
| 5721 | * Pathological example: { ""}; } should exec "}" cmd | 6112 | * Pathological example: { ""}; } should run "}" command. |
| 5722 | */ | 6113 | */ |
| 5723 | if (ch == '}') { | 6114 | if (ch == '}') { |
| 5724 | if (ctx.word.length != 0 /* word} */ | 6115 | if (!IS_NULL_WORD(ctx.word)) { |
| 5725 | || ctx.word.has_quoted_part /* ""} */ | 6116 | /* WORD} or ""} */ |
| 5726 | ) { | ||
| 5727 | goto ordinary_char; | 6117 | goto ordinary_char; |
| 5728 | } | 6118 | } |
| 5729 | if (!IS_NULL_CMD(ctx.command)) { /* cmd } */ | 6119 | if (!IS_NULL_CMD(ctx.command)) { /* CMD } */ |
| 5730 | /* Generally, there should be semicolon: "cmd; }" | 6120 | /* Generally, there should be semicolon: "CMD; }" |
| 5731 | * However, bash allows to omit it if "cmd" is | 6121 | * However, bash allows to omit it if "CMD" is |
| 5732 | * a group. Examples: | 6122 | * a group. Examples: |
| 5733 | * { { echo 1; } } | 6123 | * { { echo 1; } } |
| 5734 | * {(echo 1)} | 6124 | * {(echo 1)} |
| @@ -5740,14 +6130,17 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5740 | goto term_group; | 6130 | goto term_group; |
| 5741 | goto ordinary_char; | 6131 | goto ordinary_char; |
| 5742 | } | 6132 | } |
| 5743 | if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ | 6133 | if (!IS_NULL_PIPE(ctx.pipe)) /* CMD | } */ |
| 5744 | /* Can't be an end of {cmd}, skip the check */ | 6134 | /* Can't be an end of {CMD}, skip the check */ |
| 5745 | goto skip_end_trigger; | 6135 | goto rbrace_skips_end_trigger; |
| 5746 | /* else: } does terminate a group */ | 6136 | /* else: } does terminate a group */ |
| 5747 | } | 6137 | } |
| 5748 | term_group: | 6138 | term_group: |
| 5749 | if (end_trigger && end_trigger == ch | 6139 | if (end_trigger && end_trigger == ch |
| 5750 | && (ch != ';' || heredoc_cnt == 0) | 6140 | && (ch != ';' |
| 6141 | /* it's ";". Can exit parse_stream() only if have no heredocs to consume, and alias buffer is empty */ | ||
| 6142 | || (heredoc_cnt == 0 && !i_has_alias_buffer(input)) | ||
| 6143 | ) | ||
| 5751 | #if ENABLE_HUSH_CASE | 6144 | #if ENABLE_HUSH_CASE |
| 5752 | && (ch != ')' | 6145 | && (ch != ')' |
| 5753 | || ctx.ctx_res_w != RES_MATCH | 6146 | || ctx.ctx_res_w != RES_MATCH |
| @@ -5755,13 +6148,32 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5755 | ) | 6148 | ) |
| 5756 | #endif | 6149 | #endif |
| 5757 | ) { | 6150 | ) { |
| 5758 | if (done_word(&ctx)) { | 6151 | #if ENABLE_HUSH_ALIAS |
| 6152 | /* Check for alias expansion (only for first word of command) */ | ||
| 6153 | if (G_interactive_fd && !ctx.command->argv) { | ||
| 6154 | alias = word_matches_alias(&ctx); | ||
| 6155 | if (alias) | ||
| 6156 | goto add_to_albuf_and_get_next_char; | ||
| 6157 | } | ||
| 6158 | #endif | ||
| 6159 | if (done_word(&ctx)) | ||
| 6160 | goto parse_error_exitcode1; | ||
| 6161 | #if ENABLE_HUSH_FUNCTION_KEYWORD | ||
| 6162 | if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) { | ||
| 6163 | /* Testcase: sh -c '(function)' */ | ||
| 6164 | syntax_error("expected funcdef"); | ||
| 5759 | goto parse_error_exitcode1; | 6165 | goto parse_error_exitcode1; |
| 5760 | } | 6166 | } |
| 5761 | done_pipe(&ctx, PIPE_SEQ); | 6167 | #endif |
| 6168 | if (done_pipe(&ctx, PIPE_SEQ)) { | ||
| 6169 | /* Testcase: sh -c 'date|;not_reached' */ | ||
| 6170 | syntax_error_unterm_ch('|'); | ||
| 6171 | goto parse_error_exitcode1; | ||
| 6172 | }; | ||
| 5762 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 6173 | ctx.is_assignment = MAYBE_ASSIGNMENT; |
| 5763 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 6174 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); |
| 5764 | /* Do we sit outside of any if's, loops or case's? */ | 6175 | /* Do we sit outside of any if's, loops or case's? */ |
| 6176 | //TODO? just check ctx.stack != NULL instead? | ||
| 5765 | if (!HAS_KEYWORDS | 6177 | if (!HAS_KEYWORDS |
| 5766 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) | 6178 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) |
| 5767 | ) { | 6179 | ) { |
| @@ -5786,21 +6198,18 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5786 | "end_trigger char found\n", | 6198 | "end_trigger char found\n", |
| 5787 | ctx.list_head); | 6199 | ctx.list_head); |
| 5788 | debug_leave(); | 6200 | debug_leave(); |
| 6201 | i_free_alias_buffer(input); | ||
| 5789 | return ctx.list_head; | 6202 | return ctx.list_head; |
| 5790 | } | 6203 | } |
| 5791 | } | 6204 | } |
| 5792 | 6205 | ||
| 5793 | if (is_blank) | ||
| 5794 | continue; | ||
| 5795 | |||
| 5796 | /* Catch <, > before deciding whether this word is | 6206 | /* Catch <, > before deciding whether this word is |
| 5797 | * an assignment. a=1 2>z b=2: b=2 is still assignment */ | 6207 | * an assignment. a=1 2>z b=2: b=2 is still assignment */ |
| 5798 | switch (ch) { | 6208 | switch (ch) { |
| 5799 | case '>': | 6209 | case '>': |
| 5800 | redir_fd = redirect_opt_num(&ctx.word); | 6210 | redir_fd = redirect_opt_num(&ctx.word); |
| 5801 | if (done_word(&ctx)) { | 6211 | if (done_word(&ctx)) |
| 5802 | goto parse_error_exitcode1; | 6212 | goto parse_error_exitcode1; |
| 5803 | } | ||
| 5804 | redir_style = REDIRECT_OVERWRITE; | 6213 | redir_style = REDIRECT_OVERWRITE; |
| 5805 | if (next == '>') { | 6214 | if (next == '>') { |
| 5806 | redir_style = REDIRECT_APPEND; | 6215 | redir_style = REDIRECT_APPEND; |
| @@ -5818,9 +6227,8 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5818 | continue; /* get next char */ | 6227 | continue; /* get next char */ |
| 5819 | case '<': | 6228 | case '<': |
| 5820 | redir_fd = redirect_opt_num(&ctx.word); | 6229 | redir_fd = redirect_opt_num(&ctx.word); |
| 5821 | if (done_word(&ctx)) { | 6230 | if (done_word(&ctx)) |
| 5822 | goto parse_error_exitcode1; | 6231 | goto parse_error_exitcode1; |
| 5823 | } | ||
| 5824 | redir_style = REDIRECT_INPUT; | 6232 | redir_style = REDIRECT_INPUT; |
| 5825 | if (next == '<') { | 6233 | if (next == '<') { |
| 5826 | redir_style = REDIRECT_HEREDOC; | 6234 | redir_style = REDIRECT_HEREDOC; |
| @@ -5828,6 +6236,14 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5828 | debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt); | 6236 | debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt); |
| 5829 | ch = i_getch(input); | 6237 | ch = i_getch(input); |
| 5830 | nommu_addchr(&ctx.as_string, ch); | 6238 | nommu_addchr(&ctx.as_string, ch); |
| 6239 | /* Check for here-string (<<<) */ | ||
| 6240 | next = i_peek(input); | ||
| 6241 | if (next == '<') { | ||
| 6242 | redir_style = REDIRECT_HERESTRING; | ||
| 6243 | ch = i_getch(input); | ||
| 6244 | nommu_addchr(&ctx.as_string, ch); | ||
| 6245 | heredoc_cnt--; /* here-strings don't use heredoc lines */ | ||
| 6246 | } | ||
| 5831 | } else if (next == '>') { | 6247 | } else if (next == '>') { |
| 5832 | redir_style = REDIRECT_IO; | 6248 | redir_style = REDIRECT_IO; |
| 5833 | ch = i_getch(input); | 6249 | ch = i_getch(input); |
| @@ -5843,13 +6259,13 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5843 | goto parse_error_exitcode1; | 6259 | goto parse_error_exitcode1; |
| 5844 | continue; /* get next char */ | 6260 | continue; /* get next char */ |
| 5845 | case '#': | 6261 | case '#': |
| 5846 | if (ctx.word.length == 0 && !ctx.word.has_quoted_part) { | 6262 | if (IS_NULL_WORD(ctx.word)) { |
| 5847 | /* skip "#comment" */ | 6263 | /* skip "#COMMENT" */ |
| 5848 | /* note: we do not add it to &ctx.as_string */ | 6264 | /* note: we do not add it to &ctx.as_string */ |
| 5849 | /* TODO: in bash: | 6265 | /* TODO: in bash: |
| 5850 | * comment inside $() goes to the next \n, even inside quoted string (!): | 6266 | * comment inside $() goes to the next \n, even inside quoted string (!): |
| 5851 | * cmd "$(cmd2 #comment)" - syntax error | 6267 | * CMD "$(CMD2 #COMMENT)" - syntax error |
| 5852 | * cmd "`cmd2 #comment`" - ok | 6268 | * CMD "`CMD2 #COMMENT`" - ok |
| 5853 | * We accept both (comment ends where command subst ends, in both cases). | 6269 | * We accept both (comment ends where command subst ends, in both cases). |
| 5854 | */ | 6270 | */ |
| 5855 | while (1) { | 6271 | while (1) { |
| @@ -5860,16 +6276,16 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5860 | } | 6276 | } |
| 5861 | ch = i_getch(input); | 6277 | ch = i_getch(input); |
| 5862 | if (ch == EOF) | 6278 | if (ch == EOF) |
| 5863 | break; | 6279 | goto eof; |
| 5864 | } | 6280 | } |
| 5865 | continue; /* get next char */ | 6281 | continue; /* get next char */ |
| 5866 | } | 6282 | } |
| 5867 | break; | 6283 | break; |
| 5868 | } | 6284 | } |
| 5869 | skip_end_trigger: | 6285 | rbrace_skips_end_trigger: |
| 5870 | 6286 | ||
| 5871 | if (ctx.is_assignment == MAYBE_ASSIGNMENT | 6287 | if (ctx.is_assignment == MAYBE_ASSIGNMENT |
| 5872 | /* check that we are not in word in "a=1 2>word b=1": */ | 6288 | /* check that we are not in WORD in "a=1 2>WORD b=1": */ |
| 5873 | && !ctx.pending_redirect | 6289 | && !ctx.pending_redirect |
| 5874 | ) { | 6290 | ) { |
| 5875 | /* ch is a special char and thus this word | 6291 | /* ch is a special char and thus this word |
| @@ -5892,8 +6308,8 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5892 | o_addchr(&ctx.word, ch); | 6308 | o_addchr(&ctx.word, ch); |
| 5893 | continue; /* get next char */ | 6309 | continue; /* get next char */ |
| 5894 | case '$': | 6310 | case '$': |
| 5895 | if (parse_dollar_squote(&ctx.as_string, &ctx.word, input)) | 6311 | if (next == '\'' && parse_dollar_squote(&ctx.as_string, &ctx.word, input)) |
| 5896 | continue; /* get next char */ | 6312 | continue; /* ate $'...', get next char */ |
| 5897 | if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { | 6313 | if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { |
| 5898 | debug_printf_parse("parse_stream parse error: " | 6314 | debug_printf_parse("parse_stream parse error: " |
| 5899 | "parse_dollar returned 0 (error)\n"); | 6315 | "parse_dollar returned 0 (error)\n"); |
| @@ -5903,18 +6319,14 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5903 | case '"': | 6319 | case '"': |
| 5904 | ctx.word.has_quoted_part = 1; | 6320 | ctx.word.has_quoted_part = 1; |
| 5905 | if (next == '"' && !ctx.pending_redirect) { | 6321 | if (next == '"' && !ctx.pending_redirect) { |
| 5906 | i_getch(input); /* eat second " */ | 6322 | ch = i_getch(input); /* eat second " */ |
| 5907 | insert_empty_quoted_str_marker: | 6323 | goto insert_empty_quoted_str_marker; |
| 5908 | nommu_addchr(&ctx.as_string, next); | ||
| 5909 | o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL); | ||
| 5910 | o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL); | ||
| 5911 | continue; /* get next char */ | ||
| 5912 | } | 6324 | } |
| 5913 | if (ctx.is_assignment == NOT_ASSIGNMENT) | 6325 | if (ctx.is_assignment == NOT_ASSIGNMENT) |
| 5914 | ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; | 6326 | ctx.word.o_expflags |= EXP_FLAG_GLOBPROTECT_CHARS; |
| 5915 | if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) | 6327 | if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) |
| 5916 | goto parse_error_exitcode1; | 6328 | goto parse_error_exitcode1; |
| 5917 | ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; | 6329 | ctx.word.o_expflags &= ~EXP_FLAG_GLOBPROTECT_CHARS; |
| 5918 | continue; /* get next char */ | 6330 | continue; /* get next char */ |
| 5919 | #if ENABLE_HUSH_TICK | 6331 | #if ENABLE_HUSH_TICK |
| 5920 | case '`': { | 6332 | case '`': { |
| @@ -5935,112 +6347,240 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5935 | } | 6347 | } |
| 5936 | #endif | 6348 | #endif |
| 5937 | case ';': | 6349 | case ';': |
| 5938 | #if ENABLE_HUSH_CASE | 6350 | #if ENABLE_HUSH_ALIAS |
| 5939 | case_semi: | 6351 | /* Check for alias expansion (only for first word of command) */ |
| 6352 | if (G_interactive_fd && !ctx.command->argv) { | ||
| 6353 | alias = word_matches_alias(&ctx); | ||
| 6354 | if (alias) | ||
| 6355 | goto add_to_albuf_and_get_next_char; | ||
| 6356 | } | ||
| 5940 | #endif | 6357 | #endif |
| 5941 | if (done_word(&ctx)) { | 6358 | if (done_word(&ctx)) |
| 6359 | goto parse_error_exitcode1; | ||
| 6360 | #if ENABLE_HUSH_FUNCTION_KEYWORD | ||
| 6361 | if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) { | ||
| 6362 | /* Testcase: sh -c '{ function; }'; sh -c '{ function f; }' */ | ||
| 6363 | syntax_error("expected funcdef"); | ||
| 5942 | goto parse_error_exitcode1; | 6364 | goto parse_error_exitcode1; |
| 5943 | } | 6365 | } |
| 6366 | #endif | ||
| 5944 | done_pipe(&ctx, PIPE_SEQ); | 6367 | done_pipe(&ctx, PIPE_SEQ); |
| 5945 | #if ENABLE_HUSH_CASE | 6368 | #if ENABLE_HUSH_CASE |
| 5946 | /* Eat multiple semicolons, detect | 6369 | if (ctx.ctx_res_w == RES_CASE_BODY |
| 5947 | * whether it means something special */ | 6370 | && next == ';' /* and next char is ';' too? */ |
| 5948 | while (1) { | 6371 | ) { |
| 5949 | ch = i_peek_and_eat_bkslash_nl(input); | ||
| 5950 | if (ch != ';') | ||
| 5951 | break; | ||
| 5952 | ch = i_getch(input); | 6372 | ch = i_getch(input); |
| 5953 | nommu_addchr(&ctx.as_string, ch); | 6373 | nommu_addchr(&ctx.as_string, ch); |
| 5954 | if (ctx.ctx_res_w == RES_CASE_BODY) { | 6374 | ctx.ctx_res_w = RES_MATCH; /* "we are in PATTERN)" */ |
| 5955 | ctx.ctx_dsemicolon = 1; | ||
| 5956 | ctx.ctx_res_w = RES_MATCH; | ||
| 5957 | break; | ||
| 5958 | } | ||
| 5959 | } | 6375 | } |
| 5960 | #endif | 6376 | #endif |
| 5961 | new_cmd: | 6377 | finished_cmd: |
| 5962 | /* We just finished a cmd. New one may start | 6378 | /* We just finished a cmd. New one may start |
| 5963 | * with an assignment */ | 6379 | * with an assignment */ |
| 5964 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 6380 | ctx.is_assignment = MAYBE_ASSIGNMENT; |
| 5965 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 6381 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); |
| 5966 | continue; /* get next char */ | 6382 | continue; /* get next char */ |
| 5967 | case '&': | 6383 | case '&': |
| 5968 | if (done_word(&ctx)) { | 6384 | #if ENABLE_HUSH_ALIAS |
| 6385 | /* Check for alias expansion (only for first word of command) */ | ||
| 6386 | if (G_interactive_fd && !ctx.command->argv) { | ||
| 6387 | alias = word_matches_alias(&ctx); | ||
| 6388 | if (alias) | ||
| 6389 | goto add_to_albuf_and_get_next_char; | ||
| 6390 | } | ||
| 6391 | #endif | ||
| 6392 | if (done_word(&ctx)) | ||
| 6393 | goto parse_error_exitcode1; | ||
| 6394 | if (ctx.pipe->num_cmds == 0 && IS_NULL_CMD(ctx.command)) { | ||
| 6395 | /* Testcase: sh -c '&& date' */ | ||
| 6396 | /* Testcase: sh -c '&' */ | ||
| 6397 | syntax_error_unexpected_str("&&" + (next != '&')); | ||
| 5969 | goto parse_error_exitcode1; | 6398 | goto parse_error_exitcode1; |
| 5970 | } | 6399 | } |
| 5971 | if (next == '&') { | 6400 | if (next == '&') { |
| 5972 | ch = i_getch(input); | 6401 | ch = i_getch(input); |
| 5973 | nommu_addchr(&ctx.as_string, ch); | 6402 | nommu_addchr(&ctx.as_string, ch); |
| 5974 | done_pipe(&ctx, PIPE_AND); | 6403 | if (done_pipe(&ctx, PIPE_AND)) { |
| 6404 | /* Testcase: sh -c 'date | && date' */ | ||
| 6405 | syntax_error_unterm_ch('|'); | ||
| 6406 | goto parse_error_exitcode1; | ||
| 6407 | } | ||
| 5975 | } else { | 6408 | } else { |
| 5976 | done_pipe(&ctx, PIPE_BG); | 6409 | if (done_pipe(&ctx, PIPE_BG)) { |
| 6410 | /* Testcase: sh -c 'date | &' */ | ||
| 6411 | syntax_error_unterm_ch('|'); | ||
| 6412 | goto parse_error_exitcode1; | ||
| 6413 | } | ||
| 5977 | } | 6414 | } |
| 5978 | goto new_cmd; | 6415 | goto finished_cmd; |
| 5979 | case '|': | 6416 | case '|': |
| 5980 | if (done_word(&ctx)) { | ||
| 5981 | goto parse_error_exitcode1; | ||
| 5982 | } | ||
| 5983 | #if ENABLE_HUSH_CASE | 6417 | #if ENABLE_HUSH_CASE |
| 5984 | if (ctx.ctx_res_w == RES_MATCH) | 6418 | if (ctx.ctx_res_w == RES_MATCH) { |
| 5985 | break; /* we are in case's "word | word)" */ | 6419 | // if (IS_NULL_WORD(ctx.word)) { |
| 6420 | // /* Testcase: sh -c 'case w in |w) nice; esac' */ | ||
| 6421 | // /* Testcase: sh -c 'case w in v||w) nice; esac' */ | ||
| 6422 | //also rejects valid: sh -c 'case w in v |w) nice; esac' | ||
| 6423 | // syntax_error_unexpected_ch(ch); | ||
| 6424 | // goto parse_error_exitcode1; | ||
| 6425 | // } | ||
| 6426 | if (done_word(&ctx)) | ||
| 6427 | goto parse_error_exitcode1; | ||
| 6428 | continue; /* get next char */ | ||
| 6429 | } | ||
| 5986 | #endif | 6430 | #endif |
| 6431 | #if ENABLE_HUSH_ALIAS | ||
| 6432 | /* Check for alias expansion (only for first word of command) */ | ||
| 6433 | if (G_interactive_fd && !ctx.command->argv) { | ||
| 6434 | alias = word_matches_alias(&ctx); | ||
| 6435 | if (alias) | ||
| 6436 | goto add_to_albuf_and_get_next_char; | ||
| 6437 | } | ||
| 6438 | #endif | ||
| 6439 | if (done_word(&ctx)) | ||
| 6440 | goto parse_error_exitcode1; | ||
| 5987 | if (next == '|') { /* || */ | 6441 | if (next == '|') { /* || */ |
| 5988 | ch = i_getch(input); | 6442 | ch = i_getch(input); |
| 5989 | nommu_addchr(&ctx.as_string, ch); | 6443 | nommu_addchr(&ctx.as_string, ch); |
| 5990 | done_pipe(&ctx, PIPE_OR); | 6444 | if (ctx.pipe->num_cmds == 0 && IS_NULL_CMD(ctx.command)) { |
| 6445 | /* Testcase: sh -c '|| date' */ | ||
| 6446 | syntax_error_unexpected_str("||"); | ||
| 6447 | goto parse_error_exitcode1; | ||
| 6448 | } | ||
| 6449 | if (done_pipe(&ctx, PIPE_OR)) { | ||
| 6450 | /* Testcase: sh -c 'date | || date' */ | ||
| 6451 | syntax_error_unterm_ch('|'); | ||
| 6452 | goto parse_error_exitcode1; | ||
| 6453 | } | ||
| 5991 | } else { | 6454 | } else { |
| 5992 | /* we could pick up a file descriptor choice here | 6455 | if (IS_NULL_CMD(ctx.command)) { |
| 5993 | * with redirect_opt_num(), but bash doesn't do it. | 6456 | /* Testcase: sh -c '| cat' */ |
| 5994 | * "echo foo 2| cat" yields "foo 2". */ | 6457 | /* Testcase: sh -c 'date | | cat' */ |
| 6458 | syntax_error_unexpected_ch('|'); | ||
| 6459 | goto parse_error_exitcode1; | ||
| 6460 | } | ||
| 5995 | done_command(&ctx); | 6461 | done_command(&ctx); |
| 5996 | } | 6462 | } |
| 5997 | goto new_cmd; | 6463 | goto finished_cmd; |
| 5998 | case '(': | 6464 | case '(': |
| 5999 | #if ENABLE_HUSH_CASE | 6465 | #if ENABLE_HUSH_CASE |
| 6000 | /* "case... in [(]word)..." - skip '(' */ | 6466 | /* "case... in (PATTERNS)..."? */ |
| 6001 | if (ctx.ctx_res_w == RES_MATCH | 6467 | if (ctx.ctx_res_w == RES_MATCH |
| 6002 | && ctx.command->argv == NULL /* not (word|(... */ | 6468 | && ctx.command->argv == NULL /* not WORD (... */ |
| 6003 | && ctx.word.length == 0 /* not word(... */ | 6469 | && IS_NULL_WORD(ctx.word) /* not WORD(... or ""(... */ |
| 6004 | && ctx.word.has_quoted_part == 0 /* not ""(... */ | ||
| 6005 | ) { | 6470 | ) { |
| 6006 | continue; /* get next char */ | 6471 | continue; /* skip '(', get next char */ |
| 6007 | } | 6472 | } |
| 6008 | #endif | 6473 | #endif |
| 6009 | /* fall through */ | 6474 | /* fall through */ |
| 6010 | case '{': { | 6475 | case '{': { |
| 6011 | int n = parse_group(&ctx, input, ch); | 6476 | int n; |
| 6012 | if (n < 0) { | 6477 | /* "function WORD" -> */ |
| 6478 | IF_HUSH_FUNCTION_KEYWORD(parse_group:) | ||
| 6479 | /* Try to parse as { CMDS; } or (CMDS) */ | ||
| 6480 | n = parse_group(&ctx, input, ch); | ||
| 6481 | if (n < 0) | ||
| 6013 | goto parse_error_exitcode1; | 6482 | goto parse_error_exitcode1; |
| 6014 | } | ||
| 6015 | debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); | 6483 | debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); |
| 6016 | heredoc_cnt += n; | 6484 | heredoc_cnt += n; |
| 6017 | goto new_cmd; | 6485 | goto finished_cmd; |
| 6018 | } | 6486 | } |
| 6019 | case ')': | 6487 | case ')': |
| 6020 | #if ENABLE_HUSH_CASE | 6488 | #if ENABLE_HUSH_CASE |
| 6021 | if (ctx.ctx_res_w == RES_MATCH) | 6489 | if (ctx.ctx_res_w == RES_MATCH) { |
| 6022 | goto case_semi; | 6490 | // if (IS_NULL_WORD(ctx.word)) { |
| 6491 | // /* Testcase: sh -c 'case w in w|) nice; esac' */ | ||
| 6492 | //also rejects valid: sh -c 'case w in w ) nice; esac' | ||
| 6493 | // syntax_error_unexpected_ch(ch); | ||
| 6494 | // goto parse_error_exitcode1; | ||
| 6495 | // } | ||
| 6496 | if (done_word(&ctx)) | ||
| 6497 | goto parse_error_exitcode1; | ||
| 6498 | //FIXME: accepts "W1 W2) CMD;;" as if it was "W1|W2) CMD;;", should not allow this syntax at all | ||
| 6499 | done_pipe(&ctx, PIPE_SEQ); | ||
| 6500 | goto finished_cmd; | ||
| 6501 | } | ||
| 6023 | #endif | 6502 | #endif |
| 6024 | case '}': | 6503 | /* case ')' and !RES_MATCH: falls through to the error: */ |
| 6025 | /* proper use of this character is caught by end_trigger: | 6504 | /* Testcase for bad ')' ? */ |
| 6505 | /* case '}': */ | ||
| 6506 | /* Proper use of this character is caught by end_trigger: | ||
| 6026 | * if we see {, we call parse_group(..., end_trigger='}') | 6507 | * if we see {, we call parse_group(..., end_trigger='}') |
| 6027 | * and it will match } earlier (not here). */ | 6508 | * and it will match } earlier (not here). |
| 6509 | */ | ||
| 6510 | /* Testcase for bad '}' ? */ | ||
| 6511 | default: /* all other chars should not ever reach this... */ | ||
| 6028 | G.last_exitcode = 2; | 6512 | G.last_exitcode = 2; |
| 6029 | syntax_error_unexpected_ch(ch); | 6513 | syntax_error_unexpected_ch(ch); |
| 6030 | goto parse_error; | 6514 | goto parse_error; |
| 6031 | default: | ||
| 6032 | if (HUSH_DEBUG) | ||
| 6033 | bb_error_msg_and_die("BUG: unexpected %c", ch); | ||
| 6034 | } | 6515 | } |
| 6035 | } /* while (1) */ | 6516 | } /* while (1) */ |
| 6517 | eof: | ||
| 6518 | /* Reached EOF */ | ||
| 6519 | if (heredoc_cnt) { | ||
| 6520 | syntax_error_unterm_str("here document"); | ||
| 6521 | goto parse_error_exitcode1; | ||
| 6522 | } | ||
| 6523 | if (end_trigger == ')') { | ||
| 6524 | syntax_error_unterm_ch('('); | ||
| 6525 | goto parse_error_exitcode1; | ||
| 6526 | } | ||
| 6527 | if (end_trigger == '}') { | ||
| 6528 | syntax_error_unterm_ch('{'); | ||
| 6529 | goto parse_error_exitcode1; | ||
| 6530 | } | ||
| 6531 | |||
| 6532 | if (done_word(&ctx)) | ||
| 6533 | goto parse_error_exitcode1; | ||
| 6534 | #if ENABLE_HUSH_FUNCTION_KEYWORD | ||
| 6535 | if (ctx.command->cmd_type == CMD_FUNCTION_KWORD) { | ||
| 6536 | /* Testcase: sh -c 'function'; sh -c 'function f' */ | ||
| 6537 | syntax_error("expected funcdef"); | ||
| 6538 | goto parse_error_exitcode1; | ||
| 6539 | } | ||
| 6540 | #endif | ||
| 6541 | o_free_and_set_NULL(&ctx.word); | ||
| 6542 | if (done_pipe(&ctx, PIPE_SEQ)) { | ||
| 6543 | /* Testcase: sh -c 'date |' */ | ||
| 6544 | syntax_error_unterm_ch('|'); | ||
| 6545 | goto parse_error_exitcode1; | ||
| 6546 | } | ||
| 6547 | // TODO: catch 'date &&<whitespace><EOF>' and 'date ||<whitespace><EOF>' too | ||
| 6548 | |||
| 6549 | #if HAS_KEYWORDS | ||
| 6550 | /* Do we sit inside of any if's, loops or case's? */ | ||
| 6551 | if (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0) { | ||
| 6552 | syntax_error_unterm_str("compound statement"); | ||
| 6553 | goto parse_error_exitcode1; | ||
| 6554 | } | ||
| 6555 | #endif | ||
| 6556 | pi = ctx.list_head; | ||
| 6557 | /* If we got nothing... */ | ||
| 6558 | if (pi->num_cmds == 0 | ||
| 6559 | IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE) | ||
| 6560 | ) { | ||
| 6561 | free_pipe_list(pi); | ||
| 6562 | pi = NULL; | ||
| 6563 | } | ||
| 6564 | #if !BB_MMU | ||
| 6565 | debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data); | ||
| 6566 | if (pstring) | ||
| 6567 | *pstring = ctx.as_string.data; | ||
| 6568 | else | ||
| 6569 | o_free(&ctx.as_string); | ||
| 6570 | #endif | ||
| 6571 | // heredoc_cnt must be 0 here anyway | ||
| 6572 | //if (heredoc_cnt_ptr) | ||
| 6573 | // *heredoc_cnt_ptr = heredoc_cnt; | ||
| 6574 | debug_leave(); | ||
| 6575 | debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); | ||
| 6576 | debug_printf_parse("parse_stream return %p: EOF\n", pi); | ||
| 6577 | i_free_alias_buffer(input); | ||
| 6578 | return pi; | ||
| 6036 | 6579 | ||
| 6037 | parse_error_exitcode1: | 6580 | parse_error_exitcode1: |
| 6038 | G.last_exitcode = 1; | 6581 | G.last_exitcode = 1; |
| 6039 | parse_error: | 6582 | parse_error: |
| 6040 | { | 6583 | { |
| 6041 | struct parse_context *pctx; | ||
| 6042 | IF_HAS_KEYWORDS(struct parse_context *p2;) | ||
| 6043 | |||
| 6044 | /* Clean up allocated tree. | 6584 | /* Clean up allocated tree. |
| 6045 | * Sample for finding leaks on syntax error recovery path. | 6585 | * Sample for finding leaks on syntax error recovery path. |
| 6046 | * Run it from interactive shell, watch pmap `pidof hush`. | 6586 | * Run it from interactive shell, watch pmap `pidof hush`. |
| @@ -6049,44 +6589,39 @@ static struct pipe *parse_stream(char **pstring, | |||
| 6049 | * while if (true | { true;}); then echo ok; fi; do break; done | 6589 | * while if (true | { true;}); then echo ok; fi; do break; done |
| 6050 | * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done | 6590 | * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done |
| 6051 | */ | 6591 | */ |
| 6052 | pctx = &ctx; | 6592 | IF_HAS_KEYWORDS(struct parse_context *stk;) |
| 6593 | struct parse_context *pctx = &ctx; | ||
| 6053 | do { | 6594 | do { |
| 6054 | /* Update pipe/command counts, | 6595 | /* Update pipe/command counts, |
| 6055 | * otherwise freeing may miss some */ | 6596 | * otherwise freeing may miss some */ |
| 6056 | done_pipe(pctx, PIPE_SEQ); | 6597 | done_pipe(pctx, PIPE_SEQ); |
| 6057 | debug_printf_clean("freeing list %p from ctx %p\n", | 6598 | debug_printf_clean("freeing list %p from ctx %p\n", pctx->list_head, pctx); |
| 6058 | pctx->list_head, pctx); | ||
| 6059 | debug_print_tree(pctx->list_head, 0); | 6599 | debug_print_tree(pctx->list_head, 0); |
| 6060 | free_pipe_list(pctx->list_head); | 6600 | free_pipe_list(pctx->list_head); |
| 6061 | debug_printf_clean("freed list %p\n", pctx->list_head); | 6601 | debug_printf_clean("freed list %p\n", pctx->list_head); |
| 6062 | #if !BB_MMU | 6602 | #if !BB_MMU |
| 6063 | o_free(&pctx->as_string); | 6603 | o_free(&pctx->as_string); |
| 6064 | #endif | 6604 | #endif |
| 6065 | IF_HAS_KEYWORDS(p2 = pctx->stack;) | 6605 | IF_HAS_KEYWORDS(stk = pctx->stack;) |
| 6066 | if (pctx != &ctx) { | 6606 | if (pctx != &ctx) |
| 6067 | free(pctx); | 6607 | free(pctx); |
| 6068 | } | 6608 | IF_HAS_KEYWORDS(pctx = stk;) |
| 6069 | IF_HAS_KEYWORDS(pctx = p2;) | ||
| 6070 | } while (HAS_KEYWORDS && pctx); | 6609 | } while (HAS_KEYWORDS && pctx); |
| 6071 | 6610 | } | |
| 6072 | o_free(&ctx.word); | 6611 | o_free(&ctx.word); |
| 6073 | #if !BB_MMU | 6612 | #if !BB_MMU |
| 6074 | if (pstring) | 6613 | if (pstring) |
| 6075 | *pstring = NULL; | 6614 | *pstring = NULL; |
| 6076 | #endif | 6615 | #endif |
| 6077 | debug_leave(); | 6616 | debug_leave(); |
| 6078 | return ERR_PTR; | 6617 | i_free_alias_buffer(input); |
| 6079 | } | 6618 | return ERR_PTR; |
| 6080 | } | 6619 | } |
| 6081 | 6620 | ||
| 6082 | /* | 6621 | /* |
| 6083 | * Execution routines | 6622 | * Execution routines |
| 6084 | */ | 6623 | */ |
| 6085 | /* Expansion can recurse, need forward decls: */ | 6624 | /* Expansion can recurse, need forward decls: */ |
| 6086 | #if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE | ||
| 6087 | #define expand_string_to_string(str, EXP_flags, do_unbackslash) \ | ||
| 6088 | expand_string_to_string(str) | ||
| 6089 | #endif | ||
| 6090 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); | 6625 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); |
| 6091 | #if ENABLE_HUSH_TICK | 6626 | #if ENABLE_HUSH_TICK |
| 6092 | static int process_command_subs(o_string *dest, const char *s); | 6627 | static int process_command_subs(o_string *dest, const char *s); |
| @@ -6151,7 +6686,7 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
| 6151 | word_len = strcspn(str, G.ifs); | 6686 | word_len = strcspn(str, G.ifs); |
| 6152 | if (word_len) { | 6687 | if (word_len) { |
| 6153 | /* We have WORD_LEN leading non-IFS chars */ | 6688 | /* We have WORD_LEN leading non-IFS chars */ |
| 6154 | if (!(output->o_expflags & EXP_FLAG_GLOB)) { | 6689 | if (!(output->o_expflags & EXP_FLAG_DO_GLOBBING)) { |
| 6155 | o_addblock(output, str, word_len); | 6690 | o_addblock(output, str, word_len); |
| 6156 | } else { | 6691 | } else { |
| 6157 | /* Protect backslashes against globbing up :) | 6692 | /* Protect backslashes against globbing up :) |
| @@ -6244,7 +6779,7 @@ static char *encode_then_expand_string(const char *str) | |||
| 6244 | //TODO: error check (encode_string returns 0 on error)? | 6779 | //TODO: error check (encode_string returns 0 on error)? |
| 6245 | //bb_error_msg("'%s' -> '%s'", str, dest.data); | 6780 | //bb_error_msg("'%s' -> '%s'", str, dest.data); |
| 6246 | exp_str = expand_string_to_string(dest.data, | 6781 | exp_str = expand_string_to_string(dest.data, |
| 6247 | EXP_FLAG_ESC_GLOB_CHARS, | 6782 | EXP_FLAG_GLOBPROTECT_CHARS, /* example: `echo '_\t_\\_\"_'` in heredoc */ |
| 6248 | /*unbackslash:*/ 1 | 6783 | /*unbackslash:*/ 1 |
| 6249 | ); | 6784 | ); |
| 6250 | //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); | 6785 | //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); |
| @@ -6279,15 +6814,11 @@ static const char *first_special_char_in_vararg(const char *cp) | |||
| 6279 | * a dquoted string: "${var#"zz"}"), the difference only comes later | 6814 | * a dquoted string: "${var#"zz"}"), the difference only comes later |
| 6280 | * (word splitting and globbing of the ${var...} result). | 6815 | * (word splitting and globbing of the ${var...} result). |
| 6281 | */ | 6816 | */ |
| 6282 | #if !BASH_PATTERN_SUBST | 6817 | static char *encode_then_expand_vararg(const char *str, |
| 6283 | #define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \ | 6818 | int handle_squotes, /* 'str' substrings are parsed as literals (unless inside "")*/ |
| 6284 | encode_then_expand_vararg(str, handle_squotes) | 6819 | int protect_vars, /* glob-protect double-quoted $VARS */ |
| 6285 | #endif | 6820 | int do_unbackslash /* run unbackslash on result before returning it */ |
| 6286 | static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash) | 6821 | ) { |
| 6287 | { | ||
| 6288 | #if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE | ||
| 6289 | const int do_unbackslash = 0; | ||
| 6290 | #endif | ||
| 6291 | char *exp_str; | 6822 | char *exp_str; |
| 6292 | struct in_str input; | 6823 | struct in_str input; |
| 6293 | o_string dest = NULL_O_STRING; | 6824 | o_string dest = NULL_O_STRING; |
| @@ -6322,7 +6853,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int | |||
| 6322 | goto ret; /* error */ | 6853 | goto ret; /* error */ |
| 6323 | } | 6854 | } |
| 6324 | if (ch == '"') { | 6855 | if (ch == '"') { |
| 6325 | dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; | 6856 | dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS; |
| 6326 | continue; | 6857 | continue; |
| 6327 | } | 6858 | } |
| 6328 | if (ch == '\\') { | 6859 | if (ch == '\\') { |
| @@ -6338,7 +6869,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int | |||
| 6338 | if (ch == '$') { | 6869 | if (ch == '$') { |
| 6339 | if (parse_dollar_squote(NULL, &dest, &input)) | 6870 | if (parse_dollar_squote(NULL, &dest, &input)) |
| 6340 | continue; | 6871 | continue; |
| 6341 | if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) { | 6872 | if (!parse_dollar(NULL, &dest, &input, |
| 6873 | /*quote_mask:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) ? 0x80 : 0 | ||
| 6874 | ) | ||
| 6875 | ) { | ||
| 6342 | debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); | 6876 | debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); |
| 6343 | goto ret; | 6877 | goto ret; |
| 6344 | } | 6878 | } |
| @@ -6350,7 +6884,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int | |||
| 6350 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 6884 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
| 6351 | o_addchr(&dest, 0x80 | '`'); | 6885 | o_addchr(&dest, 0x80 | '`'); |
| 6352 | if (!add_till_backquote(&dest, &input, | 6886 | if (!add_till_backquote(&dest, &input, |
| 6353 | /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ | 6887 | /*in_dquote:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) |
| 6354 | ) | 6888 | ) |
| 6355 | ) { | 6889 | ) { |
| 6356 | goto ret; /* error */ | 6890 | goto ret; /* error */ |
| @@ -6363,9 +6897,12 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int | |||
| 6363 | o_addQchr(&dest, ch); | 6897 | o_addQchr(&dest, ch); |
| 6364 | } /* for (;;) */ | 6898 | } /* for (;;) */ |
| 6365 | 6899 | ||
| 6366 | debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data); | 6900 | debug_printf_parse("encode(do_unbackslash:%d): '%s' -> '%s'\n", do_unbackslash, str, dest.data); |
| 6367 | exp_str = expand_string_to_string(dest.data, | 6901 | exp_str = expand_string_to_string(dest.data, |
| 6368 | do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, | 6902 | 0 |
| 6903 | | (do_unbackslash ? EXP_FLAG_GLOBPROTECT_CHARS : 0) | ||
| 6904 | | (protect_vars ? EXP_FLAG_GLOBPROTECT_VARS : 0) | ||
| 6905 | , | ||
| 6369 | do_unbackslash | 6906 | do_unbackslash |
| 6370 | ); | 6907 | ); |
| 6371 | ret: | 6908 | ret: |
| @@ -6409,7 +6946,10 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, | |||
| 6409 | if (!dest.o_expflags) { | 6946 | if (!dest.o_expflags) { |
| 6410 | if (ch == EOF) | 6947 | if (ch == EOF) |
| 6411 | break; | 6948 | break; |
| 6412 | if (!dquoted && !(output->o_expflags & EXP_FLAG_SINGLEWORD) && strchr(G.ifs, ch)) { | 6949 | if (!dquoted |
| 6950 | && !(output->o_expflags & EXP_FLAG_SINGLEWORD) | ||
| 6951 | && strchr(G.ifs, ch) | ||
| 6952 | ) { | ||
| 6413 | /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word. | 6953 | /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word. |
| 6414 | * do not assume we are at the start of the word (PREFIX above). | 6954 | * do not assume we are at the start of the word (PREFIX above). |
| 6415 | */ | 6955 | */ |
| @@ -6449,7 +6989,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, | |||
| 6449 | goto ret; /* error */ | 6989 | goto ret; /* error */ |
| 6450 | } | 6990 | } |
| 6451 | if (ch == '"') { | 6991 | if (ch == '"') { |
| 6452 | dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; | 6992 | dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS; |
| 6453 | if (dest.o_expflags) { | 6993 | if (dest.o_expflags) { |
| 6454 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 6994 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
| 6455 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 6995 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
| @@ -6479,7 +7019,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, | |||
| 6479 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 7019 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
| 6480 | o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`'); | 7020 | o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`'); |
| 6481 | if (!add_till_backquote(&dest, &input, | 7021 | if (!add_till_backquote(&dest, &input, |
| 6482 | /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ | 7022 | /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_GLOBPROTECT_CHARS set */ |
| 6483 | ) | 7023 | ) |
| 6484 | ) { | 7024 | ) { |
| 6485 | goto ret; /* error */ | 7025 | goto ret; /* error */ |
| @@ -6607,27 +7147,39 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
| 6607 | #endif /* BASH_PATTERN_SUBST */ | 7147 | #endif /* BASH_PATTERN_SUBST */ |
| 6608 | 7148 | ||
| 6609 | static int append_str_maybe_ifs_split(o_string *output, int n, | 7149 | static int append_str_maybe_ifs_split(o_string *output, int n, |
| 6610 | int first_ch, const char *val) | 7150 | int quoted, const char *val) |
| 6611 | { | 7151 | { |
| 6612 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | 7152 | if (!quoted) { /* unquoted $VAR */ |
| 6613 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, | 7153 | debug_printf_expand("unquoted variable value '%s', o_escape:%d o_varescape:%d, singleword:%d\n", val, |
| 6614 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 7154 | !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS), |
| 6615 | if (val && val[0]) | 7155 | !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS), |
| 7156 | !!(output->o_expflags & EXP_FLAG_SINGLEWORD) | ||
| 7157 | ); | ||
| 7158 | if (val && val[0]) { | ||
| 7159 | if (output->o_expflags & EXP_FLAG_SINGLEWORD) | ||
| 7160 | goto singleword; | ||
| 6616 | n = expand_on_ifs(output, n, val); | 7161 | n = expand_on_ifs(output, n, val); |
| 7162 | } | ||
| 6617 | } else { /* quoted "$VAR" */ | 7163 | } else { /* quoted "$VAR" */ |
| 6618 | output->has_quoted_part = 1; | 7164 | output->has_quoted_part = 1; |
| 6619 | debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, | 7165 | debug_printf_expand("quoted variable value '%s', o_escape:%d o_varescape:%d\n", val, |
| 6620 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 7166 | !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS), |
| 6621 | if (val && val[0]) | 7167 | !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS) |
| 6622 | o_addQstr(output, val); | 7168 | ); |
| 7169 | if (val && val[0]) { | ||
| 7170 | if (output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS) | ||
| 7171 | o_addqstr(output, val); | ||
| 7172 | else | ||
| 7173 | singleword: | ||
| 7174 | o_addQstr(output, val); | ||
| 7175 | } | ||
| 6623 | } | 7176 | } |
| 6624 | return n; | 7177 | return n; |
| 6625 | } | 7178 | } |
| 6626 | 7179 | ||
| 6627 | /* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. | 7180 | /* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. |
| 6628 | */ | 7181 | */ |
| 6629 | static NOINLINE int expand_one_var(o_string *output, int n, | 7182 | static NOINLINE int expand_one_var(o_string *output, int n, char *arg, char **pp) |
| 6630 | int first_ch, char *arg, char **pp) | ||
| 6631 | { | 7183 | { |
| 6632 | const char *val; | 7184 | const char *val; |
| 6633 | char *to_be_freed; | 7185 | char *to_be_freed; |
| @@ -6763,7 +7315,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
| 6763 | if (exp_op == *exp_word) /* ## or %% */ | 7315 | if (exp_op == *exp_word) /* ## or %% */ |
| 6764 | exp_word++; | 7316 | exp_word++; |
| 6765 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); | 7317 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); |
| 6766 | exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); | 7318 | exp_exp_word = encode_then_expand_vararg(exp_word, |
| 7319 | /*handle_squotes:*/ 1, /* 'str' are processed (and glob-protected) */ | ||
| 7320 | /*globprotect_vars:*/ 1, /* value of "$VAR" is not glob-expanded */ | ||
| 7321 | /*unbackslash:*/ 0 | ||
| 7322 | ); | ||
| 6767 | if (exp_exp_word) | 7323 | if (exp_exp_word) |
| 6768 | exp_word = exp_exp_word; | 7324 | exp_word = exp_exp_word; |
| 6769 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); | 7325 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); |
| @@ -6810,7 +7366,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
| 6810 | * (note that a*z _pattern_ is never globbed!) | 7366 | * (note that a*z _pattern_ is never globbed!) |
| 6811 | */ | 7367 | */ |
| 6812 | char *pattern, *repl, *t; | 7368 | char *pattern, *repl, *t; |
| 6813 | pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); | 7369 | pattern = encode_then_expand_vararg(exp_word, |
| 7370 | /*handle_squotes:*/ 1, | ||
| 7371 | /*globprotect_vars:*/ 0, | ||
| 7372 | /*unbackslash:*/ 0 | ||
| 7373 | ); | ||
| 6814 | if (!pattern) | 7374 | if (!pattern) |
| 6815 | pattern = xstrdup(exp_word); | 7375 | pattern = xstrdup(exp_word); |
| 6816 | debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); | 7376 | debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); |
| @@ -6818,7 +7378,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
| 6818 | exp_word = p; | 7378 | exp_word = p; |
| 6819 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 7379 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
| 6820 | *p = '\0'; | 7380 | *p = '\0'; |
| 6821 | repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1); | 7381 | repl = encode_then_expand_vararg(exp_word, |
| 7382 | /*handle_squotes:*/ 1, | ||
| 7383 | /*globprotect_vars:*/ 0, | ||
| 7384 | /*unbackslash:*/ 1 | ||
| 7385 | ); | ||
| 6822 | debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); | 7386 | debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); |
| 6823 | /* HACK ALERT. We depend here on the fact that | 7387 | /* HACK ALERT. We depend here on the fact that |
| 6824 | * G.global_argv and results of utoa and get_local_var_value | 7388 | * G.global_argv and results of utoa and get_local_var_value |
| @@ -6971,6 +7535,7 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
| 6971 | /* ${var=word} - assign and use default value */ | 7535 | /* ${var=word} - assign and use default value */ |
| 6972 | to_be_freed = encode_then_expand_vararg(exp_word, | 7536 | to_be_freed = encode_then_expand_vararg(exp_word, |
| 6973 | /*handle_squotes:*/ !(arg0 & 0x80), | 7537 | /*handle_squotes:*/ !(arg0 & 0x80), |
| 7538 | /*globprotect_vars:*/ 0, | ||
| 6974 | /*unbackslash:*/ 0 | 7539 | /*unbackslash:*/ 0 |
| 6975 | ); | 7540 | ); |
| 6976 | if (to_be_freed) | 7541 | if (to_be_freed) |
| @@ -7015,7 +7580,7 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
| 7015 | arg[0] = arg0; | 7580 | arg[0] = arg0; |
| 7016 | *pp = p; | 7581 | *pp = p; |
| 7017 | 7582 | ||
| 7018 | n = append_str_maybe_ifs_split(output, n, first_ch, val); | 7583 | n = append_str_maybe_ifs_split(output, n, /*quoted:*/ (arg0 & 0x80), val); |
| 7019 | 7584 | ||
| 7020 | free(to_be_freed); | 7585 | free(to_be_freed); |
| 7021 | return n; | 7586 | return n; |
| @@ -7143,7 +7708,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
| 7143 | G.last_exitcode = process_command_subs(&subst_result, arg); | 7708 | G.last_exitcode = process_command_subs(&subst_result, arg); |
| 7144 | G.expand_exitcode = G.last_exitcode; | 7709 | G.expand_exitcode = G.last_exitcode; |
| 7145 | debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); | 7710 | debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); |
| 7146 | n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data); | 7711 | n = append_str_maybe_ifs_split(output, n, /*quoted:*/(first_ch & 0x80), subst_result.data); |
| 7147 | o_free(&subst_result); | 7712 | o_free(&subst_result); |
| 7148 | break; | 7713 | break; |
| 7149 | } | 7714 | } |
| @@ -7161,7 +7726,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
| 7161 | sprintf(arith_buf, ARITH_FMT, res); | 7726 | sprintf(arith_buf, ARITH_FMT, res); |
| 7162 | if (res < 0 | 7727 | if (res < 0 |
| 7163 | && first_ch == (char)('+'|0x80) | 7728 | && first_ch == (char)('+'|0x80) |
| 7164 | /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */ | 7729 | /* && (output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) */ |
| 7165 | ) { | 7730 | ) { |
| 7166 | /* Quoted negative ariths, like filename[0"$((-9))"], | 7731 | /* Quoted negative ariths, like filename[0"$((-9))"], |
| 7167 | * should not be interpreted as glob ranges. | 7732 | * should not be interpreted as glob ranges. |
| @@ -7176,7 +7741,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
| 7176 | #endif | 7741 | #endif |
| 7177 | default: | 7742 | default: |
| 7178 | /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */ | 7743 | /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */ |
| 7179 | n = expand_one_var(output, n, first_ch, arg, &p); | 7744 | n = expand_one_var(output, n, arg, &p); |
| 7180 | break; | 7745 | break; |
| 7181 | } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ | 7746 | } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ |
| 7182 | 7747 | ||
| @@ -7244,7 +7809,7 @@ static char **expand_variables(char **argv, unsigned expflags) | |||
| 7244 | 7809 | ||
| 7245 | static char **expand_strvec_to_strvec(char **argv) | 7810 | static char **expand_strvec_to_strvec(char **argv) |
| 7246 | { | 7811 | { |
| 7247 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); | 7812 | return expand_variables(argv, EXP_FLAG_DO_GLOBBING | EXP_FLAG_GLOBPROTECT_CHARS); |
| 7248 | } | 7813 | } |
| 7249 | 7814 | ||
| 7250 | #if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) | 7815 | #if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
| @@ -7262,10 +7827,6 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | |||
| 7262 | */ | 7827 | */ |
| 7263 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash) | 7828 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash) |
| 7264 | { | 7829 | { |
| 7265 | #if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE | ||
| 7266 | const int do_unbackslash = 1; | ||
| 7267 | const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS; | ||
| 7268 | #endif | ||
| 7269 | char *argv[2], **list; | 7830 | char *argv[2], **list; |
| 7270 | 7831 | ||
| 7271 | debug_printf_expand("string_to_string<='%s'\n", str); | 7832 | debug_printf_expand("string_to_string<='%s'\n", str); |
| @@ -7334,7 +7895,7 @@ static char **expand_assignments(char **argv, int count) | |||
| 7334 | for (i = 0; i < count; i++) { | 7895 | for (i = 0; i < count; i++) { |
| 7335 | p = add_string_to_strings(p, | 7896 | p = add_string_to_strings(p, |
| 7336 | expand_string_to_string(argv[i], | 7897 | expand_string_to_string(argv[i], |
| 7337 | EXP_FLAG_ESC_GLOB_CHARS, | 7898 | EXP_FLAG_GLOBPROTECT_CHARS, |
| 7338 | /*unbackslash:*/ 1 | 7899 | /*unbackslash:*/ 1 |
| 7339 | ) | 7900 | ) |
| 7340 | ); | 7901 | ); |
| @@ -7366,11 +7927,6 @@ static void switch_off_special_sigs(unsigned mask) | |||
| 7366 | } | 7927 | } |
| 7367 | 7928 | ||
| 7368 | #if BB_MMU | 7929 | #if BB_MMU |
| 7369 | /* never called */ | ||
| 7370 | void re_execute_shell(char ***to_free, const char *s, | ||
| 7371 | char *g_argv0, char **g_argv, | ||
| 7372 | char **builtin_argv) NORETURN; | ||
| 7373 | |||
| 7374 | static void reset_traps_to_defaults(void) | 7930 | static void reset_traps_to_defaults(void) |
| 7375 | { | 7931 | { |
| 7376 | /* This function is always called in a child shell | 7932 | /* This function is always called in a child shell |
| @@ -7420,10 +7976,8 @@ static void reset_traps_to_defaults(void) | |||
| 7420 | 7976 | ||
| 7421 | #else /* !BB_MMU */ | 7977 | #else /* !BB_MMU */ |
| 7422 | 7978 | ||
| 7423 | static void re_execute_shell(char ***to_free, const char *s, | 7979 | static NORETURN void re_execute_shell( |
| 7424 | char *g_argv0, char **g_argv, | 7980 | char * *volatile * to_free, const char *s, |
| 7425 | char **builtin_argv) NORETURN; | ||
| 7426 | static void re_execute_shell(char ***to_free, const char *s, | ||
| 7427 | char *g_argv0, char **g_argv, | 7981 | char *g_argv0, char **g_argv, |
| 7428 | char **builtin_argv) | 7982 | char **builtin_argv) |
| 7429 | { | 7983 | { |
| @@ -7653,7 +8207,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p) | |||
| 7653 | pid_t pid; | 8207 | pid_t pid; |
| 7654 | int channel[2]; | 8208 | int channel[2]; |
| 7655 | # if !BB_MMU | 8209 | # if !BB_MMU |
| 7656 | char **to_free = NULL; | 8210 | /* _volatile_ pointer to "char*". |
| 8211 | * Or else compiler can peek from inside re_execute_shell() | ||
| 8212 | * and see that this pointer is a local var (i.e. not globally visible), | ||
| 8213 | * and decide to optimize out the store to it. Yes, | ||
| 8214 | * it was seen in the wild. | ||
| 8215 | */ | ||
| 8216 | char * *volatile to_free = NULL; | ||
| 7657 | # endif | 8217 | # endif |
| 7658 | 8218 | ||
| 7659 | xpipe(channel); | 8219 | xpipe(channel); |
| @@ -7800,16 +8360,29 @@ static void setup_heredoc(struct redir_struct *redir) | |||
| 7800 | const char *heredoc = redir->rd_filename; | 8360 | const char *heredoc = redir->rd_filename; |
| 7801 | char *expanded; | 8361 | char *expanded; |
| 7802 | #if !BB_MMU | 8362 | #if !BB_MMU |
| 7803 | char **to_free; | 8363 | char * *volatile to_free; |
| 7804 | #endif | 8364 | #endif |
| 7805 | 8365 | ||
| 7806 | expanded = NULL; | 8366 | expanded = NULL; |
| 7807 | if (!(redir->rd_dup & HEREDOC_QUOTED)) { | 8367 | if (redir->rd_type == REDIRECT_HERESTRING) { |
| 8368 | heredoc = expanded = expand_string_to_string(heredoc, | ||
| 8369 | EXP_FLAG_GLOBPROTECT_CHARS, | ||
| 8370 | /* ^^^^why? testcases: | ||
| 8371 | * cat <<<`echo '_\t_\\_\"_'` prints _\t_\_\"_<newline> | ||
| 8372 | * cat <<<"`echo '_\t_\\_\"_'`" prints _\t_\_"_<newline> | ||
| 8373 | */ | ||
| 8374 | /*unbackslash:*/ 1 | ||
| 8375 | /* ^^^^^^^^^^ cat <<<\_ prints _<newline> */ | ||
| 8376 | ); | ||
| 8377 | } else if (!(redir->rd_dup & HEREDOC_QUOTED)) { | ||
| 7808 | expanded = encode_then_expand_string(heredoc); | 8378 | expanded = encode_then_expand_string(heredoc); |
| 7809 | if (expanded) | 8379 | if (expanded) |
| 7810 | heredoc = expanded; | 8380 | heredoc = expanded; |
| 7811 | } | 8381 | } |
| 8382 | |||
| 7812 | len = strlen(heredoc); | 8383 | len = strlen(heredoc); |
| 8384 | if (redir->rd_type == REDIRECT_HERESTRING) | ||
| 8385 | expanded[len++] = '\n'; | ||
| 7813 | 8386 | ||
| 7814 | close(redir->rd_fd); /* often saves dup2+close in xmove_fd */ | 8387 | close(redir->rd_fd); /* often saves dup2+close in xmove_fd */ |
| 7815 | xpiped_pair(pair); | 8388 | xpiped_pair(pair); |
| @@ -8053,7 +8626,7 @@ static void restore_redirects(struct squirrel *sq) | |||
| 8053 | * Redirect moves ->fd to e.g. 10, | 8626 | * Redirect moves ->fd to e.g. 10, |
| 8054 | * and it is not restored above (we do not restore script fds | 8627 | * and it is not restored above (we do not restore script fds |
| 8055 | * after redirects, we just use new, "moved" fds). | 8628 | * after redirects, we just use new, "moved" fds). |
| 8056 | * However for stdin, get_user_input() -> read_line_input(), | 8629 | * However for stdin, show_prompt_and_get_stdin() -> read_line_input(), |
| 8057 | * and read builtin, depend on fd == STDIN_FILENO. | 8630 | * and read builtin, depend on fd == STDIN_FILENO. |
| 8058 | */ | 8631 | */ |
| 8059 | debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd); | 8632 | debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd); |
| @@ -8105,12 +8678,14 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
| 8105 | int newfd; | 8678 | int newfd; |
| 8106 | int closed; | 8679 | int closed; |
| 8107 | 8680 | ||
| 8108 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 8681 | if (redir->rd_type == REDIRECT_HEREDOC2 |
| 8109 | /* "rd_fd<<HERE" case */ | 8682 | || redir->rd_type == REDIRECT_HERESTRING |
| 8683 | ) { | ||
| 8684 | /* "rd_fd<<HEREDOC_EOF" and "rd_fd<<<WORD" cases */ | ||
| 8110 | if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0) | 8685 | if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0) |
| 8111 | return 1; | 8686 | return 1; |
| 8112 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 8687 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ of the heredoc */ |
| 8113 | * of the heredoc */ | 8688 | /* for REDIRECT_HERESTRING, rd_filename holds "WORD" */ |
| 8114 | debug_printf_redir("set heredoc '%s'\n", | 8689 | debug_printf_redir("set heredoc '%s'\n", |
| 8115 | redir->rd_filename); | 8690 | redir->rd_filename); |
| 8116 | setup_heredoc(redir); | 8691 | setup_heredoc(redir); |
| @@ -8132,7 +8707,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
| 8132 | } | 8707 | } |
| 8133 | mode = redir_table[redir->rd_type].mode; | 8708 | mode = redir_table[redir->rd_type].mode; |
| 8134 | p = expand_string_to_string(redir->rd_filename, | 8709 | p = expand_string_to_string(redir->rd_filename, |
| 8135 | EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); | 8710 | EXP_FLAG_GLOBPROTECT_CHARS, |
| 8711 | /*unbackslash:*/ 1 | ||
| 8712 | ); | ||
| 8136 | newfd = open_or_warn(p, mode); | 8713 | newfd = open_or_warn(p, mode); |
| 8137 | free(p); | 8714 | free(p); |
| 8138 | if (newfd < 0) { | 8715 | if (newfd < 0) { |
| @@ -8275,7 +8852,7 @@ static const struct built_in_command *find_builtin(const char *name) | |||
| 8275 | return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); | 8852 | return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); |
| 8276 | } | 8853 | } |
| 8277 | 8854 | ||
| 8278 | #if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION | 8855 | #if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION |
| 8279 | static const char * FAST_FUNC hush_command_name(int i) | 8856 | static const char * FAST_FUNC hush_command_name(int i) |
| 8280 | { | 8857 | { |
| 8281 | if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { | 8858 | if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { |
| @@ -8441,10 +9018,8 @@ static void unset_func(const char *name) | |||
| 8441 | #define exec_function(to_free, funcp, argv) \ | 9018 | #define exec_function(to_free, funcp, argv) \ |
| 8442 | exec_function(funcp, argv) | 9019 | exec_function(funcp, argv) |
| 8443 | # endif | 9020 | # endif |
| 8444 | static void exec_function(char ***to_free, | 9021 | static NORETURN void exec_function( |
| 8445 | const struct function *funcp, | 9022 | char * *volatile *to_free, |
| 8446 | char **argv) NORETURN; | ||
| 8447 | static void exec_function(char ***to_free, | ||
| 8448 | const struct function *funcp, | 9023 | const struct function *funcp, |
| 8449 | char **argv) | 9024 | char **argv) |
| 8450 | { | 9025 | { |
| @@ -8540,10 +9115,8 @@ static int run_function(const struct function *funcp, char **argv) | |||
| 8540 | #define exec_builtin(to_free, x, argv) \ | 9115 | #define exec_builtin(to_free, x, argv) \ |
| 8541 | exec_builtin(to_free, argv) | 9116 | exec_builtin(to_free, argv) |
| 8542 | #endif | 9117 | #endif |
| 8543 | static void exec_builtin(char ***to_free, | 9118 | static NORETURN void exec_builtin( |
| 8544 | const struct built_in_command *x, | 9119 | char * *volatile *to_free, |
| 8545 | char **argv) NORETURN; | ||
| 8546 | static void exec_builtin(char ***to_free, | ||
| 8547 | const struct built_in_command *x, | 9120 | const struct built_in_command *x, |
| 8548 | char **argv) | 9121 | char **argv) |
| 8549 | { | 9122 | { |
| @@ -8566,8 +9139,7 @@ static void exec_builtin(char ***to_free, | |||
| 8566 | #endif | 9139 | #endif |
| 8567 | } | 9140 | } |
| 8568 | 9141 | ||
| 8569 | static void execvp_or_die(char **argv) NORETURN; | 9142 | static NORETURN void execvp_or_die(char **argv) |
| 8570 | static void execvp_or_die(char **argv) | ||
| 8571 | { | 9143 | { |
| 8572 | int e; | 9144 | int e; |
| 8573 | debug_printf_exec("execing '%s'\n", argv[0]); | 9145 | debug_printf_exec("execing '%s'\n", argv[0]); |
| @@ -8688,10 +9260,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp | |||
| 8688 | * The at_exit handlers apparently confuse the calling process, | 9260 | * The at_exit handlers apparently confuse the calling process, |
| 8689 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) | 9261 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) |
| 8690 | */ | 9262 | */ |
| 8691 | static void pseudo_exec_argv(nommu_save_t *nommu_save, | 9263 | static NORETURN NOINLINE void pseudo_exec_argv( |
| 8692 | char **argv, int assignment_cnt, | 9264 | volatile nommu_save_t *nommu_save, |
| 8693 | char **argv_expanded) NORETURN; | ||
| 8694 | static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | ||
| 8695 | char **argv, int assignment_cnt, | 9265 | char **argv, int assignment_cnt, |
| 8696 | char **argv_expanded) | 9266 | char **argv_expanded) |
| 8697 | { | 9267 | { |
| @@ -8717,7 +9287,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
| 8717 | #if BB_MMU | 9287 | #if BB_MMU |
| 8718 | G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ | 9288 | G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ |
| 8719 | #else | 9289 | #else |
| 8720 | G.shadowed_vars_pp = &nommu_save->old_vars; | 9290 | /* cast away volatility */ |
| 9291 | G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars; | ||
| 8721 | G.var_nest_level++; | 9292 | G.var_nest_level++; |
| 8722 | #endif | 9293 | #endif |
| 8723 | set_vars_and_save_old(new_env); | 9294 | set_vars_and_save_old(new_env); |
| @@ -8844,10 +9415,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
| 8844 | 9415 | ||
| 8845 | /* Called after [v]fork() in run_pipe | 9416 | /* Called after [v]fork() in run_pipe |
| 8846 | */ | 9417 | */ |
| 8847 | static void pseudo_exec(nommu_save_t *nommu_save, | 9418 | static NORETURN void pseudo_exec( |
| 8848 | struct command *command, | 9419 | volatile nommu_save_t *nommu_save, |
| 8849 | char **argv_expanded) NORETURN; | ||
| 8850 | static void pseudo_exec(nommu_save_t *nommu_save, | ||
| 8851 | struct command *command, | 9420 | struct command *command, |
| 8852 | char **argv_expanded) | 9421 | char **argv_expanded) |
| 8853 | { | 9422 | { |
| @@ -8874,13 +9443,14 @@ static void pseudo_exec(nommu_save_t *nommu_save, | |||
| 8874 | */ | 9443 | */ |
| 8875 | #if BB_MMU | 9444 | #if BB_MMU |
| 8876 | int rcode; | 9445 | int rcode; |
| 8877 | debug_printf_exec("pseudo_exec: run_list\n"); | 9446 | debug_printf_exec("pseudo_exec: run_list(command->group)\n"); |
| 8878 | reset_traps_to_defaults(); | 9447 | reset_traps_to_defaults(); |
| 8879 | rcode = run_list(command->group); | 9448 | rcode = run_list(command->group); |
| 8880 | /* OK to leak memory by not calling free_pipe_list, | 9449 | /* OK to leak memory by not calling free_pipe_list, |
| 8881 | * since this process is about to exit */ | 9450 | * since this process is about to exit */ |
| 8882 | _exit(rcode); | 9451 | _exit(rcode); |
| 8883 | #else | 9452 | #else |
| 9453 | debug_printf_exec("pseudo_exec: re_exec(command->group_as_string)\n"); | ||
| 8884 | re_execute_shell(&nommu_save->argv_from_re_execing, | 9454 | re_execute_shell(&nommu_save->argv_from_re_execing, |
| 8885 | command->group_as_string, | 9455 | command->group_as_string, |
| 8886 | G.global_argv[0], | 9456 | G.global_argv[0], |
| @@ -9456,7 +10026,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 9456 | i = 0; | 10026 | i = 0; |
| 9457 | while (i < command->assignment_cnt) { | 10027 | while (i < command->assignment_cnt) { |
| 9458 | char *p = expand_string_to_string(argv[i], | 10028 | char *p = expand_string_to_string(argv[i], |
| 9459 | EXP_FLAG_ESC_GLOB_CHARS, | 10029 | EXP_FLAG_GLOBPROTECT_CHARS, |
| 9460 | /*unbackslash:*/ 1 | 10030 | /*unbackslash:*/ 1 |
| 9461 | ); | 10031 | ); |
| 9462 | #if ENABLE_HUSH_MODE_X | 10032 | #if ENABLE_HUSH_MODE_X |
| @@ -9678,13 +10248,20 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 9678 | #if ENABLE_HUSH_LINENO_VAR | 10248 | #if ENABLE_HUSH_LINENO_VAR |
| 9679 | G.execute_lineno = command->lineno; | 10249 | G.execute_lineno = command->lineno; |
| 9680 | #endif | 10250 | #endif |
| 9681 | |||
| 9682 | command->pid = BB_MMU ? fork() : vfork(); | 10251 | command->pid = BB_MMU ? fork() : vfork(); |
| 9683 | if (!command->pid) { /* child */ | 10252 | if (!command->pid) { /* child */ |
| 9684 | #if ENABLE_HUSH_JOB | ||
| 9685 | disable_restore_tty_pgrp_on_exit(); | 10253 | disable_restore_tty_pgrp_on_exit(); |
| 9686 | CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ | ||
| 9687 | 10254 | ||
| 10255 | #if ENABLE_HUSH_INTERACTIVE | ||
| 10256 | if (BB_MMU && pi->followup == PIPE_BG) { | ||
| 10257 | /* This disables alias expansion in . FILE & */ | ||
| 10258 | /* (bash does it only for (. FILE)& */ | ||
| 10259 | G.interactive_fd = 0; | ||
| 10260 | } | ||
| 10261 | #endif | ||
| 10262 | if (BB_MMU) | ||
| 10263 | CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ | ||
| 10264 | #if ENABLE_HUSH_JOB | ||
| 9688 | /* Every child adds itself to new process group | 10265 | /* Every child adds itself to new process group |
| 9689 | * with pgid == pid_of_first_child_in_pipe */ | 10266 | * with pgid == pid_of_first_child_in_pipe */ |
| 9690 | if (G.run_list_level == 1 && G_interactive_fd) { | 10267 | if (G.run_list_level == 1 && G_interactive_fd) { |
| @@ -9732,8 +10309,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 9732 | 10309 | ||
| 9733 | /* Stores to nommu_save list of env vars putenv'ed | 10310 | /* Stores to nommu_save list of env vars putenv'ed |
| 9734 | * (NOMMU, on MMU we don't need that) */ | 10311 | * (NOMMU, on MMU we don't need that) */ |
| 9735 | /* cast away volatility... */ | 10312 | pseudo_exec(&nommu_save, command, argv_expanded); |
| 9736 | pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded); | ||
| 9737 | /* pseudo_exec() does not return */ | 10313 | /* pseudo_exec() does not return */ |
| 9738 | } | 10314 | } |
| 9739 | 10315 | ||
| @@ -9978,7 +10554,9 @@ static int run_list(struct pipe *pi) | |||
| 9978 | if (rword == RES_CASE) { | 10554 | if (rword == RES_CASE) { |
| 9979 | debug_printf_exec("CASE cond_code:%d\n", cond_code); | 10555 | debug_printf_exec("CASE cond_code:%d\n", cond_code); |
| 9980 | case_word = expand_string_to_string(pi->cmds->argv[0], | 10556 | case_word = expand_string_to_string(pi->cmds->argv[0], |
| 9981 | EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); | 10557 | EXP_FLAG_GLOBPROTECT_CHARS, |
| 10558 | /*unbackslash:*/ 1 | ||
| 10559 | ); | ||
| 9982 | debug_printf_exec("CASE word1:'%s'\n", case_word); | 10560 | debug_printf_exec("CASE word1:'%s'\n", case_word); |
| 9983 | //unbackslash(case_word); | 10561 | //unbackslash(case_word); |
| 9984 | //debug_printf_exec("CASE word2:'%s'\n", case_word); | 10562 | //debug_printf_exec("CASE word2:'%s'\n", case_word); |
| @@ -9996,7 +10574,7 @@ static int run_list(struct pipe *pi) | |||
| 9996 | char *pattern; | 10574 | char *pattern; |
| 9997 | debug_printf_exec("expand_string_to_string('%s')\n", *argv); | 10575 | debug_printf_exec("expand_string_to_string('%s')\n", *argv); |
| 9998 | pattern = expand_string_to_string(*argv, | 10576 | pattern = expand_string_to_string(*argv, |
| 9999 | EXP_FLAG_ESC_GLOB_CHARS, | 10577 | EXP_FLAG_GLOBPROTECT_CHARS, |
| 10000 | /*unbackslash:*/ 0 | 10578 | /*unbackslash:*/ 0 |
| 10001 | ); | 10579 | ); |
| 10002 | /* TODO: which FNM_xxx flags to use? */ | 10580 | /* TODO: which FNM_xxx flags to use? */ |
| @@ -10121,7 +10699,7 @@ static int run_list(struct pipe *pi) | |||
| 10121 | if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { | 10699 | if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { |
| 10122 | debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); | 10700 | debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); |
| 10123 | if (G.errexit_depth == 0) | 10701 | if (G.errexit_depth == 0) |
| 10124 | hush_exit(rcode); | 10702 | save_history_run_exit_trap_and_exit(rcode); |
| 10125 | } | 10703 | } |
| 10126 | G.errexit_depth = sv_errexit_depth; | 10704 | G.errexit_depth = sv_errexit_depth; |
| 10127 | 10705 | ||
| @@ -10195,6 +10773,53 @@ static int run_and_free_list(struct pipe *pi) | |||
| 10195 | /* | 10773 | /* |
| 10196 | * Initialization and main | 10774 | * Initialization and main |
| 10197 | */ | 10775 | */ |
| 10776 | #if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING | ||
| 10777 | static void init_line_editing(void) | ||
| 10778 | { | ||
| 10779 | G.line_input_state = new_line_input_t(FOR_SHELL); | ||
| 10780 | # if ENABLE_FEATURE_TAB_COMPLETION | ||
| 10781 | G.line_input_state->get_exe_name = hush_command_name; | ||
| 10782 | # endif | ||
| 10783 | # if EDITING_HAS_sh_get_var | ||
| 10784 | G.line_input_state->sh_get_var = get_local_var_value; | ||
| 10785 | # endif | ||
| 10786 | # if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 | ||
| 10787 | { | ||
| 10788 | const char *hp = get_local_var_value("HISTFILE"); | ||
| 10789 | if (!hp) { | ||
| 10790 | hp = get_local_var_value("HOME"); | ||
| 10791 | if (hp) { | ||
| 10792 | hp = concat_path_file(hp, ".hush_history"); | ||
| 10793 | /* Make HISTFILE set on exit (else history won't be saved) */ | ||
| 10794 | set_local_var_from_halves("HISTFILE", hp); | ||
| 10795 | } | ||
| 10796 | } else { | ||
| 10797 | hp = xstrdup(hp); | ||
| 10798 | } | ||
| 10799 | if (hp) { | ||
| 10800 | G.line_input_state->hist_file = hp; | ||
| 10801 | } | ||
| 10802 | # if ENABLE_FEATURE_SH_HISTFILESIZE | ||
| 10803 | hp = get_local_var_value("HISTSIZE"); | ||
| 10804 | /* Using HISTFILESIZE above to limit max_history would be WRONG: | ||
| 10805 | * users may set HISTFILESIZE=0 in their profile scripts | ||
| 10806 | * to prevent _saving_ of history files, but still want to have | ||
| 10807 | * non-zero history limit for in-memory list. | ||
| 10808 | */ | ||
| 10809 | // in bash, runtime history size is controlled by HISTSIZE (0=no history), | ||
| 10810 | // HISTFILESIZE controls on-disk history file size (in lines, 0=no history): | ||
| 10811 | G.line_input_state->max_history = size_from_HISTFILESIZE(hp); | ||
| 10812 | // HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files." | ||
| 10813 | // HISTSIZE: "The shell sets the default value to 500 after reading any startup files." | ||
| 10814 | // (meaning: if the value wasn't set after startup files, the default value is set as described above) | ||
| 10815 | # endif | ||
| 10816 | } | ||
| 10817 | # endif | ||
| 10818 | } | ||
| 10819 | #else | ||
| 10820 | # define init_line_editing() ((void)0) | ||
| 10821 | #endif | ||
| 10822 | |||
| 10198 | static void install_sighandlers(unsigned mask) | 10823 | static void install_sighandlers(unsigned mask) |
| 10199 | { | 10824 | { |
| 10200 | sighandler_t old_handler; | 10825 | sighandler_t old_handler; |
| @@ -10333,7 +10958,6 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
| 10333 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 10958 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 10334 | int hush_main(int argc, char **argv) | 10959 | int hush_main(int argc, char **argv) |
| 10335 | { | 10960 | { |
| 10336 | pid_t cached_getpid; | ||
| 10337 | enum { | 10961 | enum { |
| 10338 | OPT_login = (1 << 0), | 10962 | OPT_login = (1 << 0), |
| 10339 | }; | 10963 | }; |
| @@ -10346,6 +10970,11 @@ int hush_main(int argc, char **argv) | |||
| 10346 | struct variable *shell_ver; | 10970 | struct variable *shell_ver; |
| 10347 | 10971 | ||
| 10348 | INIT_G(); | 10972 | INIT_G(); |
| 10973 | #if ENABLE_HUSH_JOB | ||
| 10974 | die_func = fflush_restore_ttypgrp_and__exit; | ||
| 10975 | #else | ||
| 10976 | die_func = fflush_and__exit; | ||
| 10977 | #endif | ||
| 10349 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ | 10978 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ |
| 10350 | G.last_exitcode = EXIT_SUCCESS; | 10979 | G.last_exitcode = EXIT_SUCCESS; |
| 10351 | #if !BB_MMU | 10980 | #if !BB_MMU |
| @@ -10368,14 +10997,11 @@ int hush_main(int argc, char **argv) | |||
| 10368 | # endif | 10997 | # endif |
| 10369 | G.pre_trap_exitcode = -1; | 10998 | G.pre_trap_exitcode = -1; |
| 10370 | #endif | 10999 | #endif |
| 10371 | |||
| 10372 | #if ENABLE_HUSH_FAST | 11000 | #if ENABLE_HUSH_FAST |
| 10373 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | 11001 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ |
| 10374 | #endif | 11002 | #endif |
| 10375 | 11003 | G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ | |
| 10376 | cached_getpid = getpid(); /* for tcsetpgrp() during init */ | 11004 | G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */ |
| 10377 | G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ | ||
| 10378 | G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */ | ||
| 10379 | 11005 | ||
| 10380 | /* Deal with HUSH_VERSION */ | 11006 | /* Deal with HUSH_VERSION */ |
| 10381 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | 11007 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); |
| @@ -10424,7 +11050,7 @@ int hush_main(int argc, char **argv) | |||
| 10424 | if (!get_local_var_value("PATH")) | 11050 | if (!get_local_var_value("PATH")) |
| 10425 | set_local_var_from_halves("PATH", bb_default_root_path); | 11051 | set_local_var_from_halves("PATH", bb_default_root_path); |
| 10426 | 11052 | ||
| 10427 | /* PS1/PS2 are set later, if we determine that we are interactive */ | 11053 | /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */ |
| 10428 | 11054 | ||
| 10429 | /* bash also exports SHLVL and _, | 11055 | /* bash also exports SHLVL and _, |
| 10430 | * and sets (but doesn't export) the following variables: | 11056 | * and sets (but doesn't export) the following variables: |
| @@ -10446,7 +11072,6 @@ int hush_main(int argc, char **argv) | |||
| 10446 | * BASH_SOURCE=() | 11072 | * BASH_SOURCE=() |
| 10447 | * DIRSTACK=() | 11073 | * DIRSTACK=() |
| 10448 | * PIPESTATUS=([0]="0") | 11074 | * PIPESTATUS=([0]="0") |
| 10449 | * HISTFILE=/<xxx>/.bash_history | ||
| 10450 | * HISTFILESIZE=500 | 11075 | * HISTFILESIZE=500 |
| 10451 | * HISTSIZE=500 | 11076 | * HISTSIZE=500 |
| 10452 | * MAILCHECK=60 | 11077 | * MAILCHECK=60 |
| @@ -10459,27 +11084,24 @@ int hush_main(int argc, char **argv) | |||
| 10459 | * PS4='+ ' | 11084 | * PS4='+ ' |
| 10460 | */ | 11085 | */ |
| 10461 | 11086 | ||
| 11087 | /* Shell is non-interactive at first. We need to call | ||
| 11088 | * install_special_sighandlers() if we are going to execute "sh <script>", | ||
| 11089 | * "sh -c <cmds>" or login shell's /etc/profile and friends. | ||
| 11090 | * If we later decide that we are interactive, we run | ||
| 11091 | * install_special_sighandlers() in order to intercept more signals. | ||
| 11092 | */ | ||
| 11093 | install_special_sighandlers(); | ||
| 11094 | |||
| 10462 | #if NUM_SCRIPTS > 0 | 11095 | #if NUM_SCRIPTS > 0 |
| 10463 | if (argc < 0) { | 11096 | if (argc < 0) { |
| 10464 | char *script = get_script_content(-argc - 1); | 11097 | char *script = get_script_content(-argc - 1); |
| 10465 | G.global_argv = argv; | 11098 | G.global_argv = argv; |
| 10466 | G.global_argc = string_array_len(argv); | 11099 | G.global_argc = string_array_len(argv); |
| 10467 | //install_special_sighandlers(); - needed? | ||
| 10468 | parse_and_run_string(script); | 11100 | parse_and_run_string(script); |
| 10469 | goto final_return; | 11101 | goto final_return; |
| 10470 | } | 11102 | } |
| 10471 | #endif | 11103 | #endif |
| 10472 | 11104 | ||
| 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 */ | 11105 | /* Parse options */ |
| 10484 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ | 11106 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ |
| 10485 | flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; | 11107 | flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; |
| @@ -10543,6 +11165,7 @@ int hush_main(int argc, char **argv) | |||
| 10543 | case '$': { | 11165 | case '$': { |
| 10544 | unsigned long long empty_trap_mask; | 11166 | unsigned long long empty_trap_mask; |
| 10545 | 11167 | ||
| 11168 | G.reexeced_on_NOMMU = 1; | ||
| 10546 | G.root_pid = bb_strtou(optarg, &optarg, 16); | 11169 | G.root_pid = bb_strtou(optarg, &optarg, 16); |
| 10547 | optarg++; | 11170 | optarg++; |
| 10548 | G.root_ppid = bb_strtou(optarg, &optarg, 16); | 11171 | G.root_ppid = bb_strtou(optarg, &optarg, 16); |
| @@ -10556,7 +11179,6 @@ int hush_main(int argc, char **argv) | |||
| 10556 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); | 11179 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); |
| 10557 | if (empty_trap_mask != 0) { | 11180 | if (empty_trap_mask != 0) { |
| 10558 | IF_HUSH_TRAP(int sig;) | 11181 | IF_HUSH_TRAP(int sig;) |
| 10559 | install_special_sighandlers(); | ||
| 10560 | # if ENABLE_HUSH_TRAP | 11182 | # if ENABLE_HUSH_TRAP |
| 10561 | G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); | 11183 | G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); |
| 10562 | for (sig = 1; sig < NSIG; sig++) { | 11184 | for (sig = 1; sig < NSIG; sig++) { |
| @@ -10622,7 +11244,9 @@ int hush_main(int argc, char **argv) | |||
| 10622 | G.global_argv[0] = argv[0]; | 11244 | G.global_argv[0] = argv[0]; |
| 10623 | 11245 | ||
| 10624 | /* If we are login shell... */ | 11246 | /* If we are login shell... */ |
| 10625 | if (flags & OPT_login) { | 11247 | if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */ |
| 11248 | && (flags & OPT_login) | ||
| 11249 | ) { | ||
| 10626 | const char *hp = NULL; | 11250 | const char *hp = NULL; |
| 10627 | HFILE *input; | 11251 | HFILE *input; |
| 10628 | 11252 | ||
| @@ -10630,7 +11254,6 @@ int hush_main(int argc, char **argv) | |||
| 10630 | input = hfopen("/etc/profile"); | 11254 | input = hfopen("/etc/profile"); |
| 10631 | run_profile: | 11255 | run_profile: |
| 10632 | if (input != NULL) { | 11256 | if (input != NULL) { |
| 10633 | install_special_sighandlers(); | ||
| 10634 | parse_and_run_file(input); | 11257 | parse_and_run_file(input); |
| 10635 | hfclose(input); | 11258 | hfclose(input); |
| 10636 | } | 11259 | } |
| @@ -10652,6 +11275,10 @@ int hush_main(int argc, char **argv) | |||
| 10652 | } | 11275 | } |
| 10653 | } | 11276 | } |
| 10654 | } | 11277 | } |
| 11278 | /* "hush -l" doesn't die in startup scripts, but after -l is processed, | ||
| 11279 | * it can die if scripts. Set the flag to achieve this behavior. | ||
| 11280 | */ | ||
| 11281 | G.flag_startup_done = 1; | ||
| 10655 | 11282 | ||
| 10656 | /* -c takes effect *after* -l */ | 11283 | /* -c takes effect *after* -l */ |
| 10657 | if (G.opt_c) { | 11284 | if (G.opt_c) { |
| @@ -10667,8 +11294,6 @@ int hush_main(int argc, char **argv) | |||
| 10667 | */ | 11294 | */ |
| 10668 | char *script; | 11295 | char *script; |
| 10669 | 11296 | ||
| 10670 | install_special_sighandlers(); | ||
| 10671 | |||
| 10672 | G.global_argc--; | 11297 | G.global_argc--; |
| 10673 | G.global_argv++; | 11298 | G.global_argv++; |
| 10674 | #if !BB_MMU | 11299 | #if !BB_MMU |
| @@ -10719,7 +11344,6 @@ int hush_main(int argc, char **argv) | |||
| 10719 | bb_simple_perror_msg_and_die(G.global_argv[0]); | 11344 | bb_simple_perror_msg_and_die(G.global_argv[0]); |
| 10720 | } | 11345 | } |
| 10721 | xfunc_error_retval = 1; | 11346 | xfunc_error_retval = 1; |
| 10722 | install_special_sighandlers(); | ||
| 10723 | parse_and_run_file(input); | 11347 | parse_and_run_file(input); |
| 10724 | #if ENABLE_FEATURE_CLEAN_UP | 11348 | #if ENABLE_FEATURE_CLEAN_UP |
| 10725 | hfclose(input); | 11349 | hfclose(input); |
| @@ -10735,126 +11359,86 @@ int hush_main(int argc, char **argv) | |||
| 10735 | 11359 | ||
| 10736 | /* A shell is interactive if the '-i' flag was given, | 11360 | /* A shell is interactive if the '-i' flag was given, |
| 10737 | * or if all of the following conditions are met: | 11361 | * or if all of the following conditions are met: |
| 10738 | * no -c command | 11362 | * not -c 'CMD' |
| 10739 | * no arguments remaining or the -s flag given | 11363 | * not running a script (no arguments remaining, or -s flag given) |
| 10740 | * standard input is a terminal | 11364 | * standard input is a terminal |
| 10741 | * standard output is a terminal | 11365 | * standard output is a terminal |
| 10742 | * Refer to Posix.2, the description of the 'sh' utility. | 11366 | * Refer to Posix.2, the description of the 'sh' utility. |
| 10743 | */ | 11367 | */ |
| 10744 | #if ENABLE_HUSH_JOB | 11368 | #if ENABLE_HUSH_INTERACTIVE |
| 10745 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { | 11369 | if (!G_reexeced_on_NOMMU |
| 10746 | G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); | 11370 | && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) |
| 10747 | debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); | 11371 | ) { |
| 10748 | if (G_saved_tty_pgrp < 0) | 11372 | /* 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); | 11373 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); |
| 10753 | if (G_interactive_fd < 0) { | 11374 | if (G_interactive_fd < 0) { |
| 10754 | /* try to dup to any fd */ | 11375 | /* Try to dup to any fd */ |
| 10755 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); | 11376 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); |
| 10756 | if (G_interactive_fd < 0) { | 11377 | if (G_interactive_fd < 0) |
| 10757 | /* give up */ | 11378 | /* Give up */ |
| 10758 | G_interactive_fd = 0; | 11379 | G_interactive_fd = 0; |
| 10759 | G_saved_tty_pgrp = 0; | ||
| 10760 | } | ||
| 10761 | } | 11380 | } |
| 10762 | } | 11381 | debug_printf("interactive_fd:%d\n", G_interactive_fd); |
| 10763 | debug_printf("interactive_fd:%d\n", G_interactive_fd); | 11382 | if (G_interactive_fd) { |
| 10764 | if (G_interactive_fd) { | 11383 | // TODO? bash: |
| 10765 | if (G_saved_tty_pgrp) { | 11384 | // if interactive but not a login shell, sources ~/.bashrc |
| 10766 | /* If we were run as 'hush &', sleep until we are | 11385 | // (--norc turns this off, --rcfile <file> overrides) |
| 10767 | * in the foreground (tty pgrp == our pgrp). | 11386 | # if ENABLE_HUSH_JOB |
| 10768 | * If we get started under a job aware app (like bash), | 11387 | /* Can we do job control? */ |
| 10769 | * make sure we are now in charge so we don't fight over | 11388 | G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); |
| 10770 | * who gets the foreground */ | 11389 | debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); |
| 10771 | while (1) { | 11390 | if (G_saved_tty_pgrp < 0) |
| 10772 | pid_t shell_pgrp = getpgrp(); | 11391 | G_saved_tty_pgrp = 0; /* no */ |
| 10773 | G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); | 11392 | if (G_saved_tty_pgrp) { |
| 10774 | if (G_saved_tty_pgrp == shell_pgrp) | 11393 | /* If we were run as 'hush &', sleep until we are |
| 10775 | break; | 11394 | * in the foreground (tty pgrp == our pgrp). |
| 10776 | /* send TTIN to ourself (should stop us) */ | 11395 | * If we get started under a job aware app (like bash), |
| 10777 | kill(- shell_pgrp, SIGTTIN); | 11396 | * make sure we are now in charge so we don't fight over |
| 11397 | * who gets the foreground */ | ||
| 11398 | while (1) { | ||
| 11399 | pid_t shell_pgrp = getpgrp(); | ||
| 11400 | if (G_saved_tty_pgrp == shell_pgrp) { | ||
| 11401 | /* Often both pgrps here are set to our pid - but not always! | ||
| 11402 | * Example: sh -c 'echo $$; hush; echo FIN' | ||
| 11403 | * Here, the parent shell is not interactive, so it does NOT set up | ||
| 11404 | * a separate process group for its children, and we (hush) initially | ||
| 11405 | * run in parent's process group (until we set up our own a few lines down). | ||
| 11406 | */ | ||
| 11407 | //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp); | ||
| 11408 | break; | ||
| 11409 | } | ||
| 11410 | /* Send TTIN to ourself (should stop us) */ | ||
| 11411 | kill(- shell_pgrp, SIGTTIN); | ||
| 11412 | G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); | ||
| 11413 | } | ||
| 10778 | } | 11414 | } |
| 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 | 11415 | # endif |
| 10804 | # if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 | 11416 | /* Install more signal handlers */ |
| 10805 | { | 11417 | install_special_sighandlers(); |
| 10806 | const char *hp = get_local_var_value("HISTFILE"); | 11418 | # if ENABLE_HUSH_JOB |
| 10807 | if (!hp) { | 11419 | if (G_saved_tty_pgrp) { |
| 10808 | hp = get_local_var_value("HOME"); | 11420 | /* Set fatal signals to restore saved_tty_pgrp */ |
| 10809 | if (hp) | 11421 | install_fatal_sighandlers(); |
| 10810 | hp = concat_path_file(hp, ".hush_history"); | 11422 | /* (The if() is an optimization: can avoid two redundant syscalls) */ |
| 10811 | } else { | 11423 | if (G_saved_tty_pgrp != G.root_pid) { |
| 10812 | hp = xstrdup(hp); | 11424 | /* Put ourselves in our own process group |
| 10813 | } | 11425 | * (bash, too, does this only if ctty is available) */ |
| 10814 | if (hp) { | 11426 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ |
| 10815 | G.line_input_state->hist_file = hp; | 11427 | /* Grab control of the terminal */ |
| 10816 | //set_local_var(xasprintf("HISTFILE=%s", ...)); | 11428 | tcsetpgrp(G_interactive_fd, G.root_pid); |
| 11429 | } | ||
| 10817 | } | 11430 | } |
| 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 | 11431 | # endif |
| 10824 | } else { | 11432 | # if ENABLE_FEATURE_EDITING_FANCY_PROMPT |
| 10825 | install_special_sighandlers(); | 11433 | /* Set (but not export) PS1/2 unless already set */ |
| 10826 | } | 11434 | if (!get_local_var_value("PS1")) |
| 10827 | #elif ENABLE_HUSH_INTERACTIVE | 11435 | set_local_var_from_halves("PS1", "\\w \\$ "); |
| 10828 | /* No job control compiled in, only prompt/line editing */ | 11436 | if (!get_local_var_value("PS2")) |
| 10829 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { | 11437 | set_local_var_from_halves("PS2", "> "); |
| 10830 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); | 11438 | # endif |
| 10831 | if (G_interactive_fd < 0) { | 11439 | 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 | 11440 | ||
| 10849 | if (G_interactive_fd) { | 11441 | # 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 */ | 11442 | /* note: ash and hush share this string */ |
| 10859 | printf("\n\n%s %s\n" | 11443 | printf("\n\n%s %s\n" |
| 10860 | IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") | 11444 | IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") |
| @@ -10862,13 +11446,15 @@ int hush_main(int argc, char **argv) | |||
| 10862 | bb_banner, | 11446 | bb_banner, |
| 10863 | "hush - the humble shell" | 11447 | "hush - the humble shell" |
| 10864 | ); | 11448 | ); |
| 10865 | } | 11449 | # endif |
| 10866 | } | 11450 | } /* if become interactive */ |
| 11451 | } /* if on tty */ | ||
| 11452 | #endif /* if INTERACTIVE is allowed by build config */ | ||
| 10867 | 11453 | ||
| 10868 | parse_and_run_file(hfopen(NULL)); /* stdin */ | 11454 | parse_and_run_file(hfopen(NULL)); /* stdin */ |
| 10869 | 11455 | ||
| 10870 | final_return: | 11456 | final_return: |
| 10871 | hush_exit(G.last_exitcode); | 11457 | save_history_run_exit_trap_and_exit(G.last_exitcode); |
| 10872 | } | 11458 | } |
| 10873 | 11459 | ||
| 10874 | /* | 11460 | /* |
| @@ -11054,19 +11640,19 @@ static int FAST_FUNC builtin_exit(char **argv) | |||
| 11054 | * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" | 11640 | * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" |
| 11055 | */ | 11641 | */ |
| 11056 | 11642 | ||
| 11057 | /* note: EXIT trap is run by hush_exit */ | 11643 | /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */ |
| 11058 | argv = skip_dash_dash(argv); | 11644 | argv = skip_dash_dash(argv); |
| 11059 | if (argv[0] == NULL) { | 11645 | if (argv[0] == NULL) { |
| 11060 | #if ENABLE_HUSH_TRAP | 11646 | #if ENABLE_HUSH_TRAP |
| 11061 | if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ | 11647 | if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ |
| 11062 | hush_exit(G.pre_trap_exitcode); | 11648 | save_history_run_exit_trap_and_exit(G.pre_trap_exitcode); |
| 11063 | #endif | 11649 | #endif |
| 11064 | hush_exit(G.last_exitcode); | 11650 | save_history_run_exit_trap_and_exit(G.last_exitcode); |
| 11065 | } | 11651 | } |
| 11066 | /* mimic bash: exit 123abc == exit 255 + error msg */ | 11652 | /* mimic bash: exit 123abc == exit 255 + error msg */ |
| 11067 | xfunc_error_retval = 255; | 11653 | xfunc_error_retval = 255; |
| 11068 | /* bash: exit -2 == exit 254, no error msg */ | 11654 | /* bash: exit -2 == exit 254, no error msg */ |
| 11069 | hush_exit(xatoi(argv[0]) & 0xff); | 11655 | save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff); |
| 11070 | } | 11656 | } |
| 11071 | 11657 | ||
| 11072 | #if ENABLE_HUSH_TYPE | 11658 | #if ENABLE_HUSH_TYPE |
| @@ -11080,12 +11666,14 @@ static int FAST_FUNC builtin_type(char **argv) | |||
| 11080 | char *path = NULL; | 11666 | char *path = NULL; |
| 11081 | 11667 | ||
| 11082 | if (0) {} /* make conditional compile easier below */ | 11668 | if (0) {} /* make conditional compile easier below */ |
| 11083 | /*else if (find_alias(*argv)) | ||
| 11084 | type = "an alias";*/ | ||
| 11085 | # if ENABLE_HUSH_FUNCTIONS | 11669 | # if ENABLE_HUSH_FUNCTIONS |
| 11086 | else if (find_function(*argv)) | 11670 | else if (find_function(*argv)) |
| 11087 | type = "a function"; | 11671 | type = "a function"; |
| 11088 | # endif | 11672 | # endif |
| 11673 | # if ENABLE_HUSH_ALIAS | ||
| 11674 | else if (find_alias(*argv)) | ||
| 11675 | type = "an alias"; | ||
| 11676 | # endif | ||
| 11089 | else if (find_builtin(*argv)) | 11677 | else if (find_builtin(*argv)) |
| 11090 | type = "a shell builtin"; | 11678 | type = "a shell builtin"; |
| 11091 | else { | 11679 | else { |
| @@ -11172,6 +11760,11 @@ static int FAST_FUNC builtin_read(char **argv) | |||
| 11172 | goto again; | 11760 | goto again; |
| 11173 | } | 11761 | } |
| 11174 | 11762 | ||
| 11763 | if ((uintptr_t)r == 2) /* -t SEC timeout? */ | ||
| 11764 | /* bash: "The exit status is greater than 128 if the timeout is exceeded." */ | ||
| 11765 | /* The actual value observed with bash 5.2.15: */ | ||
| 11766 | return 128 + SIGALRM; | ||
| 11767 | |||
| 11175 | if ((uintptr_t)r > 1) { | 11768 | if ((uintptr_t)r > 1) { |
| 11176 | bb_simple_error_msg(r); | 11769 | bb_simple_error_msg(r); |
| 11177 | r = (char*)(uintptr_t)1; | 11770 | r = (char*)(uintptr_t)1; |
| @@ -11220,7 +11813,7 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
| 11220 | } | 11813 | } |
| 11221 | #endif | 11814 | #endif |
| 11222 | 11815 | ||
| 11223 | #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY || ENABLE_HUSH_SET || ENABLE_HUSH_TRAP | 11816 | #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY || ENABLE_HUSH_SET || ENABLE_HUSH_TRAP || ENABLE_HUSH_ALIAS |
| 11224 | static void print_escaped(const char *s) | 11817 | static void print_escaped(const char *s) |
| 11225 | { | 11818 | { |
| 11226 | //TODO? bash "set" does not quote variables which contain only alnums and "%+,-./:=@_~", | 11819 | //TODO? bash "set" does not quote variables which contain only alnums and "%+,-./:=@_~", |
| @@ -11245,6 +11838,18 @@ static void print_escaped(const char *s) | |||
| 11245 | putchar('"'); | 11838 | putchar('"'); |
| 11246 | } while (*s); | 11839 | } while (*s); |
| 11247 | } | 11840 | } |
| 11841 | static void print_pfx_escaped_nl(const char *pfx, const char *s) | ||
| 11842 | { | ||
| 11843 | const char *p = strchr(s, '='); | ||
| 11844 | if (p) { | ||
| 11845 | if (pfx) | ||
| 11846 | printf("%s %.*s", pfx, (int)(p - s) + 1, s); | ||
| 11847 | else | ||
| 11848 | printf("%.*s", (int)(p - s) + 1, s); | ||
| 11849 | print_escaped(p + 1); | ||
| 11850 | putchar('\n'); | ||
| 11851 | } | ||
| 11852 | } | ||
| 11248 | #endif | 11853 | #endif |
| 11249 | 11854 | ||
| 11250 | #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY | 11855 | #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY |
| @@ -11355,14 +11960,7 @@ static int FAST_FUNC builtin_export(char **argv) | |||
| 11355 | * bash: declare -x VAR="VAL" | 11960 | * bash: declare -x VAR="VAL" |
| 11356 | * we follow ash example */ | 11961 | * we follow ash example */ |
| 11357 | const char *s = *e++; | 11962 | const char *s = *e++; |
| 11358 | const char *p = strchr(s, '='); | 11963 | print_pfx_escaped_nl("export", s); |
| 11359 | |||
| 11360 | if (!p) /* wtf? take next variable */ | ||
| 11361 | continue; | ||
| 11362 | /* "export VAR=" */ | ||
| 11363 | printf("%s %.*s", "export", (int)(p - s) + 1, s); | ||
| 11364 | print_escaped(p + 1); | ||
| 11365 | putchar('\n'); | ||
| 11366 | # endif | 11964 | # endif |
| 11367 | } | 11965 | } |
| 11368 | /*fflush_all(); - done after each builtin anyway */ | 11966 | /*fflush_all(); - done after each builtin anyway */ |
| @@ -11404,15 +12002,7 @@ static int FAST_FUNC builtin_readonly(char **argv) | |||
| 11404 | struct variable *e; | 12002 | struct variable *e; |
| 11405 | for (e = G.top_var; e; e = e->next) { | 12003 | for (e = G.top_var; e; e = e->next) { |
| 11406 | if (e->flg_read_only) { | 12004 | if (e->flg_read_only) { |
| 11407 | const char *s = e->varstr; | 12005 | print_pfx_escaped_nl("readonly", e->varstr); |
| 11408 | const char *p = strchr(s, '='); | ||
| 11409 | |||
| 11410 | if (!p) /* wtf? take next variable */ | ||
| 11411 | continue; | ||
| 11412 | /* "readonly VAR=" */ | ||
| 11413 | printf("%s %.*s", "readonly", (int)(p - s) + 1, s); | ||
| 11414 | print_escaped(p + 1); | ||
| 11415 | putchar('\n'); | ||
| 11416 | } | 12006 | } |
| 11417 | } | 12007 | } |
| 11418 | return EXIT_SUCCESS; | 12008 | return EXIT_SUCCESS; |
| @@ -11491,15 +12081,7 @@ static int FAST_FUNC builtin_set(char **argv) | |||
| 11491 | if (arg == NULL) { | 12081 | if (arg == NULL) { |
| 11492 | struct variable *e; | 12082 | struct variable *e; |
| 11493 | for (e = G.top_var; e; e = e->next) { | 12083 | for (e = G.top_var; e; e = e->next) { |
| 11494 | const char *s = e->varstr; | 12084 | print_pfx_escaped_nl(NULL, e->varstr); |
| 11495 | const char *p = strchr(s, '='); | ||
| 11496 | |||
| 11497 | if (!p) /* wtf? take next variable */ | ||
| 11498 | continue; | ||
| 11499 | /* var= */ | ||
| 11500 | printf("%.*s", (int)(p - s) + 1, s); | ||
| 11501 | print_escaped(p + 1); | ||
| 11502 | putchar('\n'); | ||
| 11503 | } | 12085 | } |
| 11504 | return EXIT_SUCCESS; | 12086 | return EXIT_SUCCESS; |
| 11505 | } | 12087 | } |
| @@ -12059,8 +12641,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
| 12059 | * Or else we may race with SIGCHLD, lose it, | 12641 | * Or else we may race with SIGCHLD, lose it, |
| 12060 | * and get stuck in sigsuspend... | 12642 | * and get stuck in sigsuspend... |
| 12061 | */ | 12643 | */ |
| 12062 | sigfillset(&oldset); /* block all signals, remember old set */ | 12644 | sigblockall(&oldset); /* block all signals, remember old set */ |
| 12063 | sigprocmask2(SIG_SETMASK, &oldset); | ||
| 12064 | 12645 | ||
| 12065 | if (!sigisemptyset(&G.pending_set)) { | 12646 | if (!sigisemptyset(&G.pending_set)) { |
| 12066 | /* Crap! we raced with some signal! */ | 12647 | /* Crap! we raced with some signal! */ |
| @@ -12077,7 +12658,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
| 12077 | debug_printf_exec("job_exited_or_stopped:%d\n", rcode); | 12658 | debug_printf_exec("job_exited_or_stopped:%d\n", rcode); |
| 12078 | if (rcode >= 0) { | 12659 | if (rcode >= 0) { |
| 12079 | ret = rcode; | 12660 | ret = rcode; |
| 12080 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 12661 | sigprocmask2(SIG_SETMASK, &oldset); |
| 12081 | break; | 12662 | break; |
| 12082 | } | 12663 | } |
| 12083 | } | 12664 | } |
| @@ -12095,7 +12676,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
| 12095 | ret = 127; | 12676 | ret = 127; |
| 12096 | } | 12677 | } |
| 12097 | # endif | 12678 | # endif |
| 12098 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 12679 | sigprocmask2(SIG_SETMASK, &oldset); |
| 12099 | break; | 12680 | break; |
| 12100 | } | 12681 | } |
| 12101 | /* Wait for SIGCHLD or any other signal */ | 12682 | /* Wait for SIGCHLD or any other signal */ |
| @@ -12113,7 +12694,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
| 12113 | * making "wait" commands in SCRIPT block forever. | 12694 | * making "wait" commands in SCRIPT block forever. |
| 12114 | */ | 12695 | */ |
| 12115 | restore: | 12696 | restore: |
| 12116 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 12697 | sigprocmask2(SIG_SETMASK, &oldset); |
| 12117 | check_sig: | 12698 | check_sig: |
| 12118 | /* So, did we get a signal? */ | 12699 | /* So, did we get a signal? */ |
| 12119 | sig = check_and_run_traps(); | 12700 | sig = check_and_run_traps(); |
| @@ -12375,3 +12956,103 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) | |||
| 12375 | return l; | 12956 | return l; |
| 12376 | } | 12957 | } |
| 12377 | #endif | 12958 | #endif |
| 12959 | |||
| 12960 | #if ENABLE_HUSH_ALIAS | ||
| 12961 | static void new_alias(char *str, char *eq) | ||
| 12962 | { | ||
| 12963 | struct alias *alias, **aliaspp; | ||
| 12964 | |||
| 12965 | //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); | ||
| 12966 | aliaspp = find_alias_slot(str, eq); | ||
| 12967 | str = xstrdup(str); | ||
| 12968 | alias = *aliaspp; | ||
| 12969 | if (alias) { | ||
| 12970 | /* Replace existing alias */ | ||
| 12971 | free(alias->str); | ||
| 12972 | alias->str = str; | ||
| 12973 | } else { | ||
| 12974 | /* Create new alias */ | ||
| 12975 | alias = xzalloc(sizeof(*alias)); | ||
| 12976 | alias->str = str; | ||
| 12977 | *aliaspp = alias; | ||
| 12978 | } | ||
| 12979 | } | ||
| 12980 | static int FAST_FUNC builtin_alias(char **argv) | ||
| 12981 | { | ||
| 12982 | const struct alias *alias; | ||
| 12983 | |||
| 12984 | if (!argv[1]) { | ||
| 12985 | alias = G.top_alias; | ||
| 12986 | while (alias) { | ||
| 12987 | print_pfx_escaped_nl("alias", alias->str); | ||
| 12988 | alias = alias->next; | ||
| 12989 | } | ||
| 12990 | return 0; | ||
| 12991 | } | ||
| 12992 | |||
| 12993 | while (*++argv) { | ||
| 12994 | char *eq = end_of_alias_name(*argv); | ||
| 12995 | if (*eq == '=' && eq != *argv) { | ||
| 12996 | /* alias NAME=VALUE */ | ||
| 12997 | new_alias(*argv, eq); | ||
| 12998 | } else { | ||
| 12999 | eq = strchrnul(eq, '='); | ||
| 13000 | if (*eq == '=') { | ||
| 13001 | /* alias 'NA&ME=VALUE' (invalid chars in name) */ | ||
| 13002 | bb_error_msg("alias: '%.*s': invalid alias name", (int)(eq - *argv), *argv); | ||
| 13003 | continue; /* continue processing argv (bash compat) */ | ||
| 13004 | } | ||
| 13005 | /* alias SOMETHING_WITHOUT_EQUAL_SIGN */ | ||
| 13006 | //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); | ||
| 13007 | alias = *find_alias_slot(*argv, eq); | ||
| 13008 | if (alias) { | ||
| 13009 | print_pfx_escaped_nl("alias", alias->str); | ||
| 13010 | } else { | ||
| 13011 | bb_error_msg("unalias: '%s': not found" + 2, *argv); | ||
| 13012 | /* return 1; - no, continue processing argv (bash compat) */ | ||
| 13013 | } | ||
| 13014 | } | ||
| 13015 | } | ||
| 13016 | return 0; | ||
| 13017 | } | ||
| 13018 | |||
| 13019 | static void unset_alias(struct alias **aliaspp) | ||
| 13020 | { | ||
| 13021 | struct alias *alias = *aliaspp; | ||
| 13022 | |||
| 13023 | *aliaspp = alias->next; | ||
| 13024 | free(alias->str); | ||
| 13025 | free(alias); | ||
| 13026 | } | ||
| 13027 | static int FAST_FUNC builtin_unalias(char **argv) | ||
| 13028 | { | ||
| 13029 | int ret = 0; | ||
| 13030 | |||
| 13031 | if (!argv[1]) { | ||
| 13032 | bb_simple_error_msg("usage: unalias -a | NAME..."); | ||
| 13033 | return EXIT_FAILURE; | ||
| 13034 | } | ||
| 13035 | |||
| 13036 | while (*++argv) { | ||
| 13037 | struct alias **aliaspp; | ||
| 13038 | |||
| 13039 | if (strcmp(*argv, "-a") == 0) { | ||
| 13040 | /* Remove all aliases */ | ||
| 13041 | while (G.top_alias) { | ||
| 13042 | unset_alias(&G.top_alias); | ||
| 13043 | } | ||
| 13044 | /* bash: "unalias -a NO_SUCH_ALIAS" says nothing (won't try to unalias NO_SUCH_ALIAS): */ | ||
| 13045 | break; | ||
| 13046 | } | ||
| 13047 | //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); | ||
| 13048 | aliaspp = find_alias_slot(*argv, strchr(*argv, '\0')); /* think of "unalias a=b" case! */ | ||
| 13049 | if (*aliaspp) { | ||
| 13050 | unset_alias(aliaspp); | ||
| 13051 | } else { | ||
| 13052 | bb_error_msg("unalias: '%s': not found", *argv); | ||
| 13053 | ret = EXIT_FAILURE; | ||
| 13054 | } | ||
| 13055 | } | ||
| 13056 | return ret; | ||
| 13057 | } | ||
| 13058 | #endif | ||
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh index ca35ec144..3edd3df61 100755 --- a/shell/hush_leaktool.sh +++ b/shell/hush_leaktool.sh | |||
| @@ -7,7 +7,7 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs` | |||
| 7 | 7 | ||
| 8 | grep -v free "$output" >"$output.leaked" | 8 | grep -v free "$output" >"$output.leaked" |
| 9 | 9 | ||
| 10 | i=8 | 10 | i=16 |
| 11 | list= | 11 | list= |
| 12 | for freed in $freelist; do | 12 | for 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= |
| 20 | done | 20 | done |
| 21 | if test "$list"; then | 21 | if 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" |
| 25 | fi | 25 | fi |
| 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. | ||
| 34 | cat output.leaked \ | ||
| 35 | | sort -u \ | ||
| 36 | | cut -d' ' -f1-3 \ | ||
| 37 | | uniq -c \ | ||
| 38 | | sort -rn \ | ||
| 39 | >output.leaked.counted_uniq_alloc_address | ||
diff --git a/shell/hush_test/hush-misc/tickquote1.right b/shell/hush_test/hush-bugs/tickquote1.right index 56f8515b7..56f8515b7 100644 --- a/shell/hush_test/hush-misc/tickquote1.right +++ b/shell/hush_test/hush-bugs/tickquote1.right | |||
diff --git a/shell/hush_test/hush-misc/tickquote1.tests b/shell/hush_test/hush-bugs/tickquote1.tests index 02e3904f1..02e3904f1 100755 --- a/shell/hush_test/hush-misc/tickquote1.tests +++ b/shell/hush_test/hush-bugs/tickquote1.tests | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests new file mode 100755 index 000000000..eb2223031 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | EO\ | ||
| 4 | F | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests new file mode 100755 index 000000000..de21132d1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests new file mode 100755 index 000000000..da3860804 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right new file mode 100644 index 000000000..7af73557a --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: missing here document terminator | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests new file mode 100755 index 000000000..33ccde26b --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | echo << | ||
| 2 | echo 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 @@ | |||
| 1 | echo << > | ||
| 2 | echo Done: | ||
diff --git a/shell/hush_test/hush-heredoc/herestring1.right b/shell/hush_test/hush-heredoc/herestring1.right new file mode 100644 index 000000000..555937daa --- /dev/null +++ b/shell/hush_test/hush-heredoc/herestring1.right | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | one | ||
| 2 | _two | ||
| 3 | \_three | ||
| 4 | \_four | ||
| 5 | \_two | ||
| 6 | \_three | ||
| 7 | \\_four | ||
| 8 | two_newlines | ||
| 9 | |||
| 10 | /bin/c* | ||
| 11 | star_* | ||
| 12 | star_* | ||
| 13 | star_* | ||
| 14 | star_\* | ||
| 15 | star_* | ||
| 16 | star_\* | ||
| 17 | line1 | ||
| 18 | line2 | ||
| 19 | line3 | ||
| 20 | line1 | ||
| 21 | line2 | ||
| 22 | line3 | ||
| 23 | 512 | ||
| 24 | 256 | ||
| 25 | 128 | ||
| 26 | 64 | ||
| 27 | 32 | ||
| 28 | 16 | ||
| 29 | 8 | ||
| 30 | 4 | ||
| 31 | 2 | ||
| 32 | v-$a-\t-\-\"-\x-`-\--\z-\*-\?- | ||
| 33 | v-$a-\t-\-"-\x-`-\--\z-\*-\?- | ||
diff --git a/shell/hush_test/hush-heredoc/herestring1.tests b/shell/hush_test/hush-heredoc/herestring1.tests new file mode 100755 index 000000000..fb7bd0dda --- /dev/null +++ b/shell/hush_test/hush-heredoc/herestring1.tests | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | cat <<<one | ||
| 2 | cat <<<\_two | ||
| 3 | cat <<<"\_three" | ||
| 4 | cat <<<'\_four' | ||
| 5 | cat <<<\\_two | ||
| 6 | cat <<<"\\_three" | ||
| 7 | cat <<<'\\_four' | ||
| 8 | |||
| 9 | cat <<<$'two_newlines\n' | ||
| 10 | |||
| 11 | cat <<</bin/c* | ||
| 12 | |||
| 13 | cat <<<star_* | ||
| 14 | cat <<<star_\* | ||
| 15 | cat <<<"star_*" | ||
| 16 | cat <<<"star_\*" | ||
| 17 | cat <<<'star_*' | ||
| 18 | cat <<<'star_\*' | ||
| 19 | |||
| 20 | var=$'line1 | ||
| 21 | line2 | ||
| 22 | line3' | ||
| 23 | |||
| 24 | cat <<<$var | ||
| 25 | |||
| 26 | cat <<<"$var" | ||
| 27 | |||
| 28 | i=10 | ||
| 29 | until test $((--i)) = 0; do | ||
| 30 | cat <<<$((2**i)) | ||
| 31 | done | ||
| 32 | |||
| 33 | a=qwerty | ||
| 34 | cat <<<`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'` | ||
| 35 | cat <<<"`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`" | ||
diff --git a/shell/hush_test/hush-misc/assignment2.right b/shell/hush_test/hush-misc/assignment2.right index 9d1860be7..5e54b408b 100644 --- a/shell/hush_test/hush-misc/assignment2.right +++ b/shell/hush_test/hush-misc/assignment2.right | |||
| @@ -1,2 +1,4 @@ | |||
| 1 | hush: can't execute 'a=b': No such file or directory | 1 | hush: can't execute 'a=b': No such file or directory |
| 2 | 127 | 2 | 127 |
| 3 | hush: can't execute 'ab=c': No such file or directory | ||
| 4 | 127 | ||
diff --git a/shell/hush_test/hush-misc/assignment2.tests b/shell/hush_test/hush-misc/assignment2.tests index f6938434c..a93a48d09 100755 --- a/shell/hush_test/hush-misc/assignment2.tests +++ b/shell/hush_test/hush-misc/assignment2.tests | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | # This must not be interpreted as an assignment | 1 | # This must not be interpreted as an assignment |
| 2 | a''=b true | 2 | a''=b true |
| 3 | echo $? | 3 | echo $? |
| 4 | a'b'=c true | ||
| 5 | echo $? | ||
diff --git a/shell/hush_test/hush-misc/case2.right b/shell/hush_test/hush-misc/case2.right new file mode 100644 index 000000000..4780199de --- /dev/null +++ b/shell/hush_test/hush-misc/case2.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected ) | |||
diff --git a/shell/hush_test/hush-misc/case2.tests b/shell/hush_test/hush-misc/case2.tests new file mode 100755 index 000000000..b9475392e --- /dev/null +++ b/shell/hush_test/hush-misc/case2.tests | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # had a parser bug which incorrectly detected ";;" | ||
| 2 | case w in | ||
| 3 | a); | ||
| 4 | b);; | ||
| 5 | esac | ||
| 6 | echo Should not be reached | ||
diff --git a/shell/hush_test/hush-misc/case3.right b/shell/hush_test/hush-misc/case3.right new file mode 100644 index 000000000..4780199de --- /dev/null +++ b/shell/hush_test/hush-misc/case3.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected ) | |||
diff --git a/shell/hush_test/hush-misc/case3.tests b/shell/hush_test/hush-misc/case3.tests new file mode 100755 index 000000000..7ef6a18a9 --- /dev/null +++ b/shell/hush_test/hush-misc/case3.tests | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # had a parser bug which incorrectly detected ";;" | ||
| 2 | case w in | ||
| 3 | a) true | ||
| 4 | ; | ||
| 5 | b);; | ||
| 6 | esac | ||
| 7 | echo Should not be reached | ||
diff --git a/shell/hush_test/hush-misc/case4.right b/shell/hush_test/hush-misc/case4.right new file mode 100644 index 000000000..02a29ccbc --- /dev/null +++ b/shell/hush_test/hush-misc/case4.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | Keyword-like word may be in pattern | ||
| 2 | Ok:0 | ||
diff --git a/shell/hush_test/hush-misc/case4.tests b/shell/hush_test/hush-misc/case4.tests new file mode 100755 index 000000000..63b9dfd99 --- /dev/null +++ b/shell/hush_test/hush-misc/case4.tests | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | if=if | ||
| 2 | case if in | ||
| 3 | if) echo "Keyword-like word may be in pattern";; | ||
| 4 | fi) echo "Not reached";; | ||
| 5 | # esac) echo "Not reached";; # not accepted by bash 5.2.15 | ||
| 6 | do) echo "Not reached" | ||
| 7 | esac | ||
| 8 | echo Ok:$? | ||
diff --git a/shell/hush_test/hush-misc/func6.right b/shell/hush_test/hush-misc/func6.right new file mode 100644 index 000000000..01e79c32a --- /dev/null +++ b/shell/hush_test/hush-misc/func6.right | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | 1 | ||
| 2 | 2 | ||
| 3 | 3 | ||
diff --git a/shell/hush_test/hush-misc/func6.tests b/shell/hush_test/hush-misc/func6.tests new file mode 100755 index 000000000..5f1699c42 --- /dev/null +++ b/shell/hush_test/hush-misc/func6.tests | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | { f() { echo $1; } } | ||
| 2 | f 1 | ||
| 3 | |||
| 4 | { f() ( echo $1; )} | ||
| 5 | f 2 | ||
| 6 | |||
| 7 | { f()(echo $1)} | ||
| 8 | f 3 | ||
diff --git a/shell/hush_test/hush-misc/func7.right b/shell/hush_test/hush-misc/func7.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/hush_test/hush-misc/func7.right | |||
| @@ -0,0 +1 @@ | |||
| Ok:0 | |||
diff --git a/shell/hush_test/hush-misc/func7.tests b/shell/hush_test/hush-misc/func7.tests new file mode 100755 index 000000000..f5e03b6e1 --- /dev/null +++ b/shell/hush_test/hush-misc/func7.tests | |||
| @@ -0,0 +1 @@ | |||
| if f() { echo Ok:$?; } then f; fi | |||
diff --git a/shell/hush_test/hush-misc/sig_exitcode.tests b/shell/hush_test/hush-misc/sig_exitcode.tests index 7879dc854..67b4500f4 100755 --- a/shell/hush_test/hush-misc/sig_exitcode.tests +++ b/shell/hush_test/hush-misc/sig_exitcode.tests | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | # If job control is disabled, skip the test | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | exec 2>&1 | 4 | exec 2>&1 |
| 2 | 5 | ||
| 3 | $THIS_SH -c 'kill -9 $$' | 6 | $THIS_SH -c 'kill -9 $$' |
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.right b/shell/hush_test/hush-misc/syntax_err_negate.right index 8c7010629..39db799ea 100644 --- a/shell/hush_test/hush-misc/syntax_err_negate.right +++ b/shell/hush_test/hush-misc/syntax_err_negate.right | |||
| @@ -1,2 +1,2 @@ | |||
| 1 | bash 3.2 fails this | 1 | 0:0 |
| 2 | hush: syntax error: ! ! command | 2 | 1:1 |
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.tests b/shell/hush_test/hush-misc/syntax_err_negate.tests index d61b1b09f..64f0f8a75 100755 --- a/shell/hush_test/hush-misc/syntax_err_negate.tests +++ b/shell/hush_test/hush-misc/syntax_err_negate.tests | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | echo bash 3.2 fails this | 1 | # bash 3.2 fails this, 5.2.15 allows |
| 2 | ! ! true | 2 | ! ! true; echo 0:$? |
| 3 | ! ! ! true; echo 1:$? | ||
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests index f9cf6d48c..54120319b 100755 --- a/shell/hush_test/hush-misc/wait1.tests +++ b/shell/hush_test/hush-misc/wait1.tests | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | # If job control is disabled, skip the test | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | sleep 2 & sleep 1 & wait $! | 4 | sleep 2 & sleep 1 & wait $! |
| 2 | echo $? | 5 | echo $? |
| 3 | jobs | 6 | jobs |
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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | sleep 3 & sleep 2 & sleep 1 | 4 | sleep 3 & sleep 2 & sleep 1 |
| 2 | wait $! | 5 | wait $! |
| 3 | echo $? | 6 | echo $? |
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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | sleep 2 & (sleep 1;exit 3) & wait $! | 4 | sleep 2 & (sleep 1;exit 3) & wait $! |
| 2 | echo $? | 5 | echo $? |
| 3 | jobs | 6 | jobs |
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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | sleep 1 | (sleep 1;exit 3) & wait %1 | 4 | sleep 1 | (sleep 1;exit 3) & wait %1 |
| 2 | echo Three:$? | 5 | echo 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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | sleep 0 | (sleep 0;exit 3) & | 4 | sleep 0 | (sleep 0;exit 3) & |
| 2 | sleep 1 | 5 | sleep 1 |
| 3 | echo Zero:$? | 6 | echo 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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | # In bash, "wait $!" extracts correct exitcode even if bg task has already exited | 4 | # In bash, "wait $!" extracts correct exitcode even if bg task has already exited |
| 2 | # It prints 0, then 3: | 5 | # It prints 0, then 3: |
| 3 | (sleep 0; exit 3) & sleep 1 | 6 | (sleep 0; exit 3) & sleep 1 |
diff --git a/shell/hush_test/hush-parsing/bad_cmd1.right b/shell/hush_test/hush-parsing/bad_cmd1.right new file mode 100644 index 000000000..ad7f80d96 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_cmd1.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected || | |||
diff --git a/shell/hush_test/hush-parsing/bad_cmd1.tests b/shell/hush_test/hush-parsing/bad_cmd1.tests new file mode 100755 index 000000000..b4bf53a75 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_cmd1.tests | |||
| @@ -0,0 +1 @@ | |||
| ||date | |||
diff --git a/shell/hush_test/hush-parsing/bad_cmd2.right b/shell/hush_test/hush-parsing/bad_cmd2.right new file mode 100644 index 000000000..9c3ed0a65 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_cmd2.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected && | |||
diff --git a/shell/hush_test/hush-parsing/bad_cmd2.tests b/shell/hush_test/hush-parsing/bad_cmd2.tests new file mode 100755 index 000000000..13580e14a --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_cmd2.tests | |||
| @@ -0,0 +1 @@ | |||
| &&date | |||
diff --git a/shell/hush_test/hush-parsing/bad_cmd3.right b/shell/hush_test/hush-parsing/bad_cmd3.right new file mode 100644 index 000000000..05618a2d9 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_cmd3.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected & | |||
diff --git a/shell/hush_test/hush-parsing/bad_cmd3.tests b/shell/hush_test/hush-parsing/bad_cmd3.tests new file mode 100755 index 000000000..bec7e5180 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_cmd3.tests | |||
| @@ -0,0 +1 @@ | |||
| & date | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe1.right b/shell/hush_test/hush-parsing/bad_pipe1.right new file mode 100644 index 000000000..c8ce26235 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe1.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe1.tests b/shell/hush_test/hush-parsing/bad_pipe1.tests new file mode 100755 index 000000000..ca5f2ec48 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe1.tests | |||
| @@ -0,0 +1 @@ | |||
| |date | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe2.right b/shell/hush_test/hush-parsing/bad_pipe2.right new file mode 100644 index 000000000..6d63482b8 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe2.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unterminated | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe2.tests b/shell/hush_test/hush-parsing/bad_pipe2.tests new file mode 100755 index 000000000..d0aa5c73c --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe2.tests | |||
| @@ -0,0 +1 @@ | |||
| date| | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe3.right b/shell/hush_test/hush-parsing/bad_pipe3.right new file mode 100644 index 000000000..6d63482b8 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe3.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unterminated | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe3.tests b/shell/hush_test/hush-parsing/bad_pipe3.tests new file mode 100755 index 000000000..ab312d139 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe3.tests | |||
| @@ -0,0 +1 @@ | |||
| date|; | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe4.right b/shell/hush_test/hush-parsing/bad_pipe4.right new file mode 100644 index 000000000..6d63482b8 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe4.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unterminated | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe4.tests b/shell/hush_test/hush-parsing/bad_pipe4.tests new file mode 100755 index 000000000..c39098c8b --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe4.tests | |||
| @@ -0,0 +1 @@ | |||
| date| & | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe5.right b/shell/hush_test/hush-parsing/bad_pipe5.right new file mode 100644 index 000000000..6d63482b8 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe5.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unterminated | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe5.tests b/shell/hush_test/hush-parsing/bad_pipe5.tests new file mode 100755 index 000000000..d5866ce79 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe5.tests | |||
| @@ -0,0 +1 @@ | |||
| date| && date | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe6.right b/shell/hush_test/hush-parsing/bad_pipe6.right new file mode 100644 index 000000000..6d63482b8 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe6.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unterminated | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe6.tests b/shell/hush_test/hush-parsing/bad_pipe6.tests new file mode 100755 index 000000000..1a9476b92 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe6.tests | |||
| @@ -0,0 +1 @@ | |||
| date| || date | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe7.right b/shell/hush_test/hush-parsing/bad_pipe7.right new file mode 100644 index 000000000..c8ce26235 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe7.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error: unexpected | | |||
diff --git a/shell/hush_test/hush-parsing/bad_pipe7.tests b/shell/hush_test/hush-parsing/bad_pipe7.tests new file mode 100755 index 000000000..b048affb0 --- /dev/null +++ b/shell/hush_test/hush-parsing/bad_pipe7.tests | |||
| @@ -0,0 +1 @@ | |||
| date| |date | |||
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right index 04126cbe6..3eedae275 100644 --- a/shell/hush_test/hush-read/read_t.right +++ b/shell/hush_test/hush-read/read_t.right | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | >< | 1 | >te:142< |
| 2 | >< | 2 | >:142< |
| 3 | >test< | 3 | >test:0< |
| 4 | >test< | 4 | >test:0< |
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests index d65f1aeaa..9fbeec517 100755 --- a/shell/hush_test/hush-read/read_t.tests +++ b/shell/hush_test/hush-read/read_t.tests | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | # bash 3.2 outputs: | 1 | # bash 5.2 outputs: |
| 2 | 2 | ||
| 3 | # >< | 3 | # >te:142< |
| 4 | { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") | 4 | { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<") |
| 5 | # >< | 5 | # >:142< |
| 6 | { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") | 6 | { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<") |
| 7 | # >test< | 7 | # >test:0< |
| 8 | { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") | 8 | { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<") |
| 9 | # >test< | 9 | # >test:0< |
| 10 | { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") | 10 | { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<") |
diff --git a/shell/hush_test/hush-redir/redir_EINTR1.right b/shell/hush_test/hush-redir/redir_EINTR1.right new file mode 100644 index 000000000..287d91f67 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_EINTR1.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | Hello | ||
| 2 | Done:0 | ||
diff --git a/shell/hush_test/hush-redir/redir_EINTR1.tests b/shell/hush_test/hush-redir/redir_EINTR1.tests new file mode 100755 index 000000000..cede4d4d8 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_EINTR1.tests | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | rm -f test.fifo | ||
| 2 | mkfifo test.fifo | ||
| 3 | |||
| 4 | (sleep 1; kill -chld $$) & | ||
| 5 | (sleep 2; echo Hello >test.fifo) & | ||
| 6 | |||
| 7 | # We get open("test.fifo") interrupted by SIGCHLD from the first subshell. | ||
| 8 | # The shell MUST retry the open (no printing of error messages). | ||
| 9 | # Then, the second subshell opens fifo for writing and open unblocks and succeeds. | ||
| 10 | cat <test.fifo | ||
| 11 | |||
| 12 | echo "Done:$?" | ||
| 13 | rm -f test.fifo | ||
diff --git a/shell/hush_test/hush-redir/redir_EINTR2.right b/shell/hush_test/hush-redir/redir_EINTR2.right new file mode 100644 index 000000000..287d91f67 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_EINTR2.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | Hello | ||
| 2 | Done:0 | ||
diff --git a/shell/hush_test/hush-redir/redir_EINTR2.tests b/shell/hush_test/hush-redir/redir_EINTR2.tests new file mode 100755 index 000000000..3d343c7ea --- /dev/null +++ b/shell/hush_test/hush-redir/redir_EINTR2.tests | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | rm -f test.fifo | ||
| 2 | mkfifo test.fifo | ||
| 3 | |||
| 4 | (sleep 1; kill -chld $$) & | ||
| 5 | (sleep 2; echo Hello >test.fifo) & | ||
| 6 | |||
| 7 | # We get open("test.fifo") interrupted by SIGCHLD from the first subshell. | ||
| 8 | # The shell MUST retry the open (no printing of error messages). | ||
| 9 | # Then, the second subshell opens fifo for writing and open unblocks and succeeds. | ||
| 10 | read HELLO <test.fifo | ||
| 11 | echo "$HELLO" | ||
| 12 | |||
| 13 | echo "Done:$?" | ||
| 14 | rm -f test.fifo | ||
diff --git a/shell/hush_test/hush-redir/redir_and_constructs1.right b/shell/hush_test/hush-redir/redir_and_constructs1.right new file mode 100644 index 000000000..232cd8734 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_and_constructs1.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | hush: can't execute '!': No such file or directory | ||
| 2 | 127:127 | ||
diff --git a/shell/hush_test/hush-redir/redir_and_constructs1.tests b/shell/hush_test/hush-redir/redir_and_constructs1.tests new file mode 100755 index 000000000..a92731e04 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_and_constructs1.tests | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | # Reserved words are not recognized after redirects | ||
| 2 | </dev/null ! true; echo 127:$? | ||
diff --git a/shell/hush_test/hush-signals/catch.tests b/shell/hush_test/hush-signals/catch.tests index d2a21d17e..4b1a08e8f 100755 --- a/shell/hush_test/hush-signals/catch.tests +++ b/shell/hush_test/hush-signals/catch.tests | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | # If job control is disabled, skip the test ("User defined signal 2" text not emitted) | ||
| 2 | test "`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 |
| 2 | trap ":" USR1 USR2 | 5 | trap ":" 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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | trap "echo got signal" USR1 | 4 | trap "echo got signal" USR1 |
| 2 | 5 | ||
| 3 | for try in 1 2 3 4 5; do | 6 | for 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 | ||
| 2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 3 | |||
| 1 | "$THIS_SH" -c ' | 4 | "$THIS_SH" -c ' |
| 2 | exit_func() { | 5 | exit_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) | ||
| 2 | test "`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 $$) & |
| 3 | while true; do | 6 | while 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) | ||
| 4 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
| 5 | |||
| 3 | # HUP is special in interactive shells | 6 | # HUP is special in interactive shells |
| 4 | trap '' HUP | 7 | trap '' HUP |
| 5 | # QUIT is always special | 8 | # QUIT is always special |
diff --git a/shell/hush_test/hush-vars/var_backslash1.right b/shell/hush_test/hush-vars/var_backslash1.right new file mode 100644 index 000000000..3f2c21289 --- /dev/null +++ b/shell/hush_test/hush-vars/var_backslash1.right | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | a is '\*bc' | ||
| 2 | b is '\' | ||
| 3 | c is '*' | ||
| 4 | ${a##?*} removes everything: || - matches one char, then all | ||
| 5 | ${a##?"*"} removes \*: |bc| - matches one char, then * | ||
| 6 | ${a##\*} removes nothing: |\*bc| - first char is not * | ||
| 7 | ${a##\\*} removes everything: || - matches \, then all | ||
| 8 | ${a##\\\*} removes \*: |bc| - matches \, then * | ||
| 9 | ${a##?$c} removes everything: || - matches one char, then all | ||
| 10 | ${a##?"$c"} removes \*: |bc| - matches one char, then * | ||
| 11 | ${a##\\$c} removes everything: || - matches \, then all | ||
| 12 | ${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ | ||
| 13 | ${a##\\"$c"} removes \*: |bc| - matches \, then * | ||
| 14 | ${a##$b} removes \: |*bc| - matches \ | ||
| 15 | ${a##"$b"} removes \: |*bc| - matches \ | ||
| 16 | |||
| 17 | Single quote tests: | ||
| 18 | ${a##?'*'} removes \*: |bc| - matches one char, then * | ||
| 19 | ${a##'\'*} removes everything: || - matches \, then all | ||
| 20 | ${a##'\'\*} removes \*: |bc| - matches \, then * | ||
| 21 | ${a##'\*'} removes \*: |bc| - matches \, then * | ||
| 22 | ${a##'\'$c} removes everything: || - matches \, then all | ||
| 23 | ${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $ | ||
| 24 | ${a##'\'"$c"} removes \*: |bc| - matches \, then * | ||
| 25 | |||
| 26 | ${a##"$b"?} removes \*: |bc| - matches \, then one char | ||
| 27 | ${a##"$b"*} removes everything: || - matches \, then all | ||
| 28 | ${a##"$b""?"} removes nothing: |\*bc| - second char is not ? | ||
| 29 | ${a##"$b""*"} removes \*: |bc| - matches \, then * | ||
| 30 | ${a##"$b"\*} removes \*: |bc| - matches \, then * | ||
| 31 | ${a##"$b"$c} removes everything:|| - matches \, then all | ||
| 32 | ${a##"$b""$c"} removes \*: |bc| - matches \, then * | ||
| 33 | ${a##"$b?"} removes nothing: |\*bc| - second char is not ? | ||
| 34 | ${a##"$b*"} removes \*: |bc| - matches \, then * | ||
| 35 | ${a##"$b$c"} removes \*: |bc| - matches \, then * | ||
diff --git a/shell/hush_test/hush-vars/var_backslash1.tests b/shell/hush_test/hush-vars/var_backslash1.tests new file mode 100755 index 000000000..015a6107b --- /dev/null +++ b/shell/hush_test/hush-vars/var_backslash1.tests | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | a='\*bc' | ||
| 2 | b='\' | ||
| 3 | c='*' | ||
| 4 | echo "a is '$a'" | ||
| 5 | echo "b is '$b'" | ||
| 6 | echo "c is '$c'" | ||
| 7 | echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' | ||
| 8 | echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' | ||
| 9 | echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' | ||
| 10 | echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' | ||
| 11 | echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' | ||
| 12 | echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' | ||
| 13 | echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' | ||
| 14 | echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' | ||
| 15 | echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' | ||
| 16 | echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' | ||
| 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' | ||
| 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' | ||
| 19 | echo | ||
| 20 | sq="'" | ||
| 21 | echo 'Single quote tests:' | ||
| 22 | echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *' | ||
| 23 | echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all' | ||
| 24 | echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *' | ||
| 25 | echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *' | ||
| 26 | echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all' | ||
| 27 | echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $' | ||
| 28 | echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *' | ||
| 29 | echo | ||
| 30 | ## In bash, this isn't working as expected | ||
| 31 | #echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| | ||
| 32 | #echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| | ||
| 33 | #echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| | ||
| 34 | #echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| | ||
| 35 | ## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: | ||
| 36 | ## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) | ||
| 37 | ## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| | ||
| 38 | #echo | ||
| 39 | echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' | ||
| 40 | echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' | ||
| 41 | echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| | ||
| 42 | echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' | ||
| 43 | echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' | ||
| 44 | echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' | ||
| 45 | echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' | ||
| 46 | echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| | ||
| 47 | echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || | ||
| 48 | echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 7345fee43..ff29ca0b1 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
| @@ -67,9 +67,12 @@ do_test() | |||
| 67 | # echo Running test: "$x" | 67 | # echo Running test: "$x" |
| 68 | echo -n "$1/$x:" | 68 | echo -n "$1/$x:" |
| 69 | ( | 69 | ( |
| 70 | "$THIS_SH" "./$x" 2>&1 | \ | 70 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 |
| 71 | grep -va "^hush: using fallback suid method$" >"$name.xx" | ||
| 72 | r=$? | 71 | r=$? |
| 72 | # filter !FEATURE_SUID_CONFIG_QUIET message | ||
| 73 | sed -i \ | ||
| 74 | -e "/^hush: using fallback suid method$/d" \ | ||
| 75 | "$name.xx" | ||
| 73 | # filter C library differences | 76 | # filter C library differences |
| 74 | sed -i \ | 77 | sed -i \ |
| 75 | -e "/: invalid option /s:'::g" \ | 78 | -e "/: invalid option /s:'::g" \ |
diff --git a/shell/random.c b/shell/random.c index ffe0cc937..56c7c5a3c 100644 --- a/shell/random.c +++ b/shell/random.c | |||
| @@ -19,17 +19,6 @@ | |||
| 19 | # include "random.h" | 19 | # include "random.h" |
| 20 | # define RAND_BASH_MASK 0x7fff | 20 | # define RAND_BASH_MASK 0x7fff |
| 21 | 21 | ||
| 22 | # if ENABLE_FEATURE_PRNG_SHELL | ||
| 23 | uint32_t FAST_FUNC | ||
| 24 | next_random(random_t *rnd) | ||
| 25 | { | ||
| 26 | return full_random(rnd) & RAND_BASH_MASK; | ||
| 27 | } | ||
| 28 | # undef RAND_BASH_MASK | ||
| 29 | # define RAND_BASH_MASK 0xffffffff | ||
| 30 | # define next_random full_random | ||
| 31 | # endif | ||
| 32 | |||
| 33 | #else | 22 | #else |
| 34 | # include <stdint.h> | 23 | # include <stdint.h> |
| 35 | # include <unistd.h> | 24 | # include <unistd.h> |
diff --git a/shell/random.h b/shell/random.h index 75fe0f69f..c4eb44c13 100644 --- a/shell/random.h +++ b/shell/random.h | |||
| @@ -35,9 +35,6 @@ typedef struct random_t { | |||
| 35 | ((rnd)->galois_LFSR = 0) | 35 | ((rnd)->galois_LFSR = 0) |
| 36 | 36 | ||
| 37 | uint32_t next_random(random_t *rnd) FAST_FUNC; | 37 | uint32_t next_random(random_t *rnd) FAST_FUNC; |
| 38 | #if ENABLE_FEATURE_PRNG_SHELL | ||
| 39 | uint32_t full_random(random_t *rnd) FAST_FUNC; | ||
| 40 | #endif | ||
| 41 | 38 | ||
| 42 | POP_SAVED_FUNCTION_VISIBILITY | 39 | POP_SAVED_FUNCTION_VISIBILITY |
| 43 | 40 | ||
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 | |||
| 55 | shell_builtin_read(struct builtin_read_params *params) | 55 | shell_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 | " "" "" |
| 155 | SKIP= | 155 | SKIP= |
| 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. | ||
| 160 | optional FEATURE_PATH_TRAVERSAL_PROTECTION | ||
| 161 | rm -rf cpio.testdir | ||
| 162 | mkdir -p cpio.testdir/prepare/inner | ||
| 163 | echo "file outside of destination was written" > cpio.testdir/prepare/dont_write | ||
| 164 | echo "data" > cpio.testdir/prepare/inner/to_extract | ||
| 165 | mkdir -p cpio.testdir/extract | ||
| 166 | testing "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) | ||
| 168 | echo \$? | ||
| 169 | ls cpio.testdir/dont_write 2>&1" \ | ||
| 170 | "\ | ||
| 171 | cpio: removing leading '../' from member names | ||
| 172 | ../dont_write | ||
| 173 | to_extract | ||
| 174 | 1 blocks | ||
| 175 | 0 | ||
| 176 | ls: cpio.testdir/dont_write: No such file or directory | ||
| 177 | " "" "" | ||
| 178 | SKIP= | ||
| 179 | |||
| 157 | # Clean up | 180 | # Clean up |
| 158 | rm -rf cpio.testdir cpio.testdir2 2>/dev/null | 181 | rm -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 | ||
| 24 | optional USE_BB_CRYPT_SHA | 24 | optional USE_BB_CRYPT_SHA |
| 25 | testing "cryptpw sha256" \ | 25 | # Note: mkpasswd-5.6.2 won't accept "-m sha256", wants "-m sha256crypt" |
| 26 | "cryptpw -m sha256 QWErty '123456789012345678901234567890'" \ | 26 | testing '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) | ||
| 31 | testing 'cryptpw sha256 overlong' \ | ||
| 32 | 'cryptpw -m sha256 QWErty 123456789012345678901234567890' \ | ||
| 33 | '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \ | ||
| 34 | '' '' | ||
| 35 | testing 'cryptpw sha256 implicit' \ | ||
| 36 | 'cryptpw QWErty \$5\$1234567890123456' \ | ||
| 37 | '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \ | ||
| 38 | '' '' | ||
| 39 | testing 'cryptpw sha256 rounds=99999' \ | ||
| 40 | 'cryptpw -m sha256 QWErty rounds=99999\$123456789012345678901234567890' \ | ||
| 41 | '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \ | ||
| 42 | '' '' | ||
| 43 | testing 'cryptpw sha256 rounds=99999 implicit' \ | ||
| 44 | 'cryptpw QWErty \$5\$rounds=99999\$123456789012345678901234567890' \ | ||
| 45 | '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \ | ||
| 46 | '' '' | ||
| 28 | 47 | ||
| 29 | testing "cryptpw sha256 rounds=99999" \ | 48 | testing '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 | '' '' | |
| 33 | testing "cryptpw sha512" \ | 52 | testing '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 | '' '' | ||
| 56 | testing 'cryptpw sha512 rounds=99999' \ | ||
| 57 | 'cryptpw -m sha512 QWErty rounds=99999\$123456789012345678901234567890' \ | ||
| 58 | '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' \ | ||
| 59 | '' '' | ||
| 60 | SKIP= | ||
| 36 | 61 | ||
| 37 | testing "cryptpw sha512 rounds=99999" \ | 62 | optional USE_BB_CRYPT_YES |
| 38 | "cryptpw -m sha512 QWErty 'rounds=99999\$123456789012345678901234567890'" \ | 63 | testing '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 | '' '' | ||
| 67 | testing '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. | ||
| 75 | testing 'cryptpw yescrypt with empty salt' \ | ||
| 76 | 'cryptpw -m yescrypt qweRTY123@-+ j9T\$' \ | ||
| 77 | '$y$j9T$$hpeksL94GXNRwnA00L3c8WFy0khFAUbCpBSak.N3Bp.\n' \ | ||
| 78 | '' '' | ||
| 79 | testing '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) | ||
| 86 | testing '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) | ||
| 91 | testing '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) | ||
| 96 | testing '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). | ||
| 104 | testing 'cryptpw yescrypt with 86-char salt (max size)' \ | ||
| 105 | 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123456789012345678901234567890123456789012345678901234567890123456789012345678901234z/' \ | ||
| 106 | '$y$j9T$123456789012345678901234567890123456789012345678901234567890123456789012345678901234z/$Exxe8IoPXiddFsqj7iqCanRf8FyquAoB0/uceLmLjG.\n' \ | ||
| 107 | '' '' | ||
| 108 | testing 'cryptpw yescrypt implicit' \ | ||
| 109 | 'cryptpw qweRTY123@-+ \$y\$j9T\$123456789012345678901234' \ | ||
| 110 | '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \ | ||
| 111 | '' '' | ||
| 40 | SKIP= | 112 | SKIP= |
| 41 | 113 | ||
| 42 | exit $FAILCOUNT | 114 | exit $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 | ||
| 24 | testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" "" | 24 | testing "cut -b a,a,a" "cut -b 3,3,3 input" "e\np\ne\n" "$abc" "" |
| 25 | 25 | ||
| 26 | testing "cut -b overlaps" "cut -b 1-3,2-5,7-9,9-10 input" \ | 26 | testing "cut -b overlaps" \ |
| 27 | "one:to:th\nalphabeta\nthe qick \n" "$abc" "" | 27 | "cut -b 1-3,2-5,7-9,9-10 input" \ |
| 28 | testing "-b encapsulated" "cut -b 3-8,4-6 input" "e:two:\npha:be\ne quic\n" \ | 28 | "\ |
| 29 | "$abc" "" | 29 | one:to:th |
| 30 | # --output-delimiter not implemnted (yet?) | 30 | alphabeta |
| 31 | #testing "cut -bO overlaps" \ | 31 | the 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" "" | 33 | testing "-b encapsulated" \ |
| 34 | "cut -b 3-8,4-6 input" \ | ||
| 35 | "\ | ||
| 36 | e:two: | ||
| 37 | pha:be | ||
| 38 | e quic\n" \ | ||
| 39 | "$abc" "" | ||
| 40 | optional LONG_OPTS | ||
| 41 | testing "cut -b --output-delimiter overlaps" \ | ||
| 42 | "cut --output-delimiter='^' -b 1-3,2-5,7-9,9-10 input" \ | ||
| 43 | "\ | ||
| 44 | one:t^o:th | ||
| 45 | alpha^beta | ||
| 46 | the q^ick \n" \ | ||
| 47 | "$abc" "" | ||
| 48 | SKIP= | ||
| 49 | |||
| 34 | testing "cut high-low error" "cut -b 8-3 input 2>/dev/null || echo err" "err\n" \ | 50 | testing "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 | |||
| 68 | testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" | 84 | testing "cut with -d -f(a) -s" "cut -da -f3 -s input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" |
| 69 | testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" | 85 | testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n input" "n\nsium:Jim\n\ncion:Ed\n" "$input" "" |
| 70 | 86 | ||
| 87 | input="\ | ||
| 88 | |||
| 89 | foo bar baz | ||
| 90 | |||
| 91 | bing bong boop | ||
| 92 | |||
| 93 | " | ||
| 94 | testing "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 |
| 72 | optional FEATURE_CUT_REGEX | 97 | optional FEATURE_CUT_REGEX |
| 73 | testing "cut -DF" "cut -DF 2,7,5" \ | 98 | testing "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 | "\ |
| 100 | said and your | ||
| 101 | are | ||
| 102 | is demand. supply | ||
| 103 | forecast : | ||
| 104 | you you better, | ||
| 105 | |||
| 106 | Em: 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. |
| 76 | Cheerios are donut seeds. | 109 | Cheerios are donut seeds. |
| 77 | Talk is cheap because supply exceeds demand. | 110 | Talk is cheap because supply exceeds demand. |
| @@ -79,9 +112,92 @@ Weather forecast for tonight : dark. | |||
| 79 | Apple: you can buy better, but you can't pay more. | 112 | Apple: you can buy better, but you can't pay more. |
| 80 | Subcalifragilisticexpialidocious. | 113 | Subcalifragilisticexpialidocious. |
| 81 | Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy." | 114 | Auntie Em: Hate you, hate Kansas. Took the dog. Dorothy." |
| 115 | |||
| 116 | # No delimiter found: print entire line regardless of -F RANGES | ||
| 117 | testing "cut -F1" "cut -d: -F1" \ | ||
| 118 | "the_only_field\n" "" \ | ||
| 119 | "the_only_field\n" | ||
| 120 | testing "cut -F2" "cut -d: -F2" \ | ||
| 121 | "the_only_field\n" "" \ | ||
| 122 | "the_only_field\n" | ||
| 123 | # No delimiter found and -s: skip entire line | ||
| 124 | testing "cut -sF1" "cut -d: -sF1" \ | ||
| 125 | "" "" \ | ||
| 126 | "the_only_field\n" | ||
| 127 | #^^^ the above is probably mishandled by toybox, it prints the line | ||
| 128 | testing "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 | ||
| 132 | testing "cut -DF1" "cut -d: -DF1" \ | ||
| 133 | "the_only_field\n" "" \ | ||
| 134 | "the_only_field\n" | ||
| 135 | testing "cut -DF2" "cut -d: -DF2" \ | ||
| 136 | "\n" "" \ | ||
| 137 | "the_only_field\n" | ||
| 138 | |||
| 139 | optional FEATURE_CUT_REGEX LONG_OPTS | ||
| 140 | testing "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" | ||
| 144 | SKIP= | ||
| 145 | |||
| 146 | optional LONG_OPTS | ||
| 147 | testing "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" | ||
| 151 | SKIP= | ||
| 152 | |||
| 153 | testing "cut empty field" "cut -d ':' -f 1-3" \ | ||
| 154 | "a::b\n" \ | ||
| 155 | "" "a::b\n" | ||
| 156 | testing "cut empty field 2" "cut -d ':' -f 3-5" \ | ||
| 157 | "b::c\n" \ | ||
| 158 | "" "a::b::c:d\n" | ||
| 159 | testing "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. | ||
| 165 | testing "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 | |||
| 171 | optional LONG_OPTS | ||
| 172 | testing "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 | |||
| 178 | testing "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 | |||
| 184 | testing "cut -dNEWLINE --output-delimiter EMPTY_INPUT" \ | ||
| 185 | "cut -d' | ||
| 186 | ' --output-delimiter=@@ -f4,2,6-8" \ | ||
| 187 | "" \ | ||
| 188 | "" "" | ||
| 82 | SKIP= | 189 | SKIP= |
| 83 | 190 | ||
| 84 | testing "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. |
| 85 | testing "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: | ||
| 194 | testing "cut -d EMPTY" \ | ||
| 195 | "cut -d '' -f2-" \ | ||
| 196 | "1 2\t3 4 5\n" \ | ||
| 197 | "" "1 2\t3 4 5\n" | ||
| 198 | testing "cut -d EMPTY -s" \ | ||
| 199 | "cut -d '' -f2- -s" \ | ||
| 200 | "" \ | ||
| 201 | "" "1 2\t3 4 5\n" | ||
| 86 | 202 | ||
| 87 | exit $FAILCOUNT | 203 | exit $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 | ||
| 8 | input=\ | ||
| 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 | |||
| 15 | little_endian=false | ||
| 16 | { printf '\0\1' | hexdump -d | grep -q 256; } && little_endian=true | ||
| 17 | readonly little_endian | ||
| 18 | |||
| 8 | # testing "description" "command" "result" "infile" "stdin" | 19 | # testing "description" "command" "result" "infile" "stdin" |
| 9 | testing 'hexdump -C with four NULs' \ | 20 | testing '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"\ | 59 | testing "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 | ||
| 53 | testing "hexdump -e /1 %d" \ | 69 | testing "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 | ||
| 69 | testing "hexdump -e /2 %d" \ | 80 | $little_endian || SKIP=1 |
| 70 | "hexdump -e '8/2 \" %6d\" \"\n\"'" \ | 81 | testing "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" | ||
| 91 | SKIP= | ||
| 92 | |||
| 93 | $little_endian && SKIP=1 | ||
| 94 | testing "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" | ||
| 104 | SKIP= | ||
| 105 | |||
| 106 | $little_endian || SKIP=1 | ||
| 107 | testing "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" | ||
| 117 | SKIP= | ||
| 118 | |||
| 119 | $little_endian && SKIP=1 | ||
| 120 | testing "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" | ||
| 130 | SKIP= | ||
| 131 | |||
| 132 | $little_endian || SKIP=1 | ||
| 133 | testing "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"\ | 138 | SKIP= |
| 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"\ | 141 | testing "hexdump -n4 -e '\"%u\"' (big endian)" \ |
| 142 | "hexdump -n4 -e '\"%u\"'" \ | ||
| 143 | "1315027968" \ | ||
| 144 | "" \ | ||
| 145 | "\x4e\x61\xbc\x00AAAA" | ||
| 146 | SKIP= | ||
| 84 | 147 | ||
| 85 | exit $FAILCOUNT | 148 | exit $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___________________________________________| |
| 24 | 0002_2__Boundary_condition_test_cases_____________________________________| | 24 | 0002_2__Boundary_condition_test_cases_____________________________________| |
| 25 | 0003_2.1__First_possible_sequence_of_a_certain_length_____________________| | 25 | 0003_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___________________________________________| |
| 143 | 0002_2__Boundary_condition_test_cases_____________________________________| | 143 | 0002_2__Boundary_condition_test_cases_____________________________________| |
| 144 | 0003_2.1__First_possible_sequence_of_a_certain_length_____________________| | 144 | 0003_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 | ||
| 265 | rm -rf ls.testdir 2>/dev/null | ||
| 266 | mkdir ls.testdir || exit 1 | ||
| 267 | touch "`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 | |||
| 269 | sq="'" | ||
| 270 | |||
| 271 | # testing "test name" "command" "expected result" "file input" "stdin" | ||
| 272 | testing "ls -q" \ | ||
| 273 | 'ls -q ls.testdir' \ | ||
| 274 | '???????????????????????????????_????_"_'$sq'_\\''\n' \ | ||
| 275 | "" "" | ||
| 276 | |||
| 277 | testing "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 | |||
| 282 | testing "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 |
| 266 | rm -rf ls.testdir 2>/dev/null | 288 | rm -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 | ' |
| 87 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | 87 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null |
| 88 | 88 | ||
| 89 | # Check that single-suffix inference rules work. | ||
| 90 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 91 | touch test.sh | ||
| 92 | testing "make single-suffix inference rule works" \ | ||
| 93 | "make -f - -s; echo $?" \ | ||
| 94 | "0\n" "" ' | ||
| 95 | test: | ||
| 96 | ' | ||
| 97 | cd .. || 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. |
| 91 | testing "make return error if command fails" \ | 101 | testing "make return error if command fails" \ |
| @@ -541,6 +551,36 @@ testing "make chained inference rules" \ | |||
| 541 | ' | 551 | ' |
| 542 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | 552 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null |
| 543 | 553 | ||
| 554 | # Suffixes with multiple periods are supported | ||
| 555 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 556 | touch x.c.c | ||
| 557 | testing "make support multi-period suffixes" \ | ||
| 558 | "make -f -" "cat x.c.c >x.o.o\nx\n" "" ' | ||
| 559 | .SUFFIXES: .c.c .o.o | ||
| 560 | x.o.o: | ||
| 561 | .c.c.o.o: | ||
| 562 | cat $< >$@ | ||
| 563 | @echo $* | ||
| 564 | ' | ||
| 565 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 566 | |||
| 567 | # Suffixes with no periods are supported | ||
| 568 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 569 | touch filex | ||
| 570 | testing "make support suffixes without any periods" \ | ||
| 571 | "make -f -" "cp filex fileh\nfile\ncp filex filez\nfile\n" "" ' | ||
| 572 | .SUFFIXES: x h z | ||
| 573 | all: fileh filez | ||
| 574 | fileh: | ||
| 575 | filez: filex | ||
| 576 | cp filex filez | ||
| 577 | @echo $* | ||
| 578 | xh: | ||
| 579 | cp $< $@ | ||
| 580 | @echo $* | ||
| 581 | ' | ||
| 582 | cd .. || 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 |
| 545 | mkdir make.tempdir && cd make.tempdir || exit 1 | 585 | mkdir make.tempdir && cd make.tempdir || exit 1 |
| 546 | touch -t 202206171201 t1a t2aa t3b | 586 | touch -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 | ||
| 14 | if ! test "$1"; then | 15 | if ! test "$1"; then |
| @@ -31,9 +32,9 @@ text=`yes "$text" | head -c 9999` | |||
| 31 | result=`( | 32 | result=`( |
| 32 | n=0 | 33 | n=0 |
| 33 | while test $n -le 999; do | 34 | while 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)) |
| 36 | done | "$sum" | 37 | done | $sum |
| 37 | )` | 38 | )` |
| 38 | if test x"$result" != x"$expected -"; then | 39 | if 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 |
| 47 | if "$sum" -c EMPTY 2>/dev/null; then | 48 | if $sum -c EMPTY 2>/dev/null; then |
| 48 | echo "FAIL: $sum -c EMPTY" | 49 | echo "FAIL: $sum -c EMPTY" |
| 49 | : $((FAILCOUNT++)) | 50 | : $((FAILCOUNT++)) |
| 50 | else | 51 | else |
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" |
| 62 | SKIP= | 62 | SKIP= |
| 63 | 63 | ||
| 64 | testing "od -B" \ | 64 | $little_endian || SKIP=1 |
| 65 | testing "od -B (little-endian)" \ | ||
| 65 | "od -B" \ | 66 | "od -B" \ |
| 66 | "\ | 67 | "\ |
| 67 | 0000000 001001 005003 041101 177103 | 68 | 0000000 001001 005003 041101 177103 |
| @@ -70,6 +71,16 @@ testing "od -B" \ | |||
| 70 | "" "$input" | 71 | "" "$input" |
| 71 | SKIP= | 72 | SKIP= |
| 72 | 73 | ||
| 74 | $little_endian && SKIP=1 | ||
| 75 | testing "od -B (big-endian)" \ | ||
| 76 | "od -B" \ | ||
| 77 | "\ | ||
| 78 | 0000000 000402 001412 040502 041776 | ||
| 79 | 0000010 | ||
| 80 | " \ | ||
| 81 | "" "$input" | ||
| 82 | SKIP= | ||
| 83 | |||
| 73 | $little_endian || SKIP=1 | 84 | $little_endian || SKIP=1 |
| 74 | testing "od -o (little-endian)" \ | 85 | testing "od -o (little-endian)" \ |
| 75 | "od -o" \ | 86 | "od -o" \ |
diff --git a/testsuite/paste/paste-long-short b/testsuite/paste/paste-long-short new file mode 100644 index 000000000..e626d730e --- /dev/null +++ b/testsuite/paste/paste-long-short | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | cat > foo <<EOF | ||
| 2 | foo1 | ||
| 3 | foo2 | ||
| 4 | foo3 | ||
| 5 | EOF | ||
| 6 | |||
| 7 | cat > bar <<EOF | ||
| 8 | bar1 | ||
| 9 | bar2 | ||
| 10 | EOF | ||
| 11 | |||
| 12 | cat > baz <<EOF | ||
| 13 | foo1 bar1 | ||
| 14 | foo2 bar2 | ||
| 15 | foo3 | ||
| 16 | EOF | ||
| 17 | |||
| 18 | busybox paste foo bar > qux | ||
| 19 | diff -u baz qux | ||
diff --git a/testsuite/paste/paste-short-long b/testsuite/paste/paste-short-long new file mode 100644 index 000000000..785da60a7 --- /dev/null +++ b/testsuite/paste/paste-short-long | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | cat > foo <<EOF | ||
| 2 | foo1 | ||
| 3 | foo2 | ||
| 4 | EOF | ||
| 5 | |||
| 6 | cat > bar <<EOF | ||
| 7 | bar1 | ||
| 8 | bar2 | ||
| 9 | bar3 | ||
| 10 | EOF | ||
| 11 | |||
| 12 | cat > baz <<EOF | ||
| 13 | foo1 bar1 | ||
| 14 | foo2 bar2 | ||
| 15 | bar3 | ||
| 16 | EOF | ||
| 17 | |||
| 18 | busybox paste foo bar > qux | ||
| 19 | diff -u baz qux | ||
diff --git a/testsuite/sha384sum.tests b/testsuite/sha384sum.tests new file mode 100755 index 000000000..f1449d195 --- /dev/null +++ b/testsuite/sha384sum.tests | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | . ./md5sum.tests sha384sum c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485 | ||
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests index 2cd8e3bf2..631141735 100755 --- a/testsuite/sha3sum.tests +++ b/testsuite/sha3sum.tests | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | 2 | ||
| 3 | . ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9 | 3 | (. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \ |
| 4 | && (. ./md5sum.tests "sha3sum -a224" 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \ | ||
| 5 | && (. ./md5sum.tests "sha3sum -a256" 6f69c8d36a9a579a943d878dab38c179d2a9dde12b244aa8840002c0f3d5bb73) \ | ||
| 6 | && (. ./md5sum.tests "sha3sum -a384" 303913449042257996a869e0378323193b4f58d90eea801b12186a3d65640bd3403d3404c63527424ec43dff842c0cd0) \ | ||
| 7 | && (. ./md5sum.tests "sha3sum -a512" e14814dccc2fef967af74eb6710885b35dfe660a362c0609b642404987d24a13dac66ad037e6affa5c42631110231655fcf4c972b1457ac49fb83af8113fc51f) | ||
diff --git a/testsuite/tr.tests b/testsuite/tr.tests index 5cca299ac..96f88ccf3 100755 --- a/testsuite/tr.tests +++ b/testsuite/tr.tests | |||
| @@ -15,6 +15,10 @@ testing "tr understands 0-9A-F" \ | |||
| 15 | "tr -cd '[0-9A-F]'" \ | 15 | "tr -cd '[0-9A-F]'" \ |
| 16 | "19AF" "" "19AFH\n" | 16 | "19AF" "" "19AFH\n" |
| 17 | 17 | ||
| 18 | testing "tr does not treat [p\\-r] as a range" \ | ||
| 19 | "tr '[p\-r]' '+'" \ | ||
| 20 | "o+q+s+" "" "opqrs-" | ||
| 21 | |||
| 18 | optional FEATURE_TR_CLASSES | 22 | optional FEATURE_TR_CLASSES |
| 19 | testing "tr understands [:xdigit:]" \ | 23 | testing "tr understands [:xdigit:]" \ |
| 20 | "tr -cd '[:xdigit:]'" \ | 24 | "tr -cd '[:xdigit:]'" \ |
diff --git a/testsuite/wget/wget-handles-https b/testsuite/wget/wget-handles-https new file mode 100644 index 000000000..11915f42f --- /dev/null +++ b/testsuite/wget/wget-handles-https | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | test x"$SKIP_INTERNET_TESTS" != x"" && exit | ||
| 2 | |||
| 3 | busybox wget -q -O foo https://www.google.com/ | ||
| 4 | test -s foo | ||
diff --git a/util-linux/chrt.c b/util-linux/chrt.c index 51d08584e..f64fa6aa6 100644 --- a/util-linux/chrt.c +++ b/util-linux/chrt.c | |||
| @@ -17,9 +17,9 @@ | |||
| 17 | //kbuild:lib-$(CONFIG_CHRT) += chrt.o | 17 | //kbuild:lib-$(CONFIG_CHRT) += chrt.o |
| 18 | 18 | ||
| 19 | //usage:#define chrt_trivial_usage | 19 | //usage:#define chrt_trivial_usage |
| 20 | //usage: "-m | -p [PRIO] PID | [-rfobi] PRIO PROG ARGS" | 20 | //usage: "-m | [-rfobi] { -p [PRIO] PID | PRIO PROG ARGS }" |
| 21 | //usage:#define chrt_full_usage "\n\n" | 21 | //usage:#define chrt_full_usage "\n\n" |
| 22 | //usage: "Change scheduling priority and class for a process\n" | 22 | //usage: "Change scheduling priority and class (default RR) for a process\n" |
| 23 | //usage: "\n -m Show min/max priorities" | 23 | //usage: "\n -m Show min/max priorities" |
| 24 | //usage: "\n -p Operate on PID" | 24 | //usage: "\n -p Operate on PID" |
| 25 | //usage: "\n -r Set SCHED_RR class" | 25 | //usage: "\n -r Set SCHED_RR class" |
| @@ -133,7 +133,14 @@ int chrt_main(int argc UNUSED_PARAM, char **argv) | |||
| 133 | pid_str = *argv; | 133 | pid_str = *argv; |
| 134 | } | 134 | } |
| 135 | /* else "-p PID", and *argv == NULL */ | 135 | /* else "-p PID", and *argv == NULL */ |
| 136 | pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1); | 136 | pid = xatoul_range(pid_str, 0, ((unsigned)(pid_t)ULONG_MAX) >> 1); |
| 137 | |||
| 138 | /* sched_{get,set}scheduler accept PID 0 to mean the calling process, | ||
| 139 | * but this is needed to display the actual PID like util-linux's chrt | ||
| 140 | */ | ||
| 141 | if (pid == 0) { | ||
| 142 | pid = getpid(); | ||
| 143 | } | ||
| 137 | } else { | 144 | } else { |
| 138 | priority = *argv++; | 145 | priority = *argv++; |
| 139 | if (!*argv) | 146 | if (!*argv) |
diff --git a/util-linux/eject.c b/util-linux/eject.c index b9813262b..4fa6dbd94 100644 --- a/util-linux/eject.c +++ b/util-linux/eject.c | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | //kbuild:lib-$(CONFIG_EJECT) += eject.o | 31 | //kbuild:lib-$(CONFIG_EJECT) += eject.o |
| 32 | 32 | ||
| 33 | //usage:#define eject_trivial_usage | 33 | //usage:#define eject_trivial_usage |
| 34 | //usage: "[-t] [-T] [DEVICE]" | 34 | //usage: IF_FEATURE_EJECT_SCSI("[-s] ") "[-t] [-T] [DEVICE]" |
| 35 | //usage:#define eject_full_usage "\n\n" | 35 | //usage:#define eject_full_usage "\n\n" |
| 36 | //usage: "Eject DEVICE or default /dev/cdrom\n" | 36 | //usage: "Eject DEVICE or default /dev/cdrom\n" |
| 37 | //usage: IF_FEATURE_EJECT_SCSI( | 37 | //usage: IF_FEATURE_EJECT_SCSI( |
diff --git a/util-linux/last.c b/util-linux/last.c index 7530d013d..aafd01bb9 100644 --- a/util-linux/last.c +++ b/util-linux/last.c | |||
| @@ -157,7 +157,7 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 157 | ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4); | 157 | ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4); |
| 158 | next: | 158 | next: |
| 159 | pos -= sizeof(ut); | 159 | pos -= sizeof(ut); |
| 160 | if (pos <= 0) | 160 | if (pos < 0) |
| 161 | break; /* done. */ | 161 | break; /* done. */ |
| 162 | xlseek(file, pos, SEEK_SET); | 162 | xlseek(file, pos, SEEK_SET); |
| 163 | } | 163 | } |
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/util-linux/nsenter.c b/util-linux/nsenter.c index 9a250e43c..425d5175f 100644 --- a/util-linux/nsenter.c +++ b/util-linux/nsenter.c | |||
| @@ -251,7 +251,14 @@ int nsenter_main(int argc UNUSED_PARAM, char **argv) | |||
| 251 | * explicitly requested by the user not to. | 251 | * explicitly requested by the user not to. |
| 252 | */ | 252 | */ |
| 253 | if (!(opts & OPT_nofork) && (opts & OPT_pid)) { | 253 | if (!(opts & OPT_nofork) && (opts & OPT_pid)) { |
| 254 | xvfork_parent_waits_and_exits(); | 254 | pid_t pid = xvfork(); |
| 255 | if (pid > 0) { | ||
| 256 | /* Parent */ | ||
| 257 | int exit_status = wait_for_exitstatus(pid); | ||
| 258 | if (WIFSIGNALED(exit_status)) | ||
| 259 | kill_myself_with_sig(WTERMSIG(exit_status)); | ||
| 260 | return WEXITSTATUS(exit_status); | ||
| 261 | } | ||
| 255 | /* Child continues */ | 262 | /* Child continues */ |
| 256 | } | 263 | } |
| 257 | 264 | ||
diff --git a/util-linux/unshare.c b/util-linux/unshare.c index a9f56f388..f58cdd93e 100644 --- a/util-linux/unshare.c +++ b/util-linux/unshare.c | |||
| @@ -333,7 +333,14 @@ int unshare_main(int argc UNUSED_PARAM, char **argv) | |||
| 333 | * that'll become PID 1 in this new namespace. | 333 | * that'll become PID 1 in this new namespace. |
| 334 | */ | 334 | */ |
| 335 | if (opts & OPT_fork) { | 335 | if (opts & OPT_fork) { |
| 336 | xvfork_parent_waits_and_exits(); | 336 | pid_t pid = xvfork(); |
| 337 | if (pid > 0) { | ||
| 338 | /* Parent */ | ||
| 339 | int exit_status = wait_for_exitstatus(pid); | ||
| 340 | if (WIFSIGNALED(exit_status)) | ||
| 341 | kill_myself_with_sig(WTERMSIG(exit_status)); | ||
| 342 | return WEXITSTATUS(exit_status); | ||
| 343 | } | ||
| 337 | /* Child continues */ | 344 | /* Child continues */ |
| 338 | } | 345 | } |
| 339 | 346 | ||
diff --git a/win32/Kbuild b/win32/Kbuild index 1bb79bfd3..019c83030 100644 --- a/win32/Kbuild +++ b/win32/Kbuild | |||
| @@ -7,12 +7,12 @@ lib-y:= | |||
| 7 | lib-$(CONFIG_PLATFORM_MINGW32) += dirent.o | 7 | lib-$(CONFIG_PLATFORM_MINGW32) += dirent.o |
| 8 | lib-$(CONFIG_PLATFORM_MINGW32) += dirname.o | 8 | lib-$(CONFIG_PLATFORM_MINGW32) += dirname.o |
| 9 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o | 9 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o |
| 10 | lib-$(CONFIG_PLATFORM_MINGW32) += flock.o | ||
| 10 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o | 11 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o |
| 11 | lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o | 12 | lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o |
| 12 | lib-$(CONFIG_PLATFORM_MINGW32) += glob.o | 13 | lib-$(CONFIG_PLATFORM_MINGW32) += glob.o |
| 13 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o | 14 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o |
| 14 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o | 15 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o |
| 15 | lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o | ||
| 16 | lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o | 16 | lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o |
| 17 | lib-$(CONFIG_PLATFORM_MINGW32) += process.o | 17 | lib-$(CONFIG_PLATFORM_MINGW32) += process.o |
| 18 | lib-$(CONFIG_PLATFORM_MINGW32) += match_class.o | 18 | lib-$(CONFIG_PLATFORM_MINGW32) += match_class.o |
| @@ -22,7 +22,6 @@ lib-$(CONFIG_PLATFORM_MINGW32) += poll.o | |||
| 22 | lib-$(CONFIG_PLATFORM_MINGW32) += popen.o | 22 | lib-$(CONFIG_PLATFORM_MINGW32) += popen.o |
| 23 | lib-$(CONFIG_PLATFORM_MINGW32) += regex.o | 23 | lib-$(CONFIG_PLATFORM_MINGW32) += regex.o |
| 24 | lib-$(CONFIG_PLATFORM_MINGW32) += select.o | 24 | lib-$(CONFIG_PLATFORM_MINGW32) += select.o |
| 25 | lib-$(CONFIG_FEATURE_PRNG_SHELL) += sh_random.o | ||
| 26 | lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o | 25 | lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o |
| 27 | lib-$(CONFIG_PLATFORM_MINGW32) += strndup.o | 26 | lib-$(CONFIG_PLATFORM_MINGW32) += strndup.o |
| 28 | lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o | 27 | lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o |
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 @@ | |||
| 3 | struct DIR { | 3 | struct 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 | ||
| 9 | static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata) | 11 | static 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/flock.c b/win32/flock.c new file mode 100644 index 000000000..260b40171 --- /dev/null +++ b/win32/flock.c | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | /* Emulate flock on platforms that lack it, primarily Windows and MinGW. | ||
| 2 | |||
| 3 | This is derived from sqlite3 sources. | ||
| 4 | https://www.sqlite.org/src/finfo?name=src/os_win.c | ||
| 5 | https://www.sqlite.org/copyright.html | ||
| 6 | |||
| 7 | Written by Richard W.M. Jones <rjones.at.redhat.com> | ||
| 8 | |||
| 9 | Copyright (C) 2008-2025 Free Software Foundation, Inc. | ||
| 10 | |||
| 11 | This library is free software; you can redistribute it and/or | ||
| 12 | modify it under the terms of the GNU Lesser General Public | ||
| 13 | License as published by the Free Software Foundation; either | ||
| 14 | version 2.1 of the License, or (at your option) any later version. | ||
| 15 | |||
| 16 | This library is distributed in the hope that it will be useful, | ||
| 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 19 | Lesser General Public License for more details. | ||
| 20 | |||
| 21 | You should have received a copy of the GNU Lesser General Public License | ||
| 22 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 23 | |||
| 24 | #include "libbb.h" | ||
| 25 | #include <sys/file.h> | ||
| 26 | |||
| 27 | /* LockFileEx */ | ||
| 28 | |||
| 29 | # include <errno.h> | ||
| 30 | |||
| 31 | /* _get_osfhandle */ | ||
| 32 | # include <io.h> | ||
| 33 | |||
| 34 | /* Determine the current size of a file. Because the other braindead | ||
| 35 | * APIs we'll call need lower/upper 32 bit pairs, keep the file size | ||
| 36 | * like that too. | ||
| 37 | */ | ||
| 38 | static BOOL | ||
| 39 | file_size (HANDLE h, DWORD * lower, DWORD * upper) | ||
| 40 | { | ||
| 41 | *lower = GetFileSize (h, upper); | ||
| 42 | return 1; | ||
| 43 | } | ||
| 44 | |||
| 45 | /* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */ | ||
| 46 | # ifndef LOCKFILE_FAIL_IMMEDIATELY | ||
| 47 | # define LOCKFILE_FAIL_IMMEDIATELY 1 | ||
| 48 | # endif | ||
| 49 | |||
| 50 | /* Acquire a lock. */ | ||
| 51 | static BOOL | ||
| 52 | do_lock (HANDLE h, int non_blocking, int exclusive) | ||
| 53 | { | ||
| 54 | BOOL res; | ||
| 55 | DWORD size_lower, size_upper; | ||
| 56 | OVERLAPPED ovlp; | ||
| 57 | int flags = 0; | ||
| 58 | |||
| 59 | /* We're going to lock the whole file, so get the file size. */ | ||
| 60 | res = file_size (h, &size_lower, &size_upper); | ||
| 61 | if (!res) | ||
| 62 | return 0; | ||
| 63 | |||
| 64 | /* Start offset is 0, and also zero the remaining members of this struct. */ | ||
| 65 | memset (&ovlp, 0, sizeof ovlp); | ||
| 66 | |||
| 67 | if (non_blocking) | ||
| 68 | flags |= LOCKFILE_FAIL_IMMEDIATELY; | ||
| 69 | if (exclusive) | ||
| 70 | flags |= LOCKFILE_EXCLUSIVE_LOCK; | ||
| 71 | |||
| 72 | return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Unlock reader or exclusive lock. */ | ||
| 76 | static BOOL | ||
| 77 | do_unlock (HANDLE h) | ||
| 78 | { | ||
| 79 | int res; | ||
| 80 | DWORD size_lower, size_upper; | ||
| 81 | |||
| 82 | res = file_size (h, &size_lower, &size_upper); | ||
| 83 | if (!res) | ||
| 84 | return 0; | ||
| 85 | |||
| 86 | return UnlockFile (h, 0, 0, size_lower, size_upper); | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Now our BSD-like flock operation. */ | ||
| 90 | int | ||
| 91 | flock (int fd, int operation) | ||
| 92 | { | ||
| 93 | HANDLE h = (HANDLE) _get_osfhandle (fd); | ||
| 94 | DWORD res; | ||
| 95 | int non_blocking; | ||
| 96 | |||
| 97 | if (h == INVALID_HANDLE_VALUE) | ||
| 98 | { | ||
| 99 | errno = EBADF; | ||
| 100 | return -1; | ||
| 101 | } | ||
| 102 | |||
| 103 | non_blocking = operation & LOCK_NB; | ||
| 104 | operation &= ~LOCK_NB; | ||
| 105 | |||
| 106 | switch (operation) | ||
| 107 | { | ||
| 108 | case LOCK_SH: | ||
| 109 | res = do_lock (h, non_blocking, 0); | ||
| 110 | break; | ||
| 111 | case LOCK_EX: | ||
| 112 | res = do_lock (h, non_blocking, 1); | ||
| 113 | break; | ||
| 114 | case LOCK_UN: | ||
| 115 | res = do_unlock (h); | ||
| 116 | break; | ||
| 117 | default: | ||
| 118 | errno = EINVAL; | ||
| 119 | return -1; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Map Windows errors into Unix errnos. As usual MSDN fails to | ||
| 123 | * document the permissible error codes. | ||
| 124 | */ | ||
| 125 | if (!res) | ||
| 126 | { | ||
| 127 | errno = err_win_to_posix(); | ||
| 128 | return -1; | ||
| 129 | } | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | } | ||
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..2a7f75285 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 | ||
| 3 | static int mingw_get_terminal_width_height(struct winsize *win) | 5 | static 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 && 0 | ||
| 29 | static 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 | ||
| 25 | int ioctl(int fd UNUSED_PARAM, int code, ...) | 66 | int 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 && 0 | ||
| 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/isaac.c b/win32/isaac.c deleted file mode 100644 index 19b96de94..000000000 --- a/win32/isaac.c +++ /dev/null | |||
| @@ -1,192 +0,0 @@ | |||
| 1 | /* | ||
| 2 | ------------------------------------------------------------------------------ | ||
| 3 | readable.c: My random number generator, ISAAC. | ||
| 4 | (c) Bob Jenkins, March 1996, Public Domain | ||
| 5 | You may use this code in any way you wish, and it is free. No warrantee. | ||
| 6 | * May 2008 -- made it not depend on standard.h | ||
| 7 | ------------------------------------------------------------------------------ | ||
| 8 | |||
| 9 | The original version of this file was downloaded from Bob Jenkins website: | ||
| 10 | |||
| 11 | http://burtleburtle.net/bob/rand/isaacafa.html | ||
| 12 | |||
| 13 | The isaac and randinit functions have been slightly modified to silence | ||
| 14 | warnings in modern compilers; the get_entropy and get_random_bytes have | ||
| 15 | been added. | ||
| 16 | |||
| 17 | These changes were made by R M Yorston and are also dedicated to the | ||
| 18 | public domain. | ||
| 19 | */ | ||
| 20 | #include "libbb.h" | ||
| 21 | #include <ntsecapi.h> | ||
| 22 | |||
| 23 | typedef struct { | ||
| 24 | /* external results */ | ||
| 25 | uint32_t randrsl[256]; | ||
| 26 | |||
| 27 | /* internal state */ | ||
| 28 | uint32_t mm[256]; | ||
| 29 | uint32_t aa, bb, cc; | ||
| 30 | } isaac_t; | ||
| 31 | |||
| 32 | |||
| 33 | static void isaac(isaac_t *t) | ||
| 34 | { | ||
| 35 | register uint32_t i,x,y; | ||
| 36 | |||
| 37 | t->cc = t->cc + 1; /* cc just gets incremented once per 256 results */ | ||
| 38 | t->bb = t->bb + t->cc; /* then combined with bb */ | ||
| 39 | |||
| 40 | for (i=0; i<256; ++i) | ||
| 41 | { | ||
| 42 | x = t->mm[i]; | ||
| 43 | switch (i%4) | ||
| 44 | { | ||
| 45 | case 0: t->aa = t->aa^(t->aa<<13); break; | ||
| 46 | case 1: t->aa = t->aa^(t->aa>>6); break; | ||
| 47 | case 2: t->aa = t->aa^(t->aa<<2); break; | ||
| 48 | case 3: t->aa = t->aa^(t->aa>>16); break; | ||
| 49 | } | ||
| 50 | t->aa = t->mm[(i+128)%256] + t->aa; | ||
| 51 | t->mm[i] = y = t->mm[(x>>2)%256] + t->aa + t->bb; | ||
| 52 | t->randrsl[i] = t->bb = t->mm[(y>>10)%256] + x; | ||
| 53 | |||
| 54 | /* Note that bits 2..9 are chosen from x but 10..17 are chosen | ||
| 55 | from y. The only important thing here is that 2..9 and 10..17 | ||
| 56 | don't overlap. 2..9 and 10..17 were then chosen for speed in | ||
| 57 | the optimized version (rand.c) */ | ||
| 58 | /* See http://burtleburtle.net/bob/rand/isaac.html | ||
| 59 | for further explanations and analysis. */ | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| 64 | /* if (flag!=0), then use the contents of randrsl[] to initialize mm[]. */ | ||
| 65 | #define mix(a,b,c,d,e,f,g,h) \ | ||
| 66 | { \ | ||
| 67 | a^=b<<11; d+=a; b+=c; \ | ||
| 68 | b^=c>>2; e+=b; c+=d; \ | ||
| 69 | c^=d<<8; f+=c; d+=e; \ | ||
| 70 | d^=e>>16; g+=d; e+=f; \ | ||
| 71 | e^=f<<10; h+=e; f+=g; \ | ||
| 72 | f^=g>>4; a+=f; g+=h; \ | ||
| 73 | g^=h<<8; b+=g; h+=a; \ | ||
| 74 | h^=a>>9; c+=h; a+=b; \ | ||
| 75 | } | ||
| 76 | |||
| 77 | static void randinit(isaac_t *t, int flag) | ||
| 78 | { | ||
| 79 | int i; | ||
| 80 | uint32_t a,b,c,d,e,f,g,h; | ||
| 81 | t->aa = t->bb = t->cc = 0; | ||
| 82 | a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */ | ||
| 83 | |||
| 84 | for (i=0; i<4; ++i) /* scramble it */ | ||
| 85 | { | ||
| 86 | mix(a,b,c,d,e,f,g,h); | ||
| 87 | } | ||
| 88 | |||
| 89 | for (i=0; i<256; i+=8) /* fill in mm[] with messy stuff */ | ||
| 90 | { | ||
| 91 | if (flag) /* use all the information in the seed */ | ||
| 92 | { | ||
| 93 | a+=t->randrsl[i ]; b+=t->randrsl[i+1]; c+=t->randrsl[i+2]; | ||
| 94 | d+=t->randrsl[i+3]; e+=t->randrsl[i+4]; f+=t->randrsl[i+5]; | ||
| 95 | g+=t->randrsl[i+6]; h+=t->randrsl[i+7]; | ||
| 96 | } | ||
| 97 | mix(a,b,c,d,e,f,g,h); | ||
| 98 | t->mm[i ]=a; t->mm[i+1]=b; t->mm[i+2]=c; t->mm[i+3]=d; | ||
| 99 | t->mm[i+4]=e; t->mm[i+5]=f; t->mm[i+6]=g; t->mm[i+7]=h; | ||
| 100 | } | ||
| 101 | |||
| 102 | if (flag) | ||
| 103 | { /* do a second pass to make all of the seed affect all of mm */ | ||
| 104 | for (i=0; i<256; i+=8) | ||
| 105 | { | ||
| 106 | a+=t->mm[i ]; b+=t->mm[i+1]; c+=t->mm[i+2]; d+=t->mm[i+3]; | ||
| 107 | e+=t->mm[i+4]; f+=t->mm[i+5]; g+=t->mm[i+6]; h+=t->mm[i+7]; | ||
| 108 | mix(a,b,c,d,e,f,g,h); | ||
| 109 | t->mm[i ]=a; t->mm[i+1]=b; t->mm[i+2]=c; t->mm[i+3]=d; | ||
| 110 | t->mm[i+4]=e; t->mm[i+5]=f; t->mm[i+6]=g; t->mm[i+7]=h; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | isaac(t); /* fill in the first set of results */ | ||
| 115 | } | ||
| 116 | |||
| 117 | /* | ||
| 118 | * Stuff a few bytes of random-ish data into the generator state. | ||
| 119 | * This is unlikely to be very robust: don't rely on it for | ||
| 120 | * anything that needs to be secure. | ||
| 121 | */ | ||
| 122 | static void get_entropy(isaac_t *t) | ||
| 123 | { | ||
| 124 | if (!RtlGenRandom(t->randrsl, sizeof(uint32_t)*256)) | ||
| 125 | GetSystemTimeAsFileTime((FILETIME *)t->randrsl); | ||
| 126 | |||
| 127 | #if 0 | ||
| 128 | { | ||
| 129 | unsigned char *p = (unsigned char *)t->randrsl; | ||
| 130 | int j; | ||
| 131 | |||
| 132 | for (j=0; j<256; ++j) { | ||
| 133 | fprintf(stderr, "%02x", p[j]); | ||
| 134 | if ((j&31) == 31) { | ||
| 135 | fprintf(stderr, "\n"); | ||
| 136 | } | ||
| 137 | else if ((j&3) == 3) { | ||
| 138 | fprintf(stderr, " "); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | fprintf(stderr, "\n"); | ||
| 142 | } | ||
| 143 | #endif | ||
| 144 | } | ||
| 145 | |||
| 146 | #define RAND_BYTES sizeof(t->randrsl) | ||
| 147 | #define RAND_WORDS (sizeof(t->randrsl)/sizeof(t->randrsl[0])) | ||
| 148 | |||
| 149 | /* | ||
| 150 | * Place 'count' random bytes in the buffer 'buf'. You're responsible | ||
| 151 | * for ensuring the buffer is big enough. | ||
| 152 | */ | ||
| 153 | ssize_t get_random_bytes(void *buf, ssize_t count) | ||
| 154 | { | ||
| 155 | static isaac_t *t = NULL; | ||
| 156 | static int rand_index = 0; | ||
| 157 | ssize_t save_count = count; | ||
| 158 | unsigned char *ptr; | ||
| 159 | |||
| 160 | if (buf == NULL || count < 0) { | ||
| 161 | errno = EINVAL; | ||
| 162 | return -1; | ||
| 163 | } | ||
| 164 | |||
| 165 | if (!t) { | ||
| 166 | t = xzalloc(sizeof(isaac_t)); | ||
| 167 | |||
| 168 | get_entropy(t); | ||
| 169 | randinit(t, 1); | ||
| 170 | isaac(t); | ||
| 171 | rand_index = 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | ptr = (unsigned char *)t->randrsl; | ||
| 175 | while (count > 0) { | ||
| 176 | int bytes_left = RAND_BYTES - rand_index; | ||
| 177 | ssize_t delta = MIN(bytes_left, count); | ||
| 178 | |||
| 179 | memcpy(buf, ptr+rand_index, delta); | ||
| 180 | buf += delta; | ||
| 181 | count -= delta; | ||
| 182 | rand_index += delta; | ||
| 183 | |||
| 184 | if (rand_index >= RAND_BYTES) { | ||
| 185 | /* generate more */ | ||
| 186 | isaac(t); | ||
| 187 | rand_index = 0; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | return save_count; | ||
| 192 | } | ||
diff --git a/win32/mingw.c b/win32/mingw.c index 7a5198ccf..7c4a3547f 100644 --- a/win32/mingw.c +++ b/win32/mingw.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #endif | 6 | #endif |
| 7 | #include <ntdef.h> | 7 | #include <ntdef.h> |
| 8 | #include <psapi.h> | 8 | #include <psapi.h> |
| 9 | #include <ntsecapi.h> | ||
| 9 | 10 | ||
| 10 | #if defined(__MINGW64_VERSION_MAJOR) | 11 | #if defined(__MINGW64_VERSION_MAJOR) |
| 11 | #if ENABLE_GLOBBING | 12 | #if ENABLE_GLOBBING |
| @@ -194,7 +195,7 @@ static int rand_fd = -1; | |||
| 194 | int get_dev_type(const char *filename) | 195 | int get_dev_type(const char *filename) |
| 195 | { | 196 | { |
| 196 | if (filename && is_prefixed_with(filename, "/dev/")) | 197 | if (filename && is_prefixed_with(filename, "/dev/")) |
| 197 | return index_in_strings("null\0zero\0urandom\0", filename+5); | 198 | return index_in_strings("null\0tty\0zero\0urandom\0", filename+5); |
| 198 | 199 | ||
| 199 | return NOT_DEVICE; | 200 | return NOT_DEVICE; |
| 200 | } | 201 | } |
| @@ -230,14 +231,15 @@ int mingw_open (const char *filename, int oflags, ...) | |||
| 230 | int special = (oflags & O_SPECIAL); | 231 | int special = (oflags & O_SPECIAL); |
| 231 | int dev = get_dev_type(filename); | 232 | int dev = get_dev_type(filename); |
| 232 | 233 | ||
| 233 | /* /dev/null is always allowed, others only if O_SPECIAL is set */ | 234 | /* /dev/zero and /dev/urandom are only allowed if O_SPECIAL is set */ |
| 234 | if (dev == DEV_NULL || (special && dev != NOT_DEVICE)) { | 235 | if (dev == DEV_NULL) |
| 235 | filename = "nul"; | 236 | filename = "nul"; |
| 236 | oflags = O_RDWR; | 237 | else if (dev == DEV_TTY) |
| 237 | } | 238 | filename = "con"; |
| 238 | else if ((fd=get_dev_fd(filename)) >= 0) { | 239 | else if (special && dev != NOT_DEVICE) |
| 240 | filename = "nul"; | ||
| 241 | else if ((fd=get_dev_fd(filename)) >= 0) | ||
| 239 | return fd; | 242 | return fd; |
| 240 | } | ||
| 241 | 243 | ||
| 242 | if ((oflags & O_CREAT)) { | 244 | if ((oflags & O_CREAT)) { |
| 243 | va_start(args, oflags); | 245 | va_start(args, oflags); |
| @@ -284,12 +286,36 @@ ssize_t FAST_FUNC mingw_open_read_close(const char *fn, void *buf, size_t size) | |||
| 284 | FILE *mingw_fopen (const char *filename, const char *otype) | 286 | FILE *mingw_fopen (const char *filename, const char *otype) |
| 285 | { | 287 | { |
| 286 | int fd; | 288 | int fd; |
| 289 | FILE *stream; | ||
| 290 | int dev = get_dev_type(filename); | ||
| 287 | 291 | ||
| 288 | if (get_dev_type(filename) == DEV_NULL) | 292 | if (dev == DEV_NULL) |
| 289 | filename = "nul"; | 293 | filename = "nul"; |
| 294 | else if (dev == DEV_TTY) | ||
| 295 | filename = "con"; | ||
| 290 | else if ((fd=get_dev_fd(filename)) >= 0) | 296 | else if ((fd=get_dev_fd(filename)) >= 0) |
| 291 | return fdopen(fd, otype); | 297 | return fdopen(fd, otype); |
| 292 | return fopen(filename, otype); | 298 | stream = fopen(filename, otype); |
| 299 | if (stream == NULL && errno == EACCES && strcmp(otype, "r") == 0 && | ||
| 300 | mingw_is_directory(filename)) | ||
| 301 | errno = EISDIR; | ||
| 302 | return stream; | ||
| 303 | } | ||
| 304 | |||
| 305 | static ssize_t get_random_bytes(void *buf, ssize_t count) | ||
| 306 | { | ||
| 307 | // 32-bit mingw-w64 didn't support RtlGenRandom until 7.0.0 | ||
| 308 | #if !defined(__MINGW64_VERSION_MAJOR) || \ | ||
| 309 | (__MINGW64_VERSION_MAJOR < 7 && !defined(__MINGW64__)) | ||
| 310 | DECLARE_PROC_ADDR(BOOLEAN, SystemFunction036, PVOID, ULONG); | ||
| 311 | |||
| 312 | if (!INIT_PROC_ADDR(advapi32.dll, SystemFunction036)) | ||
| 313 | return -1; | ||
| 314 | # define RtlGenRandom SystemFunction036 | ||
| 315 | #endif | ||
| 316 | if (count < 0 || !RtlGenRandom(buf, count)) | ||
| 317 | return -1; | ||
| 318 | return count; | ||
| 293 | } | 319 | } |
| 294 | 320 | ||
| 295 | #undef read | 321 | #undef read |
| @@ -363,8 +389,9 @@ static inline mode_t file_attr_to_st_mode(DWORD attr) | |||
| 363 | static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) | 389 | static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) |
| 364 | { | 390 | { |
| 365 | char *want_dir; | 391 | char *want_dir; |
| 392 | int dev = get_dev_type(fname); | ||
| 366 | 393 | ||
| 367 | if (get_dev_type(fname) == DEV_NULL || get_dev_fd(fname) >= 0) { | 394 | if (dev == DEV_NULL || dev == DEV_TTY || get_dev_fd(fname) >= 0) { |
| 368 | /* Fake attributes for special devices */ | 395 | /* Fake attributes for special devices */ |
| 369 | /* Though not /dev/zero or /dev/urandom */ | 396 | /* Though not /dev/zero or /dev/urandom */ |
| 370 | FILETIME epoch = {0xd53e8000, 0x019db1de}; // Unix epoch as FILETIME | 397 | FILETIME epoch = {0xd53e8000, 0x019db1de}; // Unix epoch as FILETIME |
| @@ -2538,3 +2565,34 @@ char * FAST_FUNC exe_relative_path(const char *tail) | |||
| 2538 | free(exepath); | 2565 | free(exepath); |
| 2539 | return relpath; | 2566 | return relpath; |
| 2540 | } | 2567 | } |
| 2568 | |||
| 2569 | int mingw_shell_execute(SHELLEXECUTEINFO *info) | ||
| 2570 | { | ||
| 2571 | DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); | ||
| 2572 | char *lpath; | ||
| 2573 | int ret; | ||
| 2574 | |||
| 2575 | if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) { | ||
| 2576 | errno = ENOSYS; | ||
| 2577 | return FALSE; | ||
| 2578 | } | ||
| 2579 | |||
| 2580 | // ShellExecuteEx() needs backslash as separator in UNC paths. | ||
| 2581 | lpath = xstrdup(info->lpFile); | ||
| 2582 | slash_to_bs(lpath); | ||
| 2583 | info->lpFile = lpath; | ||
| 2584 | |||
| 2585 | ret = ShellExecuteExA(info); | ||
| 2586 | |||
| 2587 | free(lpath); | ||
| 2588 | return ret; | ||
| 2589 | } | ||
| 2590 | |||
| 2591 | #if ENABLE_FEATURE_USE_CNG_API | ||
| 2592 | void mingw_die_if_error(NTSTATUS status, const char *function_name) { | ||
| 2593 | if (!NT_SUCCESS(status)) { | ||
| 2594 | bb_error_msg_and_die("call to %s failed: 0x%08lX", | ||
| 2595 | function_name, (unsigned long)status); | ||
| 2596 | } | ||
| 2597 | } | ||
| 2598 | #endif | ||
diff --git a/win32/paths.h b/win32/paths.h index e69de29bb..dc81f1b7a 100644 --- a/win32/paths.h +++ b/win32/paths.h | |||
| @@ -0,0 +1 @@ | |||
| #define _PATH_DEVNULL "/dev/null" | |||
diff --git a/win32/process.c b/win32/process.c index e7c9ca187..b4686db47 100644 --- a/win32/process.c +++ b/win32/process.c | |||
| @@ -4,6 +4,10 @@ | |||
| 4 | #include "lazyload.h" | 4 | #include "lazyload.h" |
| 5 | #include "NUM_APPLETS.h" | 5 | #include "NUM_APPLETS.h" |
| 6 | 6 | ||
| 7 | #ifndef ERROR_ELEVATION_REQUIRED | ||
| 8 | # define ERROR_ELEVATION_REQUIRED __MSABI_LONG(740) | ||
| 9 | #endif | ||
| 10 | |||
| 7 | pid_t waitpid(pid_t pid, int *status, int options) | 11 | pid_t waitpid(pid_t pid, int *status, int options) |
| 8 | #if ENABLE_TIME | 12 | #if ENABLE_TIME |
| 9 | { | 13 | { |
| @@ -331,7 +335,9 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, | |||
| 331 | } else { | 335 | } else { |
| 332 | errno = ENOENT; | 336 | errno = ENOENT; |
| 333 | } | 337 | } |
| 338 | #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 | ||
| 334 | done: | 339 | done: |
| 340 | #endif | ||
| 335 | free(path); | 341 | free(path); |
| 336 | free(new_argv); | 342 | free(new_argv); |
| 337 | return ret; | 343 | return ret; |
| @@ -499,6 +505,38 @@ static NORETURN void wait_for_child(HANDLE child, const char *cmd) | |||
| 499 | exit((int)code); | 505 | exit((int)code); |
| 500 | } | 506 | } |
| 501 | 507 | ||
| 508 | static intptr_t | ||
| 509 | shell_execute(const char *path, char *const *argv) | ||
| 510 | { | ||
| 511 | SHELLEXECUTEINFO info; | ||
| 512 | char *args; | ||
| 513 | |||
| 514 | memset(&info, 0, sizeof(SHELLEXECUTEINFO)); | ||
| 515 | info.cbSize = sizeof(SHELLEXECUTEINFO); | ||
| 516 | info.fMask = SEE_MASK_NOCLOSEPROCESS; | ||
| 517 | /* info.hwnd = NULL; */ | ||
| 518 | info.lpVerb = "runas"; | ||
| 519 | info.lpFile = path; | ||
| 520 | |||
| 521 | args = NULL; | ||
| 522 | if (*argv++) { | ||
| 523 | while (*argv) { | ||
| 524 | char *q = quote_arg(*argv++); | ||
| 525 | args = xappendword(args, q); | ||
| 526 | free(q); | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | info.lpParameters = args; | ||
| 531 | /* info.lpDirectory = NULL; */ | ||
| 532 | info.nShow = SW_SHOWNORMAL; | ||
| 533 | |||
| 534 | mingw_shell_execute(&info); | ||
| 535 | |||
| 536 | free(args); | ||
| 537 | return info.hProcess ? (intptr_t)info.hProcess : -1; | ||
| 538 | } | ||
| 539 | |||
| 502 | int | 540 | int |
| 503 | mingw_execvp(const char *cmd, char *const *argv) | 541 | mingw_execvp(const char *cmd, char *const *argv) |
| 504 | { | 542 | { |
| @@ -512,6 +550,16 @@ int | |||
| 512 | mingw_execve(const char *cmd, char *const *argv, char *const *envp) | 550 | mingw_execve(const char *cmd, char *const *argv, char *const *envp) |
| 513 | { | 551 | { |
| 514 | intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); | 552 | intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); |
| 553 | |||
| 554 | if (ret == -1 && GetLastError() == ERROR_ELEVATION_REQUIRED) { | ||
| 555 | // Command exists but failed because it wants elevated privileges. | ||
| 556 | // Try again using ShellExecuteEx(). | ||
| 557 | SetLastError(0); | ||
| 558 | ret = shell_execute(cmd, argv); | ||
| 559 | if (GetLastError()) | ||
| 560 | exit(1); | ||
| 561 | } | ||
| 562 | |||
| 515 | if (ret != -1) | 563 | if (ret != -1) |
| 516 | wait_for_child((HANDLE)ret, cmd); | 564 | wait_for_child((HANDLE)ret, cmd); |
| 517 | return ret; | 565 | return ret; |
| @@ -751,7 +799,7 @@ UNUSED_PARAM | |||
| 751 | return sp; | 799 | return sp; |
| 752 | } | 800 | } |
| 753 | 801 | ||
| 754 | void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | 802 | int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) |
| 755 | { | 803 | { |
| 756 | const char *str, *cmdline; | 804 | const char *str, *cmdline; |
| 757 | 805 | ||
| @@ -763,6 +811,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
| 763 | else | 811 | else |
| 764 | cmdline = comm; | 812 | cmdline = comm; |
| 765 | safe_strncpy(buf, cmdline, col); | 813 | safe_strncpy(buf, cmdline, col); |
| 814 | return 0; | ||
| 766 | } | 815 | } |
| 767 | 816 | ||
| 768 | /** | 817 | /** |
diff --git a/win32/resources/Kbuild.src b/win32/resources/Kbuild.src index d056a5964..cf1239c5b 100644 --- a/win32/resources/Kbuild.src +++ b/win32/resources/Kbuild.src | |||
| @@ -25,5 +25,4 @@ quiet_cmd_windres = WINDRES $@ | |||
| 25 | 25 | ||
| 26 | win32/resources/resources.o: win32/resources/resources.rc .config | 26 | win32/resources/resources.o: win32/resources/resources.rc .config |
| 27 | win32/resources/resources.o: win32/resources/aterm.ico win32/resources/sterm.ico | 27 | win32/resources/resources.o: win32/resources/aterm.ico win32/resources/sterm.ico |
| 28 | win32/resources/resources.o: win32/resources/utf8.manifest | 28 | win32/resources/resources.o: win32/resources/aterm.png win32/resources/sterm.png |
| 29 | win32/resources/resources.o: win32/resources/app.manifest | ||
diff --git a/win32/resources/app.manifest b/win32/resources/app.manifest deleted file mode 100644 index 5e76b7b8e..000000000 --- a/win32/resources/app.manifest +++ /dev/null | |||
| @@ -1,24 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
| 2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
| 3 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
| 4 | <security> | ||
| 5 | <requestedPrivileges> | ||
| 6 | <requestedExecutionLevel level="asInvoker"/> | ||
| 7 | </requestedPrivileges> | ||
| 8 | </security> | ||
| 9 | </trustInfo> | ||
| 10 | <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | ||
| 11 | <application> | ||
| 12 | <!--The ID below indicates application support for Windows Vista --> | ||
| 13 | <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> | ||
| 14 | <!--The ID below indicates application support for Windows 7 --> | ||
| 15 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | ||
| 16 | <!--The ID below indicates application support for Windows 8 --> | ||
| 17 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | ||
| 18 | <!--The ID below indicates application support for Windows 8.1 --> | ||
| 19 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | ||
| 20 | <!--The ID below indicates application support for Windows 10 --> | ||
| 21 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | ||
| 22 | </application> | ||
| 23 | </compatibility> | ||
| 24 | </assembly> | ||
diff --git a/win32/resources/aterm.png b/win32/resources/aterm.png new file mode 100644 index 000000000..87a3bc6ef --- /dev/null +++ b/win32/resources/aterm.png | |||
| Binary files differ | |||
diff --git a/win32/resources/resources.rc b/win32/resources/resources.rc index 5d06dda3c..379224828 100644 --- a/win32/resources/resources.rc +++ b/win32/resources/resources.rc | |||
| @@ -9,6 +9,13 @@ | |||
| 9 | 2 ICON "sterm.ico" | 9 | 2 ICON "sterm.ico" |
| 10 | #endif | 10 | #endif |
| 11 | 11 | ||
| 12 | #if ENABLE_FEATURE_PNG_ATERM || ENABLE_FEATURE_PNG_ALL | ||
| 13 | 1 ICON "aterm.png" | ||
| 14 | #endif | ||
| 15 | #if ENABLE_FEATURE_PNG_STERM || ENABLE_FEATURE_PNG_ALL | ||
| 16 | 2 ICON "sterm.png" | ||
| 17 | #endif | ||
| 18 | |||
| 12 | #if ENABLE_FEATURE_VERSIONINFO | 19 | #if ENABLE_FEATURE_VERSIONINFO |
| 13 | 1 VERSIONINFO | 20 | 1 VERSIONINFO |
| 14 | FILEVERSION BB_VERSION,BB_PATCHLEVEL,BB_SUBLEVEL,BB_EXTRAVERSION | 21 | FILEVERSION BB_VERSION,BB_PATCHLEVEL,BB_SUBLEVEL,BB_EXTRAVERSION |
| @@ -37,9 +44,57 @@ END | |||
| 37 | 44 | ||
| 38 | /* Hardcode numeric value for MANIFEST for llvm windres */ | 45 | /* Hardcode numeric value for MANIFEST for llvm windres */ |
| 39 | #if ENABLE_FEATURE_UTF8_MANIFEST | 46 | #if ENABLE_FEATURE_UTF8_MANIFEST |
| 40 | 1 24 "utf8.manifest" | 47 | 1 24 |
| 48 | BEGIN | ||
| 49 | "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>" | ||
| 50 | "<assembly manifestVersion=""1.0"" xmlns=""urn:schemas-microsoft-com:asm.v1"">" | ||
| 51 | "<assemblyIdentity type=""win32"" name=""busybox.exe"" version=""" xstr(BB_VERSION) "." xstr(BB_PATCHLEVEL) "." xstr(BB_SUBLEVEL) "." xstr(BB_EXTRAVERSION) """/>" | ||
| 52 | "<application>" | ||
| 53 | "<windowsSettings>" | ||
| 54 | "<activeCodePage xmlns=""http://schemas.microsoft.com/SMI/2019/WindowsSettings"">UTF-8</activeCodePage>" | ||
| 55 | "</windowsSettings>" | ||
| 56 | "</application>" | ||
| 57 | "<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">" | ||
| 58 | "<security>" | ||
| 59 | "<requestedPrivileges>" | ||
| 60 | "<requestedExecutionLevel level=""asInvoker""/>" | ||
| 61 | "</requestedPrivileges>" | ||
| 62 | "</security>" | ||
| 63 | "</trustInfo>" | ||
| 64 | "<compatibility xmlns=""urn:schemas-microsoft-com:compatibility.v1"">" | ||
| 65 | "<application>" | ||
| 66 | "<supportedOS Id=""{e2011457-1546-43c5-a5fe-008deee3d3f0}""/>" /* Application support for Windows Vista */ | ||
| 67 | "<supportedOS Id=""{35138b9a-5d96-4fbd-8e2d-a2440225f93a}""/>" /* Application support for Windows 7 */ | ||
| 68 | "<supportedOS Id=""{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}""/>" /* Application support for Windows 8 */ | ||
| 69 | "<supportedOS Id=""{1f676c76-80e1-4239-95bb-83d0f6d0da78}""/>" /* Application support for Windows 8.1 */ | ||
| 70 | "<supportedOS Id=""{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}""/>" /* Application support for Windows 10 */ | ||
| 71 | "</application>" | ||
| 72 | "</compatibility>" | ||
| 73 | "</assembly>" | ||
| 74 | END | ||
| 41 | #endif | 75 | #endif |
| 42 | 76 | ||
| 43 | #if ENABLE_FEATURE_APP_MANIFEST | 77 | #if ENABLE_FEATURE_APP_MANIFEST |
| 44 | 1 24 "app.manifest" | 78 | 1 24 |
| 79 | BEGIN | ||
| 80 | "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>" | ||
| 81 | "<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">" | ||
| 82 | "<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">" | ||
| 83 | "<security>" | ||
| 84 | "<requestedPrivileges>" | ||
| 85 | "<requestedExecutionLevel level=""asInvoker""/>" | ||
| 86 | "</requestedPrivileges>" | ||
| 87 | "</security>" | ||
| 88 | "</trustInfo>" | ||
| 89 | "<compatibility xmlns=""urn:schemas-microsoft-com:compatibility.v1"">" | ||
| 90 | "<application>" | ||
| 91 | "<supportedOS Id=""{e2011457-1546-43c5-a5fe-008deee3d3f0}""/>" /* Application support for Windows Vista */ | ||
| 92 | "<supportedOS Id=""{35138b9a-5d96-4fbd-8e2d-a2440225f93a}""/>" /* Application support for Windows 7 */ | ||
| 93 | "<supportedOS Id=""{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}""/>" /* Application support for Windows 8 */ | ||
| 94 | "<supportedOS Id=""{1f676c76-80e1-4239-95bb-83d0f6d0da78}""/>" /* Application support for Windows 8.1 */ | ||
| 95 | "<supportedOS Id=""{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}""/>" /* Application support for Windows 10 */ | ||
| 96 | "</application>" | ||
| 97 | "</compatibility>" | ||
| 98 | "</assembly>" | ||
| 99 | END | ||
| 45 | #endif | 100 | #endif |
diff --git a/win32/resources/sterm.png b/win32/resources/sterm.png new file mode 100644 index 000000000..9232b4625 --- /dev/null +++ b/win32/resources/sterm.png | |||
| Binary files differ | |||
diff --git a/win32/resources/utf8.manifest b/win32/resources/utf8.manifest deleted file mode 100644 index efe6a3d2f..000000000 --- a/win32/resources/utf8.manifest +++ /dev/null | |||
| @@ -1,30 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
| 2 | <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> | ||
| 3 | <assemblyIdentity type="win32" name="busybox.exe" version="6.0.0.0"/> | ||
| 4 | <application> | ||
| 5 | <windowsSettings> | ||
| 6 | <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage> | ||
| 7 | </windowsSettings> | ||
| 8 | </application> | ||
| 9 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
| 10 | <security> | ||
| 11 | <requestedPrivileges> | ||
| 12 | <requestedExecutionLevel level="asInvoker"/> | ||
| 13 | </requestedPrivileges> | ||
| 14 | </security> | ||
| 15 | </trustInfo> | ||
| 16 | <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | ||
| 17 | <application> | ||
| 18 | <!--The ID below indicates application support for Windows Vista --> | ||
| 19 | <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> | ||
| 20 | <!--The ID below indicates application support for Windows 7 --> | ||
| 21 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | ||
| 22 | <!--The ID below indicates application support for Windows 8 --> | ||
| 23 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | ||
| 24 | <!--The ID below indicates application support for Windows 8.1 --> | ||
| 25 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | ||
| 26 | <!--The ID below indicates application support for Windows 10 --> | ||
| 27 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | ||
| 28 | </application> | ||
| 29 | </compatibility> | ||
| 30 | </assembly> | ||
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/sh_random.c b/win32/sh_random.c deleted file mode 100644 index 10e942e80..000000000 --- a/win32/sh_random.c +++ /dev/null | |||
| @@ -1,59 +0,0 @@ | |||
| 1 | #include "libbb.h" | ||
| 2 | #include <ntsecapi.h> | ||
| 3 | #include "../shell/random.h" | ||
| 4 | |||
| 5 | /* | ||
| 6 | * Obtain a few bytes of random-ish data to initialise the generator. | ||
| 7 | * This is unlikely to be very robust: don't rely on it for | ||
| 8 | * anything that needs to be secure. | ||
| 9 | */ | ||
| 10 | static void get_entropy(uint32_t state[2]) | ||
| 11 | { | ||
| 12 | #if defined(__MINGW64_VERSION_MAJOR) && \ | ||
| 13 | (__MINGW64_VERSION_MAJOR >= 7 || defined(__MINGW64__)) | ||
| 14 | if (!RtlGenRandom(state, sizeof(state[0])*2)) | ||
| 15 | #endif | ||
| 16 | GetSystemTimeAsFileTime((FILETIME *)state); | ||
| 17 | |||
| 18 | #if 0 | ||
| 19 | { | ||
| 20 | unsigned char *p = (unsigned char *)state; | ||
| 21 | int j; | ||
| 22 | |||
| 23 | for (j=0; j<8; ++j) { | ||
| 24 | fprintf(stderr, "%02x", *p++); | ||
| 25 | if ((j&3) == 3) { | ||
| 26 | fprintf(stderr, " "); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | fprintf(stderr, "\n"); | ||
| 30 | } | ||
| 31 | #endif | ||
| 32 | } | ||
| 33 | |||
| 34 | ssize_t get_random_bytes(void *buf, ssize_t count) | ||
| 35 | { | ||
| 36 | static random_t rnd; | ||
| 37 | ssize_t save_count = count; | ||
| 38 | uint32_t value; | ||
| 39 | unsigned char *ptr = (unsigned char *)&value; | ||
| 40 | |||
| 41 | if (buf == NULL || count < 0) { | ||
| 42 | errno = EINVAL; | ||
| 43 | return -1; | ||
| 44 | } | ||
| 45 | |||
| 46 | if (UNINITED_RANDOM_T(&rnd)) { | ||
| 47 | uint32_t state[2] = {0, 0}; | ||
| 48 | |||
| 49 | get_entropy(state); | ||
| 50 | INIT_RANDOM_T(&rnd, state[0] ? state[0] : 1, state[1]); | ||
| 51 | } | ||
| 52 | |||
| 53 | for (;count > 0; buf+=4, count-=4) { | ||
| 54 | value = full_random(&rnd); | ||
| 55 | memcpy(buf, ptr, count >= 4 ? 4 : count); | ||
| 56 | } | ||
| 57 | |||
| 58 | return save_count; | ||
| 59 | } | ||
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 | ||
| 36 | enum ptime_locale_status { not, loc, raw }; | 36 | enum 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 | ||
| 3 | int tcsetattr(int fd, int mode UNUSED_PARAM, const struct termios *t) | 3 | int 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 | ||
| 16 | int tcgetattr(int fd, struct termios *t) | 14 | int 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 | ||
| 13 | typedef unsigned char cc_t; | 17 | typedef unsigned char cc_t; |
| 18 | typedef unsigned int tcflag_t; | ||
| 14 | typedef unsigned int speed_t; | 19 | typedef unsigned int speed_t; |
| 15 | 20 | ||
| 16 | #define NCCS 2 | 21 | #define NCCS 18 |
| 17 | struct termios { | 22 | struct 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 | ||
| 23 | struct winsize { | 32 | struct winsize { |
diff --git a/win32/winansi.c b/win32/winansi.c index c7529c453..427c71f11 100644 --- a/win32/winansi.c +++ b/win32/winansi.c | |||
| @@ -160,7 +160,7 @@ int FAST_FUNC terminal_mode(int reset) | |||
| 160 | mode |= VT_INPUT; | 160 | mode |= VT_INPUT; |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | if (newmode != oldmode) { | 163 | if (reset && newmode != oldmode) { |
| 164 | if (!SetConsoleMode(h, newmode)) { | 164 | if (!SetConsoleMode(h, newmode)) { |
| 165 | if (mode >= 4) | 165 | if (mode >= 4) |
| 166 | mode &= ~VT_INPUT; | 166 | mode &= ~VT_INPUT; |
| @@ -1182,7 +1182,7 @@ char *winansi_fgets(char *s, int size, FILE *stream) | |||
| 1182 | /* Ensure that isatty(fd) returns 0 for the NUL device */ | 1182 | /* Ensure that isatty(fd) returns 0 for the NUL device */ |
| 1183 | int mingw_isatty(int fd) | 1183 | int mingw_isatty(int fd) |
| 1184 | { | 1184 | { |
| 1185 | int result = _isatty(fd); | 1185 | int result = _isatty(fd) != 0; |
| 1186 | 1186 | ||
| 1187 | if (result) { | 1187 | if (result) { |
| 1188 | HANDLE handle = (HANDLE) _get_osfhandle(fd); | 1188 | HANDLE handle = (HANDLE) _get_osfhandle(fd); |
