diff options
author | Ron Yorston <rmy@pobox.com> | 2016-10-19 17:01:55 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2016-10-19 17:01:55 +0100 |
commit | 075814c60a316cfd088c88f26f75ab21b5850b98 (patch) | |
tree | f6e33ac693630827deb309faa5fa4931588db57d | |
parent | 977d65c1bbc57f5cdd0c8bfd67c8b5bb1cd390dd (diff) | |
parent | f37e1155aabde6bd95d267a8aec347cedccb8bc3 (diff) | |
download | busybox-w32-075814c60a316cfd088c88f26f75ab21b5850b98.tar.gz busybox-w32-075814c60a316cfd088c88f26f75ab21b5850b98.tar.bz2 busybox-w32-075814c60a316cfd088c88f26f75ab21b5850b98.zip |
Merge branch busybox (up to "ash: comment out free(p) just before...")
456 files changed, 5694 insertions, 2350 deletions
@@ -146,6 +146,10 @@ Rob Sullivan <cogito.ergo.cogito@gmail.com> | |||
146 | Linus Torvalds | 146 | Linus Torvalds |
147 | mkswap, fsck.minix, mkfs.minix | 147 | mkswap, fsck.minix, mkfs.minix |
148 | 148 | ||
149 | Linus Walleij | ||
150 | fbset and fbsplash config RGBA parsing | ||
151 | rewrite of mdev helper to create devices from /sys/dev | ||
152 | |||
149 | Mark Whitley <markw@codepoet.org> | 153 | Mark Whitley <markw@codepoet.org> |
150 | grep, sed, cut, xargs(previous), | 154 | grep, sed, cut, xargs(previous), |
151 | style-guide, new-applet-HOWTO, bug fixes, etc. | 155 | style-guide, new-applet-HOWTO, bug fixes, etc. |
diff --git a/applets/Kbuild.src b/applets/Kbuild.src index b61239948..3aedbbffe 100644 --- a/applets/Kbuild.src +++ b/applets/Kbuild.src | |||
@@ -29,7 +29,7 @@ applets/applets.o: include/usage_compressed.h include/applet_tables.h | |||
29 | 29 | ||
30 | applets/applet_tables: .config include/applets.h | 30 | applets/applet_tables: .config include/applets.h |
31 | applets/usage: .config include/applets.h | 31 | applets/usage: .config include/applets.h |
32 | applets/usage_pod: .config include/applet_tables.h include/applets.h | 32 | applets/usage_pod: .config include/applets.h include/applet_tables.h |
33 | 33 | ||
34 | quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h | 34 | quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h |
35 | cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets | 35 | cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets |
@@ -37,11 +37,21 @@ quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h | |||
37 | include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed | 37 | include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed |
38 | $(call cmd,gen_usage_compressed) | 38 | $(call cmd,gen_usage_compressed) |
39 | 39 | ||
40 | quiet_cmd_gen_applet_tables = GEN include/applet_tables.h | 40 | quiet_cmd_gen_applet_tables = GEN include/applet_tables.h include/NUM_APPLETS.h |
41 | cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h | 41 | cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h |
42 | 42 | ||
43 | include/applet_tables.h: applets/applet_tables | 43 | include/NUM_APPLETS.h: applets/applet_tables |
44 | $(call cmd,gen_applet_tables) | 44 | $(call cmd,gen_applet_tables) |
45 | 45 | ||
46 | include/NUM_APPLETS.h: applets/applet_tables | 46 | # In fact, include/applet_tables.h depends only on applets/applet_tables, |
47 | # and is generated by it. But specifying only it can run | ||
48 | # applets/applet_tables twice, possibly in parallel. | ||
49 | # We say that it also needs NUM_APPLETS.h | ||
50 | # | ||
51 | # Unfortunately, we need to list the same command, | ||
52 | # and it can be executed twice (sequentially). | ||
53 | # The alternative is to not list any command, | ||
54 | # and then if include/applet_tables.h is deleted, it won't be rebuilt. | ||
55 | # | ||
56 | include/applet_tables.h: include/NUM_APPLETS.h applets/applet_tables | ||
47 | $(call cmd,gen_applet_tables) | 57 | $(call cmd,gen_applet_tables) |
diff --git a/applets/applet_tables.c b/applets/applet_tables.c index 7894b2c33..9a2aa5329 100644 --- a/applets/applet_tables.c +++ b/applets/applet_tables.c | |||
@@ -143,7 +143,9 @@ int main(int argc, char **argv) | |||
143 | printf("};\n"); | 143 | printf("};\n"); |
144 | printf("#endif\n\n"); | 144 | printf("#endif\n\n"); |
145 | 145 | ||
146 | #if ENABLE_FEATURE_PREFER_APPLETS | 146 | #if ENABLE_FEATURE_PREFER_APPLETS \ |
147 | || ENABLE_FEATURE_SH_STANDALONE \ | ||
148 | || ENABLE_FEATURE_SH_NOFORK | ||
147 | printf("const uint8_t applet_flags[] ALIGN1 = {\n"); | 149 | printf("const uint8_t applet_flags[] ALIGN1 = {\n"); |
148 | i = 0; | 150 | i = 0; |
149 | while (i < NUM_APPLETS) { | 151 | while (i < NUM_APPLETS) { |
@@ -190,27 +192,28 @@ int main(int argc, char **argv) | |||
190 | printf("};\n"); | 192 | printf("};\n"); |
191 | #endif | 193 | #endif |
192 | //printf("#endif /* SKIP_definitions */\n"); | 194 | //printf("#endif /* SKIP_definitions */\n"); |
195 | |||
193 | // printf("\n"); | 196 | // printf("\n"); |
194 | // printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN); | 197 | // printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN); |
195 | 198 | ||
196 | if (argv[2]) { | 199 | if (argv[2]) { |
197 | char line_old[80]; | ||
198 | char line_new[80]; | ||
199 | FILE *fp; | 200 | FILE *fp; |
201 | char line_new[80]; | ||
202 | // char line_old[80]; | ||
200 | 203 | ||
201 | line_old[0] = 0; | ||
202 | fp = fopen(argv[2], "r"); | ||
203 | if (fp) { | ||
204 | fgets(line_old, sizeof(line_old), fp); | ||
205 | fclose(fp); | ||
206 | } | ||
207 | sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS); | 204 | sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS); |
208 | if (strcmp(line_old, line_new) != 0) { | 205 | // line_old[0] = 0; |
206 | // fp = fopen(argv[2], "r"); | ||
207 | // if (fp) { | ||
208 | // fgets(line_old, sizeof(line_old), fp); | ||
209 | // fclose(fp); | ||
210 | // } | ||
211 | // if (strcmp(line_old, line_new) != 0) { | ||
209 | fp = fopen(argv[2], "w"); | 212 | fp = fopen(argv[2], "w"); |
210 | if (!fp) | 213 | if (!fp) |
211 | return 1; | 214 | return 1; |
212 | fputs(line_new, fp); | 215 | fputs(line_new, fp); |
213 | } | 216 | // } |
214 | } | 217 | } |
215 | 218 | ||
216 | return 0; | 219 | return 0; |
diff --git a/archival/cpio.c b/archival/cpio.c index 3b1550720..540218cb2 100644 --- a/archival/cpio.c +++ b/archival/cpio.c | |||
@@ -23,7 +23,7 @@ | |||
23 | //config: cpio has 110 bytes of overheads for every stored file. | 23 | //config: cpio has 110 bytes of overheads for every stored file. |
24 | //config: | 24 | //config: |
25 | //config: This implementation of cpio can extract cpio archives created in the | 25 | //config: This implementation of cpio can extract cpio archives created in the |
26 | //config: "newc" or "crc" format, it cannot create or modify them. | 26 | //config: "newc" or "crc" format. |
27 | //config: | 27 | //config: |
28 | //config: Unless you have a specific application which requires cpio, you | 28 | //config: Unless you have a specific application which requires cpio, you |
29 | //config: should probably say N here. | 29 | //config: should probably say N here. |
@@ -51,10 +51,10 @@ | |||
51 | //usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]") | 51 | //usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]") |
52 | //usage: " [EXTR_FILE]..." | 52 | //usage: " [EXTR_FILE]..." |
53 | //usage:#define cpio_full_usage "\n\n" | 53 | //usage:#define cpio_full_usage "\n\n" |
54 | //usage: "Extract or list files from a cpio archive" | 54 | //usage: "Extract (-i) or list (-t) files from a cpio archive" |
55 | //usage: IF_FEATURE_CPIO_O(", or" | 55 | //usage: IF_FEATURE_CPIO_O(", or" |
56 | //usage: "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)") | 56 | //usage: "\ntake file list from stdin and create an archive (-o)" |
57 | //usage: " using file list on stdin" | 57 | //usage: IF_FEATURE_CPIO_P(" or copy files (-p)") |
58 | //usage: ) | 58 | //usage: ) |
59 | //usage: "\n" | 59 | //usage: "\n" |
60 | //usage: "\nMain operation mode:" | 60 | //usage: "\nMain operation mode:" |
diff --git a/archival/gzip.c b/archival/gzip.c index 8f1e4ff29..9e0bee815 100644 --- a/archival/gzip.c +++ b/archival/gzip.c | |||
@@ -2220,10 +2220,7 @@ int gzip_main(int argc UNUSED_PARAM, char **argv) | |||
2220 | opt >>= ENABLE_GUNZIP ? 7 : 5; /* drop cfv[dt]qn bits */ | 2220 | opt >>= ENABLE_GUNZIP ? 7 : 5; /* drop cfv[dt]qn bits */ |
2221 | if (opt == 0) | 2221 | if (opt == 0) |
2222 | opt = 1 << 6; /* default: 6 */ | 2222 | opt = 1 << 6; /* default: 6 */ |
2223 | /* Map 1..3 to 4 */ | 2223 | opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */ |
2224 | if (opt & 0x7) | ||
2225 | opt |= 1 << 4; | ||
2226 | opt = ffs(opt >> 3); | ||
2227 | max_chain_length = 1 << gzip_level_config[opt].chain_shift; | 2224 | max_chain_length = 1 << gzip_level_config[opt].chain_shift; |
2228 | good_match = gzip_level_config[opt].good; | 2225 | good_match = gzip_level_config[opt].good; |
2229 | max_lazy_match = gzip_level_config[opt].lazy2 * 2; | 2226 | max_lazy_match = gzip_level_config[opt].lazy2 * 2; |
diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig index 5f822e598..20c2e1550 100644 --- a/configs/TEST_nommu_defconfig +++ b/configs/TEST_nommu_defconfig | |||
@@ -760,7 +760,6 @@ CONFIG_TFTP_DEBUG=y | |||
760 | CONFIG_TRACEROUTE=y | 760 | CONFIG_TRACEROUTE=y |
761 | CONFIG_TRACEROUTE6=y | 761 | CONFIG_TRACEROUTE6=y |
762 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y | 762 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y |
763 | CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y | ||
764 | CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y | 763 | CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y |
765 | CONFIG_UDHCPD=y | 764 | CONFIG_UDHCPD=y |
766 | CONFIG_DHCPRELAY=y | 765 | CONFIG_DHCPRELAY=y |
diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig index c56781e32..845032ea1 100644 --- a/configs/TEST_noprintf_defconfig +++ b/configs/TEST_noprintf_defconfig | |||
@@ -762,7 +762,6 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" | |||
762 | # CONFIG_TRACEROUTE is not set | 762 | # CONFIG_TRACEROUTE is not set |
763 | # CONFIG_TRACEROUTE6 is not set | 763 | # CONFIG_TRACEROUTE6 is not set |
764 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set | 764 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set |
765 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
766 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 765 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
767 | # CONFIG_TUNCTL is not set | 766 | # CONFIG_TUNCTL is not set |
768 | # CONFIG_FEATURE_TUNCTL_UG is not set | 767 | # CONFIG_FEATURE_TUNCTL_UG is not set |
diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig index 28daa6273..d8c5af4f7 100644 --- a/configs/TEST_rh9_defconfig +++ b/configs/TEST_rh9_defconfig | |||
@@ -778,7 +778,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y | |||
778 | CONFIG_TRACEROUTE=y | 778 | CONFIG_TRACEROUTE=y |
779 | CONFIG_TRACEROUTE6=y | 779 | CONFIG_TRACEROUTE6=y |
780 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y | 780 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y |
781 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
782 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 781 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
783 | CONFIG_TUNCTL=y | 782 | CONFIG_TUNCTL=y |
784 | CONFIG_FEATURE_TUNCTL_UG=y | 783 | CONFIG_FEATURE_TUNCTL_UG=y |
diff --git a/configs/android2_defconfig b/configs/android2_defconfig index fbc0da091..857f9f5df 100644 --- a/configs/android2_defconfig +++ b/configs/android2_defconfig | |||
@@ -814,7 +814,6 @@ CONFIG_TCPSVD=y | |||
814 | # CONFIG_TRACEROUTE is not set | 814 | # CONFIG_TRACEROUTE is not set |
815 | # CONFIG_TRACEROUTE6 is not set | 815 | # CONFIG_TRACEROUTE6 is not set |
816 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set | 816 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set |
817 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
818 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 817 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
819 | CONFIG_TUNCTL=y | 818 | CONFIG_TUNCTL=y |
820 | CONFIG_FEATURE_TUNCTL_UG=y | 819 | CONFIG_FEATURE_TUNCTL_UG=y |
diff --git a/configs/android_502_defconfig b/configs/android_502_defconfig index 7ef1585fb..cd06affab 100644 --- a/configs/android_502_defconfig +++ b/configs/android_502_defconfig | |||
@@ -959,7 +959,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y | |||
959 | CONFIG_TRACEROUTE=y | 959 | CONFIG_TRACEROUTE=y |
960 | CONFIG_TRACEROUTE6=y | 960 | CONFIG_TRACEROUTE6=y |
961 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y | 961 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y |
962 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
963 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 962 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
964 | CONFIG_TUNCTL=y | 963 | CONFIG_TUNCTL=y |
965 | CONFIG_FEATURE_TUNCTL_UG=y | 964 | CONFIG_FEATURE_TUNCTL_UG=y |
diff --git a/configs/android_defconfig b/configs/android_defconfig index 4e0224207..f1ddc45d4 100644 --- a/configs/android_defconfig +++ b/configs/android_defconfig | |||
@@ -843,7 +843,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y | |||
843 | CONFIG_TRACEROUTE=y | 843 | CONFIG_TRACEROUTE=y |
844 | # CONFIG_TRACEROUTE6 is not set | 844 | # CONFIG_TRACEROUTE6 is not set |
845 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y | 845 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y |
846 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
847 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 846 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
848 | CONFIG_TUNCTL=y | 847 | CONFIG_TUNCTL=y |
849 | CONFIG_FEATURE_TUNCTL_UG=y | 848 | CONFIG_FEATURE_TUNCTL_UG=y |
diff --git a/configs/android_ndk_defconfig b/configs/android_ndk_defconfig index d657d33e9..18651fd7c 100644 --- a/configs/android_ndk_defconfig +++ b/configs/android_ndk_defconfig | |||
@@ -869,7 +869,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y | |||
869 | CONFIG_TRACEROUTE=y | 869 | CONFIG_TRACEROUTE=y |
870 | # CONFIG_TRACEROUTE6 is not set | 870 | # CONFIG_TRACEROUTE6 is not set |
871 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y | 871 | CONFIG_FEATURE_TRACEROUTE_VERBOSE=y |
872 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
873 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 872 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
874 | CONFIG_TUNCTL=y | 873 | CONFIG_TUNCTL=y |
875 | CONFIG_FEATURE_TUNCTL_UG=y | 874 | CONFIG_FEATURE_TUNCTL_UG=y |
diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig index 38d580ad1..dd7c21edb 100644 --- a/configs/cygwin_defconfig +++ b/configs/cygwin_defconfig | |||
@@ -814,7 +814,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y | |||
814 | # CONFIG_TRACEROUTE is not set | 814 | # CONFIG_TRACEROUTE is not set |
815 | # CONFIG_TRACEROUTE6 is not set | 815 | # CONFIG_TRACEROUTE6 is not set |
816 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set | 816 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set |
817 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
818 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 817 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
819 | # CONFIG_TUNCTL is not set | 818 | # CONFIG_TUNCTL is not set |
820 | # CONFIG_FEATURE_TUNCTL_UG is not set | 819 | # CONFIG_FEATURE_TUNCTL_UG is not set |
diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig index ae62f1389..265ab1307 100644 --- a/configs/freebsd_defconfig +++ b/configs/freebsd_defconfig | |||
@@ -795,7 +795,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y | |||
795 | # CONFIG_TRACEROUTE is not set | 795 | # CONFIG_TRACEROUTE is not set |
796 | # CONFIG_TRACEROUTE6 is not set | 796 | # CONFIG_TRACEROUTE6 is not set |
797 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set | 797 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set |
798 | # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set | ||
799 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | 798 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set |
800 | # CONFIG_TUNCTL is not set | 799 | # CONFIG_TUNCTL is not set |
801 | # CONFIG_FEATURE_TUNCTL_UG is not set | 800 | # CONFIG_FEATURE_TUNCTL_UG is not set |
diff --git a/coreutils/Config.src b/coreutils/Config.src index 619c2efe8..b9dde1209 100644 --- a/coreutils/Config.src +++ b/coreutils/Config.src | |||
@@ -328,12 +328,6 @@ config FEATURE_LS_COLOR_IS_DEFAULT | |||
328 | configurable, and the output may not be legible on | 328 | configurable, and the output may not be legible on |
329 | many output screens. | 329 | many output screens. |
330 | 330 | ||
331 | config MD5SUM | ||
332 | bool "md5sum" | ||
333 | default y | ||
334 | help | ||
335 | md5sum is used to print or check MD5 checksums. | ||
336 | |||
337 | config MKDIR | 331 | config MKDIR |
338 | bool "mkdir" | 332 | bool "mkdir" |
339 | default y | 333 | default y |
@@ -458,30 +452,6 @@ config SEQ | |||
458 | help | 452 | help |
459 | print a sequence of numbers | 453 | print a sequence of numbers |
460 | 454 | ||
461 | config SHA1SUM | ||
462 | bool "sha1sum" | ||
463 | default y | ||
464 | help | ||
465 | Compute and check SHA1 message digest | ||
466 | |||
467 | config SHA256SUM | ||
468 | bool "sha256sum" | ||
469 | default y | ||
470 | help | ||
471 | Compute and check SHA256 message digest | ||
472 | |||
473 | config SHA512SUM | ||
474 | bool "sha512sum" | ||
475 | default y | ||
476 | help | ||
477 | Compute and check SHA512 message digest | ||
478 | |||
479 | config SHA3SUM | ||
480 | bool "sha3sum" | ||
481 | default y | ||
482 | help | ||
483 | Compute and check SHA3 (512-bit) message digest | ||
484 | |||
485 | config SLEEP | 455 | config SLEEP |
486 | bool "sleep" | 456 | bool "sleep" |
487 | default y | 457 | default y |
@@ -731,17 +701,4 @@ config FEATURE_HUMAN_READABLE | |||
731 | help | 701 | help |
732 | Allow df, du, and ls to have human readable output. | 702 | Allow df, du, and ls to have human readable output. |
733 | 703 | ||
734 | comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" | ||
735 | depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM | ||
736 | |||
737 | config FEATURE_MD5_SHA1_SUM_CHECK | ||
738 | bool "Enable -c, -s and -w options" | ||
739 | default y | ||
740 | depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM | ||
741 | help | ||
742 | Enabling the -c options allows files to be checked | ||
743 | against pre-calculated hash values. | ||
744 | |||
745 | -s and -w are useful options when verifying checksums. | ||
746 | |||
747 | endmenu | 704 | endmenu |
diff --git a/coreutils/Kbuild.src b/coreutils/Kbuild.src index 4ec075ac6..5a64fee35 100644 --- a/coreutils/Kbuild.src +++ b/coreutils/Kbuild.src | |||
@@ -41,7 +41,6 @@ lib-$(CONFIG_LN) += ln.o | |||
41 | lib-$(CONFIG_LOGNAME) += logname.o | 41 | lib-$(CONFIG_LOGNAME) += logname.o |
42 | lib-$(CONFIG_LS) += ls.o | 42 | lib-$(CONFIG_LS) += ls.o |
43 | lib-$(CONFIG_FTPD) += ls.o | 43 | lib-$(CONFIG_FTPD) += ls.o |
44 | lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o | ||
45 | lib-$(CONFIG_MKDIR) += mkdir.o | 44 | lib-$(CONFIG_MKDIR) += mkdir.o |
46 | lib-$(CONFIG_MKFIFO) += mkfifo.o | 45 | lib-$(CONFIG_MKFIFO) += mkfifo.o |
47 | lib-$(CONFIG_MKNOD) += mknod.o | 46 | lib-$(CONFIG_MKNOD) += mknod.o |
@@ -58,10 +57,6 @@ lib-$(CONFIG_REALPATH) += realpath.o | |||
58 | lib-$(CONFIG_RM) += rm.o | 57 | lib-$(CONFIG_RM) += rm.o |
59 | lib-$(CONFIG_RMDIR) += rmdir.o | 58 | lib-$(CONFIG_RMDIR) += rmdir.o |
60 | lib-$(CONFIG_SEQ) += seq.o | 59 | lib-$(CONFIG_SEQ) += seq.o |
61 | lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o | ||
62 | lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o | ||
63 | lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o | ||
64 | lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o | ||
65 | lib-$(CONFIG_SLEEP) += sleep.o | 60 | lib-$(CONFIG_SLEEP) += sleep.o |
66 | lib-$(CONFIG_SPLIT) += split.o | 61 | lib-$(CONFIG_SPLIT) += split.o |
67 | lib-$(CONFIG_SORT) += sort.o | 62 | lib-$(CONFIG_SORT) += sort.o |
diff --git a/coreutils/expr.c b/coreutils/expr.c index ce6b2d189..efc435443 100644 --- a/coreutils/expr.c +++ b/coreutils/expr.c | |||
@@ -114,7 +114,7 @@ static VALUE *int_value(arith_t i) | |||
114 | VALUE *v; | 114 | VALUE *v; |
115 | 115 | ||
116 | v = xzalloc(sizeof(VALUE)); | 116 | v = xzalloc(sizeof(VALUE)); |
117 | if (INTEGER) /* otherwise xzaaloc did it already */ | 117 | if (INTEGER) /* otherwise xzalloc did it already */ |
118 | v->type = INTEGER; | 118 | v->type = INTEGER; |
119 | v->u.i = i; | 119 | v->u.i = i; |
120 | return v; | 120 | return v; |
@@ -127,7 +127,7 @@ static VALUE *str_value(const char *s) | |||
127 | VALUE *v; | 127 | VALUE *v; |
128 | 128 | ||
129 | v = xzalloc(sizeof(VALUE)); | 129 | v = xzalloc(sizeof(VALUE)); |
130 | if (STRING) /* otherwise xzaaloc did it already */ | 130 | if (STRING) /* otherwise xzalloc did it already */ |
131 | v->type = STRING; | 131 | v->type = STRING; |
132 | v->u.s = xstrdup(s); | 132 | v->u.s = xstrdup(s); |
133 | return v; | 133 | return v; |
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c index 1a5342e87..783f44027 100644 --- a/coreutils/md5_sha1_sum.c +++ b/coreutils/md5_sha1_sum.c | |||
@@ -5,6 +5,60 @@ | |||
5 | * | 5 | * |
6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
7 | */ | 7 | */ |
8 | //config:config MD5SUM | ||
9 | //config: bool "md5sum" | ||
10 | //config: default y | ||
11 | //config: help | ||
12 | //config: md5sum is used to print or check MD5 checksums. | ||
13 | //config: | ||
14 | //config:config SHA1SUM | ||
15 | //config: bool "sha1sum" | ||
16 | //config: default y | ||
17 | //config: help | ||
18 | //config: Compute and check SHA1 message digest | ||
19 | //config: | ||
20 | //config:config SHA256SUM | ||
21 | //config: bool "sha256sum" | ||
22 | //config: default y | ||
23 | //config: help | ||
24 | //config: Compute and check SHA256 message digest | ||
25 | //config: | ||
26 | //config:config SHA512SUM | ||
27 | //config: bool "sha512sum" | ||
28 | //config: default y | ||
29 | //config: help | ||
30 | //config: Compute and check SHA512 message digest | ||
31 | //config: | ||
32 | //config:config SHA3SUM | ||
33 | //config: bool "sha3sum" | ||
34 | //config: default y | ||
35 | //config: help | ||
36 | //config: Compute and check SHA3 message digest | ||
37 | //config: | ||
38 | //config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" | ||
39 | //config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM | ||
40 | //config: | ||
41 | //config:config FEATURE_MD5_SHA1_SUM_CHECK | ||
42 | //config: bool "Enable -c, -s and -w options" | ||
43 | //config: default y | ||
44 | //config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM | ||
45 | //config: help | ||
46 | //config: Enabling the -c options allows files to be checked | ||
47 | //config: against pre-calculated hash values. | ||
48 | //config: | ||
49 | //config: -s and -w are useful options when verifying checksums. | ||
50 | |||
51 | //applet:IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum)) | ||
52 | //applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) | ||
53 | //applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) | ||
54 | //applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) | ||
55 | //applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) | ||
56 | |||
57 | //kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o | ||
58 | //kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o | ||
59 | //kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o | ||
60 | //kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o | ||
61 | //kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o | ||
8 | 62 | ||
9 | //usage:#define md5sum_trivial_usage | 63 | //usage:#define md5sum_trivial_usage |
10 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." | 64 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." |
@@ -57,15 +111,20 @@ | |||
57 | //usage: ) | 111 | //usage: ) |
58 | //usage: | 112 | //usage: |
59 | //usage:#define sha3sum_trivial_usage | 113 | //usage:#define sha3sum_trivial_usage |
60 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." | 114 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[-a BITS] [FILE]..." |
61 | //usage:#define sha3sum_full_usage "\n\n" | 115 | //usage:#define sha3sum_full_usage "\n\n" |
62 | //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3-512 checksums" | 116 | //usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3 checksums" |
63 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" | 117 | //usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" |
64 | //usage: "\n -c Check sums against list in FILEs" | 118 | //usage: "\n -c Check sums against list in FILEs" |
65 | //usage: "\n -s Don't output anything, status code shows success" | 119 | //usage: "\n -s Don't output anything, status code shows success" |
66 | //usage: "\n -w Warn about improperly formatted checksum lines" | 120 | //usage: "\n -w Warn about improperly formatted checksum lines" |
121 | //usage: "\n -a BITS 224 (default), 256, 384, 512" | ||
67 | //usage: ) | 122 | //usage: ) |
68 | 123 | ||
124 | //FIXME: GNU coreutils 8.25 has no -s option, it has only these two long opts: | ||
125 | // --quiet don't print OK for each successfully verified file | ||
126 | // --status don't output anything, status code shows success | ||
127 | |||
69 | #include "libbb.h" | 128 | #include "libbb.h" |
70 | 129 | ||
71 | /* This is a NOEXEC applet. Be very careful! */ | 130 | /* This is a NOEXEC applet. Be very careful! */ |
@@ -93,7 +152,10 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value, | |||
93 | return (unsigned char *)hex_value; | 152 | return (unsigned char *)hex_value; |
94 | } | 153 | } |
95 | 154 | ||
96 | static uint8_t *hash_file(const char *filename) | 155 | #if !ENABLE_SHA3SUM |
156 | # define hash_file(f,w) hash_file(f) | ||
157 | #endif | ||
158 | static uint8_t *hash_file(const char *filename, unsigned sha3_width) | ||
97 | { | 159 | { |
98 | int src_fd, hash_len, count; | 160 | int src_fd, hash_len, count; |
99 | union _ctx_ { | 161 | union _ctx_ { |
@@ -121,27 +183,47 @@ static uint8_t *hash_file(const char *filename) | |||
121 | update = (void*)md5_hash; | 183 | update = (void*)md5_hash; |
122 | final = (void*)md5_end; | 184 | final = (void*)md5_end; |
123 | hash_len = 16; | 185 | hash_len = 16; |
124 | } else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) { | 186 | } |
187 | else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) { | ||
125 | sha1_begin(&context.sha1); | 188 | sha1_begin(&context.sha1); |
126 | update = (void*)sha1_hash; | 189 | update = (void*)sha1_hash; |
127 | final = (void*)sha1_end; | 190 | final = (void*)sha1_end; |
128 | hash_len = 20; | 191 | hash_len = 20; |
129 | } else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) { | 192 | } |
193 | else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) { | ||
130 | sha256_begin(&context.sha256); | 194 | sha256_begin(&context.sha256); |
131 | update = (void*)sha256_hash; | 195 | update = (void*)sha256_hash; |
132 | final = (void*)sha256_end; | 196 | final = (void*)sha256_end; |
133 | hash_len = 32; | 197 | hash_len = 32; |
134 | } else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) { | 198 | } |
199 | else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) { | ||
135 | sha512_begin(&context.sha512); | 200 | sha512_begin(&context.sha512); |
136 | update = (void*)sha512_hash; | 201 | update = (void*)sha512_hash; |
137 | final = (void*)sha512_end; | 202 | final = (void*)sha512_end; |
138 | hash_len = 64; | 203 | hash_len = 64; |
139 | } else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) { | 204 | } |
205 | #if ENABLE_SHA3SUM | ||
206 | else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) { | ||
140 | sha3_begin(&context.sha3); | 207 | sha3_begin(&context.sha3); |
141 | update = (void*)sha3_hash; | 208 | update = (void*)sha3_hash; |
142 | final = (void*)sha3_end; | 209 | final = (void*)sha3_end; |
143 | hash_len = 64; | 210 | /* |
144 | } else { | 211 | * Should support 224, 256, 384, 512. |
212 | * We allow any value which does not blow the algorithm up. | ||
213 | */ | ||
214 | if (sha3_width >= 1600/2 /* input block can't be <= 0 */ | ||
215 | || sha3_width == 0 /* hash len can't be 0 */ | ||
216 | || (sha3_width & 0x1f) /* should be multiple of 32 */ | ||
217 | /* (because input uses up to 8 byte wide word XORs. 32/4=8) */ | ||
218 | ) { | ||
219 | bb_error_msg_and_die("bad -a%u", sha3_width); | ||
220 | } | ||
221 | sha3_width /= 4; | ||
222 | context.sha3.input_block_bytes = 1600/8 - sha3_width; | ||
223 | hash_len = sha3_width/2; | ||
224 | } | ||
225 | #endif | ||
226 | else { | ||
145 | xfunc_die(); /* can't reach this */ | 227 | xfunc_die(); /* can't reach this */ |
146 | } | 228 | } |
147 | 229 | ||
@@ -172,28 +254,33 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) | |||
172 | { | 254 | { |
173 | int return_value = EXIT_SUCCESS; | 255 | int return_value = EXIT_SUCCESS; |
174 | unsigned flags; | 256 | unsigned flags; |
257 | #if ENABLE_SHA3SUM | ||
258 | unsigned sha3_width = 224; | ||
259 | #endif | ||
175 | 260 | ||
176 | if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) { | 261 | if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) { |
262 | /* -s and -w require -c */ | ||
263 | opt_complementary = "s?c:w?c"; | ||
177 | /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ | 264 | /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ |
178 | flags = getopt32(argv, "scwbt"); | 265 | #if ENABLE_SHA3SUM |
179 | argv += optind; | 266 | if (applet_name[3] == HASH_SHA3) |
180 | //argc -= optind; | 267 | flags = getopt32(argv, "scwbta:+", &sha3_width); |
268 | else | ||
269 | #endif | ||
270 | flags = getopt32(argv, "scwbt"); | ||
181 | } else { | 271 | } else { |
182 | argv += 1; | 272 | #if ENABLE_SHA3SUM |
183 | //argc -= 1; | 273 | if (applet_name[3] == HASH_SHA3) |
274 | getopt32(argv, "a:+", &sha3_width); | ||
275 | else | ||
276 | #endif | ||
277 | getopt32(argv, ""); | ||
184 | } | 278 | } |
279 | argv += optind; | ||
280 | //argc -= optind; | ||
185 | if (!*argv) | 281 | if (!*argv) |
186 | *--argv = (char*)"-"; | 282 | *--argv = (char*)"-"; |
187 | 283 | ||
188 | if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) { | ||
189 | if (flags & FLAG_SILENT) { | ||
190 | bb_error_msg_and_die("-%c is meaningful only with -c", 's'); | ||
191 | } | ||
192 | if (flags & FLAG_WARN) { | ||
193 | bb_error_msg_and_die("-%c is meaningful only with -c", 'w'); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | do { | 284 | do { |
198 | if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { | 285 | if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { |
199 | FILE *pre_computed_stream; | 286 | FILE *pre_computed_stream; |
@@ -225,7 +312,7 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) | |||
225 | *filename_ptr = '\0'; | 312 | *filename_ptr = '\0'; |
226 | filename_ptr += 2; | 313 | filename_ptr += 2; |
227 | 314 | ||
228 | hash_value = hash_file(filename_ptr); | 315 | hash_value = hash_file(filename_ptr, sha3_width); |
229 | 316 | ||
230 | if (hash_value && (strcmp((char*)hash_value, line) == 0)) { | 317 | if (hash_value && (strcmp((char*)hash_value, line) == 0)) { |
231 | if (!(flags & FLAG_SILENT)) | 318 | if (!(flags & FLAG_SILENT)) |
@@ -244,9 +331,17 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) | |||
244 | bb_error_msg("WARNING: %d of %d computed checksums did NOT match", | 331 | bb_error_msg("WARNING: %d of %d computed checksums did NOT match", |
245 | count_failed, count_total); | 332 | count_failed, count_total); |
246 | } | 333 | } |
334 | if (count_total == 0) { | ||
335 | return_value = EXIT_FAILURE; | ||
336 | /* | ||
337 | * md5sum from GNU coreutils 8.25 says: | ||
338 | * md5sum: <FILE>: no properly formatted MD5 checksum lines found | ||
339 | */ | ||
340 | bb_error_msg("%s: no checksum lines found", *argv); | ||
341 | } | ||
247 | fclose_if_not_stdin(pre_computed_stream); | 342 | fclose_if_not_stdin(pre_computed_stream); |
248 | } else { | 343 | } else { |
249 | uint8_t *hash_value = hash_file(*argv); | 344 | uint8_t *hash_value = hash_file(*argv, sha3_width); |
250 | if (hash_value == NULL) { | 345 | if (hash_value == NULL) { |
251 | return_value = EXIT_FAILURE; | 346 | return_value = EXIT_FAILURE; |
252 | } else { | 347 | } else { |
diff --git a/coreutils/sync.c b/coreutils/sync.c index 974e90452..e65d9cd8d 100644 --- a/coreutils/sync.c +++ b/coreutils/sync.c | |||
@@ -15,7 +15,7 @@ | |||
15 | //config: help | 15 | //config: help |
16 | //config: sync is used to flush filesystem buffers. | 16 | //config: sync is used to flush filesystem buffers. |
17 | //config:config FEATURE_SYNC_FANCY | 17 | //config:config FEATURE_SYNC_FANCY |
18 | //config: bool "Enable -d and -f flags (requres syncfs(2) in libc)" | 18 | //config: bool "Enable -d and -f flags (requires syncfs(2) in libc)" |
19 | //config: default y | 19 | //config: default y |
20 | //config: depends on SYNC | 20 | //config: depends on SYNC |
21 | //config: help | 21 | //config: help |
diff --git a/coreutils/test.c b/coreutils/test.c index 422d24c54..b8a5d798c 100644 --- a/coreutils/test.c +++ b/coreutils/test.c | |||
@@ -394,6 +394,7 @@ extern struct test_statics *const test_ptr_to_statics; | |||
394 | barrier(); \ | 394 | barrier(); \ |
395 | } while (0) | 395 | } while (0) |
396 | #define DEINIT_S() do { \ | 396 | #define DEINIT_S() do { \ |
397 | free(group_array); \ | ||
397 | free(test_ptr_to_statics); \ | 398 | free(test_ptr_to_statics); \ |
398 | } while (0) | 399 | } while (0) |
399 | 400 | ||
diff --git a/docs/keep_data_small.txt b/docs/keep_data_small.txt index 3ced1a61d..218d4f2ee 100644 --- a/docs/keep_data_small.txt +++ b/docs/keep_data_small.txt | |||
@@ -59,7 +59,7 @@ wait | |||
59 | Example 1 | 59 | Example 1 |
60 | 60 | ||
61 | One example how to reduce global data usage is in | 61 | One example how to reduce global data usage is in |
62 | archival/libarchive/decompress_unzip.c: | 62 | archival/libarchive/decompress_gunzip.c: |
63 | 63 | ||
64 | /* This is somewhat complex-looking arrangement, but it allows | 64 | /* This is somewhat complex-looking arrangement, but it allows |
65 | * to place decompressor state either in bss or in | 65 | * to place decompressor state either in bss or in |
@@ -87,7 +87,7 @@ take a look at archival/gzip.c. Here all global data is replaced by | |||
87 | single global pointer (ptr_to_globals) to allocated storage. | 87 | single global pointer (ptr_to_globals) to allocated storage. |
88 | 88 | ||
89 | In order to not duplicate ptr_to_globals in every applet, you can | 89 | In order to not duplicate ptr_to_globals in every applet, you can |
90 | reuse single common one. It is defined in libbb/messages.c | 90 | reuse single common one. It is defined in libbb/ptr_to_globals.c |
91 | as struct globals *const ptr_to_globals, but the struct globals is | 91 | as struct globals *const ptr_to_globals, but the struct globals is |
92 | NOT defined in libbb.h. You first define your own struct: | 92 | NOT defined in libbb.h. You first define your own struct: |
93 | 93 | ||
diff --git a/editors/sed.c b/editors/sed.c index 2e99dc792..63ca1dc99 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -1305,16 +1305,17 @@ static void process_files(void) | |||
1305 | case 'n': | 1305 | case 'n': |
1306 | if (!G.be_quiet) | 1306 | if (!G.be_quiet) |
1307 | sed_puts(pattern_space, last_gets_char); | 1307 | sed_puts(pattern_space, last_gets_char); |
1308 | if (next_line) { | 1308 | if (next_line == NULL) { |
1309 | free(pattern_space); | 1309 | /* If no next line, jump to end of script and exit. */ |
1310 | pattern_space = next_line; | 1310 | goto discard_line; |
1311 | last_gets_char = next_gets_char; | ||
1312 | next_line = get_next_line(&next_gets_char, &last_puts_char); | ||
1313 | substituted = 0; | ||
1314 | linenum++; | ||
1315 | break; | ||
1316 | } | 1311 | } |
1317 | /* fall through */ | 1312 | free(pattern_space); |
1313 | pattern_space = next_line; | ||
1314 | last_gets_char = next_gets_char; | ||
1315 | next_line = get_next_line(&next_gets_char, &last_puts_char); | ||
1316 | substituted = 0; | ||
1317 | linenum++; | ||
1318 | break; | ||
1318 | 1319 | ||
1319 | /* Quit. End of script, end of input. */ | 1320 | /* Quit. End of script, end of input. */ |
1320 | case 'q': | 1321 | case 'q': |
diff --git a/editors/vi.c b/editors/vi.c index da7464b58..b81f2b92d 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -2722,9 +2722,8 @@ static char *swap_context(char *p) // goto new context for '' command make this | |||
2722 | // only swap context if other context is valid | 2722 | // only swap context if other context is valid |
2723 | if (text <= mark[27] && mark[27] <= end - 1) { | 2723 | if (text <= mark[27] && mark[27] <= end - 1) { |
2724 | tmp = mark[27]; | 2724 | tmp = mark[27]; |
2725 | mark[27] = mark[26]; | 2725 | mark[27] = p; |
2726 | mark[26] = tmp; | 2726 | mark[26] = p = tmp; |
2727 | p = mark[26]; // where we are going- previous context | ||
2728 | context_start = prev_line(prev_line(prev_line(p))); | 2727 | context_start = prev_line(prev_line(prev_line(p))); |
2729 | context_end = next_line(next_line(next_line(p))); | 2728 | context_end = next_line(next_line(next_line(p))); |
2730 | } | 2729 | } |
@@ -3665,8 +3664,9 @@ static void do_cmd(int c) | |||
3665 | } | 3664 | } |
3666 | break; | 3665 | break; |
3667 | case '\'': // '- goto a specific mark | 3666 | case '\'': // '- goto a specific mark |
3668 | c1 = (get_one_char() | 0x20) - 'a'; | 3667 | c1 = (get_one_char() | 0x20); |
3669 | if ((unsigned)c1 <= 25) { // a-z? | 3668 | if ((unsigned)(c1 - 'a') <= 25) { // a-z? |
3669 | c1 = (c1 - 'a'); | ||
3670 | // get the b-o-l | 3670 | // get the b-o-l |
3671 | q = mark[c1]; | 3671 | q = mark[c1]; |
3672 | if (text <= q && q < end) { | 3672 | if (text <= q && q < end) { |
@@ -3980,7 +3980,9 @@ static void do_cmd(int c) | |||
3980 | c1 = get_one_char(); | 3980 | c1 = get_one_char(); |
3981 | if (c1 != 'g') { | 3981 | if (c1 != 'g') { |
3982 | buf[0] = 'g'; | 3982 | buf[0] = 'g'; |
3983 | buf[1] = c1; // TODO: if Unicode? | 3983 | // c1 < 0 if the key was special. Try "g<up-arrow>" |
3984 | // TODO: if Unicode? | ||
3985 | buf[1] = (c1 >= 0 ? c1 : '*'); | ||
3984 | buf[2] = '\0'; | 3986 | buf[2] = '\0'; |
3985 | not_implemented(buf); | 3987 | not_implemented(buf); |
3986 | break; | 3988 | break; |
diff --git a/examples/var_service/dhcpd_if/README b/examples/var_service/dhcpd_if/README new file mode 100644 index 000000000..4ddccb22d --- /dev/null +++ b/examples/var_service/dhcpd_if/README | |||
@@ -0,0 +1,5 @@ | |||
1 | The real README file is one directory up. | ||
2 | |||
3 | This directory's run script can have useful comments. | ||
4 | If it doesn't but you feel it should, please send a patch | ||
5 | to busybox's mailing list. | ||
diff --git a/examples/var_service/dhcpd_if/log/run b/examples/var_service/dhcpd_if/log/run new file mode 100755 index 000000000..69d74b73f --- /dev/null +++ b/examples/var_service/dhcpd_if/log/run | |||
@@ -0,0 +1,21 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | user=logger | ||
4 | |||
5 | logdir="/var/log/service/`(cd ..;basename $PWD)`" | ||
6 | mkdir -p "$logdir" 2>/dev/null | ||
7 | chown -R "$user": "$logdir" | ||
8 | chmod -R go-rwxst,u+rwX "$logdir" | ||
9 | rm -rf logdir | ||
10 | ln -s "$logdir" logdir | ||
11 | |||
12 | # make this dir accessible to logger | ||
13 | chmod a+rX . | ||
14 | |||
15 | exec >/dev/null | ||
16 | exec 2>&1 | ||
17 | exec \ | ||
18 | env - PATH="$PATH" \ | ||
19 | softlimit \ | ||
20 | setuidgid "$user" \ | ||
21 | svlogd -tt "$logdir" | ||
diff --git a/examples/var_service/dhcpd_if/p_log b/examples/var_service/dhcpd_if/p_log new file mode 100755 index 000000000..a2521be05 --- /dev/null +++ b/examples/var_service/dhcpd_if/p_log | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | cd log/logdir || exit 1 | ||
4 | cat @* current | $PAGER | ||
diff --git a/examples/var_service/dhcpd_if/run b/examples/var_service/dhcpd_if/run new file mode 100755 index 000000000..de85dece0 --- /dev/null +++ b/examples/var_service/dhcpd_if/run | |||
@@ -0,0 +1,23 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | exec 2>&1 | ||
4 | exec </dev/null | ||
5 | |||
6 | pwd="$PWD" | ||
7 | |||
8 | if="${PWD##*/dhcpd_}" | ||
9 | |||
10 | echo "* Upping iface $if" | ||
11 | ip link set dev $if up | ||
12 | |||
13 | >>udhcpd.leases | ||
14 | sed 's/^interface.*$/interface '"$if/" -i udhcpc.conf | ||
15 | |||
16 | echo "* Starting udhcpd" | ||
17 | exec \ | ||
18 | env - PATH="$PATH" \ | ||
19 | softlimit \ | ||
20 | setuidgid root \ | ||
21 | udhcpd -f -vv udhcpc.conf | ||
22 | |||
23 | exit $? | ||
diff --git a/examples/var_service/dhcpd_if/udhcpc.conf b/examples/var_service/dhcpd_if/udhcpc.conf new file mode 100644 index 000000000..a81925970 --- /dev/null +++ b/examples/var_service/dhcpd_if/udhcpc.conf | |||
@@ -0,0 +1,28 @@ | |||
1 | # Directives with defaults: | ||
2 | # start 192.168.0.20 | ||
3 | # end 192.168.0.254 | ||
4 | # interface eth0 | ||
5 | # max_leases 235 | ||
6 | # auto_time 7200 | ||
7 | # decline_time 3600 | ||
8 | # conflict_time 3600 | ||
9 | # offer_time 60 | ||
10 | # min_lease 60 | ||
11 | # lease_file /var/lib/misc/udhcpd.leases | ||
12 | # pidfile /var/run/udhcpd.pid | ||
13 | # siaddr 0.0.0.0 | ||
14 | # | ||
15 | # Directives with no defaults (or with empty defaults): | ||
16 | # option/opt NAME VALUE | ||
17 | # notify_file /path/to/script_to_run_after_leasefile_is_written | ||
18 | # (it is run with $1 = lease_file_name) | ||
19 | # sname dhcp_packet_sname_field_contents | ||
20 | # boot_file dhcp_packet_bootfile_field_contents | ||
21 | # static_lease XX:XX:XX:XX:XX:XX IP.ADD.RE.SS | ||
22 | |||
23 | interface if | ||
24 | pidfile /dev/null | ||
25 | lease_file udhcpd.leases | ||
26 | option subnet 255.255.255.0 | ||
27 | option lease 3600 | ||
28 | #option router 192.168.0.1 | ||
diff --git a/examples/var_service/dhcpd_if/w_dumpleases b/examples/var_service/dhcpd_if/w_dumpleases new file mode 100755 index 000000000..ff772055a --- /dev/null +++ b/examples/var_service/dhcpd_if/w_dumpleases | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | watch -n1 'dumpleases -af udhcpd.leases' | ||
diff --git a/examples/var_service/dhcpd_if/w_dumpleases_countdown b/examples/var_service/dhcpd_if/w_dumpleases_countdown new file mode 100755 index 000000000..7fcd9602a --- /dev/null +++ b/examples/var_service/dhcpd_if/w_dumpleases_countdown | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | watch -n1 'dumpleases -f udhcpd.leases' | ||
diff --git a/examples/var_service/dhcpd_if/w_log b/examples/var_service/dhcpd_if/w_log new file mode 100755 index 000000000..dba76c69b --- /dev/null +++ b/examples/var_service/dhcpd_if/w_log | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | cd log/logdir | ||
4 | watch -n1 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b0-$((w-2))' | ||
diff --git a/examples/var_service/fw/run b/examples/var_service/fw/run index 81c7f2e7c..1fd71cc01 100755 --- a/examples/var_service/fw/run +++ b/examples/var_service/fw/run | |||
@@ -1,18 +1,20 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # (using bashism: arrays) | 2 | # (using bashism: arrays) |
3 | 3 | ||
4 | service="${PWD##*/}" | 4 | user="root" |
5 | rundir="/var/run/service/$service" | 5 | reset_all_netdevs=true |
6 | 6 | preferred_default_route_iface="if" | |
7 | user=root | 7 | extif="if" |
8 | extif=if | 8 | ext_open_tcp="22 80 88" # space-separated |
9 | ext_open_tcp="21 22 80" # space-separated | ||
10 | 9 | ||
11 | # Make ourself one-shot | 10 | # Make ourself one-shot |
12 | sv o . | 11 | sv o . |
13 | # Debug | 12 | # Debug |
14 | #date '+%Y-%m-%d %H:%M:%S' >>"$0.log" | 13 | #date '+%Y-%m-%d %H:%M:%S' >>"$0.log" |
15 | 14 | ||
15 | service=`basename $PWD` | ||
16 | rundir="/var/run/service/$service" | ||
17 | |||
16 | ### filter This is the default table (if no -t option is passed). It contains | 18 | ### filter This is the default table (if no -t option is passed). It contains |
17 | ### the built-in chains INPUT (for packets coming into the box itself), | 19 | ### the built-in chains INPUT (for packets coming into the box itself), |
18 | ### FORWARD (for packets being routed through the box), and OUTPUT (for | 20 | ### FORWARD (for packets being routed through the box), and OUTPUT (for |
@@ -70,7 +72,6 @@ ln -s "$rundir" rundir | |||
70 | # Timestamping | 72 | # Timestamping |
71 | date '+%Y-%m-%d %H:%M:%S' | 73 | date '+%Y-%m-%d %H:%M:%S' |
72 | 74 | ||
73 | |||
74 | echo; echo "* Reading IP config" | 75 | echo; echo "* Reading IP config" |
75 | cfg=-1 | 76 | cfg=-1 |
76 | # static cfg dhcp,zeroconf etc | 77 | # static cfg dhcp,zeroconf etc |
@@ -86,11 +87,19 @@ echo; echo "* Configuring hardware" | |||
86 | #doit ethtool -K if rx off tx off sg off tso off | 87 | #doit ethtool -K if rx off tx off sg off tso off |
87 | 88 | ||
88 | echo; echo "* Resetting address and routing info" | 89 | echo; echo "* Resetting address and routing info" |
89 | doit ip a f dev lo | 90 | if $reset_all_netdevs; then |
90 | i=0; while test "${if[$i]}"; do | 91 | devs=`sed -n 's/ //g;s/:.*$//p' </proc/net/dev` |
91 | doit ip a f dev "${if[$i]}" | 92 | for iface in $devs; do |
92 | doit ip r f dev "${if[$i]}" root 0/0 | 93 | doit ip a f dev "$iface" |
93 | let i++; done | 94 | doit ip r f dev "$iface" root 0/0 |
95 | done | ||
96 | else | ||
97 | doit ip a f dev lo | ||
98 | i=0; while test "${if[$i]}"; do | ||
99 | doit ip a f dev "${if[$i]}" | ||
100 | doit ip r f dev "${if[$i]}" root 0/0 | ||
101 | let i++; done | ||
102 | fi | ||
94 | 103 | ||
95 | echo; echo "* Configuring addresses" | 104 | echo; echo "* Configuring addresses" |
96 | doit ip a a dev lo 127.0.0.1/8 scope host | 105 | doit ip a a dev lo 127.0.0.1/8 scope host |
@@ -103,7 +112,22 @@ i=0; while test "${if[$i]}"; do | |||
103 | let i++; done | 112 | let i++; done |
104 | 113 | ||
105 | echo; echo "* Configuring routes" | 114 | echo; echo "* Configuring routes" |
115 | # If several ifaces are configured via DHCP, they often both have 0/0 route. | ||
116 | # They have no way of knowing that this route is offered on more than one iface. | ||
117 | # Often, it's desirable to prefer one iface: say, wired eth over wireless. | ||
118 | # if preferred_default_route_iface is not set, 0/0 route will be assigned randomly. | ||
119 | if test "$preferred_default_route_iface"; then | ||
120 | i=0; while test "${if[$i]}"; do | ||
121 | if test "${if[$i]}" = "$preferred_default_route_iface" \ | ||
122 | && test "${net[$i]}" = "0/0" \ | ||
123 | && test "${gw[$i]}"; then | ||
124 | echo "+ default route through ${if[$i]}, ${gw[$i]}:" | ||
125 | doit ip r a "${net[$i]}" via "${gw[$i]}" | ||
126 | fi | ||
127 | let i++; done | ||
128 | fi | ||
106 | i=0; while test "${if[$i]}"; do | 129 | i=0; while test "${if[$i]}"; do |
130 | #echo $i:"${if[$i]}" | ||
107 | if test "${net[$i]}" && test "${gw[$i]}"; then | 131 | if test "${net[$i]}" && test "${gw[$i]}"; then |
108 | doit ip r a "${net[$i]}" via "${gw[$i]}" | 132 | doit ip r a "${net[$i]}" via "${gw[$i]}" |
109 | fi | 133 | fi |
diff --git a/examples/var_service/ntpd/p_log_important b/examples/var_service/ntpd/p_log_important new file mode 100755 index 000000000..09b248fb3 --- /dev/null +++ b/examples/var_service/ntpd/p_log_important | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | cd log/logdir || exit 1 | ||
4 | cat @* current | grep -v -eolder -esending -esockets -e'reply from.*offset' -executing | $PAGER | ||
diff --git a/examples/var_service/supplicant_if/README b/examples/var_service/supplicant_if/README new file mode 100644 index 000000000..4ddccb22d --- /dev/null +++ b/examples/var_service/supplicant_if/README | |||
@@ -0,0 +1,5 @@ | |||
1 | The real README file is one directory up. | ||
2 | |||
3 | This directory's run script can have useful comments. | ||
4 | If it doesn't but you feel it should, please send a patch | ||
5 | to busybox's mailing list. | ||
diff --git a/examples/var_service/supplicant_if/log/run b/examples/var_service/supplicant_if/log/run new file mode 100755 index 000000000..69d74b73f --- /dev/null +++ b/examples/var_service/supplicant_if/log/run | |||
@@ -0,0 +1,21 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | user=logger | ||
4 | |||
5 | logdir="/var/log/service/`(cd ..;basename $PWD)`" | ||
6 | mkdir -p "$logdir" 2>/dev/null | ||
7 | chown -R "$user": "$logdir" | ||
8 | chmod -R go-rwxst,u+rwX "$logdir" | ||
9 | rm -rf logdir | ||
10 | ln -s "$logdir" logdir | ||
11 | |||
12 | # make this dir accessible to logger | ||
13 | chmod a+rX . | ||
14 | |||
15 | exec >/dev/null | ||
16 | exec 2>&1 | ||
17 | exec \ | ||
18 | env - PATH="$PATH" \ | ||
19 | softlimit \ | ||
20 | setuidgid "$user" \ | ||
21 | svlogd -tt "$logdir" | ||
diff --git a/examples/var_service/supplicant_if/p_log b/examples/var_service/supplicant_if/p_log new file mode 100755 index 000000000..a2521be05 --- /dev/null +++ b/examples/var_service/supplicant_if/p_log | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | cd log/logdir || exit 1 | ||
4 | cat @* current | $PAGER | ||
diff --git a/examples/var_service/supplicant_if/run b/examples/var_service/supplicant_if/run new file mode 100755 index 000000000..45211e001 --- /dev/null +++ b/examples/var_service/supplicant_if/run | |||
@@ -0,0 +1,21 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | exec 2>&1 | ||
4 | exec </dev/null | ||
5 | |||
6 | pwd="$PWD" | ||
7 | |||
8 | if="${PWD##*/dhcp_}" | ||
9 | |||
10 | echo "* Upping iface $if" | ||
11 | ip link set dev "$if" up | ||
12 | |||
13 | ##echo "* Powersave disable on $if" | ||
14 | ##iw dev "$if" set power_save off | ||
15 | |||
16 | echo "* Starting wpa_supplicant" | ||
17 | exec \ | ||
18 | env - PATH="$PATH" \ | ||
19 | softlimit \ | ||
20 | setuidgid root \ | ||
21 | wpa_supplicant -i "$if" -c "$pwd/wpa_supplicant.conf" -d | ||
diff --git a/examples/var_service/supplicant_if/w_log b/examples/var_service/supplicant_if/w_log new file mode 100755 index 000000000..aa36ef13b --- /dev/null +++ b/examples/var_service/supplicant_if/w_log | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | cd log/logdir || exit 1 | ||
4 | watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))' | ||
diff --git a/examples/var_service/supplicant_if/wpa_supplicant.conf b/examples/var_service/supplicant_if/wpa_supplicant.conf new file mode 100644 index 000000000..e317e2ebe --- /dev/null +++ b/examples/var_service/supplicant_if/wpa_supplicant.conf | |||
@@ -0,0 +1,28 @@ | |||
1 | # allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group | ||
2 | ctrl_interface=DIR=/var/run/wpa_supplicant | ||
3 | #GROUP=wheel | ||
4 | |||
5 | # Typical minimal wifi setup: | ||
6 | network={ | ||
7 | ssid="--your-ESSID--" | ||
8 | key_mgmt=WPA-PSK | ||
9 | psk="--your-password--" | ||
10 | } | ||
11 | |||
12 | # Other fields: | ||
13 | # scan_ssid=1 | ||
14 | # key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE | ||
15 | # pairwise=CCMP TKIP | ||
16 | # group=CCMP TKIP WEP104 WEP40 | ||
17 | # eap=TTLS PEAP TLS | ||
18 | # identity="user@example.com" | ||
19 | # password="foobar" | ||
20 | # ca_cert="/etc/cert/ca.pem" | ||
21 | # client_cert="/etc/cert/user.pem" | ||
22 | # private_key="/etc/cert/user.prv" | ||
23 | # private_key_passwd="password" | ||
24 | # phase1="peaplabel=0" | ||
25 | # ca_cert2="/etc/cert/ca2.pem" | ||
26 | # client_cert2="/etc/cer/user.pem" | ||
27 | # private_key2="/etc/cer/user.prv" | ||
28 | # private_key2_passwd="password" | ||
diff --git a/findutils/grep.c b/findutils/grep.c index 02ce65159..94bcb6002 100644 --- a/findutils/grep.c +++ b/findutils/grep.c | |||
@@ -255,7 +255,7 @@ typedef struct grep_list_data_t { | |||
255 | #endif | 255 | #endif |
256 | #define ALLOCATED 1 | 256 | #define ALLOCATED 1 |
257 | #define COMPILED 2 | 257 | #define COMPILED 2 |
258 | int flg_mem_alocated_compiled; | 258 | int flg_mem_allocated_compiled; |
259 | } grep_list_data_t; | 259 | } grep_list_data_t; |
260 | 260 | ||
261 | #if !ENABLE_EXTRA_COMPAT | 261 | #if !ENABLE_EXTRA_COMPAT |
@@ -388,8 +388,8 @@ static int grep_file(FILE *file) | |||
388 | #endif | 388 | #endif |
389 | char *match_at; | 389 | char *match_at; |
390 | 390 | ||
391 | if (!(gl->flg_mem_alocated_compiled & COMPILED)) { | 391 | if (!(gl->flg_mem_allocated_compiled & COMPILED)) { |
392 | gl->flg_mem_alocated_compiled |= COMPILED; | 392 | gl->flg_mem_allocated_compiled |= COMPILED; |
393 | #if !ENABLE_EXTRA_COMPAT | 393 | #if !ENABLE_EXTRA_COMPAT |
394 | xregcomp(&gl->compiled_regex, gl->pattern, reflags); | 394 | xregcomp(&gl->compiled_regex, gl->pattern, reflags); |
395 | #else | 395 | #else |
@@ -627,9 +627,9 @@ static char *add_grep_list_data(char *pattern) | |||
627 | grep_list_data_t *gl = xzalloc(sizeof(*gl)); | 627 | grep_list_data_t *gl = xzalloc(sizeof(*gl)); |
628 | gl->pattern = pattern; | 628 | gl->pattern = pattern; |
629 | #if ENABLE_FEATURE_CLEAN_UP | 629 | #if ENABLE_FEATURE_CLEAN_UP |
630 | gl->flg_mem_alocated_compiled = flg_used_mem; | 630 | gl->flg_mem_allocated_compiled = flg_used_mem; |
631 | #else | 631 | #else |
632 | /*gl->flg_mem_alocated_compiled = 0;*/ | 632 | /*gl->flg_mem_allocated_compiled = 0;*/ |
633 | #endif | 633 | #endif |
634 | return (char *)gl; | 634 | return (char *)gl; |
635 | } | 635 | } |
@@ -845,9 +845,9 @@ int grep_main(int argc UNUSED_PARAM, char **argv) | |||
845 | grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data; | 845 | grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data; |
846 | 846 | ||
847 | pattern_head = pattern_head->link; | 847 | pattern_head = pattern_head->link; |
848 | if (gl->flg_mem_alocated_compiled & ALLOCATED) | 848 | if (gl->flg_mem_allocated_compiled & ALLOCATED) |
849 | free(gl->pattern); | 849 | free(gl->pattern); |
850 | if (gl->flg_mem_alocated_compiled & COMPILED) | 850 | if (gl->flg_mem_allocated_compiled & COMPILED) |
851 | regfree(&gl->compiled_regex); | 851 | regfree(&gl->compiled_regex); |
852 | free(gl); | 852 | free(gl); |
853 | free(pattern_head_ptr); | 853 | free(pattern_head_ptr); |
diff --git a/include/applets.src.h b/include/applets.src.h index 6e1b02fc3..248d539c4 100644 --- a/include/applets.src.h +++ b/include/applets.src.h | |||
@@ -205,7 +205,6 @@ IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP)) | |||
205 | IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP)) | 205 | IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP)) |
206 | IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP)) | 206 | IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP)) |
207 | IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP)) | 207 | IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP)) |
208 | IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum)) | ||
209 | IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP)) | 208 | IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP)) |
210 | IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir)) | 209 | IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir)) |
211 | IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat)) | 210 | IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat)) |
@@ -280,10 +279,6 @@ IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP)) | |||
280 | IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP)) | 279 | IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP)) |
281 | IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP)) | 280 | IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP)) |
282 | IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP)) | 281 | IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP)) |
283 | IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) | ||
284 | IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) | ||
285 | IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) | ||
286 | IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) | ||
287 | IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP)) | 282 | IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP)) |
288 | IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) | 283 | IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) |
289 | /* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */ | 284 | /* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */ |
diff --git a/include/busybox.h b/include/busybox.h index 737627bd0..6a003d544 100644 --- a/include/busybox.h +++ b/include/busybox.h | |||
@@ -19,7 +19,9 @@ extern const uint8_t applet_flags[] ALIGN1; | |||
19 | extern const uint8_t applet_suid[] ALIGN1; | 19 | extern const uint8_t applet_suid[] ALIGN1; |
20 | extern const uint8_t applet_install_loc[] ALIGN1; | 20 | extern const uint8_t applet_install_loc[] ALIGN1; |
21 | 21 | ||
22 | #if ENABLE_FEATURE_PREFER_APPLETS | 22 | #if ENABLE_FEATURE_PREFER_APPLETS \ |
23 | || ENABLE_FEATURE_SH_STANDALONE \ | ||
24 | || ENABLE_FEATURE_SH_NOFORK | ||
23 | # define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4)))) | 25 | # define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4)))) |
24 | # define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1))) | 26 | # define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1))) |
25 | #else | 27 | #else |
diff --git a/include/libbb.h b/include/libbb.h index 6db03d2df..30a0e00ba 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -196,24 +196,33 @@ int klogctl(int type, char *b, int len); | |||
196 | /* Busybox does not use threads, we can speed up stdio. */ | 196 | /* Busybox does not use threads, we can speed up stdio. */ |
197 | #ifdef HAVE_UNLOCKED_STDIO | 197 | #ifdef HAVE_UNLOCKED_STDIO |
198 | # undef getc | 198 | # undef getc |
199 | # define getc(stream) getc_unlocked(stream) | 199 | # define getc(stream) getc_unlocked(stream) |
200 | # undef getchar | 200 | # undef getchar |
201 | # define getchar() getchar_unlocked() | 201 | # define getchar() getchar_unlocked() |
202 | # undef putc | 202 | # undef putc |
203 | # define putc(c, stream) putc_unlocked(c, stream) | 203 | # define putc(c,stream) putc_unlocked(c,stream) |
204 | # undef putchar | 204 | # undef putchar |
205 | # define putchar(c) putchar_unlocked(c) | 205 | # define putchar(c) putchar_unlocked(c) |
206 | # undef fgetc | 206 | # undef fgetc |
207 | # define fgetc(stream) getc_unlocked(stream) | 207 | # define fgetc(stream) getc_unlocked(stream) |
208 | # undef fputc | 208 | # undef fputc |
209 | # define fputc(c, stream) putc_unlocked(c, stream) | 209 | # define fputc(c,stream) putc_unlocked(c,stream) |
210 | #endif | 210 | #endif |
211 | /* Above functions are required by POSIX.1-2008, below ones are extensions */ | 211 | /* Above functions are required by POSIX.1-2008, below ones are extensions */ |
212 | #ifdef HAVE_UNLOCKED_LINE_OPS | 212 | #ifdef HAVE_UNLOCKED_LINE_OPS |
213 | # undef fgets | 213 | # undef fgets |
214 | # define fgets(s, n, stream) fgets_unlocked(s, n, stream) | 214 | # define fgets(s,n,stream) fgets_unlocked(s,n,stream) |
215 | # undef fputs | 215 | # undef fputs |
216 | # define fputs(s, stream) fputs_unlocked(s, stream) | 216 | # define fputs(s,stream) fputs_unlocked(s,stream) |
217 | /* musl <= 1.1.15 does not support fflush_unlocked(NULL) */ | ||
218 | //# undef fflush | ||
219 | //# define fflush(stream) fflush_unlocked(stream) | ||
220 | # undef feof | ||
221 | # define feof(stream) feof_unlocked(stream) | ||
222 | # undef ferror | ||
223 | # define ferror(stream) ferror_unlocked(stream) | ||
224 | # undef fileno | ||
225 | # define fileno(stream) fileno_unlocked(stream) | ||
217 | #endif | 226 | #endif |
218 | 227 | ||
219 | 228 | ||
@@ -1755,6 +1764,7 @@ typedef struct sha512_ctx_t { | |||
1755 | typedef struct sha3_ctx_t { | 1764 | typedef struct sha3_ctx_t { |
1756 | uint64_t state[25]; | 1765 | uint64_t state[25]; |
1757 | unsigned bytes_queued; | 1766 | unsigned bytes_queued; |
1767 | unsigned input_block_bytes; | ||
1758 | } sha3_ctx_t; | 1768 | } sha3_ctx_t; |
1759 | void md5_begin(md5_ctx_t *ctx) FAST_FUNC; | 1769 | void md5_begin(md5_ctx_t *ctx) FAST_FUNC; |
1760 | void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; | 1770 | void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; |
diff --git a/init/init.c b/init/init.c index 6eb76b80e..0813d1b5f 100644 --- a/init/init.c +++ b/init/init.c | |||
@@ -297,6 +297,11 @@ static void console_init(void) | |||
297 | s = getenv("CONSOLE"); | 297 | s = getenv("CONSOLE"); |
298 | if (!s) | 298 | if (!s) |
299 | s = getenv("console"); | 299 | s = getenv("console"); |
300 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | ||
301 | /* BSD people say their kernels do not open fd 0,1,2; they need this: */ | ||
302 | if (!s) | ||
303 | s = (char*)"/dev/console"; | ||
304 | #endif | ||
300 | if (s) { | 305 | if (s) { |
301 | int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY); | 306 | int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY); |
302 | if (fd >= 0) { | 307 | if (fd >= 0) { |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 28adf2740..4aa40454f 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -930,7 +930,9 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | |||
930 | } | 930 | } |
931 | if (ENABLE_FEATURE_SUID) | 931 | if (ENABLE_FEATURE_SUID) |
932 | check_suid(applet_no); | 932 | check_suid(applet_no); |
933 | exit(applet_main[applet_no](argc, argv)); | 933 | xfunc_error_retval = applet_main[applet_no](argc, argv); |
934 | /* Note: applet_main() may also not return (die on a xfunc or such) */ | ||
935 | xfunc_die(); | ||
934 | } | 936 | } |
935 | # endif /* NUM_APPLETS > 0 */ | 937 | # endif /* NUM_APPLETS > 0 */ |
936 | 938 | ||
@@ -993,6 +995,14 @@ int main(int argc UNUSED_PARAM, char **argv) | |||
993 | */ | 995 | */ |
994 | mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256); | 996 | mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256); |
995 | #endif | 997 | #endif |
998 | #if 0 /*def M_TOP_PAD*/ | ||
999 | /* When the program break is increased, then M_TOP_PAD bytes are added | ||
1000 | * to the sbrk(2) request. When the heap is trimmed because of free(3), | ||
1001 | * this much free space is preserved at the top of the heap. | ||
1002 | * glibc default seems to be way too big: 128k, but need to verify. | ||
1003 | */ | ||
1004 | mallopt(M_TOP_PAD, 8 * 1024); | ||
1005 | #endif | ||
996 | 1006 | ||
997 | #if !BB_MMU | 1007 | #if !BB_MMU |
998 | /* NOMMU re-exec trick sets high-order bit in first byte of name */ | 1008 | /* NOMMU re-exec trick sets high-order bit in first byte of name */ |
diff --git a/libbb/copy_file.c b/libbb/copy_file.c index c52297376..cb6d12359 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c | |||
@@ -299,11 +299,16 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
299 | if (!S_ISREG(source_stat.st_mode)) | 299 | if (!S_ISREG(source_stat.st_mode)) |
300 | new_mode = 0666; | 300 | new_mode = 0666; |
301 | 301 | ||
302 | // POSIX way is a security problem versus (sym)link attacks | 302 | if (ENABLE_FEATURE_NON_POSIX_CP || (flags & FILEUTILS_INTERACTIVE)) { |
303 | if (!ENABLE_FEATURE_NON_POSIX_CP) { | 303 | /* |
304 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); | 304 | * O_CREAT|O_EXCL: require that file did not exist before creation |
305 | } else { /* safe way: */ | 305 | */ |
306 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); | 306 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); |
307 | } else { /* POSIX, and not "cp -i" */ | ||
308 | /* | ||
309 | * O_CREAT|O_TRUNC: create, or truncate (security problem versus (sym)link attacks) | ||
310 | */ | ||
311 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); | ||
307 | } | 312 | } |
308 | if (dst_fd == -1) { | 313 | if (dst_fd == -1) { |
309 | ovr = ask_and_unlink(dest, flags); | 314 | ovr = ask_and_unlink(dest, flags); |
@@ -378,7 +383,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
378 | } | 383 | } |
379 | /* _Not_ jumping to preserve_mode_ugid_time: | 384 | /* _Not_ jumping to preserve_mode_ugid_time: |
380 | * symlinks don't have those */ | 385 | * symlinks don't have those */ |
381 | return 0; | 386 | goto verb_and_exit; |
382 | } | 387 | } |
383 | if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) | 388 | if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) |
384 | || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) | 389 | || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) |
@@ -413,6 +418,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
413 | bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); | 418 | bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); |
414 | } | 419 | } |
415 | 420 | ||
421 | verb_and_exit: | ||
416 | if (flags & FILEUTILS_VERBOSE) { | 422 | if (flags & FILEUTILS_VERBOSE) { |
417 | printf("'%s' -> '%s'\n", source, dest); | 423 | printf("'%s' -> '%s'\n", source, dest); |
418 | } | 424 | } |
diff --git a/libbb/dump.c b/libbb/dump.c index 566881a78..154be5d80 100644 --- a/libbb/dump.c +++ b/libbb/dump.c | |||
@@ -157,7 +157,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) | |||
157 | /* | 157 | /* |
158 | * figure out the byte count for each conversion; | 158 | * figure out the byte count for each conversion; |
159 | * rewrite the format as necessary, set up blank- | 159 | * rewrite the format as necessary, set up blank- |
160 | * pbb_dump_adding for end of data. | 160 | * padding for end of data. |
161 | */ | 161 | */ |
162 | if (*p1 == 'c') { | 162 | if (*p1 == 'c') { |
163 | pr->flags = F_CHAR; | 163 | pr->flags = F_CHAR; |
@@ -466,14 +466,14 @@ static void bpad(PR *pr) | |||
466 | } | 466 | } |
467 | 467 | ||
468 | static const char conv_str[] ALIGN1 = | 468 | static const char conv_str[] ALIGN1 = |
469 | "\0\\0\0" | 469 | "\0" "\\""0""\0" |
470 | "\007\\a\0" /* \a */ | 470 | "\007""\\""a""\0" /* \a */ |
471 | "\b\\b\0" | 471 | "\b" "\\""b""\0" |
472 | "\f\\b\0" | 472 | "\f" "\\""f""\0" |
473 | "\n\\n\0" | 473 | "\n" "\\""n""\0" |
474 | "\r\\r\0" | 474 | "\r" "\\""r""\0" |
475 | "\t\\t\0" | 475 | "\t" "\\""t""\0" |
476 | "\v\\v\0" | 476 | "\v" "\\""v""\0" |
477 | ; | 477 | ; |
478 | 478 | ||
479 | 479 | ||
@@ -485,7 +485,7 @@ static void conv_c(PR *pr, unsigned char *p) | |||
485 | do { | 485 | do { |
486 | if (*p == *str) { | 486 | if (*p == *str) { |
487 | ++str; | 487 | ++str; |
488 | goto strpr; | 488 | goto strpr; /* map e.g. '\n' to "\\n" */ |
489 | } | 489 | } |
490 | str += 4; | 490 | str += 4; |
491 | } while (*str); | 491 | } while (*str); |
@@ -702,8 +702,6 @@ int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv) | |||
702 | void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) | 702 | void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) |
703 | { | 703 | { |
704 | const char *p; | 704 | const char *p; |
705 | char *p1; | ||
706 | char *p2; | ||
707 | FS *tfs; | 705 | FS *tfs; |
708 | FU *tfu, **nextfupp; | 706 | FU *tfu, **nextfupp; |
709 | const char *savep; | 707 | const char *savep; |
@@ -779,29 +777,42 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt) | |||
779 | } | 777 | } |
780 | } | 778 | } |
781 | tfu->fmt = xstrndup(savep, p - savep); | 779 | tfu->fmt = xstrndup(savep, p - savep); |
782 | /* escape(tfu->fmt); */ | ||
783 | |||
784 | p1 = tfu->fmt; | ||
785 | 780 | ||
786 | /* alphabetic escape sequences have to be done in place */ | 781 | /* alphabetic escape sequences have to be done in place */ |
782 | strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt); | ||
783 | /* unknown mappings are not changed: "\z" -> '\\' 'z' */ | ||
784 | /* trailing backslash, if any, is preserved */ | ||
785 | #if 0 | ||
786 | char *p1; | ||
787 | char *p2; | ||
788 | p1 = tfu->fmt; | ||
787 | for (p2 = p1;; ++p1, ++p2) { | 789 | for (p2 = p1;; ++p1, ++p2) { |
788 | if (*p1 == '\0') { | 790 | *p2 = *p1; |
789 | *p2 = *p1; | 791 | if (*p1 == '\0') |
790 | break; | 792 | break; |
791 | } | 793 | |
792 | if (*p1 == '\\') { | 794 | if (*p1 == '\\') { |
793 | const char *cs = conv_str + 4; | 795 | const char *cs; |
794 | ++p1; | 796 | |
797 | p1++; | ||
795 | *p2 = *p1; | 798 | *p2 = *p1; |
799 | if (*p1 == '\0') { | ||
800 | /* "...\" trailing backslash. Eaten. */ | ||
801 | break; | ||
802 | } | ||
803 | cs = conv_str + 4; /* skip NUL element */ | ||
796 | do { | 804 | do { |
805 | /* map e.g. "\n" -> '\n' */ | ||
797 | if (*p1 == cs[2]) { | 806 | if (*p1 == cs[2]) { |
798 | *p2 = cs[0]; | 807 | *p2 = cs[0]; |
799 | break; | 808 | break; |
800 | } | 809 | } |
801 | cs += 4; | 810 | cs += 4; |
802 | } while (*cs); | 811 | } while (*cs); |
812 | /* unknown mappings remove bkslash: "\z" -> 'z' */ | ||
803 | } | 813 | } |
804 | } | 814 | } |
815 | #endif | ||
805 | 816 | ||
806 | p++; | 817 | p++; |
807 | } | 818 | } |
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c index d08c6b2f7..7e7d8da2f 100644 --- a/libbb/hash_md5_sha.c +++ b/libbb/hash_md5_sha.c | |||
@@ -941,10 +941,6 @@ void FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) | |||
941 | # define OPTIMIZE_SHA3_FOR_32 1 | 941 | # define OPTIMIZE_SHA3_FOR_32 1 |
942 | #endif | 942 | #endif |
943 | 943 | ||
944 | enum { | ||
945 | SHA3_IBLK_BYTES = 72, /* 576 bits / 8 */ | ||
946 | }; | ||
947 | |||
948 | #if OPTIMIZE_SHA3_FOR_32 | 944 | #if OPTIMIZE_SHA3_FOR_32 |
949 | /* This splits every 64-bit word into a pair of 32-bit words, | 945 | /* This splits every 64-bit word into a pair of 32-bit words, |
950 | * even bits go into first word, odd bits go to second one. | 946 | * even bits go into first word, odd bits go to second one. |
@@ -1352,6 +1348,8 @@ static void sha3_process_block72(uint64_t *state) | |||
1352 | void FAST_FUNC sha3_begin(sha3_ctx_t *ctx) | 1348 | void FAST_FUNC sha3_begin(sha3_ctx_t *ctx) |
1353 | { | 1349 | { |
1354 | memset(ctx, 0, sizeof(*ctx)); | 1350 | memset(ctx, 0, sizeof(*ctx)); |
1351 | /* SHA3-512, user can override */ | ||
1352 | ctx->input_block_bytes = (1600 - 512*2) / 8; /* 72 bytes */ | ||
1355 | } | 1353 | } |
1356 | 1354 | ||
1357 | void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) | 1355 | void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) |
@@ -1361,7 +1359,7 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) | |||
1361 | unsigned bufpos = ctx->bytes_queued; | 1359 | unsigned bufpos = ctx->bytes_queued; |
1362 | 1360 | ||
1363 | while (1) { | 1361 | while (1) { |
1364 | unsigned remaining = SHA3_IBLK_BYTES - bufpos; | 1362 | unsigned remaining = ctx->input_block_bytes - bufpos; |
1365 | if (remaining > len) | 1363 | if (remaining > len) |
1366 | remaining = len; | 1364 | remaining = len; |
1367 | len -= remaining; | 1365 | len -= remaining; |
@@ -1373,38 +1371,41 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) | |||
1373 | remaining--; | 1371 | remaining--; |
1374 | } | 1372 | } |
1375 | /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ | 1373 | /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */ |
1376 | bufpos -= SHA3_IBLK_BYTES; | 1374 | bufpos -= ctx->input_block_bytes; |
1377 | if (bufpos != 0) | 1375 | if (bufpos != 0) |
1378 | break; | 1376 | break; |
1379 | /* Buffer is filled up, process it */ | 1377 | /* Buffer is filled up, process it */ |
1380 | sha3_process_block72(ctx->state); | 1378 | sha3_process_block72(ctx->state); |
1381 | /*bufpos = 0; - already is */ | 1379 | /*bufpos = 0; - already is */ |
1382 | } | 1380 | } |
1383 | ctx->bytes_queued = bufpos + SHA3_IBLK_BYTES; | 1381 | ctx->bytes_queued = bufpos + ctx->input_block_bytes; |
1384 | #else | 1382 | #else |
1385 | /* +50 bytes code size, but a bit faster because of long-sized XORs */ | 1383 | /* +50 bytes code size, but a bit faster because of long-sized XORs */ |
1386 | const uint8_t *data = buffer; | 1384 | const uint8_t *data = buffer; |
1387 | unsigned bufpos = ctx->bytes_queued; | 1385 | unsigned bufpos = ctx->bytes_queued; |
1386 | unsigned iblk_bytes = ctx->input_block_bytes; | ||
1388 | 1387 | ||
1389 | /* If already data in queue, continue queuing first */ | 1388 | /* If already data in queue, continue queuing first */ |
1390 | while (len != 0 && bufpos != 0) { | 1389 | if (bufpos != 0) { |
1391 | uint8_t *buf = (uint8_t*)ctx->state; | 1390 | while (len != 0) { |
1392 | buf[bufpos] ^= *data++; | 1391 | uint8_t *buf = (uint8_t*)ctx->state; |
1393 | len--; | 1392 | buf[bufpos] ^= *data++; |
1394 | bufpos++; | 1393 | len--; |
1395 | if (bufpos == SHA3_IBLK_BYTES) { | 1394 | bufpos++; |
1396 | bufpos = 0; | 1395 | if (bufpos == iblk_bytes) { |
1397 | goto do_block; | 1396 | bufpos = 0; |
1397 | goto do_block; | ||
1398 | } | ||
1398 | } | 1399 | } |
1399 | } | 1400 | } |
1400 | 1401 | ||
1401 | /* Absorb complete blocks */ | 1402 | /* Absorb complete blocks */ |
1402 | while (len >= SHA3_IBLK_BYTES) { | 1403 | while (len >= iblk_bytes) { |
1403 | /* XOR data onto beginning of state[]. | 1404 | /* XOR data onto beginning of state[]. |
1404 | * We try to be efficient - operate one word at a time, not byte. | 1405 | * We try to be efficient - operate one word at a time, not byte. |
1405 | * Careful wrt unaligned access: can't just use "*(long*)data"! | 1406 | * Careful wrt unaligned access: can't just use "*(long*)data"! |
1406 | */ | 1407 | */ |
1407 | unsigned count = SHA3_IBLK_BYTES / sizeof(long); | 1408 | unsigned count = iblk_bytes / sizeof(long); |
1408 | long *buf = (long*)ctx->state; | 1409 | long *buf = (long*)ctx->state; |
1409 | do { | 1410 | do { |
1410 | long v; | 1411 | long v; |
@@ -1412,7 +1413,7 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) | |||
1412 | *buf++ ^= v; | 1413 | *buf++ ^= v; |
1413 | data += sizeof(long); | 1414 | data += sizeof(long); |
1414 | } while (--count); | 1415 | } while (--count); |
1415 | len -= SHA3_IBLK_BYTES; | 1416 | len -= iblk_bytes; |
1416 | do_block: | 1417 | do_block: |
1417 | sha3_process_block72(ctx->state); | 1418 | sha3_process_block72(ctx->state); |
1418 | } | 1419 | } |
@@ -1433,8 +1434,22 @@ void FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) | |||
1433 | { | 1434 | { |
1434 | /* Padding */ | 1435 | /* Padding */ |
1435 | uint8_t *buf = (uint8_t*)ctx->state; | 1436 | uint8_t *buf = (uint8_t*)ctx->state; |
1436 | buf[ctx->bytes_queued] ^= 1; | 1437 | /* |
1437 | buf[SHA3_IBLK_BYTES - 1] ^= 0x80; | 1438 | * Keccak block padding is: add 1 bit after last bit of input, |
1439 | * then add zero bits until the end of block, and add the last 1 bit | ||
1440 | * (the last bit in the block) - the "10*1" pattern. | ||
1441 | * SHA3 standard appends additional two bits, 01, before that padding: | ||
1442 | * | ||
1443 | * SHA3-224(M) = KECCAK[448](M||01, 224) | ||
1444 | * SHA3-256(M) = KECCAK[512](M||01, 256) | ||
1445 | * SHA3-384(M) = KECCAK[768](M||01, 384) | ||
1446 | * SHA3-512(M) = KECCAK[1024](M||01, 512) | ||
1447 | * (M is the input, || is bit concatenation) | ||
1448 | * | ||
1449 | * The 6 below contains 01 "SHA3" bits and the first 1 "Keccak" bit: | ||
1450 | */ | ||
1451 | buf[ctx->bytes_queued] ^= 6; /* bit pattern 00000110 */ | ||
1452 | buf[ctx->input_block_bytes - 1] ^= 0x80; | ||
1438 | 1453 | ||
1439 | sha3_process_block72(ctx->state); | 1454 | sha3_process_block72(ctx->state); |
1440 | 1455 | ||
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 2566abd38..34eb16e6b 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -81,7 +81,9 @@ | |||
81 | # define CHAR_T wchar_t | 81 | # define CHAR_T wchar_t |
82 | static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); } | 82 | static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); } |
83 | # if ENABLE_FEATURE_EDITING_VI | 83 | # if ENABLE_FEATURE_EDITING_VI |
84 | static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); } | 84 | static bool BB_isalnum_or_underscore(CHAR_T c) { |
85 | return ((unsigned)c < 256 && isalnum(c)) || c == '_'; | ||
86 | } | ||
85 | # endif | 87 | # endif |
86 | static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } | 88 | static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } |
87 | # undef isspace | 89 | # undef isspace |
@@ -96,7 +98,11 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } | |||
96 | # define BB_NUL '\0' | 98 | # define BB_NUL '\0' |
97 | # define CHAR_T char | 99 | # define CHAR_T char |
98 | # define BB_isspace(c) isspace(c) | 100 | # define BB_isspace(c) isspace(c) |
99 | # define BB_isalnum(c) isalnum(c) | 101 | # if ENABLE_FEATURE_EDITING_VI |
102 | static bool BB_isalnum_or_underscore(CHAR_T c) { | ||
103 | return ((unsigned)c < 256 && isalnum(c)) || c == '_'; | ||
104 | } | ||
105 | # endif | ||
100 | # define BB_ispunct(c) ispunct(c) | 106 | # define BB_ispunct(c) ispunct(c) |
101 | #endif | 107 | #endif |
102 | #if ENABLE_UNICODE_PRESERVE_BROKEN | 108 | #if ENABLE_UNICODE_PRESERVE_BROKEN |
@@ -1647,9 +1653,9 @@ vi_word_motion(int eat) | |||
1647 | { | 1653 | { |
1648 | CHAR_T *command = command_ps; | 1654 | CHAR_T *command = command_ps; |
1649 | 1655 | ||
1650 | if (BB_isalnum(command[cursor]) || command[cursor] == '_') { | 1656 | if (BB_isalnum_or_underscore(command[cursor])) { |
1651 | while (cursor < command_len | 1657 | while (cursor < command_len |
1652 | && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') | 1658 | && (BB_isalnum_or_underscore(command[cursor+1])) |
1653 | ) { | 1659 | ) { |
1654 | input_forward(); | 1660 | input_forward(); |
1655 | } | 1661 | } |
@@ -1691,9 +1697,9 @@ vi_end_motion(void) | |||
1691 | input_forward(); | 1697 | input_forward(); |
1692 | if (cursor >= command_len-1) | 1698 | if (cursor >= command_len-1) |
1693 | return; | 1699 | return; |
1694 | if (BB_isalnum(command[cursor]) || command[cursor] == '_') { | 1700 | if (BB_isalnum_or_underscore(command[cursor])) { |
1695 | while (cursor < command_len-1 | 1701 | while (cursor < command_len-1 |
1696 | && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') | 1702 | && (BB_isalnum_or_underscore(command[cursor+1])) |
1697 | ) { | 1703 | ) { |
1698 | input_forward(); | 1704 | input_forward(); |
1699 | } | 1705 | } |
@@ -1726,9 +1732,9 @@ vi_back_motion(void) | |||
1726 | input_backward(1); | 1732 | input_backward(1); |
1727 | if (cursor <= 0) | 1733 | if (cursor <= 0) |
1728 | return; | 1734 | return; |
1729 | if (BB_isalnum(command[cursor]) || command[cursor] == '_') { | 1735 | if (BB_isalnum_or_underscore(command[cursor])) { |
1730 | while (cursor > 0 | 1736 | while (cursor > 0 |
1731 | && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_') | 1737 | && (BB_isalnum_or_underscore(command[cursor-1])) |
1732 | ) { | 1738 | ) { |
1733 | input_backward(1); | 1739 | input_backward(1); |
1734 | } | 1740 | } |
diff --git a/libbb/login.c b/libbb/login.c index 8f080b775..5a7acfcf0 100644 --- a/libbb/login.c +++ b/libbb/login.c | |||
@@ -45,6 +45,45 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty) | |||
45 | if (c == '\\' || c == '%') { | 45 | if (c == '\\' || c == '%') { |
46 | c = fgetc(fp); | 46 | c = fgetc(fp); |
47 | switch (c) { | 47 | switch (c) { |
48 | //From getty manpage (* - supported by us) | ||
49 | //======================================== | ||
50 | //4 or 4{interface} | ||
51 | // Insert the IPv4 address of the network interface (example: \4{eth0}). | ||
52 | // If the interface argument is not specified, then select the first | ||
53 | // fully configured (UP, non-LOOPBACK, RUNNING) interface. | ||
54 | //6 or 6{interface} -- The same as \4 but for IPv6. | ||
55 | //b -- Insert the baudrate of the current line. | ||
56 | //*d -- Insert the current date. | ||
57 | //*t -- Insert the current time. | ||
58 | //e or e{name} | ||
59 | // Translate the human-readable name to an escape sequence and insert it | ||
60 | // (for example: \e{red}Alert text.\e{reset}). If the name argument | ||
61 | // is not specified, then insert \033. The currently supported names are: | ||
62 | // black, blink, blue, bold, brown, cyan, darkgray, gray, green, halfbright, | ||
63 | // lightblue, lightcyan, lightgray, lightgreen, lightmagenta, lightred, | ||
64 | // magenta, red, reset, reverse, and yellow. Unknown names are ignored. | ||
65 | //*s | ||
66 | // Insert the system name (the name of the operating system - `uname -s`) | ||
67 | //*S or S{VARIABLE} | ||
68 | // Insert the VARIABLE data from /etc/os-release. | ||
69 | // If the VARIABLE argument is not specified, use PRETTY_NAME. | ||
70 | // If PRETTY_NAME is not in /etc/os-release, \S is the same as \s. | ||
71 | //*l -- Insert the name of the current tty line. | ||
72 | //*m -- Insert the architecture identifier of the machine: `uname -m`. | ||
73 | //*n -- Insert the nodename of the machine: `uname -n`. | ||
74 | //*o -- Insert the NIS domainname of the machine. Same as `hostname -d'. | ||
75 | //*O -- Insert the DNS domainname of the machine. | ||
76 | //*r -- Insert the release number of the OS: `uname -r`. | ||
77 | //u -- Insert the number of current users logged in. | ||
78 | //U -- Insert the string "1 user" or "N users" (current users logged in). | ||
79 | //*v -- Insert the version of the OS, e.g. the build-date etc: `uname -v`. | ||
80 | //We also implement: | ||
81 | //*D -- same as \O "DNS domainname" | ||
82 | //*h -- same as \n "nodename" | ||
83 | |||
84 | case 'S': | ||
85 | /* minimal implementation, not reading /etc/os-release */ | ||
86 | /*FALLTHROUGH*/ | ||
48 | case 's': | 87 | case 's': |
49 | outbuf = uts.sysname; | 88 | outbuf = uts.sysname; |
50 | break; | 89 | break; |
@@ -65,6 +104,7 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty) | |||
65 | #if defined(__linux__) | 104 | #if defined(__linux__) |
66 | case 'D': | 105 | case 'D': |
67 | case 'o': | 106 | case 'o': |
107 | case 'O': | ||
68 | outbuf = uts.domainname; | 108 | outbuf = uts.domainname; |
69 | break; | 109 | break; |
70 | #endif | 110 | #endif |
diff --git a/libbb/speed_table.c b/libbb/speed_table.c index 174d531b2..11ced01d0 100644 --- a/libbb/speed_table.c +++ b/libbb/speed_table.c | |||
@@ -10,7 +10,16 @@ | |||
10 | #include "libbb.h" | 10 | #include "libbb.h" |
11 | 11 | ||
12 | struct speed_map { | 12 | struct speed_map { |
13 | #if defined __FreeBSD__ | 13 | #if defined __FreeBSD__ \ |
14 | || (defined B115200 && B115200 > 0xffff) \ | ||
15 | || (defined B230400 && B230400 > 0xffff) \ | ||
16 | || (defined B460800 && B460800 > 0xffff) \ | ||
17 | || (defined B921600 && B921600 > 0xffff) \ | ||
18 | || (defined B1152000 && B1152000 > 0xffff) \ | ||
19 | || (defined B1000000 && B1000000 > 0xffff) \ | ||
20 | || (defined B2000000 && B2000000 > 0xffff) \ | ||
21 | || (defined B3000000 && B3000000 > 0xffff) \ | ||
22 | || (defined B4000000 && B4000000 > 0xffff) | ||
14 | /* On FreeBSD, B<num> constants don't fit into a short */ | 23 | /* On FreeBSD, B<num> constants don't fit into a short */ |
15 | unsigned speed; | 24 | unsigned speed; |
16 | #else | 25 | #else |
@@ -19,6 +28,7 @@ struct speed_map { | |||
19 | unsigned short value; | 28 | unsigned short value; |
20 | }; | 29 | }; |
21 | 30 | ||
31 | /* On Linux, Bxx constants are 0..15 (up to B38400) and 0x1001..0x100f */ | ||
22 | static const struct speed_map speeds[] = { | 32 | static const struct speed_map speeds[] = { |
23 | {B0, 0}, | 33 | {B0, 0}, |
24 | {B50, 50}, | 34 | {B50, 50}, |
@@ -37,30 +47,79 @@ static const struct speed_map speeds[] = { | |||
37 | #ifdef B19200 | 47 | #ifdef B19200 |
38 | {B19200, 19200}, | 48 | {B19200, 19200}, |
39 | #elif defined(EXTA) | 49 | #elif defined(EXTA) |
40 | {EXTA, 19200}, | 50 | {EXTA, 19200}, |
41 | #endif | 51 | #endif |
52 | /* 19200 = 0x4b00 */ | ||
53 | /* 38400 = 0x9600, this value would use bit#15 if not "/200" encoded: */ | ||
42 | #ifdef B38400 | 54 | #ifdef B38400 |
43 | {B38400, 38400/256 + 0x8000U}, | 55 | {B38400, 38400/200 + 0x8000u}, |
44 | #elif defined(EXTB) | 56 | #elif defined(EXTB) |
45 | {EXTB, 38400/256 + 0x8000U}, | 57 | {EXTB, 38400/200 + 0x8000u}, |
46 | #endif | 58 | #endif |
47 | #ifdef B57600 | 59 | #ifdef B57600 |
48 | {B57600, 57600/256 + 0x8000U}, | 60 | {B57600, 57600/200 + 0x8000u}, |
49 | #endif | 61 | #endif |
50 | #ifdef B115200 | 62 | #ifdef B115200 |
51 | {B115200, 115200/256 + 0x8000U}, | 63 | {B115200, 115200/200 + 0x8000u}, |
52 | #endif | 64 | #endif |
53 | #ifdef B230400 | 65 | #ifdef B230400 |
54 | {B230400, 230400/256 + 0x8000U}, | 66 | {B230400, 230400/200 + 0x8000u}, |
55 | #endif | 67 | #endif |
56 | #ifdef B460800 | 68 | #ifdef B460800 |
57 | {B460800, 460800/256 + 0x8000U}, | 69 | {B460800, 460800/200 + 0x8000u}, |
70 | #endif | ||
71 | #ifdef B576000 | ||
72 | {B576000, 576000/200 + 0x8000u}, | ||
58 | #endif | 73 | #endif |
59 | #ifdef B921600 | 74 | #ifdef B921600 |
60 | {B921600, 921600/256 + 0x8000U}, | 75 | {B921600, 921600/200 + 0x8000u}, |
76 | #endif | ||
77 | #ifdef B1152000 | ||
78 | {B1152000, 1152000/200 + 0x8000u}, | ||
79 | #endif | ||
80 | |||
81 | #ifdef B500000 | ||
82 | {B500000, 500000/200 + 0x8000u}, | ||
83 | #endif | ||
84 | #ifdef B1000000 | ||
85 | {B1000000, 1000000/200 + 0x8000u}, | ||
86 | #endif | ||
87 | #ifdef B1500000 | ||
88 | {B1500000, 1500000/200 + 0x8000u}, | ||
89 | #endif | ||
90 | #ifdef B2000000 | ||
91 | {B2000000, 2000000/200 + 0x8000u}, | ||
92 | #endif | ||
93 | #ifdef B2500000 | ||
94 | {B2500000, 2500000/200 + 0x8000u}, | ||
61 | #endif | 95 | #endif |
96 | #ifdef B3000000 | ||
97 | {B3000000, 3000000/200 + 0x8000u}, | ||
98 | #endif | ||
99 | #ifdef B3500000 | ||
100 | {B3500000, 3500000/200 + 0x8000u}, | ||
101 | #endif | ||
102 | #ifdef B4000000 | ||
103 | {B4000000, 4000000/200 + 0x8000u}, | ||
104 | #endif | ||
105 | /* 4000000/200 = 0x4e20, bit#15 still does not interfere with the value */ | ||
106 | /* (can use /800 if higher speeds would appear, /1600 won't work for B500000) */ | ||
62 | }; | 107 | }; |
63 | 108 | ||
109 | /* | ||
110 | * TODO: maybe we can just bite the bullet, ditch the table and use termios2 | ||
111 | * Linux API (supports arbitrary baud rates, no Bxxxx mess needed)? Example: | ||
112 | * | ||
113 | * #include <asm/termios.h> | ||
114 | * #include <asm/ioctls.h> | ||
115 | * struct termios2 t; | ||
116 | * ioctl(fd, TCGETS2, &t); | ||
117 | * t.c_ospeed = t.c_ispeed = 543210; | ||
118 | * t.c_cflag &= ~CBAUD; | ||
119 | * t.c_cflag |= BOTHER; | ||
120 | * ioctl(fd, TCSETS2, &t); | ||
121 | */ | ||
122 | |||
64 | enum { NUM_SPEEDS = ARRAY_SIZE(speeds) }; | 123 | enum { NUM_SPEEDS = ARRAY_SIZE(speeds) }; |
65 | 124 | ||
66 | unsigned FAST_FUNC tty_baud_to_value(speed_t speed) | 125 | unsigned FAST_FUNC tty_baud_to_value(speed_t speed) |
@@ -69,8 +128,8 @@ unsigned FAST_FUNC tty_baud_to_value(speed_t speed) | |||
69 | 128 | ||
70 | do { | 129 | do { |
71 | if (speed == speeds[i].speed) { | 130 | if (speed == speeds[i].speed) { |
72 | if (speeds[i].value & 0x8000U) { | 131 | if (speeds[i].value & 0x8000u) { |
73 | return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256; | 132 | return ((unsigned)(speeds[i].value) & 0x7fffU) * 200; |
74 | } | 133 | } |
75 | return speeds[i].value; | 134 | return speeds[i].value; |
76 | } | 135 | } |
diff --git a/libbb/udp_io.c b/libbb/udp_io.c index a32af9bd2..6e3ef484e 100644 --- a/libbb/udp_io.c +++ b/libbb/udp_io.c | |||
@@ -70,7 +70,13 @@ send_to_from(int fd, void *buf, size_t len, int flags, | |||
70 | msg.msg_flags = flags; | 70 | msg.msg_flags = flags; |
71 | 71 | ||
72 | cmsgptr = CMSG_FIRSTHDR(&msg); | 72 | cmsgptr = CMSG_FIRSTHDR(&msg); |
73 | if (to->sa_family == AF_INET && from->sa_family == AF_INET) { | 73 | /* |
74 | * Users report that to->sa_family can be AF_INET6 too, | ||
75 | * if "to" was acquired by recv_from_to(). IOW: recv_from_to() | ||
76 | * was seen showing IPv6 "from" even when the destination | ||
77 | * of received packet (our local address) was IPv4. | ||
78 | */ | ||
79 | if (/* to->sa_family == AF_INET && */ from->sa_family == AF_INET) { | ||
74 | struct in_pktinfo *pktptr; | 80 | struct in_pktinfo *pktptr; |
75 | cmsgptr->cmsg_level = IPPROTO_IP; | 81 | cmsgptr->cmsg_level = IPPROTO_IP; |
76 | cmsgptr->cmsg_type = IP_PKTINFO; | 82 | cmsgptr->cmsg_type = IP_PKTINFO; |
@@ -86,7 +92,7 @@ send_to_from(int fd, void *buf, size_t len, int flags, | |||
86 | pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; | 92 | pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr; |
87 | } | 93 | } |
88 | # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) | 94 | # if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO) |
89 | else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { | 95 | else if (/* to->sa_family == AF_INET6 && */ from->sa_family == AF_INET6) { |
90 | struct in6_pktinfo *pktptr; | 96 | struct in6_pktinfo *pktptr; |
91 | cmsgptr->cmsg_level = IPPROTO_IPV6; | 97 | cmsgptr->cmsg_level = IPPROTO_IPV6; |
92 | cmsgptr->cmsg_type = IPV6_PKTINFO; | 98 | cmsgptr->cmsg_type = IPV6_PKTINFO; |
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index f488f8e0c..766a89e5f 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c | |||
@@ -70,7 +70,8 @@ pid_t FAST_FUNC xspawn(char **argv) | |||
70 | return pid; | 70 | return pid; |
71 | } | 71 | } |
72 | 72 | ||
73 | #if ENABLE_FEATURE_PREFER_APPLETS | 73 | #if ENABLE_FEATURE_PREFER_APPLETS \ |
74 | || ENABLE_FEATURE_SH_NOFORK | ||
74 | static jmp_buf die_jmp; | 75 | static jmp_buf die_jmp; |
75 | static void jump(void) | 76 | static void jump(void) |
76 | { | 77 | { |
@@ -176,7 +177,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) | |||
176 | 177 | ||
177 | return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ | 178 | return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ |
178 | } | 179 | } |
179 | #endif /* FEATURE_PREFER_APPLETS */ | 180 | #endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */ |
180 | 181 | ||
181 | int FAST_FUNC spawn_and_wait(char **argv) | 182 | int FAST_FUNC spawn_and_wait(char **argv) |
182 | { | 183 | { |
diff --git a/libbb/xconnect.c b/libbb/xconnect.c index c318af419..ee54898e3 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c | |||
@@ -500,12 +500,15 @@ char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) | |||
500 | { | 500 | { |
501 | return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); | 501 | return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); |
502 | } | 502 | } |
503 | #ifndef NI_NUMERICSCOPE | ||
504 | # define NI_NUMERICSCOPE 0 | ||
505 | #endif | ||
503 | char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) | 506 | char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) |
504 | { | 507 | { |
505 | return sockaddr2str(sa, NI_NUMERICHOST); | 508 | return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE); |
506 | } | 509 | } |
507 | 510 | ||
508 | char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) | 511 | char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) |
509 | { | 512 | { |
510 | return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); | 513 | return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE | IGNORE_PORT); |
511 | } | 514 | } |
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c index e9222f690..1b11caf6b 100644 --- a/libbb/xfuncs_printf.c +++ b/libbb/xfuncs_printf.c | |||
@@ -235,8 +235,16 @@ void FAST_FUNC xwrite(int fd, const void *buf, size_t count) | |||
235 | { | 235 | { |
236 | if (count) { | 236 | if (count) { |
237 | ssize_t size = full_write(fd, buf, count); | 237 | ssize_t size = full_write(fd, buf, count); |
238 | if ((size_t)size != count) | 238 | if ((size_t)size != count) { |
239 | bb_error_msg_and_die("short write"); | 239 | /* |
240 | * Two cases: write error immediately; | ||
241 | * or some writes succeeded, then we hit an error. | ||
242 | * In either case, errno is set. | ||
243 | */ | ||
244 | bb_perror_msg_and_die( | ||
245 | size >= 0 ? "short write" : "write error" | ||
246 | ); | ||
247 | } | ||
240 | } | 248 | } |
241 | } | 249 | } |
242 | void FAST_FUNC xwrite_str(int fd, const char *str) | 250 | void FAST_FUNC xwrite_str(int fd, const char *str) |
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index fb4dbb3da..1242795b8 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c | |||
@@ -10,7 +10,8 @@ | |||
10 | //kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o | 10 | //kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o |
11 | 11 | ||
12 | //usage:#define sendmail_trivial_usage | 12 | //usage:#define sendmail_trivial_usage |
13 | //usage: "[OPTIONS] [RECIPIENT_EMAIL]..." | 13 | //usage: "[-tv] [-f SENDER] [-amLOGIN 4<user_pass.txt | -auUSER -apPASS]" |
14 | //usage: "\n [-w SECS] [-H 'PROG ARGS' | -S HOST] [RECIPIENT_EMAIL]..." | ||
14 | //usage:#define sendmail_full_usage "\n\n" | 15 | //usage:#define sendmail_full_usage "\n\n" |
15 | //usage: "Read email from stdin and send it\n" | 16 | //usage: "Read email from stdin and send it\n" |
16 | //usage: "\nStandard options:" | 17 | //usage: "\nStandard options:" |
@@ -18,27 +19,25 @@ | |||
18 | //usage: "\n -f SENDER For use in MAIL FROM:<sender>. Can be empty string" | 19 | //usage: "\n -f SENDER For use in MAIL FROM:<sender>. Can be empty string" |
19 | //usage: "\n Default: -auUSER, or username of current UID" | 20 | //usage: "\n Default: -auUSER, or username of current UID" |
20 | //usage: "\n -o OPTIONS Various options. -oi implied, others are ignored" | 21 | //usage: "\n -o OPTIONS Various options. -oi implied, others are ignored" |
21 | //usage: "\n -i -oi synonym. implied and ignored" | 22 | //usage: "\n -i -oi synonym, implied and ignored" |
22 | //usage: "\n" | 23 | //usage: "\n" |
23 | //usage: "\nBusybox specific options:" | 24 | //usage: "\nBusybox specific options:" |
24 | //usage: "\n -v Verbose" | 25 | //usage: "\n -v Verbose" |
25 | //usage: "\n -w SECS Network timeout" | 26 | //usage: "\n -w SECS Network timeout" |
26 | //usage: "\n -H 'PROG ARGS' Run connection helper" | 27 | //usage: "\n -H 'PROG ARGS' Run connection helper. Examples:" |
27 | //usage: "\n Examples:" | 28 | //usage: "\n openssl s_client -quiet -tls1 -starttls smtp -connect smtp.gmail.com:25" |
28 | //usage: "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp" | 29 | //usage: "\n openssl s_client -quiet -tls1 -connect smtp.gmail.com:465" |
29 | //usage: "\n -connect smtp.gmail.com:25' <email.txt" | 30 | //usage: "\n $SMTP_ANTISPAM_DELAY: seconds to wait after helper connect" |
30 | //usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]" | 31 | //usage: "\n -S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)" |
31 | //usage: "\n -H 'exec openssl s_client -quiet -tls1" | 32 | //usage: "\n -amLOGIN Log in using AUTH LOGIN (-amCRAM-MD5 not supported)" |
32 | //usage: "\n -connect smtp.gmail.com:465' <email.txt" | 33 | //usage: "\n -auUSER Username for AUTH" |
33 | //usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]" | 34 | //usage: "\n -apPASS Password for AUTH" |
34 | //usage: "\n -S HOST[:PORT] Server" | ||
35 | //usage: "\n -auUSER Username for AUTH LOGIN" | ||
36 | //usage: "\n -apPASS Password for AUTH LOGIN" | ||
37 | ////usage: "\n -amMETHOD Authentication method. Ignored. LOGIN is implied" | ||
38 | //usage: "\n" | 35 | //usage: "\n" |
39 | //usage: "\nOther options are silently ignored; -oi -t is implied" | 36 | //usage: "\nIf no -a options are given, authentication is not done." |
37 | //usage: "\nIf -amLOGIN is given but no -au/-ap, user/password is read from fd #4." | ||
38 | //usage: "\nOther options are silently ignored; -oi is implied." | ||
40 | //usage: IF_MAKEMIME( | 39 | //usage: IF_MAKEMIME( |
41 | //usage: "\nUse makemime to create emails with attachments" | 40 | //usage: "\nUse makemime to create emails with attachments." |
42 | //usage: ) | 41 | //usage: ) |
43 | 42 | ||
44 | /* Currently we don't sanitize or escape user-supplied SENDER and RECIPIENT_EMAILs. | 43 | /* Currently we don't sanitize or escape user-supplied SENDER and RECIPIENT_EMAILs. |
@@ -209,7 +208,7 @@ static void rcptto_list(const char *list) | |||
209 | int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 208 | int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
210 | int sendmail_main(int argc UNUSED_PARAM, char **argv) | 209 | int sendmail_main(int argc UNUSED_PARAM, char **argv) |
211 | { | 210 | { |
212 | char *opt_connect = opt_connect; | 211 | char *opt_connect; |
213 | char *opt_from = NULL; | 212 | char *opt_from = NULL; |
214 | char *s; | 213 | char *s; |
215 | llist_t *list = NULL; | 214 | llist_t *list = NULL; |
@@ -241,6 +240,11 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
241 | // init global variables | 240 | // init global variables |
242 | INIT_G(); | 241 | INIT_G(); |
243 | 242 | ||
243 | // default HOST[:PORT] is $SMTPHOST, or localhost | ||
244 | opt_connect = getenv("SMTPHOST"); | ||
245 | if (!opt_connect) | ||
246 | opt_connect = (char *)"127.0.0.1"; | ||
247 | |||
244 | // save initial stdin since body is piped! | 248 | // save initial stdin since body is piped! |
245 | xdup2(STDIN_FILENO, 3); | 249 | xdup2(STDIN_FILENO, 3); |
246 | G.fp0 = xfdopen_for_read(3); | 250 | G.fp0 = xfdopen_for_read(3); |
@@ -276,6 +280,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
276 | 280 | ||
277 | // connection helper ordered? -> | 281 | // connection helper ordered? -> |
278 | if (opts & OPT_H) { | 282 | if (opts & OPT_H) { |
283 | const char *delay; | ||
279 | const char *args[] = { "sh", "-c", opt_connect, NULL }; | 284 | const char *args[] = { "sh", "-c", opt_connect, NULL }; |
280 | // plug it in | 285 | // plug it in |
281 | launch_helper(args); | 286 | launch_helper(args); |
@@ -294,7 +299,12 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
294 | // before 220 reached it. The code below is unsafe in this regard: | 299 | // before 220 reached it. The code below is unsafe in this regard: |
295 | // in non-STARTTLSed case, we potentially send NOOP before 220 | 300 | // in non-STARTTLSed case, we potentially send NOOP before 220 |
296 | // is sent by server. | 301 | // is sent by server. |
297 | // Ideas? (--delay SECS opt? --assume-starttls-helper opt?) | 302 | // |
303 | // If $SMTP_ANTISPAM_DELAY is set, we pause before sending NOOP. | ||
304 | // | ||
305 | delay = getenv("SMTP_ANTISPAM_DELAY"); | ||
306 | if (delay) | ||
307 | sleep(atoi(delay)); | ||
298 | code = smtp_check("NOOP", -1); | 308 | code = smtp_check("NOOP", -1); |
299 | if (code == 220) | 309 | if (code == 220) |
300 | // we got 220 - this is not STARTTLSed connection, | 310 | // we got 220 - this is not STARTTLSed connection, |
@@ -306,14 +316,6 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
306 | } else { | 316 | } else { |
307 | // vanilla connection | 317 | // vanilla connection |
308 | int fd; | 318 | int fd; |
309 | // host[:port] not explicitly specified? -> use $SMTPHOST | ||
310 | // no $SMTPHOST? -> use localhost | ||
311 | if (!(opts & OPT_S)) { | ||
312 | opt_connect = getenv("SMTPHOST"); | ||
313 | if (!opt_connect) | ||
314 | opt_connect = (char *)"127.0.0.1"; | ||
315 | } | ||
316 | // do connect | ||
317 | fd = create_and_connect_stream_or_die(opt_connect, 25); | 319 | fd = create_and_connect_stream_or_die(opt_connect, 25); |
318 | // and make ourselves a simple IO filter | 320 | // and make ourselves a simple IO filter |
319 | xmove_fd(fd, STDIN_FILENO); | 321 | xmove_fd(fd, STDIN_FILENO); |
diff --git a/miscutils/less.c b/miscutils/less.c index f9f6f62d9..8ba687872 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -173,6 +173,7 @@ enum { pattern_valid = 0 }; | |||
173 | struct globals { | 173 | struct globals { |
174 | int cur_fline; /* signed */ | 174 | int cur_fline; /* signed */ |
175 | int kbd_fd; /* fd to get input from */ | 175 | int kbd_fd; /* fd to get input from */ |
176 | int kbd_fd_orig_flags; | ||
176 | int less_gets_pos; | 177 | int less_gets_pos; |
177 | /* last position in last line, taking into account tabs */ | 178 | /* last position in last line, taking into account tabs */ |
178 | size_t last_line_pos; | 179 | size_t last_line_pos; |
@@ -308,6 +309,8 @@ static void print_statusline(const char *str) | |||
308 | static void less_exit(int code) | 309 | static void less_exit(int code) |
309 | { | 310 | { |
310 | set_tty_cooked(); | 311 | set_tty_cooked(); |
312 | if (!(G.kbd_fd_orig_flags & O_NONBLOCK)) | ||
313 | ndelay_off(kbd_fd); | ||
311 | clear_line(); | 314 | clear_line(); |
312 | if (code < 0) | 315 | if (code < 0) |
313 | kill_myself_with_sig(- code); /* does not return */ | 316 | kill_myself_with_sig(- code); /* does not return */ |
@@ -1661,16 +1664,23 @@ static char opp_bracket(char bracket) | |||
1661 | 1664 | ||
1662 | static void match_right_bracket(char bracket) | 1665 | static void match_right_bracket(char bracket) |
1663 | { | 1666 | { |
1664 | unsigned i; | 1667 | unsigned i = cur_fline; |
1665 | 1668 | ||
1666 | if (strchr(flines[cur_fline], bracket) == NULL) { | 1669 | if (i >= max_fline |
1670 | || strchr(flines[i], bracket) == NULL | ||
1671 | ) { | ||
1667 | print_statusline("No bracket in top line"); | 1672 | print_statusline("No bracket in top line"); |
1668 | return; | 1673 | return; |
1669 | } | 1674 | } |
1675 | |||
1670 | bracket = opp_bracket(bracket); | 1676 | bracket = opp_bracket(bracket); |
1671 | for (i = cur_fline + 1; i < max_fline; i++) { | 1677 | for (; i < max_fline; i++) { |
1672 | if (strchr(flines[i], bracket) != NULL) { | 1678 | if (strchr(flines[i], bracket) != NULL) { |
1673 | buffer_line(i); | 1679 | /* |
1680 | * Line with matched right bracket becomes | ||
1681 | * last visible line | ||
1682 | */ | ||
1683 | buffer_line(i - max_displayed_line); | ||
1674 | return; | 1684 | return; |
1675 | } | 1685 | } |
1676 | } | 1686 | } |
@@ -1679,16 +1689,22 @@ static void match_right_bracket(char bracket) | |||
1679 | 1689 | ||
1680 | static void match_left_bracket(char bracket) | 1690 | static void match_left_bracket(char bracket) |
1681 | { | 1691 | { |
1682 | int i; | 1692 | int i = cur_fline + max_displayed_line; |
1683 | 1693 | ||
1684 | if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) { | 1694 | if (i >= max_fline |
1695 | || strchr(flines[i], bracket) == NULL | ||
1696 | ) { | ||
1685 | print_statusline("No bracket in bottom line"); | 1697 | print_statusline("No bracket in bottom line"); |
1686 | return; | 1698 | return; |
1687 | } | 1699 | } |
1688 | 1700 | ||
1689 | bracket = opp_bracket(bracket); | 1701 | bracket = opp_bracket(bracket); |
1690 | for (i = cur_fline + max_displayed_line; i >= 0; i--) { | 1702 | for (; i >= 0; i--) { |
1691 | if (strchr(flines[i], bracket) != NULL) { | 1703 | if (strchr(flines[i], bracket) != NULL) { |
1704 | /* | ||
1705 | * Line with matched left bracket becomes | ||
1706 | * first visible line | ||
1707 | */ | ||
1692 | buffer_line(i); | 1708 | buffer_line(i); |
1693 | return; | 1709 | return; |
1694 | } | 1710 | } |
@@ -1859,9 +1875,10 @@ int less_main(int argc, char **argv) | |||
1859 | /* Some versions of less can survive w/o controlling tty, | 1875 | /* Some versions of less can survive w/o controlling tty, |
1860 | * try to do the same. This also allows to specify an alternative | 1876 | * try to do the same. This also allows to specify an alternative |
1861 | * tty via "less 1<>TTY". | 1877 | * tty via "less 1<>TTY". |
1862 | * We don't try to use STDOUT_FILENO directly, | 1878 | * |
1879 | * We prefer not to use STDOUT_FILENO directly, | ||
1863 | * since we want to set this fd to non-blocking mode, | 1880 | * since we want to set this fd to non-blocking mode, |
1864 | * and not bother with restoring it on exit. | 1881 | * and not interfere with other processes which share stdout with us. |
1865 | */ | 1882 | */ |
1866 | tty_name = xmalloc_ttyname(STDOUT_FILENO); | 1883 | tty_name = xmalloc_ttyname(STDOUT_FILENO); |
1867 | if (tty_name) { | 1884 | if (tty_name) { |
@@ -1873,10 +1890,12 @@ int less_main(int argc, char **argv) | |||
1873 | /* Try controlling tty */ | 1890 | /* Try controlling tty */ |
1874 | try_ctty: | 1891 | try_ctty: |
1875 | tty_fd = open(CURRENT_TTY, O_RDONLY); | 1892 | tty_fd = open(CURRENT_TTY, O_RDONLY); |
1876 | if (tty_fd < 0) | 1893 | if (tty_fd < 0) { |
1877 | return bb_cat(argv); | 1894 | /* If all else fails, less 481 uses stdout. Mimic that */ |
1895 | tty_fd = STDOUT_FILENO; | ||
1896 | } | ||
1878 | } | 1897 | } |
1879 | ndelay_on(tty_fd); | 1898 | G.kbd_fd_orig_flags = ndelay_on(tty_fd); |
1880 | kbd_fd = tty_fd; /* save in a global */ | 1899 | kbd_fd = tty_fd; /* save in a global */ |
1881 | #else | 1900 | #else |
1882 | kbd_fd = 0; | 1901 | kbd_fd = 0; |
diff --git a/miscutils/man.c b/miscutils/man.c index 4ce40b247..6d32890d1 100644 --- a/miscutils/man.c +++ b/miscutils/man.c | |||
@@ -232,8 +232,8 @@ int man_main(int argc UNUSED_PARAM, char **argv) | |||
232 | if (!token[1]) | 232 | if (!token[1]) |
233 | continue; | 233 | continue; |
234 | if (strcmp("DEFINE", token[0]) == 0) { | 234 | if (strcmp("DEFINE", token[0]) == 0) { |
235 | if (is_prefixed_with("pager", token[1])) { | 235 | if (is_prefixed_with(token[1], "pager")) { |
236 | pager = xstrdup(skip_whitespace(token[1]) + 5); | 236 | pager = xstrdup(skip_whitespace(token[1] + 5)); |
237 | } | 237 | } |
238 | } else | 238 | } else |
239 | if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ | 239 | if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ |
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 5936e48cf..a47e52234 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c | |||
@@ -18,9 +18,13 @@ | |||
18 | /* After libbb.h, since it needs sys/types.h on some systems */ | 18 | /* After libbb.h, since it needs sys/types.h on some systems */ |
19 | #include <sys/utsname.h> /* uname() */ | 19 | #include <sys/utsname.h> /* uname() */ |
20 | #include <fnmatch.h> | 20 | #include <fnmatch.h> |
21 | #include <sys/syscall.h> | ||
21 | 22 | ||
22 | extern int init_module(void *module, unsigned long len, const char *options); | 23 | extern int init_module(void *module, unsigned long len, const char *options); |
23 | extern int delete_module(const char *module, unsigned flags); | 24 | extern int delete_module(const char *module, unsigned flags); |
25 | #ifdef __NR_finit_module | ||
26 | # define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags) | ||
27 | #endif | ||
24 | /* linux/include/linux/module.h has limit of 64 chars on module names */ | 28 | /* linux/include/linux/module.h has limit of 64 chars on module names */ |
25 | #undef MODULE_NAME_LEN | 29 | #undef MODULE_NAME_LEN |
26 | #define MODULE_NAME_LEN 64 | 30 | #define MODULE_NAME_LEN 64 |
@@ -209,11 +213,34 @@ static int load_module(const char *fname, const char *options) | |||
209 | int r; | 213 | int r; |
210 | size_t len = MAXINT(ssize_t); | 214 | size_t len = MAXINT(ssize_t); |
211 | char *module_image; | 215 | char *module_image; |
216 | |||
217 | if (!options) | ||
218 | options = ""; | ||
219 | |||
212 | dbg1_error_msg("load_module('%s','%s')", fname, options); | 220 | dbg1_error_msg("load_module('%s','%s')", fname, options); |
213 | 221 | ||
214 | module_image = xmalloc_open_zipped_read_close(fname, &len); | 222 | /* |
215 | r = (!module_image || init_module(module_image, len, options ? options : "") != 0); | 223 | * First we try finit_module if available. Some kernels are configured |
216 | free(module_image); | 224 | * to only allow loading of modules off of secure storage (like a read- |
225 | * only rootfs) which needs the finit_module call. If it fails, we fall | ||
226 | * back to normal module loading to support compressed modules. | ||
227 | */ | ||
228 | r = 1; | ||
229 | # ifdef __NR_finit_module | ||
230 | { | ||
231 | int fd = open(fname, O_RDONLY | O_CLOEXEC); | ||
232 | if (fd >= 0) { | ||
233 | r = finit_module(fd, options, 0) != 0; | ||
234 | close(fd); | ||
235 | } | ||
236 | } | ||
237 | # endif | ||
238 | if (r != 0) { | ||
239 | module_image = xmalloc_open_zipped_read_close(fname, &len); | ||
240 | r = (!module_image || init_module(module_image, len, options) != 0); | ||
241 | free(module_image); | ||
242 | } | ||
243 | |||
217 | dbg1_error_msg("load_module:%d", r); | 244 | dbg1_error_msg("load_module:%d", r); |
218 | return r; /* 0 = success */ | 245 | return r; /* 0 = success */ |
219 | #else | 246 | #else |
diff --git a/modutils/modutils.c b/modutils/modutils.c index 0a056731d..d36caaf68 100644 --- a/modutils/modutils.c +++ b/modutils/modutils.c | |||
@@ -13,6 +13,9 @@ extern int delete_module(const char *module, unsigned int flags); | |||
13 | #else | 13 | #else |
14 | # include <sys/syscall.h> | 14 | # include <sys/syscall.h> |
15 | # define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) | 15 | # define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) |
16 | # if defined(__NR_finit_module) | ||
17 | # define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags) | ||
18 | # endif | ||
16 | # define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) | 19 | # define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) |
17 | #endif | 20 | #endif |
18 | 21 | ||
@@ -212,6 +215,24 @@ int FAST_FUNC bb_init_module(const char *filename, const char *options) | |||
212 | return bb_init_module_24(filename, options); | 215 | return bb_init_module_24(filename, options); |
213 | #endif | 216 | #endif |
214 | 217 | ||
218 | /* | ||
219 | * First we try finit_module if available. Some kernels are configured | ||
220 | * to only allow loading of modules off of secure storage (like a read- | ||
221 | * only rootfs) which needs the finit_module call. If it fails, we fall | ||
222 | * back to normal module loading to support compressed modules. | ||
223 | */ | ||
224 | # ifdef __NR_finit_module | ||
225 | { | ||
226 | int fd = open(filename, O_RDONLY | O_CLOEXEC); | ||
227 | if (fd >= 0) { | ||
228 | rc = finit_module(fd, options, 0) != 0; | ||
229 | close(fd); | ||
230 | if (rc == 0) | ||
231 | return rc; | ||
232 | } | ||
233 | } | ||
234 | # endif | ||
235 | |||
215 | image_size = INT_MAX - 4095; | 236 | image_size = INT_MAX - 4095; |
216 | mmaped = 0; | 237 | mmaped = 0; |
217 | image = try_to_mmap_module(filename, &image_size); | 238 | image = try_to_mmap_module(filename, &image_size); |
diff --git a/networking/Config.src b/networking/Config.src index 27c604a31..eb0536a7c 100644 --- a/networking/Config.src +++ b/networking/Config.src | |||
@@ -912,17 +912,9 @@ config FEATURE_TRACEROUTE_VERBOSE | |||
912 | Add some verbosity to traceroute. This includes among other things | 912 | Add some verbosity to traceroute. This includes among other things |
913 | hostnames and ICMP response types. | 913 | hostnames and ICMP response types. |
914 | 914 | ||
915 | config FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
916 | bool "Enable loose source route" | ||
917 | default n | ||
918 | depends on TRACEROUTE | ||
919 | help | ||
920 | Add option to specify a loose source route gateway | ||
921 | (8 maximum). | ||
922 | |||
923 | config FEATURE_TRACEROUTE_USE_ICMP | 915 | config FEATURE_TRACEROUTE_USE_ICMP |
924 | bool "Use ICMP instead of UDP" | 916 | bool "Enable -I option (use ICMP instead of UDP)" |
925 | default n | 917 | default y |
926 | depends on TRACEROUTE | 918 | depends on TRACEROUTE |
927 | help | 919 | help |
928 | Add option -I to use ICMP ECHO instead of UDP datagrams. | 920 | Add option -I to use ICMP ECHO instead of UDP datagrams. |
diff --git a/networking/ifplugd.c b/networking/ifplugd.c index 28c49e218..029cba147 100644 --- a/networking/ifplugd.c +++ b/networking/ifplugd.c | |||
@@ -358,8 +358,12 @@ static void up_iface(void) | |||
358 | ifrequest.ifr_flags |= IFF_UP; | 358 | ifrequest.ifr_flags |= IFF_UP; |
359 | /* Let user know we mess up with interface */ | 359 | /* Let user know we mess up with interface */ |
360 | bb_error_msg("upping interface"); | 360 | bb_error_msg("upping interface"); |
361 | if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) | 361 | if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) { |
362 | xfunc_die(); | 362 | if (errno != ENODEV) |
363 | xfunc_die(); | ||
364 | G.iface_exists = 0; | ||
365 | return; | ||
366 | } | ||
363 | } | 367 | } |
364 | 368 | ||
365 | #if 0 /* why do we mess with IP addr? It's not our business */ | 369 | #if 0 /* why do we mess with IP addr? It's not our business */ |
diff --git a/networking/ifupdown.c b/networking/ifupdown.c index 25b04c9d7..b0bc0d70f 100644 --- a/networking/ifupdown.c +++ b/networking/ifupdown.c | |||
@@ -21,7 +21,7 @@ | |||
21 | //usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..." | 21 | //usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..." |
22 | //usage:#define ifup_full_usage "\n\n" | 22 | //usage:#define ifup_full_usage "\n\n" |
23 | //usage: " -a De/configure all interfaces automatically" | 23 | //usage: " -a De/configure all interfaces automatically" |
24 | //usage: "\n -i FILE Use FILE for interface definitions" | 24 | //usage: "\n -i FILE Use FILE instead of /etc/network/interfaces" |
25 | //usage: "\n -n Print out what would happen, but don't do it" | 25 | //usage: "\n -n Print out what would happen, but don't do it" |
26 | //usage: IF_FEATURE_IFUPDOWN_MAPPING( | 26 | //usage: IF_FEATURE_IFUPDOWN_MAPPING( |
27 | //usage: "\n (note: doesn't disable mappings)" | 27 | //usage: "\n (note: doesn't disable mappings)" |
@@ -492,7 +492,7 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec) | |||
492 | result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up", | 492 | result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up", |
493 | ifd, exec); | 493 | ifd, exec); |
494 | result += execute("ifconfig %iface% %address% netmask %netmask%" | 494 | result += execute("ifconfig %iface% %address% netmask %netmask%" |
495 | "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", | 495 | "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]]", |
496 | ifd, exec); | 496 | ifd, exec); |
497 | result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec); | 497 | result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec); |
498 | return ((result == 3) ? 3 : 0); | 498 | return ((result == 3) ? 3 : 0); |
@@ -503,7 +503,10 @@ static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec) | |||
503 | { | 503 | { |
504 | int result; | 504 | int result; |
505 | # if ENABLE_FEATURE_IFUPDOWN_IP | 505 | # if ENABLE_FEATURE_IFUPDOWN_IP |
506 | result = execute("ip addr flush dev %iface%", ifd, exec); | 506 | /* Optional "label LBL" is necessary if interface is an alias (eth0:0), |
507 | * otherwise "ip addr flush dev eth0:0" flushes all addresses on eth0. | ||
508 | */ | ||
509 | result = execute("ip addr flush dev %iface%[[ label %label%]]", ifd, exec); | ||
507 | result += execute("ip link set %iface% down", ifd, exec); | 510 | result += execute("ip link set %iface% down", ifd, exec); |
508 | # else | 511 | # else |
509 | /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */ | 512 | /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */ |
@@ -1066,6 +1069,11 @@ static int execute_all(struct interface_defn_t *ifd, const char *opt) | |||
1066 | } | 1069 | } |
1067 | } | 1070 | } |
1068 | 1071 | ||
1072 | /* Tested on Debian Squeeze: "standard" ifup runs this without | ||
1073 | * checking that directory exists. If it doesn't, run-parts | ||
1074 | * complains, and this message _is_ annoyingly visible. | ||
1075 | * Don't "fix" this (unless newer Debian does). | ||
1076 | */ | ||
1069 | buf = xasprintf("run-parts /etc/network/if-%s.d", opt); | 1077 | buf = xasprintf("run-parts /etc/network/if-%s.d", opt); |
1070 | /* heh, we don't bother free'ing it */ | 1078 | /* heh, we don't bother free'ing it */ |
1071 | return doit(buf); | 1079 | return doit(buf); |
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c index e674e9a0d..48dc6e3d9 100644 --- a/networking/libiproute/iproute.c +++ b/networking/libiproute/iproute.c | |||
@@ -362,10 +362,9 @@ IF_FEATURE_IP_RULE(ARG_table,) | |||
362 | req.r.rtm_scope = RT_SCOPE_NOWHERE; | 362 | req.r.rtm_scope = RT_SCOPE_NOWHERE; |
363 | 363 | ||
364 | if (cmd != RTM_DELROUTE) { | 364 | if (cmd != RTM_DELROUTE) { |
365 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | ||
365 | if (RTPROT_BOOT != 0) | 366 | if (RTPROT_BOOT != 0) |
366 | req.r.rtm_protocol = RTPROT_BOOT; | 367 | req.r.rtm_protocol = RTPROT_BOOT; |
367 | if (RT_SCOPE_UNIVERSE != 0) | ||
368 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | ||
369 | if (RTN_UNICAST != 0) | 368 | if (RTN_UNICAST != 0) |
370 | req.r.rtm_type = RTN_UNICAST; | 369 | req.r.rtm_type = RTN_UNICAST; |
371 | } | 370 | } |
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c index dba64346f..c486834b0 100644 --- a/networking/libiproute/iprule.c +++ b/networking/libiproute/iprule.c | |||
@@ -197,9 +197,11 @@ static int iprule_modify(int cmd, char **argv) | |||
197 | req.n.nlmsg_flags = NLM_F_REQUEST; | 197 | req.n.nlmsg_flags = NLM_F_REQUEST; |
198 | req.r.rtm_family = preferred_family; | 198 | req.r.rtm_family = preferred_family; |
199 | req.r.rtm_protocol = RTPROT_BOOT; | 199 | req.r.rtm_protocol = RTPROT_BOOT; |
200 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | 200 | if (RT_SCOPE_UNIVERSE != 0) |
201 | req.r.rtm_table = 0; | 201 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; |
202 | req.r.rtm_type = RTN_UNSPEC; | 202 | /*req.r.rtm_table = 0; - already is */ |
203 | if (RTN_UNSPEC != 0) | ||
204 | req.r.rtm_type = RTN_UNSPEC; | ||
203 | 205 | ||
204 | if (cmd == RTM_NEWRULE) { | 206 | if (cmd == RTM_NEWRULE) { |
205 | req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; | 207 | req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; |
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c index cbb5daf95..7e0ff1b6c 100644 --- a/networking/libiproute/libnetlink.c +++ b/networking/libiproute/libnetlink.c | |||
@@ -68,30 +68,32 @@ int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len) | |||
68 | 68 | ||
69 | int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) | 69 | int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) |
70 | { | 70 | { |
71 | struct nlmsghdr nlh; | 71 | struct { |
72 | struct sockaddr_nl nladdr; | 72 | struct nlmsghdr nlh; |
73 | struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; | 73 | struct msghdr msg; |
74 | /* Use designated initializers, struct layout is non-portable */ | 74 | struct sockaddr_nl nladdr; |
75 | struct msghdr msg = { | 75 | } s; |
76 | .msg_name = (void*)&nladdr, | 76 | struct iovec iov[2] = { { &s.nlh, sizeof(s.nlh) }, { req, len } }; |
77 | .msg_namelen = sizeof(nladdr), | 77 | |
78 | .msg_iov = iov, | 78 | memset(&s, 0, sizeof(s)); |
79 | .msg_iovlen = 2, | 79 | |
80 | .msg_control = NULL, | 80 | s.msg.msg_name = (void*)&s.nladdr; |
81 | .msg_controllen = 0, | 81 | s.msg.msg_namelen = sizeof(s.nladdr); |
82 | .msg_flags = 0 | 82 | s.msg.msg_iov = iov; |
83 | }; | 83 | s.msg.msg_iovlen = 2; |
84 | 84 | /*s.msg.msg_control = NULL; - already is */ | |
85 | memset(&nladdr, 0, sizeof(nladdr)); | 85 | /*s.msg.msg_controllen = 0; - already is */ |
86 | nladdr.nl_family = AF_NETLINK; | 86 | /*s.msg.msg_flags = 0; - already is */ |
87 | 87 | ||
88 | nlh.nlmsg_len = NLMSG_LENGTH(len); | 88 | s.nladdr.nl_family = AF_NETLINK; |
89 | nlh.nlmsg_type = type; | 89 | |
90 | nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; | 90 | s.nlh.nlmsg_len = NLMSG_LENGTH(len); |
91 | nlh.nlmsg_pid = 0; | 91 | s.nlh.nlmsg_type = type; |
92 | nlh.nlmsg_seq = rth->dump = ++rth->seq; | 92 | s.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; |
93 | 93 | /*s.nlh.nlmsg_pid = 0; - already is */ | |
94 | return sendmsg(rth->fd, &msg, 0); | 94 | s.nlh.nlmsg_seq = rth->dump = ++rth->seq; |
95 | |||
96 | return sendmsg(rth->fd, &s.msg, 0); | ||
95 | } | 97 | } |
96 | 98 | ||
97 | static int rtnl_dump_filter(struct rtnl_handle *rth, | 99 | static int rtnl_dump_filter(struct rtnl_handle *rth, |
@@ -338,14 +340,14 @@ int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) | |||
338 | int len = RTA_LENGTH(4); | 340 | int len = RTA_LENGTH(4); |
339 | struct rtattr *rta; | 341 | struct rtattr *rta; |
340 | 342 | ||
341 | if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { | 343 | if ((int)(NLMSG_ALIGN(n->nlmsg_len + len)) > maxlen) { |
342 | return -1; | 344 | return -1; |
343 | } | 345 | } |
344 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); | 346 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); |
345 | rta->rta_type = type; | 347 | rta->rta_type = type; |
346 | rta->rta_len = len; | 348 | rta->rta_len = len; |
347 | move_to_unaligned32(RTA_DATA(rta), data); | 349 | move_to_unaligned32(RTA_DATA(rta), data); |
348 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; | 350 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len + len); |
349 | return 0; | 351 | return 0; |
350 | } | 352 | } |
351 | 353 | ||
@@ -354,14 +356,14 @@ int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, in | |||
354 | int len = RTA_LENGTH(alen); | 356 | int len = RTA_LENGTH(alen); |
355 | struct rtattr *rta; | 357 | struct rtattr *rta; |
356 | 358 | ||
357 | if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { | 359 | if ((int)(NLMSG_ALIGN(n->nlmsg_len + len)) > maxlen) { |
358 | return -1; | 360 | return -1; |
359 | } | 361 | } |
360 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); | 362 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); |
361 | rta->rta_type = type; | 363 | rta->rta_type = type; |
362 | rta->rta_len = len; | 364 | rta->rta_len = len; |
363 | memcpy(RTA_DATA(rta), data, alen); | 365 | memcpy(RTA_DATA(rta), data, alen); |
364 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; | 366 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len + len); |
365 | return 0; | 367 | return 0; |
366 | } | 368 | } |
367 | 369 | ||
@@ -370,14 +372,14 @@ int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t d | |||
370 | int len = RTA_LENGTH(4); | 372 | int len = RTA_LENGTH(4); |
371 | struct rtattr *subrta; | 373 | struct rtattr *subrta; |
372 | 374 | ||
373 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { | 375 | if (RTA_ALIGN(rta->rta_len + len) > maxlen) { |
374 | return -1; | 376 | return -1; |
375 | } | 377 | } |
376 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); | 378 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); |
377 | subrta->rta_type = type; | 379 | subrta->rta_type = type; |
378 | subrta->rta_len = len; | 380 | subrta->rta_len = len; |
379 | move_to_unaligned32(RTA_DATA(subrta), data); | 381 | move_to_unaligned32(RTA_DATA(subrta), data); |
380 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; | 382 | rta->rta_len = NLMSG_ALIGN(rta->rta_len + len); |
381 | return 0; | 383 | return 0; |
382 | } | 384 | } |
383 | 385 | ||
@@ -386,14 +388,14 @@ int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data | |||
386 | struct rtattr *subrta; | 388 | struct rtattr *subrta; |
387 | int len = RTA_LENGTH(alen); | 389 | int len = RTA_LENGTH(alen); |
388 | 390 | ||
389 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { | 391 | if (RTA_ALIGN(rta->rta_len + len) > maxlen) { |
390 | return -1; | 392 | return -1; |
391 | } | 393 | } |
392 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); | 394 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); |
393 | subrta->rta_type = type; | 395 | subrta->rta_type = type; |
394 | subrta->rta_len = len; | 396 | subrta->rta_len = len; |
395 | memcpy(RTA_DATA(subrta), data, alen); | 397 | memcpy(RTA_DATA(subrta), data, alen); |
396 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; | 398 | rta->rta_len = NLMSG_ALIGN(rta->rta_len + len); |
397 | return 0; | 399 | return 0; |
398 | } | 400 | } |
399 | 401 | ||
diff --git a/networking/ntpd.c b/networking/ntpd.c index 130cef0af..8ca62cf1b 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c | |||
@@ -2051,6 +2051,13 @@ recv_and_process_client_pkt(void /*int fd*/) | |||
2051 | goto bail; | 2051 | goto bail; |
2052 | } | 2052 | } |
2053 | 2053 | ||
2054 | /* Respond only to client and symmetric active packets */ | ||
2055 | if ((msg.m_status & MODE_MASK) != MODE_CLIENT | ||
2056 | && (msg.m_status & MODE_MASK) != MODE_SYM_ACT | ||
2057 | ) { | ||
2058 | goto bail; | ||
2059 | } | ||
2060 | |||
2054 | query_status = msg.m_status; | 2061 | query_status = msg.m_status; |
2055 | query_xmttime = msg.m_xmttime; | 2062 | query_xmttime = msg.m_xmttime; |
2056 | 2063 | ||
diff --git a/networking/route.c b/networking/route.c index 65c2fb7c8..102a6ec67 100644 --- a/networking/route.c +++ b/networking/route.c | |||
@@ -295,7 +295,7 @@ static NOINLINE void INET_setroute(int action, char **args) | |||
295 | #endif | 295 | #endif |
296 | 296 | ||
297 | /* Device is special in that it can be the last arg specified | 297 | /* Device is special in that it can be the last arg specified |
298 | * and doesn't requre the dev/device keyword in that case. */ | 298 | * and doesn't require the dev/device keyword in that case. */ |
299 | if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { | 299 | if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { |
300 | /* Don't use args_m1 here since args may have changed! */ | 300 | /* Don't use args_m1 here since args may have changed! */ |
301 | rt->rt_dev = args[-1]; | 301 | rt->rt_dev = args[-1]; |
@@ -416,7 +416,7 @@ static NOINLINE void INET6_setroute(int action, char **args) | |||
416 | } | 416 | } |
417 | 417 | ||
418 | /* Device is special in that it can be the last arg specified | 418 | /* Device is special in that it can be the last arg specified |
419 | * and doesn't requre the dev/device keyword in that case. */ | 419 | * and doesn't require the dev/device keyword in that case. */ |
420 | if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { | 420 | if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { |
421 | /* Don't use args_m1 here since args may have changed! */ | 421 | /* Don't use args_m1 here since args may have changed! */ |
422 | devname = args[-1]; | 422 | devname = args[-1]; |
diff --git a/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 new file mode 100755 index 000000000..208a7087d --- /dev/null +++ b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 | |||
@@ -0,0 +1,39 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | # How to configure & build a static wolfssl library | ||
4 | # suitable for static build of ssl_helper. | ||
5 | |||
6 | export CC="i686-gcc" | ||
7 | export CFLAGS="\ | ||
8 | -Os \ | ||
9 | -static \ | ||
10 | -fomit-frame-pointer \ | ||
11 | -falign-functions=1 -falign-labels=1 -falign-loops=1 -falign-jumps=1 \ | ||
12 | -ffunction-sections -fdata-sections \ | ||
13 | " | ||
14 | |||
15 | { | ||
16 | |||
17 | ./configure \ | ||
18 | --host="i686" \ | ||
19 | --enable-static \ | ||
20 | --enable-singlethreaded \ | ||
21 | --disable-shared \ | ||
22 | \ | ||
23 | C_EXTRA_FLAGS="-DWOLFSSL_STATIC_RSA" \ | ||
24 | || exit $? | ||
25 | |||
26 | # The second group of options was added when "vanilla" config did not work. | ||
27 | # A good tool to debug problems is to try wolfssl's client tool, e.g.: | ||
28 | # examples/client/client -h www.google.com -p 443 -d -x | ||
29 | # | ||
30 | # configure has many other options, see ./configure --help | ||
31 | # --enable-ecc \ | ||
32 | # --enable-sni \ | ||
33 | # | ||
34 | # Also consult "wolfSSL - Embedded SSL Library Product Support Forums" | ||
35 | # for recent report of users having problems connecting. | ||
36 | |||
37 | make | ||
38 | |||
39 | } 2>&1 | tee "$0.log" | ||
diff --git a/networking/ssl_helper-wolfssl/README b/networking/ssl_helper-wolfssl/README index 58a381c20..ff46f4bdf 100644 --- a/networking/ssl_helper-wolfssl/README +++ b/networking/ssl_helper-wolfssl/README | |||
@@ -11,6 +11,13 @@ Build instructions: | |||
11 | * Drop this directory into wolfssl-3.6.8/ssl_helper | 11 | * Drop this directory into wolfssl-3.6.8/ssl_helper |
12 | * Run ssl_helper.sh to compile and link the helper | 12 | * Run ssl_helper.sh to compile and link the helper |
13 | 13 | ||
14 | * Unpack wolfssl-3.9.8.tar.gz from https://github.com/wolfSSL/wolfssl/releases | ||
15 | * Create configure: | ||
16 | ./autogen.sh | ||
17 | * Build it: see 00cfg-wolfssl-3.9.8 shell script | ||
18 | * Drop this directory into wolfssl-x.y.z/ssl_helper | ||
19 | * Run ssl_helper.sh to compile and link the helper | ||
20 | |||
14 | Usage: "ssl_helper -d FILE_DESCRIPTOR" where FILE_DESCRIPTOR is open to the peer. | 21 | Usage: "ssl_helper -d FILE_DESCRIPTOR" where FILE_DESCRIPTOR is open to the peer. |
15 | 22 | ||
16 | In bash, you can do it this way: | 23 | In bash, you can do it this way: |
diff --git a/networking/ssl_helper-wolfssl/ssl_helper.sh b/networking/ssl_helper-wolfssl/ssl_helper.sh index 184ffe67e..c6cbf353f 100755 --- a/networking/ssl_helper-wolfssl/ssl_helper.sh +++ b/networking/ssl_helper-wolfssl/ssl_helper.sh | |||
@@ -9,4 +9,4 @@ STATIC="-static" | |||
9 | 9 | ||
10 | ${PREFIX}gcc -Os -Wall -I.. -c ssl_helper.c -o ssl_helper.o | 10 | ${PREFIX}gcc -Os -Wall -I.. -c ssl_helper.c -o ssl_helper.o |
11 | ${PREFIX}gcc $STATIC --start-group ssl_helper.o -lm ../src/.libs/libwolfssl.a --end-group -o ssl_helper | 11 | ${PREFIX}gcc $STATIC --start-group ssl_helper.o -lm ../src/.libs/libwolfssl.a --end-group -o ssl_helper |
12 | ${PREFIX}-strip ssl_helper | 12 | ${PREFIX}strip ssl_helper |
diff --git a/networking/traceroute.c b/networking/traceroute.c index e43a36dc7..b9a9ca4bb 100644 --- a/networking/traceroute.c +++ b/networking/traceroute.c | |||
@@ -212,8 +212,7 @@ | |||
212 | 212 | ||
213 | //usage:#define traceroute_trivial_usage | 213 | //usage:#define traceroute_trivial_usage |
214 | //usage: "[-"IF_TRACEROUTE6("46")"FIlnrv] [-f 1ST_TTL] [-m MAXTTL] [-q PROBES] [-p PORT]\n" | 214 | //usage: "[-"IF_TRACEROUTE6("46")"FIlnrv] [-f 1ST_TTL] [-m MAXTTL] [-q PROBES] [-p PORT]\n" |
215 | //usage: " [-t TOS] [-w WAIT_SEC]" | 215 | //usage: " [-t TOS] [-w WAIT_SEC] [-s SRC_IP] [-i IFACE]\n" |
216 | //usage: IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(" [-g GATEWAY]")" [-s SRC_IP] [-i IFACE]\n" | ||
217 | //usage: " [-z PAUSE_MSEC] HOST [BYTES]" | 216 | //usage: " [-z PAUSE_MSEC] HOST [BYTES]" |
218 | //usage:#define traceroute_full_usage "\n\n" | 217 | //usage:#define traceroute_full_usage "\n\n" |
219 | //usage: "Trace the route to HOST\n" | 218 | //usage: "Trace the route to HOST\n" |
@@ -294,7 +293,6 @@ | |||
294 | 293 | ||
295 | #define OPT_STRING \ | 294 | #define OPT_STRING \ |
296 | "FIlnrdvxt:i:m:p:q:s:w:z:f:" \ | 295 | "FIlnrdvxt:i:m:p:q:s:w:z:f:" \ |
297 | IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:*") \ | ||
298 | "4" IF_TRACEROUTE6("6") | 296 | "4" IF_TRACEROUTE6("6") |
299 | enum { | 297 | enum { |
300 | OPT_DONT_FRAGMNT = (1 << 0), /* F */ | 298 | OPT_DONT_FRAGMNT = (1 << 0), /* F */ |
@@ -314,9 +312,8 @@ enum { | |||
314 | OPT_WAITTIME = (1 << 14), /* w */ | 312 | OPT_WAITTIME = (1 << 14), /* w */ |
315 | OPT_PAUSE_MS = (1 << 15), /* z */ | 313 | OPT_PAUSE_MS = (1 << 15), /* z */ |
316 | OPT_FIRST_TTL = (1 << 16), /* f */ | 314 | OPT_FIRST_TTL = (1 << 16), /* f */ |
317 | OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */ | 315 | OPT_IPV4 = (1 << 17), /* 4 */ |
318 | OPT_IPV4 = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)), /* 4 */ | 316 | OPT_IPV6 = (1 << 18) * ENABLE_TRACEROUTE6, /* 6 */ |
319 | OPT_IPV6 = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */ | ||
320 | }; | 317 | }; |
321 | #define verbose (option_mask32 & OPT_VERBOSE) | 318 | #define verbose (option_mask32 & OPT_VERBOSE) |
322 | 319 | ||
@@ -343,26 +340,18 @@ struct outdata6_t { | |||
343 | #endif | 340 | #endif |
344 | 341 | ||
345 | struct globals { | 342 | struct globals { |
343 | /* Pointer to entire malloced IP packet, "packlen" bytes long: */ | ||
346 | struct ip *outip; | 344 | struct ip *outip; |
345 | /* Pointer to ICMP or UDP payload (not header): */ | ||
347 | struct outdata_t *outdata; | 346 | struct outdata_t *outdata; |
347 | |||
348 | len_and_sockaddr *dest_lsa; | 348 | len_and_sockaddr *dest_lsa; |
349 | int packlen; /* total length of packet */ | 349 | int packlen; /* total length of packet */ |
350 | int pmtu; /* Path MTU Discovery (RFC1191) */ | 350 | int pmtu; /* Path MTU Discovery (RFC1191) */ |
351 | uint32_t ident; | 351 | uint32_t ident; |
352 | uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */ | 352 | uint16_t port; // 33434; /* start udp dest port # for probe packets */ |
353 | int waittime; // 5; /* time to wait for response (in seconds) */ | 353 | int waittime; // 5; /* time to wait for response (in seconds) */ |
354 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
355 | int optlen; /* length of ip options */ | ||
356 | #else | ||
357 | #define optlen 0 | ||
358 | #endif | ||
359 | unsigned char recv_pkt[512]; /* last inbound (icmp) packet */ | 354 | unsigned char recv_pkt[512]; /* last inbound (icmp) packet */ |
360 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
361 | /* Maximum number of gateways (include room for one noop) */ | ||
362 | #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t))) | ||
363 | /* loose source route gateway list (including room for final destination) */ | ||
364 | uint32_t gwlist[NGATEWAYS + 1]; | ||
365 | #endif | ||
366 | }; | 355 | }; |
367 | 356 | ||
368 | #define G (*ptr_to_globals) | 357 | #define G (*ptr_to_globals) |
@@ -374,14 +363,11 @@ struct globals { | |||
374 | #define ident (G.ident ) | 363 | #define ident (G.ident ) |
375 | #define port (G.port ) | 364 | #define port (G.port ) |
376 | #define waittime (G.waittime ) | 365 | #define waittime (G.waittime ) |
377 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
378 | # define optlen (G.optlen ) | ||
379 | #endif | ||
380 | #define recv_pkt (G.recv_pkt ) | 366 | #define recv_pkt (G.recv_pkt ) |
381 | #define gwlist (G.gwlist ) | 367 | #define gwlist (G.gwlist ) |
382 | #define INIT_G() do { \ | 368 | #define INIT_G() do { \ |
383 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 369 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
384 | port = 32768 + 666; \ | 370 | port = 33434; \ |
385 | waittime = 5; \ | 371 | waittime = 5; \ |
386 | } while (0) | 372 | } while (0) |
387 | 373 | ||
@@ -421,7 +407,7 @@ send_probe(int seq, int ttl) | |||
421 | /* Payload */ | 407 | /* Payload */ |
422 | #if ENABLE_TRACEROUTE6 | 408 | #if ENABLE_TRACEROUTE6 |
423 | if (dest_lsa->u.sa.sa_family == AF_INET6) { | 409 | if (dest_lsa->u.sa.sa_family == AF_INET6) { |
424 | struct outdata6_t *pkt = (struct outdata6_t *) outip; | 410 | struct outdata6_t *pkt = (struct outdata6_t *) outdata; |
425 | pkt->ident6 = htonl(ident); | 411 | pkt->ident6 = htonl(ident); |
426 | pkt->seq6 = htonl(seq); | 412 | pkt->seq6 = htonl(seq); |
427 | /*gettimeofday(&pkt->tv, &tz);*/ | 413 | /*gettimeofday(&pkt->tv, &tz);*/ |
@@ -438,8 +424,10 @@ send_probe(int seq, int ttl) | |||
438 | 424 | ||
439 | /* Always calculate checksum for icmp packets */ | 425 | /* Always calculate checksum for icmp packets */ |
440 | outicmp->icmp_cksum = 0; | 426 | outicmp->icmp_cksum = 0; |
441 | outicmp->icmp_cksum = inet_cksum((uint16_t *)outicmp, | 427 | outicmp->icmp_cksum = inet_cksum( |
442 | packlen - (sizeof(*outip) + optlen)); | 428 | (uint16_t *)outicmp, |
429 | ((char*)outip + packlen) - (char*)outicmp | ||
430 | ); | ||
443 | if (outicmp->icmp_cksum == 0) | 431 | if (outicmp->icmp_cksum == 0) |
444 | outicmp->icmp_cksum = 0xffff; | 432 | outicmp->icmp_cksum = 0xffff; |
445 | } | 433 | } |
@@ -471,13 +459,12 @@ send_probe(int seq, int ttl) | |||
471 | } | 459 | } |
472 | #endif | 460 | #endif |
473 | 461 | ||
462 | out = outdata; | ||
474 | #if ENABLE_TRACEROUTE6 | 463 | #if ENABLE_TRACEROUTE6 |
475 | if (dest_lsa->u.sa.sa_family == AF_INET6) { | 464 | if (dest_lsa->u.sa.sa_family == AF_INET6) { |
476 | res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl); | 465 | res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl); |
477 | if (res != 0) | 466 | if (res != 0) |
478 | bb_perror_msg_and_die("setsockopt(%s) %d", "UNICAST_HOPS", ttl); | 467 | bb_perror_msg_and_die("setsockopt(%s) %d", "UNICAST_HOPS", ttl); |
479 | out = outip; | ||
480 | len = packlen; | ||
481 | } else | 468 | } else |
482 | #endif | 469 | #endif |
483 | { | 470 | { |
@@ -486,15 +473,14 @@ send_probe(int seq, int ttl) | |||
486 | if (res != 0) | 473 | if (res != 0) |
487 | bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl); | 474 | bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl); |
488 | #endif | 475 | #endif |
489 | out = outicmp; | 476 | if (option_mask32 & OPT_USE_ICMP) |
490 | len = packlen - sizeof(*outip); | 477 | out = outicmp; |
491 | if (!(option_mask32 & OPT_USE_ICMP)) { | ||
492 | out = outdata; | ||
493 | len -= sizeof(*outudp); | ||
494 | set_nport(&dest_lsa->u.sa, htons(port + seq)); | ||
495 | } | ||
496 | } | 478 | } |
497 | 479 | ||
480 | if (!(option_mask32 & OPT_USE_ICMP)) { | ||
481 | set_nport(&dest_lsa->u.sa, htons(port + seq)); | ||
482 | } | ||
483 | len = ((char*)outip + packlen) - (char*)out; | ||
498 | res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len); | 484 | res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len); |
499 | if (res != len) | 485 | if (res != len) |
500 | bb_error_msg("sent %d octets, ret=%d", len, res); | 486 | bb_error_msg("sent %d octets, ret=%d", len, res); |
@@ -801,10 +787,6 @@ common_traceroute_main(int op, char **argv) | |||
801 | char *pausemsecs_str; | 787 | char *pausemsecs_str; |
802 | char *first_ttl_str; | 788 | char *first_ttl_str; |
803 | char *dest_str; | 789 | char *dest_str; |
804 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
805 | llist_t *source_route_list = NULL; | ||
806 | int lsrr = 0; | ||
807 | #endif | ||
808 | #if ENABLE_TRACEROUTE6 | 790 | #if ENABLE_TRACEROUTE6 |
809 | sa_family_t af; | 791 | sa_family_t af; |
810 | #else | 792 | #else |
@@ -823,9 +805,6 @@ common_traceroute_main(int op, char **argv) | |||
823 | op |= getopt32(argv, OPT_STRING | 805 | op |= getopt32(argv, OPT_STRING |
824 | , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str | 806 | , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str |
825 | , &source, &waittime_str, &pausemsecs_str, &first_ttl_str | 807 | , &source, &waittime_str, &pausemsecs_str, &first_ttl_str |
826 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
827 | , &source_route_list | ||
828 | #endif | ||
829 | ); | 808 | ); |
830 | argv += optind; | 809 | argv += optind; |
831 | 810 | ||
@@ -858,26 +837,14 @@ common_traceroute_main(int op, char **argv) | |||
858 | if (op & OPT_FIRST_TTL) | 837 | if (op & OPT_FIRST_TTL) |
859 | first_ttl = xatou_range(first_ttl_str, 1, max_ttl); | 838 | first_ttl = xatou_range(first_ttl_str, 1, max_ttl); |
860 | 839 | ||
861 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE | ||
862 | if (source_route_list) { | ||
863 | while (source_route_list) { | ||
864 | len_and_sockaddr *lsa; | ||
865 | |||
866 | if (lsrr >= NGATEWAYS) | ||
867 | bb_error_msg_and_die("no more than %d gateways", NGATEWAYS); | ||
868 | lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET); | ||
869 | gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr; | ||
870 | free(lsa); | ||
871 | ++lsrr; | ||
872 | } | ||
873 | optlen = (lsrr + 1) * sizeof(gwlist[0]); | ||
874 | } | ||
875 | #endif | ||
876 | |||
877 | /* Process destination and optional packet size */ | 840 | /* Process destination and optional packet size */ |
878 | minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen; | 841 | minpacket = sizeof(struct ip) |
842 | + SIZEOF_ICMP_HDR | ||
843 | + sizeof(struct outdata_t); | ||
879 | if (!(op & OPT_USE_ICMP)) | 844 | if (!(op & OPT_USE_ICMP)) |
880 | minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR; | 845 | minpacket = sizeof(struct ip) |
846 | + sizeof(struct udphdr) | ||
847 | + sizeof(struct outdata_t); | ||
881 | #if ENABLE_TRACEROUTE6 | 848 | #if ENABLE_TRACEROUTE6 |
882 | af = AF_UNSPEC; | 849 | af = AF_UNSPEC; |
883 | if (op & OPT_IPV4) | 850 | if (op & OPT_IPV4) |
@@ -887,7 +854,9 @@ common_traceroute_main(int op, char **argv) | |||
887 | dest_lsa = xhost_and_af2sockaddr(argv[0], port, af); | 854 | dest_lsa = xhost_and_af2sockaddr(argv[0], port, af); |
888 | af = dest_lsa->u.sa.sa_family; | 855 | af = dest_lsa->u.sa.sa_family; |
889 | if (af == AF_INET6) | 856 | if (af == AF_INET6) |
890 | minpacket = sizeof(struct outdata6_t); | 857 | minpacket = sizeof(struct ip6_hdr) |
858 | + sizeof(struct udphdr) | ||
859 | + sizeof(struct outdata6_t); | ||
891 | #else | 860 | #else |
892 | dest_lsa = xhost2sockaddr(argv[0], port); | 861 | dest_lsa = xhost2sockaddr(argv[0], port); |
893 | #endif | 862 | #endif |
@@ -932,31 +901,6 @@ common_traceroute_main(int op, char **argv) | |||
932 | xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock); | 901 | xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock); |
933 | else | 902 | else |
934 | xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock); | 903 | xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock); |
935 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS | ||
936 | if (lsrr > 0) { | ||
937 | unsigned char optlist[MAX_IPOPTLEN]; | ||
938 | unsigned size; | ||
939 | |||
940 | /* final hop */ | ||
941 | gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; | ||
942 | ++lsrr; | ||
943 | |||
944 | /* force 4 byte alignment */ | ||
945 | optlist[0] = IPOPT_NOP; | ||
946 | /* loose source route option */ | ||
947 | optlist[1] = IPOPT_LSRR; | ||
948 | size = lsrr * sizeof(gwlist[0]); | ||
949 | optlist[2] = size + 3; | ||
950 | /* pointer to LSRR addresses */ | ||
951 | optlist[3] = IPOPT_MINOFF; | ||
952 | memcpy(optlist + 4, gwlist, size); | ||
953 | |||
954 | if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, | ||
955 | (char *)optlist, size + sizeof(gwlist[0])) < 0) { | ||
956 | bb_perror_msg_and_die("IP_OPTIONS"); | ||
957 | } | ||
958 | } | ||
959 | #endif | ||
960 | } | 904 | } |
961 | 905 | ||
962 | #ifdef SO_SNDBUF | 906 | #ifdef SO_SNDBUF |
@@ -984,7 +928,7 @@ common_traceroute_main(int op, char **argv) | |||
984 | 928 | ||
985 | ident = getpid(); | 929 | ident = getpid(); |
986 | 930 | ||
987 | if (af == AF_INET) { | 931 | if (!ENABLE_TRACEROUTE6 || af == AF_INET) { |
988 | if (op & OPT_USE_ICMP) { | 932 | if (op & OPT_USE_ICMP) { |
989 | ident |= 0x8000; | 933 | ident |= 0x8000; |
990 | outicmp->icmp_type = ICMP_ECHO; | 934 | outicmp->icmp_type = ICMP_ECHO; |
@@ -994,6 +938,14 @@ common_traceroute_main(int op, char **argv) | |||
994 | outdata = (struct outdata_t *)(outudp + 1); | 938 | outdata = (struct outdata_t *)(outudp + 1); |
995 | } | 939 | } |
996 | } | 940 | } |
941 | #if ENABLE_TRACEROUTE6 | ||
942 | if (af == AF_INET6) { | ||
943 | outdata = (void*)((char*)outip | ||
944 | + sizeof(struct ip6_hdr) | ||
945 | + sizeof(struct udphdr) | ||
946 | ); | ||
947 | } | ||
948 | #endif | ||
997 | 949 | ||
998 | if (op & OPT_DEVICE) /* hmm, do we need error check? */ | 950 | if (op & OPT_DEVICE) /* hmm, do we need error check? */ |
999 | setsockopt_bindtodevice(sndsock, device); | 951 | setsockopt_bindtodevice(sndsock, device); |
diff --git a/networking/udhcp/Kbuild.src b/networking/udhcp/Kbuild.src index b8767baea..5ea77df06 100644 --- a/networking/udhcp/Kbuild.src +++ b/networking/udhcp/Kbuild.src | |||
@@ -13,7 +13,7 @@ lib-$(CONFIG_UDHCPC) += common.o packet.o signalpipe.o socket.o | |||
13 | lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o | 13 | lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o |
14 | 14 | ||
15 | lib-$(CONFIG_UDHCPC) += dhcpc.o | 15 | lib-$(CONFIG_UDHCPC) += dhcpc.o |
16 | lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o files.o leases.o static_leases.o | 16 | lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o |
17 | lib-$(CONFIG_DUMPLEASES) += dumpleases.o | 17 | lib-$(CONFIG_DUMPLEASES) += dumpleases.o |
18 | lib-$(CONFIG_DHCPRELAY) += dhcprelay.o | 18 | lib-$(CONFIG_DHCPRELAY) += dhcprelay.o |
19 | 19 | ||
diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c index c98027316..215d023ce 100644 --- a/networking/udhcp/arpping.c +++ b/networking/udhcp/arpping.c | |||
@@ -9,7 +9,6 @@ | |||
9 | #include <net/if_arp.h> | 9 | #include <net/if_arp.h> |
10 | 10 | ||
11 | #include "common.h" | 11 | #include "common.h" |
12 | #include "dhcpd.h" | ||
13 | 12 | ||
14 | struct arpMsg { | 13 | struct arpMsg { |
15 | /* Ethernet header */ | 14 | /* Ethernet header */ |
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 6ff040d9e..ddf3412a0 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c | |||
@@ -800,10 +800,15 @@ static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *ou | |||
800 | ) { | 800 | ) { |
801 | bb_error_msg("unicasting a release"); | 801 | bb_error_msg("unicasting a release"); |
802 | send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */ | 802 | send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */ |
803 | d6_run_script(NULL, "deconfig"); | ||
804 | } | 803 | } |
805 | bb_error_msg("entering released state"); | 804 | bb_error_msg("entering released state"); |
806 | 805 | /* | |
806 | * We can be here on: SIGUSR2, | ||
807 | * or on exit (SIGTERM) and -R "release on quit" is specified. | ||
808 | * Users requested to be notified in all cases, even if not in one | ||
809 | * of the states above. | ||
810 | */ | ||
811 | d6_run_script(NULL, "deconfig"); | ||
807 | change_listen_mode(LISTEN_NONE); | 812 | change_listen_mode(LISTEN_NONE); |
808 | state = RELEASED; | 813 | state = RELEASED; |
809 | } | 814 | } |
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 8a16e987d..bef73277a 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c | |||
@@ -1129,9 +1129,15 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip) | |||
1129 | bb_error_msg("unicasting a release of %s to %s", | 1129 | bb_error_msg("unicasting a release of %s to %s", |
1130 | inet_ntoa(temp_addr), buffer); | 1130 | inet_ntoa(temp_addr), buffer); |
1131 | send_release(server_addr, requested_ip); /* unicast */ | 1131 | send_release(server_addr, requested_ip); /* unicast */ |
1132 | udhcp_run_script(NULL, "deconfig"); | ||
1133 | } | 1132 | } |
1134 | bb_error_msg("entering released state"); | 1133 | bb_error_msg("entering released state"); |
1134 | /* | ||
1135 | * We can be here on: SIGUSR2, | ||
1136 | * or on exit (SIGTERM) and -R "release on quit" is specified. | ||
1137 | * Users requested to be notified in all cases, even if not in one | ||
1138 | * of the states above. | ||
1139 | */ | ||
1140 | udhcp_run_script(NULL, "deconfig"); | ||
1135 | 1141 | ||
1136 | change_listen_mode(LISTEN_NONE); | 1142 | change_listen_mode(LISTEN_NONE); |
1137 | state = RELEASED; | 1143 | state = RELEASED; |
@@ -1813,9 +1819,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
1813 | temp = udhcp_get_option(&packet, DHCP_SERVER_ID); | 1819 | temp = udhcp_get_option(&packet, DHCP_SERVER_ID); |
1814 | if (!temp) { | 1820 | if (!temp) { |
1815 | non_matching_svid: | 1821 | non_matching_svid: |
1816 | log1("%s with wrong server ID, ignoring packet", | 1822 | log1("received DHCP NAK with wrong" |
1817 | "Received DHCP NAK" | 1823 | " server ID, ignoring packet"); |
1818 | ); | ||
1819 | continue; | 1824 | continue; |
1820 | } | 1825 | } |
1821 | move_from_unaligned32(svid, temp); | 1826 | move_from_unaligned32(svid, temp); |
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index e93a9f1da..56116d01f 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c | |||
@@ -33,11 +33,507 @@ | |||
33 | //usage: "\n -P N Use port N (default 67)" | 33 | //usage: "\n -P N Use port N (default 67)" |
34 | //usage: ) | 34 | //usage: ) |
35 | 35 | ||
36 | #include <netinet/ether.h> | ||
36 | #include <syslog.h> | 37 | #include <syslog.h> |
37 | #include "common.h" | 38 | #include "common.h" |
38 | #include "dhcpc.h" | 39 | #include "dhcpc.h" |
39 | #include "dhcpd.h" | 40 | #include "dhcpd.h" |
40 | 41 | ||
42 | /* globals */ | ||
43 | struct dyn_lease *g_leases; | ||
44 | /* struct server_config_t server_config is in bb_common_bufsiz1 */ | ||
45 | |||
46 | /* Takes the address of the pointer to the static_leases linked list, | ||
47 | * address to a 6 byte mac address, | ||
48 | * 4 byte IP address */ | ||
49 | static void add_static_lease(struct static_lease **st_lease_pp, | ||
50 | uint8_t *mac, | ||
51 | uint32_t nip) | ||
52 | { | ||
53 | struct static_lease *st_lease; | ||
54 | |||
55 | /* Find the tail of the list */ | ||
56 | while ((st_lease = *st_lease_pp) != NULL) { | ||
57 | st_lease_pp = &st_lease->next; | ||
58 | } | ||
59 | |||
60 | /* Add new node */ | ||
61 | *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease)); | ||
62 | memcpy(st_lease->mac, mac, 6); | ||
63 | st_lease->nip = nip; | ||
64 | /*st_lease->next = NULL;*/ | ||
65 | } | ||
66 | |||
67 | /* Find static lease IP by mac */ | ||
68 | static uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *mac) | ||
69 | { | ||
70 | while (st_lease) { | ||
71 | if (memcmp(st_lease->mac, mac, 6) == 0) | ||
72 | return st_lease->nip; | ||
73 | st_lease = st_lease->next; | ||
74 | } | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int is_nip_reserved(struct static_lease *st_lease, uint32_t nip) | ||
80 | { | ||
81 | while (st_lease) { | ||
82 | if (st_lease->nip == nip) | ||
83 | return 1; | ||
84 | st_lease = st_lease->next; | ||
85 | } | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 | ||
91 | /* Print out static leases just to check what's going on */ | ||
92 | /* Takes the address of the pointer to the static_leases linked list */ | ||
93 | static void log_static_leases(struct static_lease **st_lease_pp) | ||
94 | { | ||
95 | struct static_lease *cur; | ||
96 | |||
97 | if (dhcp_verbose < 2) | ||
98 | return; | ||
99 | |||
100 | cur = *st_lease_pp; | ||
101 | while (cur) { | ||
102 | bb_error_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x", | ||
103 | cur->mac[0], cur->mac[1], cur->mac[2], | ||
104 | cur->mac[3], cur->mac[4], cur->mac[5], | ||
105 | cur->nip | ||
106 | ); | ||
107 | cur = cur->next; | ||
108 | } | ||
109 | } | ||
110 | #else | ||
111 | # define log_static_leases(st_lease_pp) ((void)0) | ||
112 | #endif | ||
113 | |||
114 | /* Find the oldest expired lease, NULL if there are no expired leases */ | ||
115 | static struct dyn_lease *oldest_expired_lease(void) | ||
116 | { | ||
117 | struct dyn_lease *oldest_lease = NULL; | ||
118 | leasetime_t oldest_time = time(NULL); | ||
119 | unsigned i; | ||
120 | |||
121 | /* Unexpired leases have g_leases[i].expires >= current time | ||
122 | * and therefore can't ever match */ | ||
123 | for (i = 0; i < server_config.max_leases; i++) { | ||
124 | if (g_leases[i].expires == 0 /* empty entry */ | ||
125 | || g_leases[i].expires < oldest_time | ||
126 | ) { | ||
127 | oldest_time = g_leases[i].expires; | ||
128 | oldest_lease = &g_leases[i]; | ||
129 | } | ||
130 | } | ||
131 | return oldest_lease; | ||
132 | } | ||
133 | |||
134 | /* Clear out all leases with matching nonzero chaddr OR yiaddr. | ||
135 | * If chaddr == NULL, this is a conflict lease. | ||
136 | */ | ||
137 | static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr) | ||
138 | { | ||
139 | unsigned i; | ||
140 | |||
141 | for (i = 0; i < server_config.max_leases; i++) { | ||
142 | if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0) | ||
143 | || (yiaddr && g_leases[i].lease_nip == yiaddr) | ||
144 | ) { | ||
145 | memset(&g_leases[i], 0, sizeof(g_leases[i])); | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | /* Add a lease into the table, clearing out any old ones. | ||
151 | * If chaddr == NULL, this is a conflict lease. | ||
152 | */ | ||
153 | static struct dyn_lease *add_lease( | ||
154 | const uint8_t *chaddr, uint32_t yiaddr, | ||
155 | leasetime_t leasetime, | ||
156 | const char *hostname, int hostname_len) | ||
157 | { | ||
158 | struct dyn_lease *oldest; | ||
159 | |||
160 | /* clean out any old ones */ | ||
161 | clear_leases(chaddr, yiaddr); | ||
162 | |||
163 | oldest = oldest_expired_lease(); | ||
164 | |||
165 | if (oldest) { | ||
166 | memset(oldest, 0, sizeof(*oldest)); | ||
167 | if (hostname) { | ||
168 | char *p; | ||
169 | |||
170 | hostname_len++; /* include NUL */ | ||
171 | if (hostname_len > sizeof(oldest->hostname)) | ||
172 | hostname_len = sizeof(oldest->hostname); | ||
173 | p = safe_strncpy(oldest->hostname, hostname, hostname_len); | ||
174 | /* | ||
175 | * Sanitization (s/bad_char/./g). | ||
176 | * The intent is not to allow only "DNS-valid" hostnames, | ||
177 | * but merely make dumpleases output safe for shells to use. | ||
178 | * We accept "0-9A-Za-z._-", all other chars turn to dots. | ||
179 | */ | ||
180 | while (*p) { | ||
181 | if (!isalnum(*p) && *p != '-' && *p != '_') | ||
182 | *p = '.'; | ||
183 | p++; | ||
184 | } | ||
185 | } | ||
186 | if (chaddr) | ||
187 | memcpy(oldest->lease_mac, chaddr, 6); | ||
188 | oldest->lease_nip = yiaddr; | ||
189 | oldest->expires = time(NULL) + leasetime; | ||
190 | } | ||
191 | |||
192 | return oldest; | ||
193 | } | ||
194 | |||
195 | /* True if a lease has expired */ | ||
196 | static int is_expired_lease(struct dyn_lease *lease) | ||
197 | { | ||
198 | return (lease->expires < (leasetime_t) time(NULL)); | ||
199 | } | ||
200 | |||
201 | /* Find the first lease that matches MAC, NULL if no match */ | ||
202 | static struct dyn_lease *find_lease_by_mac(const uint8_t *mac) | ||
203 | { | ||
204 | unsigned i; | ||
205 | |||
206 | for (i = 0; i < server_config.max_leases; i++) | ||
207 | if (memcmp(g_leases[i].lease_mac, mac, 6) == 0) | ||
208 | return &g_leases[i]; | ||
209 | |||
210 | return NULL; | ||
211 | } | ||
212 | |||
213 | /* Find the first lease that matches IP, NULL is no match */ | ||
214 | static struct dyn_lease *find_lease_by_nip(uint32_t nip) | ||
215 | { | ||
216 | unsigned i; | ||
217 | |||
218 | for (i = 0; i < server_config.max_leases; i++) | ||
219 | if (g_leases[i].lease_nip == nip) | ||
220 | return &g_leases[i]; | ||
221 | |||
222 | return NULL; | ||
223 | } | ||
224 | |||
225 | /* Check if the IP is taken; if it is, add it to the lease table */ | ||
226 | static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms) | ||
227 | { | ||
228 | struct in_addr temp; | ||
229 | int r; | ||
230 | |||
231 | r = arpping(nip, safe_mac, | ||
232 | server_config.server_nip, | ||
233 | server_config.server_mac, | ||
234 | server_config.interface, | ||
235 | arpping_ms); | ||
236 | if (r) | ||
237 | return r; | ||
238 | |||
239 | temp.s_addr = nip; | ||
240 | bb_error_msg("%s belongs to someone, reserving it for %u seconds", | ||
241 | inet_ntoa(temp), (unsigned)server_config.conflict_time); | ||
242 | add_lease(NULL, nip, server_config.conflict_time, NULL, 0); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | /* Find a new usable (we think) address */ | ||
247 | static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms) | ||
248 | { | ||
249 | uint32_t addr; | ||
250 | struct dyn_lease *oldest_lease = NULL; | ||
251 | |||
252 | #if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC | ||
253 | uint32_t stop; | ||
254 | unsigned i, hash; | ||
255 | |||
256 | /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good | ||
257 | * dispersal even with similarly-valued "strings". | ||
258 | */ | ||
259 | hash = 0; | ||
260 | for (i = 0; i < 6; i++) | ||
261 | hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash; | ||
262 | |||
263 | /* pick a seed based on hwaddr then iterate until we find a free address. */ | ||
264 | addr = server_config.start_ip | ||
265 | + (hash % (1 + server_config.end_ip - server_config.start_ip)); | ||
266 | stop = addr; | ||
267 | #else | ||
268 | addr = server_config.start_ip; | ||
269 | #define stop (server_config.end_ip + 1) | ||
270 | #endif | ||
271 | do { | ||
272 | uint32_t nip; | ||
273 | struct dyn_lease *lease; | ||
274 | |||
275 | /* ie, 192.168.55.0 */ | ||
276 | if ((addr & 0xff) == 0) | ||
277 | goto next_addr; | ||
278 | /* ie, 192.168.55.255 */ | ||
279 | if ((addr & 0xff) == 0xff) | ||
280 | goto next_addr; | ||
281 | nip = htonl(addr); | ||
282 | /* skip our own address */ | ||
283 | if (nip == server_config.server_nip) | ||
284 | goto next_addr; | ||
285 | /* is this a static lease addr? */ | ||
286 | if (is_nip_reserved(server_config.static_leases, nip)) | ||
287 | goto next_addr; | ||
288 | |||
289 | lease = find_lease_by_nip(nip); | ||
290 | if (!lease) { | ||
291 | //TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping! | ||
292 | if (nobody_responds_to_arp(nip, safe_mac, arpping_ms)) | ||
293 | return nip; | ||
294 | } else { | ||
295 | if (!oldest_lease || lease->expires < oldest_lease->expires) | ||
296 | oldest_lease = lease; | ||
297 | } | ||
298 | |||
299 | next_addr: | ||
300 | addr++; | ||
301 | #if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC | ||
302 | if (addr > server_config.end_ip) | ||
303 | addr = server_config.start_ip; | ||
304 | #endif | ||
305 | } while (addr != stop); | ||
306 | |||
307 | if (oldest_lease | ||
308 | && is_expired_lease(oldest_lease) | ||
309 | && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms) | ||
310 | ) { | ||
311 | return oldest_lease->lease_nip; | ||
312 | } | ||
313 | |||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | /* On these functions, make sure your datatype matches */ | ||
318 | static int FAST_FUNC read_str(const char *line, void *arg) | ||
319 | { | ||
320 | char **dest = arg; | ||
321 | |||
322 | free(*dest); | ||
323 | *dest = xstrdup(line); | ||
324 | return 1; | ||
325 | } | ||
326 | |||
327 | static int FAST_FUNC read_u32(const char *line, void *arg) | ||
328 | { | ||
329 | *(uint32_t*)arg = bb_strtou32(line, NULL, 10); | ||
330 | return errno == 0; | ||
331 | } | ||
332 | |||
333 | static int FAST_FUNC read_staticlease(const char *const_line, void *arg) | ||
334 | { | ||
335 | char *line; | ||
336 | char *mac_string; | ||
337 | char *ip_string; | ||
338 | struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */ | ||
339 | uint32_t nip; | ||
340 | |||
341 | /* Read mac */ | ||
342 | line = (char *) const_line; | ||
343 | mac_string = strtok_r(line, " \t", &line); | ||
344 | if (!mac_string || !ether_aton_r(mac_string, &mac_bytes)) | ||
345 | return 0; | ||
346 | |||
347 | /* Read ip */ | ||
348 | ip_string = strtok_r(NULL, " \t", &line); | ||
349 | if (!ip_string || !udhcp_str2nip(ip_string, &nip)) | ||
350 | return 0; | ||
351 | |||
352 | add_static_lease(arg, (uint8_t*) &mac_bytes, nip); | ||
353 | |||
354 | log_static_leases(arg); | ||
355 | |||
356 | return 1; | ||
357 | } | ||
358 | |||
359 | struct config_keyword { | ||
360 | const char *keyword; | ||
361 | int (*handler)(const char *line, void *var) FAST_FUNC; | ||
362 | unsigned ofs; | ||
363 | const char *def; | ||
364 | }; | ||
365 | |||
366 | #define OFS(field) offsetof(struct server_config_t, field) | ||
367 | |||
368 | static const struct config_keyword keywords[] = { | ||
369 | /* keyword handler variable address default */ | ||
370 | {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"}, | ||
371 | {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"}, | ||
372 | {"interface" , read_str , OFS(interface ), "eth0"}, | ||
373 | /* Avoid "max_leases value not sane" warning by setting default | ||
374 | * to default_end_ip - default_start_ip + 1: */ | ||
375 | {"max_leases" , read_u32 , OFS(max_leases ), "235"}, | ||
376 | {"auto_time" , read_u32 , OFS(auto_time ), "7200"}, | ||
377 | {"decline_time" , read_u32 , OFS(decline_time ), "3600"}, | ||
378 | {"conflict_time", read_u32 , OFS(conflict_time), "3600"}, | ||
379 | {"offer_time" , read_u32 , OFS(offer_time ), "60"}, | ||
380 | {"min_lease" , read_u32 , OFS(min_lease_sec), "60"}, | ||
381 | {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE}, | ||
382 | {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"}, | ||
383 | {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"}, | ||
384 | /* keywords with no defaults must be last! */ | ||
385 | {"option" , udhcp_str2optset, OFS(options ), ""}, | ||
386 | {"opt" , udhcp_str2optset, OFS(options ), ""}, | ||
387 | {"notify_file" , read_str , OFS(notify_file ), NULL}, | ||
388 | {"sname" , read_str , OFS(sname ), NULL}, | ||
389 | {"boot_file" , read_str , OFS(boot_file ), NULL}, | ||
390 | {"static_lease" , read_staticlease, OFS(static_leases), ""}, | ||
391 | }; | ||
392 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; | ||
393 | |||
394 | static NOINLINE void read_config(const char *file) | ||
395 | { | ||
396 | parser_t *parser; | ||
397 | const struct config_keyword *k; | ||
398 | unsigned i; | ||
399 | char *token[2]; | ||
400 | |||
401 | for (i = 0; i < KWS_WITH_DEFAULTS; i++) | ||
402 | keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs); | ||
403 | |||
404 | parser = config_open(file); | ||
405 | while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { | ||
406 | for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) { | ||
407 | if (strcasecmp(token[0], k->keyword) == 0) { | ||
408 | if (!k->handler(token[1], (char*)&server_config + k->ofs)) { | ||
409 | bb_error_msg("can't parse line %u in %s", | ||
410 | parser->lineno, file); | ||
411 | /* reset back to the default value */ | ||
412 | k->handler(k->def, (char*)&server_config + k->ofs); | ||
413 | } | ||
414 | break; | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | config_close(parser); | ||
419 | |||
420 | server_config.start_ip = ntohl(server_config.start_ip); | ||
421 | server_config.end_ip = ntohl(server_config.end_ip); | ||
422 | } | ||
423 | |||
424 | static void write_leases(void) | ||
425 | { | ||
426 | int fd; | ||
427 | unsigned i; | ||
428 | leasetime_t curr; | ||
429 | int64_t written_at; | ||
430 | |||
431 | fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); | ||
432 | if (fd < 0) | ||
433 | return; | ||
434 | |||
435 | curr = written_at = time(NULL); | ||
436 | |||
437 | written_at = SWAP_BE64(written_at); | ||
438 | full_write(fd, &written_at, sizeof(written_at)); | ||
439 | |||
440 | for (i = 0; i < server_config.max_leases; i++) { | ||
441 | leasetime_t tmp_time; | ||
442 | |||
443 | if (g_leases[i].lease_nip == 0) | ||
444 | continue; | ||
445 | |||
446 | /* Screw with the time in the struct, for easier writing */ | ||
447 | tmp_time = g_leases[i].expires; | ||
448 | |||
449 | g_leases[i].expires -= curr; | ||
450 | if ((signed_leasetime_t) g_leases[i].expires < 0) | ||
451 | g_leases[i].expires = 0; | ||
452 | g_leases[i].expires = htonl(g_leases[i].expires); | ||
453 | |||
454 | /* No error check. If the file gets truncated, | ||
455 | * we lose some leases on restart. Oh well. */ | ||
456 | full_write(fd, &g_leases[i], sizeof(g_leases[i])); | ||
457 | |||
458 | /* Then restore it when done */ | ||
459 | g_leases[i].expires = tmp_time; | ||
460 | } | ||
461 | close(fd); | ||
462 | |||
463 | if (server_config.notify_file) { | ||
464 | char *argv[3]; | ||
465 | argv[0] = server_config.notify_file; | ||
466 | argv[1] = server_config.lease_file; | ||
467 | argv[2] = NULL; | ||
468 | spawn_and_wait(argv); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | static NOINLINE void read_leases(const char *file) | ||
473 | { | ||
474 | struct dyn_lease lease; | ||
475 | int64_t written_at, time_passed; | ||
476 | int fd; | ||
477 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 | ||
478 | unsigned i = 0; | ||
479 | #endif | ||
480 | |||
481 | fd = open_or_warn(file, O_RDONLY); | ||
482 | if (fd < 0) | ||
483 | return; | ||
484 | |||
485 | if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at)) | ||
486 | goto ret; | ||
487 | written_at = SWAP_BE64(written_at); | ||
488 | |||
489 | time_passed = time(NULL) - written_at; | ||
490 | /* Strange written_at, or lease file from old version of udhcpd | ||
491 | * which had no "written_at" field? */ | ||
492 | if ((uint64_t)time_passed > 12 * 60 * 60) | ||
493 | goto ret; | ||
494 | |||
495 | while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) { | ||
496 | uint32_t y = ntohl(lease.lease_nip); | ||
497 | if (y >= server_config.start_ip && y <= server_config.end_ip) { | ||
498 | signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed; | ||
499 | uint32_t static_nip; | ||
500 | |||
501 | if (expires <= 0) | ||
502 | /* We keep expired leases: add_lease() will add | ||
503 | * a lease with 0 seconds remaining. | ||
504 | * Fewer IP address changes this way for mass reboot scenario. | ||
505 | */ | ||
506 | expires = 0; | ||
507 | |||
508 | /* Check if there is a different static lease for this IP or MAC */ | ||
509 | static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac); | ||
510 | if (static_nip) { | ||
511 | /* NB: we do not add lease even if static_nip == lease.lease_nip. | ||
512 | */ | ||
513 | continue; | ||
514 | } | ||
515 | if (is_nip_reserved(server_config.static_leases, lease.lease_nip)) | ||
516 | continue; | ||
517 | |||
518 | /* NB: add_lease takes "relative time", IOW, | ||
519 | * lease duration, not lease deadline. */ | ||
520 | if (add_lease(lease.lease_mac, lease.lease_nip, | ||
521 | expires, | ||
522 | lease.hostname, sizeof(lease.hostname) | ||
523 | ) == 0 | ||
524 | ) { | ||
525 | bb_error_msg("too many leases while loading %s", file); | ||
526 | break; | ||
527 | } | ||
528 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 | ||
529 | i++; | ||
530 | #endif | ||
531 | } | ||
532 | } | ||
533 | log1("read %d leases", i); | ||
534 | ret: | ||
535 | close(fd); | ||
536 | } | ||
41 | 537 | ||
42 | /* Send a packet to a specific mac address and ip address by creating our own ip packet */ | 538 | /* Send a packet to a specific mac address and ip address by creating our own ip packet */ |
43 | static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast) | 539 | static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast) |
@@ -290,12 +786,6 @@ static NOINLINE void send_inform(struct dhcp_packet *oldpacket) | |||
290 | send_packet(&packet, /*force_bcast:*/ 0); | 786 | send_packet(&packet, /*force_bcast:*/ 0); |
291 | } | 787 | } |
292 | 788 | ||
293 | |||
294 | /* globals */ | ||
295 | struct dyn_lease *g_leases; | ||
296 | /* struct server_config_t server_config is in bb_common_bufsiz1 */ | ||
297 | |||
298 | |||
299 | int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 789 | int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
300 | int udhcpd_main(int argc UNUSED_PARAM, char **argv) | 790 | int udhcpd_main(int argc UNUSED_PARAM, char **argv) |
301 | { | 791 | { |
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h index 9dd5bef9e..b8f96b029 100644 --- a/networking/udhcp/dhcpd.h +++ b/networking/udhcp/dhcpd.h | |||
@@ -90,39 +90,6 @@ struct dyn_lease { | |||
90 | /* total size is a multiply of 4 */ | 90 | /* total size is a multiply of 4 */ |
91 | } PACKED; | 91 | } PACKED; |
92 | 92 | ||
93 | extern struct dyn_lease *g_leases; | ||
94 | |||
95 | struct dyn_lease *add_lease( | ||
96 | const uint8_t *chaddr, uint32_t yiaddr, | ||
97 | leasetime_t leasetime, | ||
98 | const char *hostname, int hostname_len | ||
99 | ) FAST_FUNC; | ||
100 | int is_expired_lease(struct dyn_lease *lease) FAST_FUNC; | ||
101 | struct dyn_lease *find_lease_by_mac(const uint8_t *mac) FAST_FUNC; | ||
102 | struct dyn_lease *find_lease_by_nip(uint32_t nip) FAST_FUNC; | ||
103 | uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms) FAST_FUNC; | ||
104 | |||
105 | |||
106 | /* Config file parser will pass static lease info to this function | ||
107 | * which will add it to a data structure that can be searched later */ | ||
108 | void add_static_lease(struct static_lease **st_lease_pp, uint8_t *mac, uint32_t nip) FAST_FUNC; | ||
109 | /* Find static lease IP by mac */ | ||
110 | uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *arg) FAST_FUNC; | ||
111 | /* Check to see if an IP is reserved as a static IP */ | ||
112 | int is_nip_reserved(struct static_lease *st_lease, uint32_t nip) FAST_FUNC; | ||
113 | /* Print out static leases just to check what's going on (debug code) */ | ||
114 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 | ||
115 | void log_static_leases(struct static_lease **st_lease_pp) FAST_FUNC; | ||
116 | #else | ||
117 | # define log_static_leases(st_lease_pp) ((void)0) | ||
118 | #endif | ||
119 | |||
120 | |||
121 | void read_config(const char *file) FAST_FUNC; | ||
122 | void write_leases(void) FAST_FUNC; | ||
123 | void read_leases(const char *file) FAST_FUNC; | ||
124 | |||
125 | |||
126 | POP_SAVED_FUNCTION_VISIBILITY | 93 | POP_SAVED_FUNCTION_VISIBILITY |
127 | 94 | ||
128 | #endif | 95 | #endif |
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c deleted file mode 100644 index b22425352..000000000 --- a/networking/udhcp/files.c +++ /dev/null | |||
@@ -1,234 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * DHCP server config and lease file manipulation | ||
4 | * | ||
5 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
6 | * | ||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | #include <netinet/ether.h> | ||
10 | |||
11 | #include "common.h" | ||
12 | #include "dhcpd.h" | ||
13 | |||
14 | /* on these functions, make sure your datatype matches */ | ||
15 | static int FAST_FUNC read_str(const char *line, void *arg) | ||
16 | { | ||
17 | char **dest = arg; | ||
18 | |||
19 | free(*dest); | ||
20 | *dest = xstrdup(line); | ||
21 | return 1; | ||
22 | } | ||
23 | |||
24 | static int FAST_FUNC read_u32(const char *line, void *arg) | ||
25 | { | ||
26 | *(uint32_t*)arg = bb_strtou32(line, NULL, 10); | ||
27 | return errno == 0; | ||
28 | } | ||
29 | |||
30 | static int FAST_FUNC read_staticlease(const char *const_line, void *arg) | ||
31 | { | ||
32 | char *line; | ||
33 | char *mac_string; | ||
34 | char *ip_string; | ||
35 | struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */ | ||
36 | uint32_t nip; | ||
37 | |||
38 | /* Read mac */ | ||
39 | line = (char *) const_line; | ||
40 | mac_string = strtok_r(line, " \t", &line); | ||
41 | if (!mac_string || !ether_aton_r(mac_string, &mac_bytes)) | ||
42 | return 0; | ||
43 | |||
44 | /* Read ip */ | ||
45 | ip_string = strtok_r(NULL, " \t", &line); | ||
46 | if (!ip_string || !udhcp_str2nip(ip_string, &nip)) | ||
47 | return 0; | ||
48 | |||
49 | add_static_lease(arg, (uint8_t*) &mac_bytes, nip); | ||
50 | |||
51 | log_static_leases(arg); | ||
52 | |||
53 | return 1; | ||
54 | } | ||
55 | |||
56 | |||
57 | struct config_keyword { | ||
58 | const char *keyword; | ||
59 | int (*handler)(const char *line, void *var) FAST_FUNC; | ||
60 | unsigned ofs; | ||
61 | const char *def; | ||
62 | }; | ||
63 | |||
64 | #define OFS(field) offsetof(struct server_config_t, field) | ||
65 | |||
66 | static const struct config_keyword keywords[] = { | ||
67 | /* keyword handler variable address default */ | ||
68 | {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"}, | ||
69 | {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"}, | ||
70 | {"interface" , read_str , OFS(interface ), "eth0"}, | ||
71 | /* Avoid "max_leases value not sane" warning by setting default | ||
72 | * to default_end_ip - default_start_ip + 1: */ | ||
73 | {"max_leases" , read_u32 , OFS(max_leases ), "235"}, | ||
74 | {"auto_time" , read_u32 , OFS(auto_time ), "7200"}, | ||
75 | {"decline_time" , read_u32 , OFS(decline_time ), "3600"}, | ||
76 | {"conflict_time", read_u32 , OFS(conflict_time), "3600"}, | ||
77 | {"offer_time" , read_u32 , OFS(offer_time ), "60"}, | ||
78 | {"min_lease" , read_u32 , OFS(min_lease_sec), "60"}, | ||
79 | {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE}, | ||
80 | {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"}, | ||
81 | {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"}, | ||
82 | /* keywords with no defaults must be last! */ | ||
83 | {"option" , udhcp_str2optset, OFS(options ), ""}, | ||
84 | {"opt" , udhcp_str2optset, OFS(options ), ""}, | ||
85 | {"notify_file" , read_str , OFS(notify_file ), NULL}, | ||
86 | {"sname" , read_str , OFS(sname ), NULL}, | ||
87 | {"boot_file" , read_str , OFS(boot_file ), NULL}, | ||
88 | {"static_lease" , read_staticlease, OFS(static_leases), ""}, | ||
89 | }; | ||
90 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; | ||
91 | |||
92 | void FAST_FUNC read_config(const char *file) | ||
93 | { | ||
94 | parser_t *parser; | ||
95 | const struct config_keyword *k; | ||
96 | unsigned i; | ||
97 | char *token[2]; | ||
98 | |||
99 | for (i = 0; i < KWS_WITH_DEFAULTS; i++) | ||
100 | keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs); | ||
101 | |||
102 | parser = config_open(file); | ||
103 | while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { | ||
104 | for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) { | ||
105 | if (strcasecmp(token[0], k->keyword) == 0) { | ||
106 | if (!k->handler(token[1], (char*)&server_config + k->ofs)) { | ||
107 | bb_error_msg("can't parse line %u in %s", | ||
108 | parser->lineno, file); | ||
109 | /* reset back to the default value */ | ||
110 | k->handler(k->def, (char*)&server_config + k->ofs); | ||
111 | } | ||
112 | break; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | config_close(parser); | ||
117 | |||
118 | server_config.start_ip = ntohl(server_config.start_ip); | ||
119 | server_config.end_ip = ntohl(server_config.end_ip); | ||
120 | } | ||
121 | |||
122 | void FAST_FUNC write_leases(void) | ||
123 | { | ||
124 | int fd; | ||
125 | unsigned i; | ||
126 | leasetime_t curr; | ||
127 | int64_t written_at; | ||
128 | |||
129 | fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); | ||
130 | if (fd < 0) | ||
131 | return; | ||
132 | |||
133 | curr = written_at = time(NULL); | ||
134 | |||
135 | written_at = SWAP_BE64(written_at); | ||
136 | full_write(fd, &written_at, sizeof(written_at)); | ||
137 | |||
138 | for (i = 0; i < server_config.max_leases; i++) { | ||
139 | leasetime_t tmp_time; | ||
140 | |||
141 | if (g_leases[i].lease_nip == 0) | ||
142 | continue; | ||
143 | |||
144 | /* Screw with the time in the struct, for easier writing */ | ||
145 | tmp_time = g_leases[i].expires; | ||
146 | |||
147 | g_leases[i].expires -= curr; | ||
148 | if ((signed_leasetime_t) g_leases[i].expires < 0) | ||
149 | g_leases[i].expires = 0; | ||
150 | g_leases[i].expires = htonl(g_leases[i].expires); | ||
151 | |||
152 | /* No error check. If the file gets truncated, | ||
153 | * we lose some leases on restart. Oh well. */ | ||
154 | full_write(fd, &g_leases[i], sizeof(g_leases[i])); | ||
155 | |||
156 | /* Then restore it when done */ | ||
157 | g_leases[i].expires = tmp_time; | ||
158 | } | ||
159 | close(fd); | ||
160 | |||
161 | if (server_config.notify_file) { | ||
162 | char *argv[3]; | ||
163 | argv[0] = server_config.notify_file; | ||
164 | argv[1] = server_config.lease_file; | ||
165 | argv[2] = NULL; | ||
166 | spawn_and_wait(argv); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | void FAST_FUNC read_leases(const char *file) | ||
171 | { | ||
172 | struct dyn_lease lease; | ||
173 | int64_t written_at, time_passed; | ||
174 | int fd; | ||
175 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 | ||
176 | unsigned i = 0; | ||
177 | #endif | ||
178 | |||
179 | fd = open_or_warn(file, O_RDONLY); | ||
180 | if (fd < 0) | ||
181 | return; | ||
182 | |||
183 | if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at)) | ||
184 | goto ret; | ||
185 | written_at = SWAP_BE64(written_at); | ||
186 | |||
187 | time_passed = time(NULL) - written_at; | ||
188 | /* Strange written_at, or lease file from old version of udhcpd | ||
189 | * which had no "written_at" field? */ | ||
190 | if ((uint64_t)time_passed > 12 * 60 * 60) | ||
191 | goto ret; | ||
192 | |||
193 | while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) { | ||
194 | uint32_t y = ntohl(lease.lease_nip); | ||
195 | if (y >= server_config.start_ip && y <= server_config.end_ip) { | ||
196 | signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed; | ||
197 | uint32_t static_nip; | ||
198 | |||
199 | if (expires <= 0) | ||
200 | /* We keep expired leases: add_lease() will add | ||
201 | * a lease with 0 seconds remaining. | ||
202 | * Fewer IP address changes this way for mass reboot scenario. | ||
203 | */ | ||
204 | expires = 0; | ||
205 | |||
206 | /* Check if there is a different static lease for this IP or MAC */ | ||
207 | static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac); | ||
208 | if (static_nip) { | ||
209 | /* NB: we do not add lease even if static_nip == lease.lease_nip. | ||
210 | */ | ||
211 | continue; | ||
212 | } | ||
213 | if (is_nip_reserved(server_config.static_leases, lease.lease_nip)) | ||
214 | continue; | ||
215 | |||
216 | /* NB: add_lease takes "relative time", IOW, | ||
217 | * lease duration, not lease deadline. */ | ||
218 | if (add_lease(lease.lease_mac, lease.lease_nip, | ||
219 | expires, | ||
220 | lease.hostname, sizeof(lease.hostname) | ||
221 | ) == 0 | ||
222 | ) { | ||
223 | bb_error_msg("too many leases while loading %s", file); | ||
224 | break; | ||
225 | } | ||
226 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 | ||
227 | i++; | ||
228 | #endif | ||
229 | } | ||
230 | } | ||
231 | log1("read %d leases", i); | ||
232 | ret: | ||
233 | close(fd); | ||
234 | } | ||
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c deleted file mode 100644 index 6642e396d..000000000 --- a/networking/udhcp/leases.c +++ /dev/null | |||
@@ -1,211 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
4 | * | ||
5 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
6 | */ | ||
7 | #include "common.h" | ||
8 | #include "dhcpd.h" | ||
9 | |||
10 | /* Find the oldest expired lease, NULL if there are no expired leases */ | ||
11 | static struct dyn_lease *oldest_expired_lease(void) | ||
12 | { | ||
13 | struct dyn_lease *oldest_lease = NULL; | ||
14 | leasetime_t oldest_time = time(NULL); | ||
15 | unsigned i; | ||
16 | |||
17 | /* Unexpired leases have g_leases[i].expires >= current time | ||
18 | * and therefore can't ever match */ | ||
19 | for (i = 0; i < server_config.max_leases; i++) { | ||
20 | if (g_leases[i].expires == 0 /* empty entry */ | ||
21 | || g_leases[i].expires < oldest_time | ||
22 | ) { | ||
23 | oldest_time = g_leases[i].expires; | ||
24 | oldest_lease = &g_leases[i]; | ||
25 | } | ||
26 | } | ||
27 | return oldest_lease; | ||
28 | } | ||
29 | |||
30 | /* Clear out all leases with matching nonzero chaddr OR yiaddr. | ||
31 | * If chaddr == NULL, this is a conflict lease. | ||
32 | */ | ||
33 | static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr) | ||
34 | { | ||
35 | unsigned i; | ||
36 | |||
37 | for (i = 0; i < server_config.max_leases; i++) { | ||
38 | if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0) | ||
39 | || (yiaddr && g_leases[i].lease_nip == yiaddr) | ||
40 | ) { | ||
41 | memset(&g_leases[i], 0, sizeof(g_leases[i])); | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | |||
46 | /* Add a lease into the table, clearing out any old ones. | ||
47 | * If chaddr == NULL, this is a conflict lease. | ||
48 | */ | ||
49 | struct dyn_lease* FAST_FUNC add_lease( | ||
50 | const uint8_t *chaddr, uint32_t yiaddr, | ||
51 | leasetime_t leasetime, | ||
52 | const char *hostname, int hostname_len) | ||
53 | { | ||
54 | struct dyn_lease *oldest; | ||
55 | |||
56 | /* clean out any old ones */ | ||
57 | clear_leases(chaddr, yiaddr); | ||
58 | |||
59 | oldest = oldest_expired_lease(); | ||
60 | |||
61 | if (oldest) { | ||
62 | memset(oldest, 0, sizeof(*oldest)); | ||
63 | if (hostname) { | ||
64 | char *p; | ||
65 | |||
66 | hostname_len++; /* include NUL */ | ||
67 | if (hostname_len > sizeof(oldest->hostname)) | ||
68 | hostname_len = sizeof(oldest->hostname); | ||
69 | p = safe_strncpy(oldest->hostname, hostname, hostname_len); | ||
70 | /* | ||
71 | * Sanitization (s/bad_char/./g). | ||
72 | * The intent is not to allow only "DNS-valid" hostnames, | ||
73 | * but merely make dumpleases output safe for shells to use. | ||
74 | * We accept "0-9A-Za-z._-", all other chars turn to dots. | ||
75 | */ | ||
76 | while (*p) { | ||
77 | if (!isalnum(*p) && *p != '-' && *p != '_') | ||
78 | *p = '.'; | ||
79 | p++; | ||
80 | } | ||
81 | } | ||
82 | if (chaddr) | ||
83 | memcpy(oldest->lease_mac, chaddr, 6); | ||
84 | oldest->lease_nip = yiaddr; | ||
85 | oldest->expires = time(NULL) + leasetime; | ||
86 | } | ||
87 | |||
88 | return oldest; | ||
89 | } | ||
90 | |||
91 | /* True if a lease has expired */ | ||
92 | int FAST_FUNC is_expired_lease(struct dyn_lease *lease) | ||
93 | { | ||
94 | return (lease->expires < (leasetime_t) time(NULL)); | ||
95 | } | ||
96 | |||
97 | /* Find the first lease that matches MAC, NULL if no match */ | ||
98 | struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac) | ||
99 | { | ||
100 | unsigned i; | ||
101 | |||
102 | for (i = 0; i < server_config.max_leases; i++) | ||
103 | if (memcmp(g_leases[i].lease_mac, mac, 6) == 0) | ||
104 | return &g_leases[i]; | ||
105 | |||
106 | return NULL; | ||
107 | } | ||
108 | |||
109 | /* Find the first lease that matches IP, NULL is no match */ | ||
110 | struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip) | ||
111 | { | ||
112 | unsigned i; | ||
113 | |||
114 | for (i = 0; i < server_config.max_leases; i++) | ||
115 | if (g_leases[i].lease_nip == nip) | ||
116 | return &g_leases[i]; | ||
117 | |||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | /* Check if the IP is taken; if it is, add it to the lease table */ | ||
122 | static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms) | ||
123 | { | ||
124 | struct in_addr temp; | ||
125 | int r; | ||
126 | |||
127 | r = arpping(nip, safe_mac, | ||
128 | server_config.server_nip, | ||
129 | server_config.server_mac, | ||
130 | server_config.interface, | ||
131 | arpping_ms); | ||
132 | if (r) | ||
133 | return r; | ||
134 | |||
135 | temp.s_addr = nip; | ||
136 | bb_error_msg("%s belongs to someone, reserving it for %u seconds", | ||
137 | inet_ntoa(temp), (unsigned)server_config.conflict_time); | ||
138 | add_lease(NULL, nip, server_config.conflict_time, NULL, 0); | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* Find a new usable (we think) address */ | ||
143 | uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms) | ||
144 | { | ||
145 | uint32_t addr; | ||
146 | struct dyn_lease *oldest_lease = NULL; | ||
147 | |||
148 | #if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC | ||
149 | uint32_t stop; | ||
150 | unsigned i, hash; | ||
151 | |||
152 | /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good | ||
153 | * dispersal even with similarly-valued "strings". | ||
154 | */ | ||
155 | hash = 0; | ||
156 | for (i = 0; i < 6; i++) | ||
157 | hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash; | ||
158 | |||
159 | /* pick a seed based on hwaddr then iterate until we find a free address. */ | ||
160 | addr = server_config.start_ip | ||
161 | + (hash % (1 + server_config.end_ip - server_config.start_ip)); | ||
162 | stop = addr; | ||
163 | #else | ||
164 | addr = server_config.start_ip; | ||
165 | #define stop (server_config.end_ip + 1) | ||
166 | #endif | ||
167 | do { | ||
168 | uint32_t nip; | ||
169 | struct dyn_lease *lease; | ||
170 | |||
171 | /* ie, 192.168.55.0 */ | ||
172 | if ((addr & 0xff) == 0) | ||
173 | goto next_addr; | ||
174 | /* ie, 192.168.55.255 */ | ||
175 | if ((addr & 0xff) == 0xff) | ||
176 | goto next_addr; | ||
177 | nip = htonl(addr); | ||
178 | /* skip our own address */ | ||
179 | if (nip == server_config.server_nip) | ||
180 | goto next_addr; | ||
181 | /* is this a static lease addr? */ | ||
182 | if (is_nip_reserved(server_config.static_leases, nip)) | ||
183 | goto next_addr; | ||
184 | |||
185 | lease = find_lease_by_nip(nip); | ||
186 | if (!lease) { | ||
187 | //TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping! | ||
188 | if (nobody_responds_to_arp(nip, safe_mac, arpping_ms)) | ||
189 | return nip; | ||
190 | } else { | ||
191 | if (!oldest_lease || lease->expires < oldest_lease->expires) | ||
192 | oldest_lease = lease; | ||
193 | } | ||
194 | |||
195 | next_addr: | ||
196 | addr++; | ||
197 | #if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC | ||
198 | if (addr > server_config.end_ip) | ||
199 | addr = server_config.start_ip; | ||
200 | #endif | ||
201 | } while (addr != stop); | ||
202 | |||
203 | if (oldest_lease | ||
204 | && is_expired_lease(oldest_lease) | ||
205 | && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms) | ||
206 | ) { | ||
207 | return oldest_lease->lease_nip; | ||
208 | } | ||
209 | |||
210 | return 0; | ||
211 | } | ||
diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c deleted file mode 100644 index b7f9e5c59..000000000 --- a/networking/udhcp/static_leases.c +++ /dev/null | |||
@@ -1,77 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Storing and retrieving data for static leases | ||
4 | * | ||
5 | * Wade Berrier <wberrier@myrealbox.com> September 2004 | ||
6 | * | ||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | #include "common.h" | ||
10 | #include "dhcpd.h" | ||
11 | |||
12 | /* Takes the address of the pointer to the static_leases linked list, | ||
13 | * address to a 6 byte mac address, | ||
14 | * 4 byte IP address */ | ||
15 | void FAST_FUNC add_static_lease(struct static_lease **st_lease_pp, | ||
16 | uint8_t *mac, | ||
17 | uint32_t nip) | ||
18 | { | ||
19 | struct static_lease *st_lease; | ||
20 | |||
21 | /* Find the tail of the list */ | ||
22 | while ((st_lease = *st_lease_pp) != NULL) { | ||
23 | st_lease_pp = &st_lease->next; | ||
24 | } | ||
25 | |||
26 | /* Add new node */ | ||
27 | *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease)); | ||
28 | memcpy(st_lease->mac, mac, 6); | ||
29 | st_lease->nip = nip; | ||
30 | /*st_lease->next = NULL;*/ | ||
31 | } | ||
32 | |||
33 | /* Find static lease IP by mac */ | ||
34 | uint32_t FAST_FUNC get_static_nip_by_mac(struct static_lease *st_lease, void *mac) | ||
35 | { | ||
36 | while (st_lease) { | ||
37 | if (memcmp(st_lease->mac, mac, 6) == 0) | ||
38 | return st_lease->nip; | ||
39 | st_lease = st_lease->next; | ||
40 | } | ||
41 | |||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | /* Check to see if an IP is reserved as a static IP */ | ||
46 | int FAST_FUNC is_nip_reserved(struct static_lease *st_lease, uint32_t nip) | ||
47 | { | ||
48 | while (st_lease) { | ||
49 | if (st_lease->nip == nip) | ||
50 | return 1; | ||
51 | st_lease = st_lease->next; | ||
52 | } | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 | ||
58 | /* Print out static leases just to check what's going on */ | ||
59 | /* Takes the address of the pointer to the static_leases linked list */ | ||
60 | void FAST_FUNC log_static_leases(struct static_lease **st_lease_pp) | ||
61 | { | ||
62 | struct static_lease *cur; | ||
63 | |||
64 | if (dhcp_verbose < 2) | ||
65 | return; | ||
66 | |||
67 | cur = *st_lease_pp; | ||
68 | while (cur) { | ||
69 | bb_error_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x", | ||
70 | cur->mac[0], cur->mac[1], cur->mac[2], | ||
71 | cur->mac[3], cur->mac[4], cur->mac[5], | ||
72 | cur->nip | ||
73 | ); | ||
74 | cur = cur->next; | ||
75 | } | ||
76 | } | ||
77 | #endif | ||
diff --git a/networking/wget.c b/networking/wget.c index c886dd391..460b4b833 100644 --- a/networking/wget.c +++ b/networking/wget.c | |||
@@ -62,9 +62,10 @@ | |||
62 | //config: a helper program to talk over HTTPS. | 62 | //config: a helper program to talk over HTTPS. |
63 | //config: | 63 | //config: |
64 | //config: OpenSSL has a simple SSL client for debug purposes. | 64 | //config: OpenSSL has a simple SSL client for debug purposes. |
65 | //config: If you select "openssl" helper, wget will effectively call | 65 | //config: If you select "openssl" helper, wget will effectively run: |
66 | //config: "openssl s_client -quiet -connect IP:443 2>/dev/null" | 66 | //config: "openssl s_client -quiet -connect hostname:443 |
67 | //config: and pipe its data through it. | 67 | //config: -servername hostname 2>/dev/null" and pipe its data |
68 | //config: through it. -servername is not used if hostname is numeric. | ||
68 | //config: Note inconvenient API: host resolution is done twice, | 69 | //config: Note inconvenient API: host resolution is done twice, |
69 | //config: and there is no guarantee openssl's idea of IPv6 address | 70 | //config: and there is no guarantee openssl's idea of IPv6 address |
70 | //config: format is the same as ours. | 71 | //config: format is the same as ours. |
@@ -99,7 +100,7 @@ | |||
99 | 100 | ||
100 | //usage:#define wget_trivial_usage | 101 | //usage:#define wget_trivial_usage |
101 | //usage: IF_FEATURE_WGET_LONG_OPTIONS( | 102 | //usage: IF_FEATURE_WGET_LONG_OPTIONS( |
102 | //usage: "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n" | 103 | //usage: "[-c|--continue] [--spider] [-q|--quiet] [-O|--output-document FILE]\n" |
103 | //usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n" | 104 | //usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n" |
104 | /* Since we ignore these opts, we don't show them in --help */ | 105 | /* Since we ignore these opts, we don't show them in --help */ |
105 | /* //usage: " [--no-check-certificate] [--no-cache] [--passive-ftp] [-t TRIES]" */ | 106 | /* //usage: " [--no-check-certificate] [--no-cache] [--passive-ftp] [-t TRIES]" */ |
@@ -107,21 +108,23 @@ | |||
107 | //usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." | 108 | //usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." |
108 | //usage: ) | 109 | //usage: ) |
109 | //usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS( | 110 | //usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS( |
110 | //usage: "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]" | 111 | //usage: "[-cq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]" |
111 | //usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." | 112 | //usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..." |
112 | //usage: ) | 113 | //usage: ) |
113 | //usage:#define wget_full_usage "\n\n" | 114 | //usage:#define wget_full_usage "\n\n" |
114 | //usage: "Retrieve files via HTTP or FTP\n" | 115 | //usage: "Retrieve files via HTTP or FTP\n" |
115 | //usage: "\n -s Spider mode - only check file existence" | 116 | //usage: IF_FEATURE_WGET_LONG_OPTIONS( |
116 | //usage: "\n -c Continue retrieval of aborted transfer" | 117 | //usage: "\n --spider Spider mode - only check file existence" |
117 | //usage: "\n -q Quiet" | 118 | //usage: ) |
118 | //usage: "\n -P DIR Save to DIR (default .)" | 119 | //usage: "\n -c Continue retrieval of aborted transfer" |
120 | //usage: "\n -q Quiet" | ||
121 | //usage: "\n -P DIR Save to DIR (default .)" | ||
119 | //usage: IF_FEATURE_WGET_TIMEOUT( | 122 | //usage: IF_FEATURE_WGET_TIMEOUT( |
120 | //usage: "\n -T SEC Network read timeout is SEC seconds" | 123 | //usage: "\n -T SEC Network read timeout is SEC seconds" |
121 | //usage: ) | 124 | //usage: ) |
122 | //usage: "\n -O FILE Save to FILE ('-' for stdout)" | 125 | //usage: "\n -O FILE Save to FILE ('-' for stdout)" |
123 | //usage: "\n -U STR Use STR for User-Agent header" | 126 | //usage: "\n -U STR Use STR for User-Agent header" |
124 | //usage: "\n -Y Use proxy ('on' or 'off')" | 127 | //usage: "\n -Y on/off Use proxy" |
125 | 128 | ||
126 | #include "libbb.h" | 129 | #include "libbb.h" |
127 | 130 | ||
@@ -228,17 +231,17 @@ struct globals { | |||
228 | /* Must match option string! */ | 231 | /* Must match option string! */ |
229 | enum { | 232 | enum { |
230 | WGET_OPT_CONTINUE = (1 << 0), | 233 | WGET_OPT_CONTINUE = (1 << 0), |
231 | WGET_OPT_SPIDER = (1 << 1), | 234 | WGET_OPT_QUIET = (1 << 1), |
232 | WGET_OPT_QUIET = (1 << 2), | 235 | WGET_OPT_OUTNAME = (1 << 2), |
233 | WGET_OPT_OUTNAME = (1 << 3), | 236 | WGET_OPT_PREFIX = (1 << 3), |
234 | WGET_OPT_PREFIX = (1 << 4), | 237 | WGET_OPT_PROXY = (1 << 4), |
235 | WGET_OPT_PROXY = (1 << 5), | 238 | WGET_OPT_USER_AGENT = (1 << 5), |
236 | WGET_OPT_USER_AGENT = (1 << 6), | 239 | WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 6), |
237 | WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7), | 240 | WGET_OPT_RETRIES = (1 << 7), |
238 | WGET_OPT_RETRIES = (1 << 8), | 241 | WGET_OPT_nsomething = (1 << 8), |
239 | WGET_OPT_PASSIVE = (1 << 9), | 242 | WGET_OPT_HEADER = (1 << 9) * ENABLE_FEATURE_WGET_LONG_OPTIONS, |
240 | WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS, | 243 | WGET_OPT_POST_DATA = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS, |
241 | WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, | 244 | WGET_OPT_SPIDER = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, |
242 | }; | 245 | }; |
243 | 246 | ||
244 | enum { | 247 | enum { |
@@ -349,6 +352,30 @@ static void set_alarm(void) | |||
349 | # define clear_alarm() ((void)0) | 352 | # define clear_alarm() ((void)0) |
350 | #endif | 353 | #endif |
351 | 354 | ||
355 | #if ENABLE_FEATURE_WGET_OPENSSL | ||
356 | /* | ||
357 | * is_ip_address() attempts to verify whether or not a string | ||
358 | * contains an IPv4 or IPv6 address (vs. an FQDN). The result | ||
359 | * of inet_pton() can be used to determine this. | ||
360 | * | ||
361 | * TODO add proper error checking when inet_pton() returns -1 | ||
362 | * (some form of system error has occurred, and errno is set) | ||
363 | */ | ||
364 | static int is_ip_address(const char *string) | ||
365 | { | ||
366 | struct sockaddr_in sa; | ||
367 | |||
368 | int result = inet_pton(AF_INET, string, &(sa.sin_addr)); | ||
369 | # if ENABLE_FEATURE_IPV6 | ||
370 | if (result == 0) { | ||
371 | struct sockaddr_in6 sa6; | ||
372 | result = inet_pton(AF_INET6, string, &(sa6.sin6_addr)); | ||
373 | } | ||
374 | # endif | ||
375 | return (result == 1); | ||
376 | } | ||
377 | #endif | ||
378 | |||
352 | static FILE *open_socket(len_and_sockaddr *lsa) | 379 | static FILE *open_socket(len_and_sockaddr *lsa) |
353 | { | 380 | { |
354 | int fd; | 381 | int fd; |
@@ -635,6 +662,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_ | |||
635 | static int spawn_https_helper_openssl(const char *host, unsigned port) | 662 | static int spawn_https_helper_openssl(const char *host, unsigned port) |
636 | { | 663 | { |
637 | char *allocated = NULL; | 664 | char *allocated = NULL; |
665 | char *servername; | ||
638 | int sp[2]; | 666 | int sp[2]; |
639 | int pid; | 667 | int pid; |
640 | IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;) | 668 | IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;) |
@@ -645,12 +673,14 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) | |||
645 | 673 | ||
646 | if (!strchr(host, ':')) | 674 | if (!strchr(host, ':')) |
647 | host = allocated = xasprintf("%s:%u", host, port); | 675 | host = allocated = xasprintf("%s:%u", host, port); |
676 | servername = xstrdup(host); | ||
677 | strrchr(servername, ':')[0] = '\0'; | ||
648 | 678 | ||
649 | fflush_all(); | 679 | fflush_all(); |
650 | pid = xvfork(); | 680 | pid = xvfork(); |
651 | if (pid == 0) { | 681 | if (pid == 0) { |
652 | /* Child */ | 682 | /* Child */ |
653 | char *argv[6]; | 683 | char *argv[8]; |
654 | 684 | ||
655 | close(sp[0]); | 685 | close(sp[0]); |
656 | xmove_fd(sp[1], 0); | 686 | xmove_fd(sp[1], 0); |
@@ -662,12 +692,22 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) | |||
662 | */ | 692 | */ |
663 | xmove_fd(2, 3); | 693 | xmove_fd(2, 3); |
664 | xopen("/dev/null", O_RDWR); | 694 | xopen("/dev/null", O_RDWR); |
695 | memset(&argv, 0, sizeof(argv)); | ||
665 | argv[0] = (char*)"openssl"; | 696 | argv[0] = (char*)"openssl"; |
666 | argv[1] = (char*)"s_client"; | 697 | argv[1] = (char*)"s_client"; |
667 | argv[2] = (char*)"-quiet"; | 698 | argv[2] = (char*)"-quiet"; |
668 | argv[3] = (char*)"-connect"; | 699 | argv[3] = (char*)"-connect"; |
669 | argv[4] = (char*)host; | 700 | argv[4] = (char*)host; |
670 | argv[5] = NULL; | 701 | /* |
702 | * Per RFC 6066 Section 3, the only permitted values in the | ||
703 | * TLS server_name (SNI) field are FQDNs (DNS hostnames). | ||
704 | * IPv4 and IPv6 addresses, port numbers are not allowed. | ||
705 | */ | ||
706 | if (!is_ip_address(servername)) { | ||
707 | argv[5] = (char*)"-servername"; | ||
708 | argv[6] = (char*)servername; | ||
709 | } | ||
710 | |||
671 | BB_EXECVP(argv[0], argv); | 711 | BB_EXECVP(argv[0], argv); |
672 | xmove_fd(3, 2); | 712 | xmove_fd(3, 2); |
673 | # if ENABLE_FEATURE_WGET_SSL_HELPER | 713 | # if ENABLE_FEATURE_WGET_SSL_HELPER |
@@ -680,6 +720,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) | |||
680 | } | 720 | } |
681 | 721 | ||
682 | /* Parent */ | 722 | /* Parent */ |
723 | free(servername); | ||
683 | free(allocated); | 724 | free(allocated); |
684 | close(sp[1]); | 725 | close(sp[1]); |
685 | # if ENABLE_FEATURE_WGET_SSL_HELPER | 726 | # if ENABLE_FEATURE_WGET_SSL_HELPER |
@@ -1058,6 +1099,12 @@ static void download_one_url(const char *url) | |||
1058 | } | 1099 | } |
1059 | 1100 | ||
1060 | fflush(sfp); | 1101 | fflush(sfp); |
1102 | /* If we use SSL helper, keeping our end of the socket open for writing | ||
1103 | * makes our end (i.e. the same fd!) readable (EAGAIN instead of EOF) | ||
1104 | * even after child closes its copy of the fd. | ||
1105 | * This helps: | ||
1106 | */ | ||
1107 | shutdown(fileno(sfp), SHUT_WR); | ||
1061 | 1108 | ||
1062 | /* | 1109 | /* |
1063 | * Retrieve HTTP response line and check for "200" status code. | 1110 | * Retrieve HTTP response line and check for "200" status code. |
@@ -1077,7 +1124,21 @@ static void download_one_url(const char *url) | |||
1077 | while (gethdr(sfp) != NULL) | 1124 | while (gethdr(sfp) != NULL) |
1078 | /* eat all remaining headers */; | 1125 | /* eat all remaining headers */; |
1079 | goto read_response; | 1126 | goto read_response; |
1127 | |||
1128 | /* Success responses */ | ||
1080 | case 200: | 1129 | case 200: |
1130 | /* fall through */ | ||
1131 | case 201: /* 201 Created */ | ||
1132 | /* "The request has been fulfilled and resulted in a new resource being created" */ | ||
1133 | /* Standard wget is reported to treat this as success */ | ||
1134 | /* fall through */ | ||
1135 | case 202: /* 202 Accepted */ | ||
1136 | /* "The request has been accepted for processing, but the processing has not been completed" */ | ||
1137 | /* Treat as success: fall through */ | ||
1138 | case 203: /* 203 Non-Authoritative Information */ | ||
1139 | /* "Use of this response code is not required and is only appropriate when the response would otherwise be 200 (OK)" */ | ||
1140 | /* fall through */ | ||
1141 | case 204: /* 204 No Content */ | ||
1081 | /* | 1142 | /* |
1082 | Response 204 doesn't say "null file", it says "metadata | 1143 | Response 204 doesn't say "null file", it says "metadata |
1083 | has changed but data didn't": | 1144 | has changed but data didn't": |
@@ -1102,7 +1163,6 @@ is always terminated by the first empty line after the header fields." | |||
1102 | However, in real world it was observed that some web servers | 1163 | However, in real world it was observed that some web servers |
1103 | (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero. | 1164 | (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero. |
1104 | */ | 1165 | */ |
1105 | case 204: | ||
1106 | if (G.beg_range != 0) { | 1166 | if (G.beg_range != 0) { |
1107 | /* "Range:..." was not honored by the server. | 1167 | /* "Range:..." was not honored by the server. |
1108 | * Restart download from the beginning. | 1168 | * Restart download from the beginning. |
@@ -1110,11 +1170,14 @@ However, in real world it was observed that some web servers | |||
1110 | reset_beg_range_to_zero(); | 1170 | reset_beg_range_to_zero(); |
1111 | } | 1171 | } |
1112 | break; | 1172 | break; |
1173 | /* 205 Reset Content ?? what to do on this ?? */ | ||
1174 | |||
1113 | case 300: /* redirection */ | 1175 | case 300: /* redirection */ |
1114 | case 301: | 1176 | case 301: |
1115 | case 302: | 1177 | case 302: |
1116 | case 303: | 1178 | case 303: |
1117 | break; | 1179 | break; |
1180 | |||
1118 | case 206: /* Partial Content */ | 1181 | case 206: /* Partial Content */ |
1119 | if (G.beg_range != 0) | 1182 | if (G.beg_range != 0) |
1120 | /* "Range:..." worked. Good. */ | 1183 | /* "Range:..." worked. Good. */ |
@@ -1231,8 +1294,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
1231 | static const char wget_longopts[] ALIGN1 = | 1294 | static const char wget_longopts[] ALIGN1 = |
1232 | /* name, has_arg, val */ | 1295 | /* name, has_arg, val */ |
1233 | "continue\0" No_argument "c" | 1296 | "continue\0" No_argument "c" |
1234 | //FIXME: -s isn't --spider, it's --save-headers! | ||
1235 | "spider\0" No_argument "s" | ||
1236 | "quiet\0" No_argument "q" | 1297 | "quiet\0" No_argument "q" |
1237 | "output-document\0" Required_argument "O" | 1298 | "output-document\0" Required_argument "O" |
1238 | "directory-prefix\0" Required_argument "P" | 1299 | "directory-prefix\0" Required_argument "P" |
@@ -1244,6 +1305,7 @@ IF_FEATURE_WGET_TIMEOUT( | |||
1244 | IF_DESKTOP( "tries\0" Required_argument "t") | 1305 | IF_DESKTOP( "tries\0" Required_argument "t") |
1245 | "header\0" Required_argument "\xff" | 1306 | "header\0" Required_argument "\xff" |
1246 | "post-data\0" Required_argument "\xfe" | 1307 | "post-data\0" Required_argument "\xfe" |
1308 | "spider\0" No_argument "\xfd" | ||
1247 | /* Ignored (we always use PASV): */ | 1309 | /* Ignored (we always use PASV): */ |
1248 | IF_DESKTOP( "passive-ftp\0" No_argument "\xf0") | 1310 | IF_DESKTOP( "passive-ftp\0" No_argument "\xf0") |
1249 | /* Ignored (we don't do ssl) */ | 1311 | /* Ignored (we don't do ssl) */ |
@@ -1275,7 +1337,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0") | |||
1275 | #endif | 1337 | #endif |
1276 | opt_complementary = "-1" /* at least one URL */ | 1338 | opt_complementary = "-1" /* at least one URL */ |
1277 | IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */ | 1339 | IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */ |
1278 | getopt32(argv, "csqO:P:Y:U:T:+" | 1340 | getopt32(argv, "cqO:P:Y:U:T:+" |
1279 | /*ignored:*/ "t:" | 1341 | /*ignored:*/ "t:" |
1280 | /*ignored:*/ "n::" | 1342 | /*ignored:*/ "n::" |
1281 | /* wget has exactly four -n<letter> opts, all of which we can ignore: | 1343 | /* wget has exactly four -n<letter> opts, all of which we can ignore: |
@@ -1294,6 +1356,14 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0") | |||
1294 | IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) | 1356 | IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) |
1295 | IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data) | 1357 | IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data) |
1296 | ); | 1358 | ); |
1359 | #if 0 /* option bits debug */ | ||
1360 | if (option_mask32 & WGET_OPT_RETRIES) bb_error_msg("-t NUM"); | ||
1361 | if (option_mask32 & WGET_OPT_nsomething) bb_error_msg("-nsomething"); | ||
1362 | if (option_mask32 & WGET_OPT_HEADER) bb_error_msg("--header"); | ||
1363 | if (option_mask32 & WGET_OPT_POST_DATA) bb_error_msg("--post-data"); | ||
1364 | if (option_mask32 & WGET_OPT_SPIDER) bb_error_msg("--spider"); | ||
1365 | exit(0); | ||
1366 | #endif | ||
1297 | argv += optind; | 1367 | argv += optind; |
1298 | 1368 | ||
1299 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | 1369 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS |
diff --git a/procps/top.c b/procps/top.c index 73cd285f0..6f7f7d382 100644 --- a/procps/top.c +++ b/procps/top.c | |||
@@ -728,12 +728,6 @@ static void reset_term(void) | |||
728 | { | 728 | { |
729 | if (!OPT_BATCH_MODE) | 729 | if (!OPT_BATCH_MODE) |
730 | tcsetattr_stdin_TCSANOW(&initial_settings); | 730 | tcsetattr_stdin_TCSANOW(&initial_settings); |
731 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
732 | clearmems(); | ||
733 | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
734 | free(prev_hist); | ||
735 | # endif | ||
736 | } | ||
737 | } | 731 | } |
738 | 732 | ||
739 | static void sig_catcher(int sig) | 733 | static void sig_catcher(int sig) |
@@ -1258,5 +1252,11 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1258 | #if ENABLE_FEATURE_USE_TERMIOS | 1252 | #if ENABLE_FEATURE_USE_TERMIOS |
1259 | reset_term(); | 1253 | reset_term(); |
1260 | #endif | 1254 | #endif |
1255 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
1256 | clearmems(); | ||
1257 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
1258 | free(prev_hist); | ||
1259 | #endif | ||
1260 | } | ||
1261 | return EXIT_SUCCESS; | 1261 | return EXIT_SUCCESS; |
1262 | } | 1262 | } |
diff --git a/runit/runsvdir.c b/runit/runsvdir.c index 2b7927542..84916e929 100644 --- a/runit/runsvdir.c +++ b/runit/runsvdir.c | |||
@@ -181,9 +181,9 @@ static NOINLINE int do_rescan(void) | |||
181 | continue; | 181 | continue; |
182 | /* Do we have this service listed already? */ | 182 | /* Do we have this service listed already? */ |
183 | for (i = 0; i < svnum; i++) { | 183 | for (i = 0; i < svnum; i++) { |
184 | if ((sv[i].ino == s.st_ino) | 184 | if (sv[i].ino == s.st_ino |
185 | #if CHECK_DEVNO_TOO | 185 | #if CHECK_DEVNO_TOO |
186 | && (sv[i].dev == s.st_dev) | 186 | && sv[i].dev == s.st_dev |
187 | #endif | 187 | #endif |
188 | ) { | 188 | ) { |
189 | if (sv[i].pid == 0) /* restart if it has died */ | 189 | if (sv[i].pid == 0) /* restart if it has died */ |
diff --git a/runit/svlogd.c b/runit/svlogd.c index 7cae81cb2..3ed13b67b 100644 --- a/runit/svlogd.c +++ b/runit/svlogd.c | |||
@@ -784,7 +784,7 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn) | |||
784 | case '!': | 784 | case '!': |
785 | if (s[1]) { | 785 | if (s[1]) { |
786 | free(ld->processor); | 786 | free(ld->processor); |
787 | ld->processor = wstrdup(s); | 787 | ld->processor = wstrdup(&s[1]); |
788 | } | 788 | } |
789 | break; | 789 | break; |
790 | } | 790 | } |
diff --git a/scripts/trylink b/scripts/trylink index 145df9959..c2a431626 100755 --- a/scripts/trylink +++ b/scripts/trylink | |||
@@ -46,7 +46,7 @@ try() { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | check_cc() { | 48 | check_cc() { |
49 | local tempname="$(mktemp)" | 49 | local tempname="$(mktemp tmp.XXXXXXXXXX)" |
50 | local r | 50 | local r |
51 | echo "int main(int argc,char**argv){return argv?argc:0;}" >"$tempname".c | 51 | echo "int main(int argc,char**argv){return argv?argc:0;}" >"$tempname".c |
52 | # Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :( | 52 | # Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :( |
@@ -61,7 +61,7 @@ check_cc() { | |||
61 | } | 61 | } |
62 | 62 | ||
63 | check_libc_is_glibc() { | 63 | check_libc_is_glibc() { |
64 | local tempname="$(mktemp)" | 64 | local tempname="$(mktemp tmp.XXXXXXXXXX)" |
65 | local r | 65 | local r |
66 | echo "\ | 66 | echo "\ |
67 | #include <stdlib.h> | 67 | #include <stdlib.h> |
diff --git a/shell/Config.src b/shell/Config.src index b31e62dda..e4df35973 100644 --- a/shell/Config.src +++ b/shell/Config.src | |||
@@ -88,7 +88,7 @@ config FEATURE_SH_EXTRA_QUIET | |||
88 | config FEATURE_SH_STANDALONE | 88 | config FEATURE_SH_STANDALONE |
89 | bool "Standalone shell" | 89 | bool "Standalone shell" |
90 | default n | 90 | default n |
91 | depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS | 91 | depends on (HUSH || ASH) |
92 | help | 92 | help |
93 | This option causes busybox shells to use busybox applets | 93 | This option causes busybox shells to use busybox applets |
94 | in preference to executables in the PATH whenever possible. For | 94 | in preference to executables in the PATH whenever possible. For |
@@ -121,7 +121,7 @@ config FEATURE_SH_STANDALONE | |||
121 | config FEATURE_SH_NOFORK | 121 | config FEATURE_SH_NOFORK |
122 | bool "Run 'nofork' applets directly" | 122 | bool "Run 'nofork' applets directly" |
123 | default n | 123 | default n |
124 | depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS | 124 | depends on (HUSH || ASH) |
125 | help | 125 | help |
126 | This option causes busybox shells to not execute typical | 126 | This option causes busybox shells to not execute typical |
127 | fork/exec/wait sequence, but call <applet>_main directly, | 127 | fork/exec/wait sequence, but call <applet>_main directly, |
diff --git a/shell/ash.c b/shell/ash.c index 750ca7b6d..02e76c0ae 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -57,8 +57,18 @@ | |||
57 | #include <sys/utsname.h> /* for setting $HOSTNAME */ | 57 | #include <sys/utsname.h> /* for setting $HOSTNAME */ |
58 | 58 | ||
59 | #include "busybox.h" /* for applet_names */ | 59 | #include "busybox.h" /* for applet_names */ |
60 | #include "unicode.h" | ||
61 | 60 | ||
61 | #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 | ||
62 | /* Bionic at least up to version 24 has no glob() */ | ||
63 | # undef ENABLE_ASH_INTERNAL_GLOB | ||
64 | # define ENABLE_ASH_INTERNAL_GLOB 1 | ||
65 | #endif | ||
66 | |||
67 | #if !ENABLE_ASH_INTERNAL_GLOB | ||
68 | # include <glob.h> | ||
69 | #endif | ||
70 | |||
71 | #include "unicode.h" | ||
62 | #include "shell_common.h" | 72 | #include "shell_common.h" |
63 | #if ENABLE_SH_MATH_SUPPORT | 73 | #if ENABLE_SH_MATH_SUPPORT |
64 | # include "math.h" | 74 | # include "math.h" |
@@ -104,6 +114,42 @@ | |||
104 | //config: shell (by Herbert Xu), which was created by porting the 'ash' shell | 114 | //config: shell (by Herbert Xu), which was created by porting the 'ash' shell |
105 | //config: (written by Kenneth Almquist) from NetBSD. | 115 | //config: (written by Kenneth Almquist) from NetBSD. |
106 | //config: | 116 | //config: |
117 | //config:config ASH_OPTIMIZE_FOR_SIZE | ||
118 | //config: bool "Optimize for size instead of speed" | ||
119 | //config: default y | ||
120 | //config: depends on ASH | ||
121 | //config: help | ||
122 | //config: Compile ash for reduced size at the price of speed. | ||
123 | //config: | ||
124 | //config:config ASH_INTERNAL_GLOB | ||
125 | //config: bool "Use internal glob() implementation" | ||
126 | //config: default n | ||
127 | //config: depends on ASH | ||
128 | //config: help | ||
129 | //config: Do not use glob() function from libc, use internal implementation. | ||
130 | //config: Use this if you are getting "glob.h: No such file or directory" | ||
131 | //config: or similar build errors. | ||
132 | //config: | ||
133 | //config:config ASH_RANDOM_SUPPORT | ||
134 | //config: bool "Pseudorandom generator and $RANDOM variable" | ||
135 | //config: default y | ||
136 | //config: depends on ASH | ||
137 | //config: help | ||
138 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". | ||
139 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. | ||
140 | //config: You can reset the generator by using a specified start value. | ||
141 | //config: After "unset RANDOM" the generator will switch off and this | ||
142 | //config: variable will no longer have special treatment. | ||
143 | //config: | ||
144 | //config:config ASH_EXPAND_PRMT | ||
145 | //config: bool "Expand prompt string" | ||
146 | //config: default y | ||
147 | //config: depends on ASH | ||
148 | //config: help | ||
149 | //config: "PS#" may contain volatile content, such as backquote commands. | ||
150 | //config: This option recreates the prompt string from the environment | ||
151 | //config: variable each time it is displayed. | ||
152 | //config: | ||
107 | //config:config ASH_BASH_COMPAT | 153 | //config:config ASH_BASH_COMPAT |
108 | //config: bool "bash-compatible extensions" | 154 | //config: bool "bash-compatible extensions" |
109 | //config: default y | 155 | //config: default y |
@@ -183,33 +229,6 @@ | |||
183 | //config: help | 229 | //config: help |
184 | //config: Enable "check for new mail" function in the ash shell. | 230 | //config: Enable "check for new mail" function in the ash shell. |
185 | //config: | 231 | //config: |
186 | //config:config ASH_OPTIMIZE_FOR_SIZE | ||
187 | //config: bool "Optimize for size instead of speed" | ||
188 | //config: default y | ||
189 | //config: depends on ASH | ||
190 | //config: help | ||
191 | //config: Compile ash for reduced size at the price of speed. | ||
192 | //config: | ||
193 | //config:config ASH_RANDOM_SUPPORT | ||
194 | //config: bool "Pseudorandom generator and $RANDOM variable" | ||
195 | //config: default y | ||
196 | //config: depends on ASH | ||
197 | //config: help | ||
198 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". | ||
199 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. | ||
200 | //config: You can reset the generator by using a specified start value. | ||
201 | //config: After "unset RANDOM" the generator will switch off and this | ||
202 | //config: variable will no longer have special treatment. | ||
203 | //config: | ||
204 | //config:config ASH_EXPAND_PRMT | ||
205 | //config: bool "Expand prompt string" | ||
206 | //config: default y | ||
207 | //config: depends on ASH | ||
208 | //config: help | ||
209 | //config: "PS#" may contain volatile content, such as backquote commands. | ||
210 | //config: This option recreates the prompt string from the environment | ||
211 | //config: variable each time it is displayed. | ||
212 | //config: | ||
213 | 232 | ||
214 | //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) | 233 | //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) |
215 | //applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) | 234 | //applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) |
@@ -324,8 +343,10 @@ struct jmploc { | |||
324 | }; | 343 | }; |
325 | 344 | ||
326 | struct globals_misc { | 345 | struct globals_misc { |
327 | /* pid of main shell */ | 346 | uint8_t exitstatus; /* exit status of last command */ |
328 | int rootpid; | 347 | uint8_t back_exitstatus;/* exit status of backquoted command */ |
348 | smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ | ||
349 | int rootpid; /* pid of main shell */ | ||
329 | /* shell level: 0 for the main shell, 1 for its children, and so on */ | 350 | /* shell level: 0 for the main shell, 1 for its children, and so on */ |
330 | int shlvl; | 351 | int shlvl; |
331 | #define rootshell (!shlvl) | 352 | #define rootshell (!shlvl) |
@@ -393,7 +414,7 @@ struct globals_misc { | |||
393 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ | 414 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ |
394 | #define S_CATCH 2 /* signal is caught */ | 415 | #define S_CATCH 2 /* signal is caught */ |
395 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ | 416 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
396 | #define S_HARD_IGN 4 /* signal is ignored permenantly */ | 417 | #define S_HARD_IGN 4 /* signal is ignored permanently */ |
397 | 418 | ||
398 | /* indicates specified signal received */ | 419 | /* indicates specified signal received */ |
399 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ | 420 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
@@ -406,10 +427,12 @@ struct globals_misc { | |||
406 | random_t random_gen; | 427 | random_t random_gen; |
407 | #endif | 428 | #endif |
408 | pid_t backgndpid; /* pid of last background process */ | 429 | pid_t backgndpid; /* pid of last background process */ |
409 | smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ | ||
410 | }; | 430 | }; |
411 | extern struct globals_misc *const ash_ptr_to_globals_misc; | 431 | extern struct globals_misc *const ash_ptr_to_globals_misc; |
412 | #define G_misc (*ash_ptr_to_globals_misc) | 432 | #define G_misc (*ash_ptr_to_globals_misc) |
433 | #define exitstatus (G_misc.exitstatus ) | ||
434 | #define back_exitstatus (G_misc.back_exitstatus ) | ||
435 | #define job_warning (G_misc.job_warning) | ||
413 | #define rootpid (G_misc.rootpid ) | 436 | #define rootpid (G_misc.rootpid ) |
414 | #define shlvl (G_misc.shlvl ) | 437 | #define shlvl (G_misc.shlvl ) |
415 | #define minusc (G_misc.minusc ) | 438 | #define minusc (G_misc.minusc ) |
@@ -431,7 +454,6 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
431 | #define trap_ptr (G_misc.trap_ptr ) | 454 | #define trap_ptr (G_misc.trap_ptr ) |
432 | #define random_gen (G_misc.random_gen ) | 455 | #define random_gen (G_misc.random_gen ) |
433 | #define backgndpid (G_misc.backgndpid ) | 456 | #define backgndpid (G_misc.backgndpid ) |
434 | #define job_warning (G_misc.job_warning) | ||
435 | #define INIT_G_misc() do { \ | 457 | #define INIT_G_misc() do { \ |
436 | (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ | 458 | (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ |
437 | barrier(); \ | 459 | barrier(); \ |
@@ -460,12 +482,11 @@ static void trace_vprintf(const char *fmt, va_list va); | |||
460 | 482 | ||
461 | 483 | ||
462 | /* ============ Utility functions */ | 484 | /* ============ Utility functions */ |
463 | #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) | ||
464 | |||
465 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | 485 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) |
466 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | 486 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
467 | 487 | ||
468 | static int isdigit_str9(const char *str) | 488 | static int |
489 | isdigit_str9(const char *str) | ||
469 | { | 490 | { |
470 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ | 491 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ |
471 | while (--maxlen && isdigit(*str)) | 492 | while (--maxlen && isdigit(*str)) |
@@ -473,7 +494,8 @@ static int isdigit_str9(const char *str) | |||
473 | return (*str == '\0'); | 494 | return (*str == '\0'); |
474 | } | 495 | } |
475 | 496 | ||
476 | static const char *var_end(const char *var) | 497 | static const char * |
498 | var_end(const char *var) | ||
477 | { | 499 | { |
478 | while (*var) | 500 | while (*var) |
479 | if (*var++ == '=') | 501 | if (*var++ == '=') |
@@ -494,7 +516,7 @@ static void exitshell(void) NORETURN; | |||
494 | */ | 516 | */ |
495 | #define INT_OFF do { \ | 517 | #define INT_OFF do { \ |
496 | suppress_int++; \ | 518 | suppress_int++; \ |
497 | xbarrier(); \ | 519 | barrier(); \ |
498 | } while (0) | 520 | } while (0) |
499 | 521 | ||
500 | /* | 522 | /* |
@@ -522,7 +544,7 @@ raise_exception(int e) | |||
522 | #endif | 544 | #endif |
523 | 545 | ||
524 | /* | 546 | /* |
525 | * Called from trap.c when a SIGINT is received. (If the user specifies | 547 | * Called when a SIGINT is received. (If the user specifies |
526 | * that SIGINT is to be trapped or ignored using the trap builtin, then | 548 | * that SIGINT is to be trapped or ignored using the trap builtin, then |
527 | * this routine is not called.) Suppressint is nonzero when interrupts | 549 | * this routine is not called.) Suppressint is nonzero when interrupts |
528 | * are held using the INT_OFF macro. (The test for iflag is just | 550 | * are held using the INT_OFF macro. (The test for iflag is just |
@@ -549,6 +571,8 @@ raise_interrupt(void) | |||
549 | } | 571 | } |
550 | ex_type = EXINT; | 572 | ex_type = EXINT; |
551 | } | 573 | } |
574 | /* bash: ^C even on empty command line sets $? */ | ||
575 | exitstatus = SIGINT + 128; | ||
552 | raise_exception(ex_type); | 576 | raise_exception(ex_type); |
553 | /* NOTREACHED */ | 577 | /* NOTREACHED */ |
554 | } | 578 | } |
@@ -562,7 +586,7 @@ raise_interrupt(void) | |||
562 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | 586 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
563 | int_on(void) | 587 | int_on(void) |
564 | { | 588 | { |
565 | xbarrier(); | 589 | barrier(); |
566 | if (--suppress_int == 0 && pending_int) { | 590 | if (--suppress_int == 0 && pending_int) { |
567 | raise_interrupt(); | 591 | raise_interrupt(); |
568 | } | 592 | } |
@@ -571,7 +595,7 @@ int_on(void) | |||
571 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | 595 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
572 | force_int_on(void) | 596 | force_int_on(void) |
573 | { | 597 | { |
574 | xbarrier(); | 598 | barrier(); |
575 | suppress_int = 0; | 599 | suppress_int = 0; |
576 | if (pending_int) | 600 | if (pending_int) |
577 | raise_interrupt(); | 601 | raise_interrupt(); |
@@ -581,7 +605,7 @@ force_int_on(void) | |||
581 | #define SAVE_INT(v) ((v) = suppress_int) | 605 | #define SAVE_INT(v) ((v) = suppress_int) |
582 | 606 | ||
583 | #define RESTORE_INT(v) do { \ | 607 | #define RESTORE_INT(v) do { \ |
584 | xbarrier(); \ | 608 | barrier(); \ |
585 | suppress_int = (v); \ | 609 | suppress_int = (v); \ |
586 | if (suppress_int == 0 && pending_int) \ | 610 | if (suppress_int == 0 && pending_int) \ |
587 | raise_interrupt(); \ | 611 | raise_interrupt(); \ |
@@ -1237,6 +1261,12 @@ struct strpush { | |||
1237 | struct alias *ap; /* if push was associated with an alias */ | 1261 | struct alias *ap; /* if push was associated with an alias */ |
1238 | #endif | 1262 | #endif |
1239 | char *string; /* remember the string since it may change */ | 1263 | char *string; /* remember the string since it may change */ |
1264 | |||
1265 | /* Remember last two characters for pungetc. */ | ||
1266 | int lastc[2]; | ||
1267 | |||
1268 | /* Number of outstanding calls to pungetc. */ | ||
1269 | int unget; | ||
1240 | }; | 1270 | }; |
1241 | 1271 | ||
1242 | struct parsefile { | 1272 | struct parsefile { |
@@ -1249,6 +1279,12 @@ struct parsefile { | |||
1249 | char *buf; /* input buffer */ | 1279 | char *buf; /* input buffer */ |
1250 | struct strpush *strpush; /* for pushing strings at this level */ | 1280 | struct strpush *strpush; /* for pushing strings at this level */ |
1251 | struct strpush basestrpush; /* so pushing one is fast */ | 1281 | struct strpush basestrpush; /* so pushing one is fast */ |
1282 | |||
1283 | /* Remember last two characters for pungetc. */ | ||
1284 | int lastc[2]; | ||
1285 | |||
1286 | /* Number of outstanding calls to pungetc. */ | ||
1287 | int unget; | ||
1252 | }; | 1288 | }; |
1253 | 1289 | ||
1254 | static struct parsefile basepf; /* top level input file */ | 1290 | static struct parsefile basepf; /* top level input file */ |
@@ -1256,7 +1292,6 @@ static struct parsefile *g_parsefile = &basepf; /* current input file */ | |||
1256 | static int startlinno; /* line # where last token started */ | 1292 | static int startlinno; /* line # where last token started */ |
1257 | static char *commandname; /* currently executing command */ | 1293 | static char *commandname; /* currently executing command */ |
1258 | static struct strlist *cmdenviron; /* environment for builtin command */ | 1294 | static struct strlist *cmdenviron; /* environment for builtin command */ |
1259 | static uint8_t exitstatus; /* exit status of last command */ | ||
1260 | 1295 | ||
1261 | 1296 | ||
1262 | /* ============ Message printing */ | 1297 | /* ============ Message printing */ |
@@ -1428,13 +1463,11 @@ struct stackmark { | |||
1428 | struct stack_block *stackp; | 1463 | struct stack_block *stackp; |
1429 | char *stacknxt; | 1464 | char *stacknxt; |
1430 | size_t stacknleft; | 1465 | size_t stacknleft; |
1431 | struct stackmark *marknext; | ||
1432 | }; | 1466 | }; |
1433 | 1467 | ||
1434 | 1468 | ||
1435 | struct globals_memstack { | 1469 | struct globals_memstack { |
1436 | struct stack_block *g_stackp; // = &stackbase; | 1470 | struct stack_block *g_stackp; // = &stackbase; |
1437 | struct stackmark *markp; | ||
1438 | char *g_stacknxt; // = stackbase.space; | 1471 | char *g_stacknxt; // = stackbase.space; |
1439 | char *sstrend; // = stackbase.space + MINSIZE; | 1472 | char *sstrend; // = stackbase.space + MINSIZE; |
1440 | size_t g_stacknleft; // = MINSIZE; | 1473 | size_t g_stacknleft; // = MINSIZE; |
@@ -1444,7 +1477,6 @@ struct globals_memstack { | |||
1444 | extern struct globals_memstack *const ash_ptr_to_globals_memstack; | 1477 | extern struct globals_memstack *const ash_ptr_to_globals_memstack; |
1445 | #define G_memstack (*ash_ptr_to_globals_memstack) | 1478 | #define G_memstack (*ash_ptr_to_globals_memstack) |
1446 | #define g_stackp (G_memstack.g_stackp ) | 1479 | #define g_stackp (G_memstack.g_stackp ) |
1447 | #define markp (G_memstack.markp ) | ||
1448 | #define g_stacknxt (G_memstack.g_stacknxt ) | 1480 | #define g_stacknxt (G_memstack.g_stacknxt ) |
1449 | #define sstrend (G_memstack.sstrend ) | 1481 | #define sstrend (G_memstack.sstrend ) |
1450 | #define g_stacknleft (G_memstack.g_stacknleft) | 1482 | #define g_stacknleft (G_memstack.g_stacknleft) |
@@ -1528,20 +1560,33 @@ stunalloc(void *p) | |||
1528 | * Like strdup but works with the ash stack. | 1560 | * Like strdup but works with the ash stack. |
1529 | */ | 1561 | */ |
1530 | static char * | 1562 | static char * |
1531 | ststrdup(const char *p) | 1563 | sstrdup(const char *p) |
1532 | { | 1564 | { |
1533 | size_t len = strlen(p) + 1; | 1565 | size_t len = strlen(p) + 1; |
1534 | return memcpy(stalloc(len), p, len); | 1566 | return memcpy(stalloc(len), p, len); |
1535 | } | 1567 | } |
1536 | 1568 | ||
1537 | static void | 1569 | static void |
1538 | setstackmark(struct stackmark *mark) | 1570 | grabstackblock(size_t len) |
1571 | { | ||
1572 | len = SHELL_ALIGN(len); | ||
1573 | g_stacknxt += len; | ||
1574 | g_stacknleft -= len; | ||
1575 | } | ||
1576 | |||
1577 | static void | ||
1578 | pushstackmark(struct stackmark *mark, size_t len) | ||
1539 | { | 1579 | { |
1540 | mark->stackp = g_stackp; | 1580 | mark->stackp = g_stackp; |
1541 | mark->stacknxt = g_stacknxt; | 1581 | mark->stacknxt = g_stacknxt; |
1542 | mark->stacknleft = g_stacknleft; | 1582 | mark->stacknleft = g_stacknleft; |
1543 | mark->marknext = markp; | 1583 | grabstackblock(len); |
1544 | markp = mark; | 1584 | } |
1585 | |||
1586 | static void | ||
1587 | setstackmark(struct stackmark *mark) | ||
1588 | { | ||
1589 | pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase); | ||
1545 | } | 1590 | } |
1546 | 1591 | ||
1547 | static void | 1592 | static void |
@@ -1553,7 +1598,6 @@ popstackmark(struct stackmark *mark) | |||
1553 | return; | 1598 | return; |
1554 | 1599 | ||
1555 | INT_OFF; | 1600 | INT_OFF; |
1556 | markp = mark->marknext; | ||
1557 | while (g_stackp != mark->stackp) { | 1601 | while (g_stackp != mark->stackp) { |
1558 | sp = g_stackp; | 1602 | sp = g_stackp; |
1559 | g_stackp = sp->prev; | 1603 | g_stackp = sp->prev; |
@@ -1586,14 +1630,11 @@ growstackblock(void) | |||
1586 | newlen += 128; | 1630 | newlen += 128; |
1587 | 1631 | ||
1588 | if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { | 1632 | if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { |
1589 | struct stack_block *oldstackp; | ||
1590 | struct stackmark *xmark; | ||
1591 | struct stack_block *sp; | 1633 | struct stack_block *sp; |
1592 | struct stack_block *prevstackp; | 1634 | struct stack_block *prevstackp; |
1593 | size_t grosslen; | 1635 | size_t grosslen; |
1594 | 1636 | ||
1595 | INT_OFF; | 1637 | INT_OFF; |
1596 | oldstackp = g_stackp; | ||
1597 | sp = g_stackp; | 1638 | sp = g_stackp; |
1598 | prevstackp = sp->prev; | 1639 | prevstackp = sp->prev; |
1599 | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; | 1640 | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; |
@@ -1603,18 +1644,6 @@ growstackblock(void) | |||
1603 | g_stacknxt = sp->space; | 1644 | g_stacknxt = sp->space; |
1604 | g_stacknleft = newlen; | 1645 | g_stacknleft = newlen; |
1605 | sstrend = sp->space + newlen; | 1646 | sstrend = sp->space + newlen; |
1606 | |||
1607 | /* | ||
1608 | * Stack marks pointing to the start of the old block | ||
1609 | * must be relocated to point to the new block | ||
1610 | */ | ||
1611 | xmark = markp; | ||
1612 | while (xmark != NULL && xmark->stackp == oldstackp) { | ||
1613 | xmark->stackp = g_stackp; | ||
1614 | xmark->stacknxt = g_stacknxt; | ||
1615 | xmark->stacknleft = g_stacknleft; | ||
1616 | xmark = xmark->marknext; | ||
1617 | } | ||
1618 | INT_ON; | 1647 | INT_ON; |
1619 | } else { | 1648 | } else { |
1620 | char *oldspace = g_stacknxt; | 1649 | char *oldspace = g_stacknxt; |
@@ -1627,14 +1656,6 @@ growstackblock(void) | |||
1627 | } | 1656 | } |
1628 | } | 1657 | } |
1629 | 1658 | ||
1630 | static void | ||
1631 | grabstackblock(size_t len) | ||
1632 | { | ||
1633 | len = SHELL_ALIGN(len); | ||
1634 | g_stacknxt += len; | ||
1635 | g_stacknleft -= len; | ||
1636 | } | ||
1637 | |||
1638 | /* | 1659 | /* |
1639 | * The following routines are somewhat easier to use than the above. | 1660 | * The following routines are somewhat easier to use than the above. |
1640 | * The user declares a variable of type STACKSTR, which may be declared | 1661 | * The user declares a variable of type STACKSTR, which may be declared |
@@ -1671,7 +1692,7 @@ static char * | |||
1671 | makestrspace(size_t newlen, char *p) | 1692 | makestrspace(size_t newlen, char *p) |
1672 | { | 1693 | { |
1673 | size_t len = p - g_stacknxt; | 1694 | size_t len = p - g_stacknxt; |
1674 | size_t size = stackblocksize(); | 1695 | size_t size; |
1675 | 1696 | ||
1676 | for (;;) { | 1697 | for (;;) { |
1677 | size_t nleft; | 1698 | size_t nleft; |
@@ -1990,7 +2011,7 @@ static const struct { | |||
1990 | { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, | 2011 | { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, |
1991 | { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, | 2012 | { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, |
1992 | #if ENABLE_ASH_GETOPTS | 2013 | #if ENABLE_ASH_GETOPTS |
1993 | { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset }, | 2014 | { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, |
1994 | #endif | 2015 | #endif |
1995 | #if ENABLE_ASH_RANDOM_SUPPORT | 2016 | #if ENABLE_ASH_RANDOM_SUPPORT |
1996 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, | 2017 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, |
@@ -2193,7 +2214,8 @@ lookupvar(const char *name) | |||
2193 | return NULL; | 2214 | return NULL; |
2194 | } | 2215 | } |
2195 | 2216 | ||
2196 | static void reinit_unicode_for_ash(void) | 2217 | static void |
2218 | reinit_unicode_for_ash(void) | ||
2197 | { | 2219 | { |
2198 | /* Unicode support should be activated even if LANG is set | 2220 | /* Unicode support should be activated even if LANG is set |
2199 | * _during_ shell execution, not only if it was set when | 2221 | * _during_ shell execution, not only if it was set when |
@@ -2560,8 +2582,7 @@ setprompt_if(smallint do_set, int whichprompt) | |||
2560 | prompt = nullstr; | 2582 | prompt = nullstr; |
2561 | } | 2583 | } |
2562 | #if ENABLE_ASH_EXPAND_PRMT | 2584 | #if ENABLE_ASH_EXPAND_PRMT |
2563 | setstackmark(&smark); | 2585 | pushstackmark(&smark, stackblocksize()); |
2564 | stalloc(stackblocksize()); | ||
2565 | #endif | 2586 | #endif |
2566 | putprompt(expandstr(prompt)); | 2587 | putprompt(expandstr(prompt)); |
2567 | #if ENABLE_ASH_EXPAND_PRMT | 2588 | #if ENABLE_ASH_EXPAND_PRMT |
@@ -2704,7 +2725,7 @@ updatepwd(const char *dir) | |||
2704 | char *cdcomppath; | 2725 | char *cdcomppath; |
2705 | const char *lim; | 2726 | const char *lim; |
2706 | 2727 | ||
2707 | cdcomppath = ststrdup(dir); | 2728 | cdcomppath = sstrdup(dir); |
2708 | STARTSTACKSTR(new); | 2729 | STARTSTACKSTR(new); |
2709 | if (*dir != '/') { | 2730 | if (*dir != '/') { |
2710 | if (curdir == nullstr) | 2731 | if (curdir == nullstr) |
@@ -3000,18 +3021,27 @@ enum { | |||
3000 | static int | 3021 | static int |
3001 | SIT(int c, int syntax) | 3022 | SIT(int c, int syntax) |
3002 | { | 3023 | { |
3003 | static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; | 3024 | /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */ |
3025 | static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~"; | ||
3026 | /* | ||
3027 | * This causes '/' to be prepended with CTLESC in dquoted string, | ||
3028 | * making "./file"* treated incorrectly because we feed | ||
3029 | * ".\/file*" string to glob(), confusing it (see expandmeta func). | ||
3030 | * The "homegrown" glob implementation is okay with that, | ||
3031 | * but glibc one isn't. With '/' always treated as CWORD, | ||
3032 | * both work fine. | ||
3033 | */ | ||
3004 | # if ENABLE_ASH_ALIAS | 3034 | # if ENABLE_ASH_ALIAS |
3005 | static const uint8_t syntax_index_table[] ALIGN1 = { | 3035 | static const uint8_t syntax_index_table[] ALIGN1 = { |
3006 | 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ | 3036 | 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ |
3007 | 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ | 3037 | 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */ |
3008 | 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ | 3038 | 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ |
3009 | 11, 3 /* "}~" */ | 3039 | 11, 3 /* "}~" */ |
3010 | }; | 3040 | }; |
3011 | # else | 3041 | # else |
3012 | static const uint8_t syntax_index_table[] ALIGN1 = { | 3042 | static const uint8_t syntax_index_table[] ALIGN1 = { |
3013 | 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ | 3043 | 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ |
3014 | 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ | 3044 | 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */ |
3015 | 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ | 3045 | 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ |
3016 | 10, 2 /* "}~" */ | 3046 | 10, 2 /* "}~" */ |
3017 | }; | 3047 | }; |
@@ -3093,7 +3123,8 @@ static const uint8_t syntax_index_table[] ALIGN1 = { | |||
3093 | /* 44 "," */ CWORD_CWORD_CWORD_CWORD, | 3123 | /* 44 "," */ CWORD_CWORD_CWORD_CWORD, |
3094 | /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, | 3124 | /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, |
3095 | /* 46 "." */ CWORD_CWORD_CWORD_CWORD, | 3125 | /* 46 "." */ CWORD_CWORD_CWORD_CWORD, |
3096 | /* 47 "/" */ CWORD_CCTL_CCTL_CWORD, | 3126 | /* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */ |
3127 | /* 47 "/" */ CWORD_CWORD_CWORD_CWORD, | ||
3097 | /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, | 3128 | /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, |
3098 | /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, | 3129 | /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, |
3099 | /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, | 3130 | /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, |
@@ -3335,7 +3366,8 @@ static struct alias **atab; // [ATABSIZE]; | |||
3335 | 3366 | ||
3336 | 3367 | ||
3337 | static struct alias ** | 3368 | static struct alias ** |
3338 | __lookupalias(const char *name) { | 3369 | __lookupalias(const char *name) |
3370 | { | ||
3339 | unsigned int hashval; | 3371 | unsigned int hashval; |
3340 | struct alias **app; | 3372 | struct alias **app; |
3341 | const char *p; | 3373 | const char *p; |
@@ -3517,8 +3549,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
3517 | #endif /* ASH_ALIAS */ | 3549 | #endif /* ASH_ALIAS */ |
3518 | 3550 | ||
3519 | 3551 | ||
3520 | /* ============ jobs.c */ | ||
3521 | |||
3522 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ | 3552 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ |
3523 | #define FORK_FG 0 | 3553 | #define FORK_FG 0 |
3524 | #define FORK_BG 1 | 3554 | #define FORK_BG 1 |
@@ -3842,7 +3872,7 @@ getjob(const char *name, int getctl) | |||
3842 | 3872 | ||
3843 | if (is_number(p)) { | 3873 | if (is_number(p)) { |
3844 | num = atoi(p); | 3874 | num = atoi(p); |
3845 | if (num <= njobs) { | 3875 | if (num > 0 && num <= njobs) { |
3846 | jp = jobtab + num - 1; | 3876 | jp = jobtab + num - 1; |
3847 | if (jp->used) | 3877 | if (jp->used) |
3848 | goto gotit; | 3878 | goto gotit; |
@@ -5419,6 +5449,19 @@ openredirect(union node *redir) | |||
5419 | char *fname; | 5449 | char *fname; |
5420 | int f; | 5450 | int f; |
5421 | 5451 | ||
5452 | switch (redir->nfile.type) { | ||
5453 | /* Can't happen, our single caller does this itself */ | ||
5454 | // case NTOFD: | ||
5455 | // case NFROMFD: | ||
5456 | // return -1; | ||
5457 | case NHERE: | ||
5458 | case NXHERE: | ||
5459 | return openhere(redir); | ||
5460 | } | ||
5461 | |||
5462 | /* For N[X]HERE, reading redir->nfile.expfname would touch beyond | ||
5463 | * allocated space. Do it only when we know it is safe. | ||
5464 | */ | ||
5422 | fname = redir->nfile.expfname; | 5465 | fname = redir->nfile.expfname; |
5423 | #if ENABLE_PLATFORM_MINGW32 | 5466 | #if ENABLE_PLATFORM_MINGW32 |
5424 | /* Support for /dev/null */ | 5467 | /* Support for /dev/null */ |
@@ -5445,7 +5488,12 @@ openredirect(union node *redir) | |||
5445 | break; | 5488 | break; |
5446 | } | 5489 | } |
5447 | #endif | 5490 | #endif |
5491 | |||
5448 | switch (redir->nfile.type) { | 5492 | switch (redir->nfile.type) { |
5493 | default: | ||
5494 | #if DEBUG | ||
5495 | abort(); | ||
5496 | #endif | ||
5449 | case NFROM: | 5497 | case NFROM: |
5450 | f = open(fname, O_RDONLY); | 5498 | f = open(fname, O_RDONLY); |
5451 | if (f < 0) | 5499 | if (f < 0) |
@@ -5481,20 +5529,6 @@ openredirect(union node *redir) | |||
5481 | lseek(f, 0, SEEK_END); | 5529 | lseek(f, 0, SEEK_END); |
5482 | #endif | 5530 | #endif |
5483 | break; | 5531 | break; |
5484 | default: | ||
5485 | #if DEBUG | ||
5486 | abort(); | ||
5487 | #endif | ||
5488 | /* Fall through to eliminate warning. */ | ||
5489 | /* Our single caller does this itself */ | ||
5490 | // case NTOFD: | ||
5491 | // case NFROMFD: | ||
5492 | // f = -1; | ||
5493 | // break; | ||
5494 | case NHERE: | ||
5495 | case NXHERE: | ||
5496 | f = openhere(redir); | ||
5497 | break; | ||
5498 | } | 5532 | } |
5499 | 5533 | ||
5500 | return f; | 5534 | return f; |
@@ -5548,7 +5582,8 @@ struct redirtab { | |||
5548 | }; | 5582 | }; |
5549 | #define redirlist (G_var.redirlist) | 5583 | #define redirlist (G_var.redirlist) |
5550 | 5584 | ||
5551 | static int need_to_remember(struct redirtab *rp, int fd) | 5585 | static int |
5586 | need_to_remember(struct redirtab *rp, int fd) | ||
5552 | { | 5587 | { |
5553 | int i; | 5588 | int i; |
5554 | 5589 | ||
@@ -5565,7 +5600,8 @@ static int need_to_remember(struct redirtab *rp, int fd) | |||
5565 | } | 5600 | } |
5566 | 5601 | ||
5567 | /* "hidden" fd is a fd used to read scripts, or a copy of such */ | 5602 | /* "hidden" fd is a fd used to read scripts, or a copy of such */ |
5568 | static int is_hidden_fd(struct redirtab *rp, int fd) | 5603 | static int |
5604 | is_hidden_fd(struct redirtab *rp, int fd) | ||
5569 | { | 5605 | { |
5570 | int i; | 5606 | int i; |
5571 | struct parsefile *pf; | 5607 | struct parsefile *pf; |
@@ -5979,7 +6015,6 @@ rmescapes(char *str, int flag) | |||
5979 | while (*p) { | 6015 | while (*p) { |
5980 | if ((unsigned char)*p == CTLQUOTEMARK) { | 6016 | if ((unsigned char)*p == CTLQUOTEMARK) { |
5981 | // Note: both inquotes and protect_against_glob only affect whether | 6017 | // Note: both inquotes and protect_against_glob only affect whether |
5982 | // CTLESC,<ch> gets converted to <ch> or to \<ch> | ||
5983 | inquotes = ~inquotes; | 6018 | inquotes = ~inquotes; |
5984 | p++; | 6019 | p++; |
5985 | protect_against_glob = globbing; | 6020 | protect_against_glob = globbing; |
@@ -5987,6 +6022,10 @@ rmescapes(char *str, int flag) | |||
5987 | } | 6022 | } |
5988 | if ((unsigned char)*p == CTLESC) { | 6023 | if ((unsigned char)*p == CTLESC) { |
5989 | p++; | 6024 | p++; |
6025 | #if DEBUG | ||
6026 | if (*p == '\0') | ||
6027 | ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)"); | ||
6028 | #endif | ||
5990 | if (protect_against_glob) { | 6029 | if (protect_against_glob) { |
5991 | *q++ = '\\'; | 6030 | *q++ = '\\'; |
5992 | } | 6031 | } |
@@ -6043,11 +6082,14 @@ memtodest(const char *p, size_t len, int syntax, int quotes) | |||
6043 | unsigned char c = *p++; | 6082 | unsigned char c = *p++; |
6044 | if (c) { | 6083 | if (c) { |
6045 | int n = SIT(c, syntax); | 6084 | int n = SIT(c, syntax); |
6046 | if ((quotes & QUOTES_ESC) && | 6085 | if ((quotes & QUOTES_ESC) |
6047 | ((n == CCTL) || | 6086 | && ((n == CCTL) |
6048 | (((quotes & EXP_FULL) || syntax != BASESYNTAX) && | 6087 | || (((quotes & EXP_FULL) || syntax != BASESYNTAX) |
6049 | n == CBACK))) | 6088 | && n == CBACK) |
6089 | ) | ||
6090 | ) { | ||
6050 | USTPUTC(CTLESC, q); | 6091 | USTPUTC(CTLESC, q); |
6092 | } | ||
6051 | } else if (!(quotes & QUOTES_KEEPNUL)) | 6093 | } else if (!(quotes & QUOTES_KEEPNUL)) |
6052 | continue; | 6094 | continue; |
6053 | USTPUTC(c, q); | 6095 | USTPUTC(c, q); |
@@ -6188,9 +6230,8 @@ struct backcmd { /* result of evalbackcmd */ | |||
6188 | }; | 6230 | }; |
6189 | 6231 | ||
6190 | /* These forward decls are needed to use "eval" code for backticks handling: */ | 6232 | /* These forward decls are needed to use "eval" code for backticks handling: */ |
6191 | static uint8_t back_exitstatus; /* exit status of backquoted command */ | ||
6192 | #define EV_EXIT 01 /* exit after evaluating tree */ | 6233 | #define EV_EXIT 01 /* exit after evaluating tree */ |
6193 | static void evaltree(union node *, int); | 6234 | static int evaltree(union node *, int); |
6194 | 6235 | ||
6195 | static void FAST_FUNC | 6236 | static void FAST_FUNC |
6196 | evalbackcmd(union node *n, struct backcmd *result) | 6237 | evalbackcmd(union node *n, struct backcmd *result) |
@@ -6262,10 +6303,8 @@ expbackq(union node *cmd, int flag) | |||
6262 | struct stackmark smark; | 6303 | struct stackmark smark; |
6263 | 6304 | ||
6264 | INT_OFF; | 6305 | INT_OFF; |
6265 | setstackmark(&smark); | 6306 | startloc = expdest - (char *)stackblock(); |
6266 | dest = expdest; | 6307 | pushstackmark(&smark, startloc); |
6267 | startloc = dest - (char *)stackblock(); | ||
6268 | grabstackstr(dest); | ||
6269 | evalbackcmd(cmd, &in); | 6308 | evalbackcmd(cmd, &in); |
6270 | popstackmark(&smark); | 6309 | popstackmark(&smark); |
6271 | 6310 | ||
@@ -6681,6 +6720,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6681 | 6720 | ||
6682 | #if ENABLE_ASH_BASH_COMPAT | 6721 | #if ENABLE_ASH_BASH_COMPAT |
6683 | case VSSUBSTR: | 6722 | case VSSUBSTR: |
6723 | //TODO: support more general format ${v:EXPR:EXPR}, | ||
6724 | // where EXPR follows $(()) rules | ||
6684 | loc = str = stackblock() + strloc; | 6725 | loc = str = stackblock() + strloc; |
6685 | /* Read POS in ${var:POS:LEN} */ | 6726 | /* Read POS in ${var:POS:LEN} */ |
6686 | pos = atoi(loc); /* number(loc) errors out on "1:4" */ | 6727 | pos = atoi(loc); /* number(loc) errors out on "1:4" */ |
@@ -6791,7 +6832,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6791 | char *idx, *end; | 6832 | char *idx, *end; |
6792 | 6833 | ||
6793 | if (!repl) { | 6834 | if (!repl) { |
6794 | if ((repl=strchr(str, CTLESC))) | 6835 | repl = strchr(str, CTLESC); |
6836 | if (repl) | ||
6795 | *repl++ = '\0'; | 6837 | *repl++ = '\0'; |
6796 | else | 6838 | else |
6797 | repl = nullstr; | 6839 | repl = nullstr; |
@@ -6924,20 +6966,21 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6924 | * ash -c 'echo ${#1#}' name:'1=#' | 6966 | * ash -c 'echo ${#1#}' name:'1=#' |
6925 | */ | 6967 | */ |
6926 | static NOINLINE ssize_t | 6968 | static NOINLINE ssize_t |
6927 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | 6969 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp) |
6928 | { | 6970 | { |
6929 | const char *p; | 6971 | const char *p; |
6930 | int num; | 6972 | int num; |
6931 | int i; | 6973 | int i; |
6932 | ssize_t len = 0; | 6974 | ssize_t len = 0; |
6933 | int sep; | 6975 | int sep; |
6934 | int quoted = flags & EXP_QUOTED; | 6976 | int quoted = *quotedp; |
6935 | int subtype = varflags & VSTYPE; | 6977 | int subtype = varflags & VSTYPE; |
6936 | int discard = subtype == VSPLUS || subtype == VSLENGTH; | 6978 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
6937 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; | 6979 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
6938 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; | 6980 | int syntax; |
6939 | 6981 | ||
6940 | sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; | 6982 | sep = (flags & EXP_FULL) << CHAR_BIT; |
6983 | syntax = quoted ? DQSYNTAX : BASESYNTAX; | ||
6941 | 6984 | ||
6942 | switch (*name) { | 6985 | switch (*name) { |
6943 | case '$': | 6986 | case '$': |
@@ -6971,21 +7014,21 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6971 | raise_error_syntax("bad substitution"); | 7014 | raise_error_syntax("bad substitution"); |
6972 | #endif | 7015 | #endif |
6973 | break; | 7016 | break; |
6974 | case '@': { | 7017 | case '@': |
7018 | if (quoted && sep) | ||
7019 | goto param; | ||
7020 | /* fall through */ | ||
7021 | case '*': { | ||
6975 | char **ap; | 7022 | char **ap; |
6976 | char sepc; | 7023 | char sepc; |
6977 | 7024 | ||
6978 | if (quoted && (flags & EXP_FULL)) { | 7025 | if (quoted) |
6979 | /* note: this is not meant as PEOF value */ | 7026 | sep = 0; |
6980 | sep = 1 << CHAR_BIT; | 7027 | sep |= ifsset() ? ifsval()[0] : ' '; |
6981 | goto param; | ||
6982 | } | ||
6983 | /* fall through */ | ||
6984 | case '*': | ||
6985 | sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; | ||
6986 | param: | 7028 | param: |
6987 | ap = shellparam.p; | ||
6988 | sepc = sep; | 7029 | sepc = sep; |
7030 | *quotedp = !sepc; | ||
7031 | ap = shellparam.p; | ||
6989 | if (!ap) | 7032 | if (!ap) |
6990 | return -1; | 7033 | return -1; |
6991 | while ((p = *ap++) != NULL) { | 7034 | while ((p = *ap++) != NULL) { |
@@ -6997,7 +7040,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6997 | } | 7040 | } |
6998 | } | 7041 | } |
6999 | break; | 7042 | break; |
7000 | } /* case '@' and '*' */ | 7043 | } /* case '*' */ |
7001 | case '0': | 7044 | case '0': |
7002 | case '1': | 7045 | case '1': |
7003 | case '2': | 7046 | case '2': |
@@ -7069,7 +7112,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
7069 | * input string. | 7112 | * input string. |
7070 | */ | 7113 | */ |
7071 | static char * | 7114 | static char * |
7072 | evalvar(char *p, int flags, struct strlist *var_str_list) | 7115 | evalvar(char *p, int flag, struct strlist *var_str_list) |
7073 | { | 7116 | { |
7074 | char varflags; | 7117 | char varflags; |
7075 | char subtype; | 7118 | char subtype; |
@@ -7082,14 +7125,14 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7082 | 7125 | ||
7083 | varflags = (unsigned char) *p++; | 7126 | varflags = (unsigned char) *p++; |
7084 | subtype = varflags & VSTYPE; | 7127 | subtype = varflags & VSTYPE; |
7085 | quoted = flags & EXP_QUOTED; | 7128 | quoted = flag & EXP_QUOTED; |
7086 | var = p; | 7129 | var = p; |
7087 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | 7130 | easy = (!quoted || (*var == '@' && shellparam.nparam)); |
7088 | startloc = expdest - (char *)stackblock(); | 7131 | startloc = expdest - (char *)stackblock(); |
7089 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? | 7132 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? |
7090 | 7133 | ||
7091 | again: | 7134 | again: |
7092 | varlen = varvalue(var, varflags, flags, var_str_list); | 7135 | varlen = varvalue(var, varflags, flag, var_str_list, "ed); |
7093 | if (varflags & VSNUL) | 7136 | if (varflags & VSNUL) |
7094 | varlen--; | 7137 | varlen--; |
7095 | 7138 | ||
@@ -7103,36 +7146,27 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7103 | if (varlen < 0) { | 7146 | if (varlen < 0) { |
7104 | argstr( | 7147 | argstr( |
7105 | p, | 7148 | p, |
7106 | flags | EXP_TILDE | EXP_WORD, | 7149 | flag | EXP_TILDE | EXP_WORD, |
7107 | var_str_list | 7150 | var_str_list |
7108 | ); | 7151 | ); |
7109 | goto end; | 7152 | goto end; |
7110 | } | 7153 | } |
7111 | if (easy) | 7154 | goto record; |
7112 | goto record; | ||
7113 | goto end; | ||
7114 | } | 7155 | } |
7115 | 7156 | ||
7116 | if (subtype == VSASSIGN || subtype == VSQUESTION) { | 7157 | if (subtype == VSASSIGN || subtype == VSQUESTION) { |
7117 | if (varlen < 0) { | 7158 | if (varlen >= 0) |
7118 | if (subevalvar(p, var, /* strloc: */ 0, | ||
7119 | subtype, startloc, varflags, | ||
7120 | /* quotes: */ flags & ~QUOTES_ESC, | ||
7121 | var_str_list) | ||
7122 | ) { | ||
7123 | varflags &= ~VSNUL; | ||
7124 | /* | ||
7125 | * Remove any recorded regions beyond | ||
7126 | * start of variable | ||
7127 | */ | ||
7128 | removerecordregions(startloc); | ||
7129 | goto again; | ||
7130 | } | ||
7131 | goto end; | ||
7132 | } | ||
7133 | if (easy) | ||
7134 | goto record; | 7159 | goto record; |
7135 | goto end; | 7160 | |
7161 | subevalvar(p, var, 0, subtype, startloc, varflags, | ||
7162 | flag & ~QUOTES_ESC, var_str_list); | ||
7163 | varflags &= ~VSNUL; | ||
7164 | /* | ||
7165 | * Remove any recorded regions beyond | ||
7166 | * start of variable | ||
7167 | */ | ||
7168 | removerecordregions(startloc); | ||
7169 | goto again; | ||
7136 | } | 7170 | } |
7137 | 7171 | ||
7138 | if (varlen < 0 && uflag) | 7172 | if (varlen < 0 && uflag) |
@@ -7144,8 +7178,10 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7144 | } | 7178 | } |
7145 | 7179 | ||
7146 | if (subtype == VSNORMAL) { | 7180 | if (subtype == VSNORMAL) { |
7147 | if (easy) | 7181 | record: |
7148 | goto record; | 7182 | if (!easy) |
7183 | goto end; | ||
7184 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
7149 | goto end; | 7185 | goto end; |
7150 | } | 7186 | } |
7151 | 7187 | ||
@@ -7174,7 +7210,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7174 | STPUTC('\0', expdest); | 7210 | STPUTC('\0', expdest); |
7175 | patloc = expdest - (char *)stackblock(); | 7211 | patloc = expdest - (char *)stackblock(); |
7176 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, | 7212 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, |
7177 | startloc, varflags, flags, var_str_list)) { | 7213 | startloc, varflags, flag, var_str_list)) { |
7178 | int amount = expdest - ( | 7214 | int amount = expdest - ( |
7179 | (char *)stackblock() + patloc - 1 | 7215 | (char *)stackblock() + patloc - 1 |
7180 | ); | 7216 | ); |
@@ -7182,8 +7218,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7182 | } | 7218 | } |
7183 | /* Remove any recorded regions beyond start of variable */ | 7219 | /* Remove any recorded regions beyond start of variable */ |
7184 | removerecordregions(startloc); | 7220 | removerecordregions(startloc); |
7185 | record: | 7221 | goto record; |
7186 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
7187 | } | 7222 | } |
7188 | 7223 | ||
7189 | end: | 7224 | end: |
@@ -7327,11 +7362,90 @@ addfname(const char *name) | |||
7327 | struct strlist *sp; | 7362 | struct strlist *sp; |
7328 | 7363 | ||
7329 | sp = stzalloc(sizeof(*sp)); | 7364 | sp = stzalloc(sizeof(*sp)); |
7330 | sp->text = ststrdup(name); | 7365 | sp->text = sstrdup(name); |
7331 | *exparg.lastp = sp; | 7366 | *exparg.lastp = sp; |
7332 | exparg.lastp = &sp->next; | 7367 | exparg.lastp = &sp->next; |
7333 | } | 7368 | } |
7334 | 7369 | ||
7370 | /* If we want to use glob() from libc... */ | ||
7371 | #if !ENABLE_ASH_INTERNAL_GLOB | ||
7372 | |||
7373 | /* Add the result of glob() to the list */ | ||
7374 | static void | ||
7375 | addglob(const glob_t *pglob) | ||
7376 | { | ||
7377 | char **p = pglob->gl_pathv; | ||
7378 | |||
7379 | do { | ||
7380 | addfname(*p); | ||
7381 | } while (*++p); | ||
7382 | } | ||
7383 | static void | ||
7384 | expandmeta(struct strlist *str /*, int flag*/) | ||
7385 | { | ||
7386 | /* TODO - EXP_REDIR */ | ||
7387 | |||
7388 | while (str) { | ||
7389 | char *p; | ||
7390 | glob_t pglob; | ||
7391 | int i; | ||
7392 | |||
7393 | if (fflag) | ||
7394 | goto nometa; | ||
7395 | INT_OFF; | ||
7396 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | ||
7397 | // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match | ||
7398 | // GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) | ||
7399 | // | ||
7400 | // glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping: | ||
7401 | // if you pass it "file\?", it returns "file\?", not "file?", if no match. | ||
7402 | // Which means you need to unescape the string, right? Not so fast: | ||
7403 | // if there _is_ a file named "file\?" (with backslash), it is returned | ||
7404 | // as "file\?" too (whichever pattern you used to find it, say, "file*"). | ||
7405 | // You DONT KNOW by looking at the result whether you need to unescape it. | ||
7406 | // | ||
7407 | // Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?", | ||
7408 | // returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file. | ||
7409 | // Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match). | ||
7410 | // With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly. | ||
7411 | // i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob); | ||
7412 | // i = glob(p, GLOB_NOMAGIC, NULL, &pglob); | ||
7413 | i = glob(p, 0, NULL, &pglob); | ||
7414 | //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-"); | ||
7415 | if (p != str->text) | ||
7416 | free(p); | ||
7417 | switch (i) { | ||
7418 | case 0: | ||
7419 | #if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR | ||
7420 | /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */ | ||
7421 | if (!(pglob.gl_flags & GLOB_MAGCHAR)) | ||
7422 | goto nometa2; | ||
7423 | #endif | ||
7424 | addglob(&pglob); | ||
7425 | globfree(&pglob); | ||
7426 | INT_ON; | ||
7427 | break; | ||
7428 | case GLOB_NOMATCH: | ||
7429 | //nometa2: | ||
7430 | globfree(&pglob); | ||
7431 | INT_ON; | ||
7432 | nometa: | ||
7433 | *exparg.lastp = str; | ||
7434 | rmescapes(str->text, 0); | ||
7435 | exparg.lastp = &str->next; | ||
7436 | break; | ||
7437 | default: /* GLOB_NOSPACE */ | ||
7438 | globfree(&pglob); | ||
7439 | INT_ON; | ||
7440 | ash_msg_and_raise_error(bb_msg_memory_exhausted); | ||
7441 | } | ||
7442 | str = str->next; | ||
7443 | } | ||
7444 | } | ||
7445 | |||
7446 | #else | ||
7447 | /* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */ | ||
7448 | |||
7335 | /* | 7449 | /* |
7336 | * Do metacharacter (i.e. *, ?, [...]) expansion. | 7450 | * Do metacharacter (i.e. *, ?, [...]) expansion. |
7337 | */ | 7451 | */ |
@@ -7535,7 +7649,8 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7535 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 7649 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
7536 | { | 7650 | { |
7537 | int i = strlen(str->text); | 7651 | int i = strlen(str->text); |
7538 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ | 7652 | //BUGGY estimation of how long expanded name can be |
7653 | expdir = ckmalloc(i < 2048 ? 2048 : i+1); | ||
7539 | } | 7654 | } |
7540 | expmeta(expdir, expdir, p); | 7655 | expmeta(expdir, expdir, p); |
7541 | free(expdir); | 7656 | free(expdir); |
@@ -7560,6 +7675,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7560 | str = str->next; | 7675 | str = str->next; |
7561 | } | 7676 | } |
7562 | } | 7677 | } |
7678 | #endif /* ENABLE_ASH_INTERNAL_GLOB */ | ||
7563 | 7679 | ||
7564 | /* | 7680 | /* |
7565 | * Perform variable substitution and command substitution on an argument, | 7681 | * Perform variable substitution and command substitution on an argument, |
@@ -8126,49 +8242,86 @@ enum { | |||
8126 | }; | 8242 | }; |
8127 | typedef smallint token_id_t; | 8243 | typedef smallint token_id_t; |
8128 | 8244 | ||
8129 | /* first char is indicating which tokens mark the end of a list */ | 8245 | /* Nth bit indicates if token marks the end of a list */ |
8246 | enum { | ||
8247 | tokendlist = 0 | ||
8248 | /* 0 */ | (1u << TEOF) | ||
8249 | /* 1 */ | (0u << TNL) | ||
8250 | /* 2 */ | (0u << TREDIR) | ||
8251 | /* 3 */ | (0u << TWORD) | ||
8252 | /* 4 */ | (0u << TSEMI) | ||
8253 | /* 5 */ | (0u << TBACKGND) | ||
8254 | /* 6 */ | (0u << TAND) | ||
8255 | /* 7 */ | (0u << TOR) | ||
8256 | /* 8 */ | (0u << TPIPE) | ||
8257 | /* 9 */ | (0u << TLP) | ||
8258 | /* 10 */ | (1u << TRP) | ||
8259 | /* 11 */ | (1u << TENDCASE) | ||
8260 | /* 12 */ | (1u << TENDBQUOTE) | ||
8261 | /* 13 */ | (0u << TNOT) | ||
8262 | /* 14 */ | (0u << TCASE) | ||
8263 | /* 15 */ | (1u << TDO) | ||
8264 | /* 16 */ | (1u << TDONE) | ||
8265 | /* 17 */ | (1u << TELIF) | ||
8266 | /* 18 */ | (1u << TELSE) | ||
8267 | /* 19 */ | (1u << TESAC) | ||
8268 | /* 20 */ | (1u << TFI) | ||
8269 | /* 21 */ | (0u << TFOR) | ||
8270 | #if ENABLE_ASH_BASH_COMPAT | ||
8271 | /* 22 */ | (0u << TFUNCTION) | ||
8272 | #endif | ||
8273 | /* 23 */ | (0u << TIF) | ||
8274 | /* 24 */ | (0u << TIN) | ||
8275 | /* 25 */ | (1u << TTHEN) | ||
8276 | /* 26 */ | (0u << TUNTIL) | ||
8277 | /* 27 */ | (0u << TWHILE) | ||
8278 | /* 28 */ | (0u << TBEGIN) | ||
8279 | /* 29 */ | (1u << TEND) | ||
8280 | , /* thus far 29 bits used */ | ||
8281 | }; | ||
8282 | |||
8130 | static const char *const tokname_array[] = { | 8283 | static const char *const tokname_array[] = { |
8131 | "\1end of file", | 8284 | "end of file", |
8132 | "\0newline", | 8285 | "newline", |
8133 | "\0redirection", | 8286 | "redirection", |
8134 | "\0word", | 8287 | "word", |
8135 | "\0;", | 8288 | ";", |
8136 | "\0&", | 8289 | "&", |
8137 | "\0&&", | 8290 | "&&", |
8138 | "\0||", | 8291 | "||", |
8139 | "\0|", | 8292 | "|", |
8140 | "\0(", | 8293 | "(", |
8141 | "\1)", | 8294 | ")", |
8142 | "\1;;", | 8295 | ";;", |
8143 | "\1`", | 8296 | "`", |
8144 | #define KWDOFFSET 13 | 8297 | #define KWDOFFSET 13 |
8145 | /* the following are keywords */ | 8298 | /* the following are keywords */ |
8146 | "\0!", | 8299 | "!", |
8147 | "\0case", | 8300 | "case", |
8148 | "\1do", | 8301 | "do", |
8149 | "\1done", | 8302 | "done", |
8150 | "\1elif", | 8303 | "elif", |
8151 | "\1else", | 8304 | "else", |
8152 | "\1esac", | 8305 | "esac", |
8153 | "\1fi", | 8306 | "fi", |
8154 | "\0for", | 8307 | "for", |
8155 | #if ENABLE_ASH_BASH_COMPAT | 8308 | #if ENABLE_ASH_BASH_COMPAT |
8156 | "\0function", | 8309 | "function", |
8157 | #endif | 8310 | #endif |
8158 | "\0if", | 8311 | "if", |
8159 | "\0in", | 8312 | "in", |
8160 | "\1then", | 8313 | "then", |
8161 | "\0until", | 8314 | "until", |
8162 | "\0while", | 8315 | "while", |
8163 | "\0{", | 8316 | "{", |
8164 | "\1}", | 8317 | "}", |
8165 | }; | 8318 | }; |
8166 | 8319 | ||
8167 | /* Wrapper around strcmp for qsort/bsearch/... */ | 8320 | /* Wrapper around strcmp for qsort/bsearch/... */ |
8168 | static int | 8321 | static int |
8169 | pstrcmp(const void *a, const void *b) | 8322 | pstrcmp(const void *a, const void *b) |
8170 | { | 8323 | { |
8171 | return strcmp((char*) a, (*(char**) b) + 1); | 8324 | return strcmp((char*)a, *(char**)b); |
8172 | } | 8325 | } |
8173 | 8326 | ||
8174 | static const char *const * | 8327 | static const char *const * |
@@ -8297,9 +8450,45 @@ typecmd(int argc UNUSED_PARAM, char **argv) | |||
8297 | } | 8450 | } |
8298 | 8451 | ||
8299 | #if ENABLE_ASH_CMDCMD | 8452 | #if ENABLE_ASH_CMDCMD |
8453 | /* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */ | ||
8454 | static char ** | ||
8455 | parse_command_args(char **argv, const char **path) | ||
8456 | { | ||
8457 | char *cp, c; | ||
8458 | |||
8459 | for (;;) { | ||
8460 | cp = *++argv; | ||
8461 | if (!cp) | ||
8462 | return NULL; | ||
8463 | if (*cp++ != '-') | ||
8464 | break; | ||
8465 | c = *cp++; | ||
8466 | if (!c) | ||
8467 | break; | ||
8468 | if (c == '-' && !*cp) { | ||
8469 | if (!*++argv) | ||
8470 | return NULL; | ||
8471 | break; | ||
8472 | } | ||
8473 | do { | ||
8474 | switch (c) { | ||
8475 | case 'p': | ||
8476 | *path = bb_default_path; | ||
8477 | break; | ||
8478 | default: | ||
8479 | /* run 'typecmd' for other options */ | ||
8480 | return NULL; | ||
8481 | } | ||
8482 | c = *cp++; | ||
8483 | } while (c); | ||
8484 | } | ||
8485 | return argv; | ||
8486 | } | ||
8487 | |||
8300 | static int FAST_FUNC | 8488 | static int FAST_FUNC |
8301 | commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 8489 | commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
8302 | { | 8490 | { |
8491 | char *cmd; | ||
8303 | int c; | 8492 | int c; |
8304 | enum { | 8493 | enum { |
8305 | VERIFY_BRIEF = 1, | 8494 | VERIFY_BRIEF = 1, |
@@ -8307,29 +8496,32 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
8307 | } verify = 0; | 8496 | } verify = 0; |
8308 | const char *path = NULL; | 8497 | const char *path = NULL; |
8309 | 8498 | ||
8499 | /* "command [-p] PROG ARGS" (that is, without -V or -v) | ||
8500 | * never reaches this function. | ||
8501 | */ | ||
8502 | |||
8310 | while ((c = nextopt("pvV")) != '\0') | 8503 | while ((c = nextopt("pvV")) != '\0') |
8311 | if (c == 'V') | 8504 | if (c == 'V') |
8312 | verify |= VERIFY_VERBOSE; | 8505 | verify |= VERIFY_VERBOSE; |
8313 | else if (c == 'v') | 8506 | else if (c == 'v') |
8314 | verify |= VERIFY_BRIEF; | 8507 | /*verify |= VERIFY_BRIEF*/; |
8315 | #if DEBUG | 8508 | #if DEBUG |
8316 | else if (c != 'p') | 8509 | else if (c != 'p') |
8317 | abort(); | 8510 | abort(); |
8318 | #endif | 8511 | #endif |
8319 | else | 8512 | else |
8320 | path = bb_default_path; | 8513 | path = bb_default_path; |
8514 | |||
8321 | /* Mimic bash: just "command -v" doesn't complain, it's a nop */ | 8515 | /* Mimic bash: just "command -v" doesn't complain, it's a nop */ |
8322 | if (verify && (*argptr != NULL)) { | 8516 | cmd = *argptr; |
8323 | return describe_command(*argptr, path, verify - VERIFY_BRIEF); | 8517 | if (/*verify && */ cmd) |
8324 | } | 8518 | return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/); |
8325 | 8519 | ||
8326 | return 0; | 8520 | return 0; |
8327 | } | 8521 | } |
8328 | #endif | 8522 | #endif |
8329 | 8523 | ||
8330 | 8524 | ||
8331 | /* ============ eval.c */ | ||
8332 | |||
8333 | static int funcblocksize; /* size of structures in function */ | 8525 | static int funcblocksize; /* size of structures in function */ |
8334 | static int funcstringsize; /* size of strings in node */ | 8526 | static int funcstringsize; /* size of strings in node */ |
8335 | static void *funcblock; /* block to allocate function from */ | 8527 | static void *funcblock; /* block to allocate function from */ |
@@ -8342,7 +8534,6 @@ static char **nodeptr; | |||
8342 | /* flags in argument to evaltree */ | 8534 | /* flags in argument to evaltree */ |
8343 | #define EV_EXIT 01 /* exit after evaluating tree */ | 8535 | #define EV_EXIT 01 /* exit after evaluating tree */ |
8344 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ | 8536 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
8345 | #define EV_BACKCMD 04 /* command executing within back quotes */ | ||
8346 | 8537 | ||
8347 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | 8538 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { |
8348 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), | 8539 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), |
@@ -8674,15 +8865,13 @@ defun(char *name, union node *func) | |||
8674 | #define SKIPBREAK (1 << 0) | 8865 | #define SKIPBREAK (1 << 0) |
8675 | #define SKIPCONT (1 << 1) | 8866 | #define SKIPCONT (1 << 1) |
8676 | #define SKIPFUNC (1 << 2) | 8867 | #define SKIPFUNC (1 << 2) |
8677 | #define SKIPFILE (1 << 3) | ||
8678 | #define SKIPEVAL (1 << 4) | ||
8679 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ | 8868 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ |
8680 | static int skipcount; /* number of levels to skip */ | 8869 | static int skipcount; /* number of levels to skip */ |
8681 | static int funcnest; /* depth of function calls */ | 8870 | static int funcnest; /* depth of function calls */ |
8682 | static int loopnest; /* current loop nesting level */ | 8871 | static int loopnest; /* current loop nesting level */ |
8683 | 8872 | ||
8684 | /* Forward decl way out to parsing code - dotrap needs it */ | 8873 | /* Forward decl way out to parsing code - dotrap needs it */ |
8685 | static int evalstring(char *s, int mask); | 8874 | static int evalstring(char *s, int flags); |
8686 | 8875 | ||
8687 | /* Called to execute a trap. | 8876 | /* Called to execute a trap. |
8688 | * Single callsite - at the end of evaltree(). | 8877 | * Single callsite - at the end of evaltree(). |
@@ -8691,69 +8880,71 @@ static int evalstring(char *s, int mask); | |||
8691 | * Perhaps we should avoid entering new trap handlers | 8880 | * Perhaps we should avoid entering new trap handlers |
8692 | * while we are executing a trap handler. [is it a TODO?] | 8881 | * while we are executing a trap handler. [is it a TODO?] |
8693 | */ | 8882 | */ |
8694 | static int | 8883 | static void |
8695 | dotrap(void) | 8884 | dotrap(void) |
8696 | { | 8885 | { |
8697 | uint8_t *g; | 8886 | uint8_t *g; |
8698 | int sig; | 8887 | int sig; |
8699 | uint8_t savestatus; | 8888 | uint8_t last_status; |
8889 | |||
8890 | if (!pending_sig) | ||
8891 | return; | ||
8700 | 8892 | ||
8701 | savestatus = exitstatus; | 8893 | last_status = exitstatus; |
8702 | pending_sig = 0; | 8894 | pending_sig = 0; |
8703 | xbarrier(); | 8895 | barrier(); |
8704 | 8896 | ||
8705 | TRACE(("dotrap entered\n")); | 8897 | TRACE(("dotrap entered\n")); |
8706 | for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { | 8898 | for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { |
8707 | int want_exexit; | 8899 | char *p; |
8708 | char *t; | ||
8709 | 8900 | ||
8710 | if (*g == 0) | 8901 | if (!*g) |
8711 | continue; | 8902 | continue; |
8712 | t = trap[sig]; | 8903 | |
8904 | if (evalskip) { | ||
8905 | pending_sig = sig; | ||
8906 | break; | ||
8907 | } | ||
8908 | |||
8909 | p = trap[sig]; | ||
8713 | /* non-trapped SIGINT is handled separately by raise_interrupt, | 8910 | /* non-trapped SIGINT is handled separately by raise_interrupt, |
8714 | * don't upset it by resetting gotsig[SIGINT-1] */ | 8911 | * don't upset it by resetting gotsig[SIGINT-1] */ |
8715 | if (sig == SIGINT && !t) | 8912 | if (sig == SIGINT && !p) |
8716 | continue; | 8913 | continue; |
8717 | 8914 | ||
8718 | TRACE(("sig %d is active, will run handler '%s'\n", sig, t)); | 8915 | TRACE(("sig %d is active, will run handler '%s'\n", sig, p)); |
8719 | *g = 0; | 8916 | *g = 0; |
8720 | if (!t) | 8917 | if (!p) |
8721 | continue; | 8918 | continue; |
8722 | want_exexit = evalstring(t, SKIPEVAL); | 8919 | evalstring(p, 0); |
8723 | exitstatus = savestatus; | ||
8724 | if (want_exexit) { | ||
8725 | TRACE(("dotrap returns %d\n", want_exexit)); | ||
8726 | return want_exexit; | ||
8727 | } | ||
8728 | } | 8920 | } |
8729 | 8921 | exitstatus = last_status; | |
8730 | TRACE(("dotrap returns 0\n")); | 8922 | TRACE(("dotrap returns\n")); |
8731 | return 0; | ||
8732 | } | 8923 | } |
8733 | 8924 | ||
8734 | /* forward declarations - evaluation is fairly recursive business... */ | 8925 | /* forward declarations - evaluation is fairly recursive business... */ |
8735 | static void evalloop(union node *, int); | 8926 | static int evalloop(union node *, int); |
8736 | static void evalfor(union node *, int); | 8927 | static int evalfor(union node *, int); |
8737 | static void evalcase(union node *, int); | 8928 | static int evalcase(union node *, int); |
8738 | static void evalsubshell(union node *, int); | 8929 | static int evalsubshell(union node *, int); |
8739 | static void expredir(union node *); | 8930 | static void expredir(union node *); |
8740 | static void evalpipe(union node *, int); | 8931 | static int evalpipe(union node *, int); |
8741 | static void evalcommand(union node *, int); | 8932 | static int evalcommand(union node *, int); |
8742 | static int evalbltin(const struct builtincmd *, int, char **); | 8933 | static int evalbltin(const struct builtincmd *, int, char **, int); |
8743 | static void prehash(union node *); | 8934 | static void prehash(union node *); |
8744 | 8935 | ||
8745 | /* | 8936 | /* |
8746 | * Evaluate a parse tree. The value is left in the global variable | 8937 | * Evaluate a parse tree. The value is left in the global variable |
8747 | * exitstatus. | 8938 | * exitstatus. |
8748 | */ | 8939 | */ |
8749 | static void | 8940 | static int |
8750 | evaltree(union node *n, int flags) | 8941 | evaltree(union node *n, int flags) |
8751 | { | 8942 | { |
8752 | struct jmploc *volatile savehandler = exception_handler; | 8943 | struct jmploc *volatile savehandler = exception_handler; |
8753 | struct jmploc jmploc; | 8944 | struct jmploc jmploc; |
8754 | int checkexit = 0; | 8945 | int checkexit = 0; |
8755 | void (*evalfn)(union node *, int); | 8946 | int (*evalfn)(union node *, int); |
8756 | int status; | 8947 | int status = 0; |
8757 | int int_level; | 8948 | int int_level; |
8758 | 8949 | ||
8759 | SAVE_INT(int_level); | 8950 | SAVE_INT(int_level); |
@@ -8764,6 +8955,8 @@ evaltree(union node *n, int flags) | |||
8764 | } | 8955 | } |
8765 | TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); | 8956 | TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); |
8766 | 8957 | ||
8958 | dotrap(); | ||
8959 | |||
8767 | exception_handler = &jmploc; | 8960 | exception_handler = &jmploc; |
8768 | { | 8961 | { |
8769 | int err = setjmp(jmploc.loc); | 8962 | int err = setjmp(jmploc.loc); |
@@ -8790,15 +8983,13 @@ evaltree(union node *n, int flags) | |||
8790 | break; | 8983 | break; |
8791 | #endif | 8984 | #endif |
8792 | case NNOT: | 8985 | case NNOT: |
8793 | evaltree(n->nnot.com, EV_TESTED); | 8986 | status = !evaltree(n->nnot.com, EV_TESTED); |
8794 | status = !exitstatus; | ||
8795 | goto setstatus; | 8987 | goto setstatus; |
8796 | case NREDIR: | 8988 | case NREDIR: |
8797 | expredir(n->nredir.redirect); | 8989 | expredir(n->nredir.redirect); |
8798 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); | 8990 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); |
8799 | if (!status) { | 8991 | if (!status) { |
8800 | evaltree(n->nredir.n, flags & EV_TESTED); | 8992 | status = evaltree(n->nredir.n, flags & EV_TESTED); |
8801 | status = exitstatus; | ||
8802 | } | 8993 | } |
8803 | popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); | 8994 | popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); |
8804 | goto setstatus; | 8995 | goto setstatus; |
@@ -8816,6 +9007,8 @@ evaltree(union node *n, int flags) | |||
8816 | evalfn = evalloop; | 9007 | evalfn = evalloop; |
8817 | goto calleval; | 9008 | goto calleval; |
8818 | case NSUBSHELL: | 9009 | case NSUBSHELL: |
9010 | evalfn = evalsubshell; | ||
9011 | goto checkexit; | ||
8819 | case NBACKGND: | 9012 | case NBACKGND: |
8820 | evalfn = evalsubshell; | 9013 | evalfn = evalsubshell; |
8821 | goto calleval; | 9014 | goto calleval; |
@@ -8836,27 +9029,24 @@ evaltree(union node *n, int flags) | |||
8836 | #error NOR + 1 != NSEMI | 9029 | #error NOR + 1 != NSEMI |
8837 | #endif | 9030 | #endif |
8838 | unsigned is_or = n->type - NAND; | 9031 | unsigned is_or = n->type - NAND; |
8839 | evaltree( | 9032 | status = evaltree( |
8840 | n->nbinary.ch1, | 9033 | n->nbinary.ch1, |
8841 | (flags | ((is_or >> 1) - 1)) & EV_TESTED | 9034 | (flags | ((is_or >> 1) - 1)) & EV_TESTED |
8842 | ); | 9035 | ); |
8843 | if ((!exitstatus) == is_or) | 9036 | if ((!status) == is_or || evalskip) |
8844 | break; | 9037 | break; |
8845 | if (!evalskip) { | 9038 | n = n->nbinary.ch2; |
8846 | n = n->nbinary.ch2; | ||
8847 | evaln: | 9039 | evaln: |
8848 | evalfn = evaltree; | 9040 | evalfn = evaltree; |
8849 | calleval: | 9041 | calleval: |
8850 | evalfn(n, flags); | 9042 | status = evalfn(n, flags); |
8851 | break; | 9043 | goto setstatus; |
8852 | } | ||
8853 | break; | ||
8854 | } | 9044 | } |
8855 | case NIF: | 9045 | case NIF: |
8856 | evaltree(n->nif.test, EV_TESTED); | 9046 | status = evaltree(n->nif.test, EV_TESTED); |
8857 | if (evalskip) | 9047 | if (evalskip) |
8858 | break; | 9048 | break; |
8859 | if (exitstatus == 0) { | 9049 | if (!status) { |
8860 | n = n->nif.ifpart; | 9050 | n = n->nif.ifpart; |
8861 | goto evaln; | 9051 | goto evaln; |
8862 | } | 9052 | } |
@@ -8864,11 +9054,14 @@ evaltree(union node *n, int flags) | |||
8864 | n = n->nif.elsepart; | 9054 | n = n->nif.elsepart; |
8865 | goto evaln; | 9055 | goto evaln; |
8866 | } | 9056 | } |
8867 | goto success; | 9057 | status = 0; |
9058 | goto setstatus; | ||
8868 | case NDEFUN: | 9059 | case NDEFUN: |
8869 | defun(n->narg.text, n->narg.next); | 9060 | defun(n->narg.text, n->narg.next); |
8870 | success: | 9061 | /* Not necessary. To test it: |
8871 | status = 0; | 9062 | * "false; f() { qwerty; }; echo $?" should print 0. |
9063 | */ | ||
9064 | /* status = 0; */ | ||
8872 | setstatus: | 9065 | setstatus: |
8873 | exitstatus = status; | 9066 | exitstatus = status; |
8874 | break; | 9067 | break; |
@@ -8881,119 +9074,127 @@ evaltree(union node *n, int flags) | |||
8881 | /* Order of checks below is important: | 9074 | /* Order of checks below is important: |
8882 | * signal handlers trigger before exit caused by "set -e". | 9075 | * signal handlers trigger before exit caused by "set -e". |
8883 | */ | 9076 | */ |
8884 | if (pending_sig && dotrap()) | 9077 | dotrap(); |
8885 | goto exexit; | ||
8886 | if (checkexit & exitstatus) | ||
8887 | evalskip |= SKIPEVAL; | ||
8888 | 9078 | ||
8889 | if (flags & EV_EXIT) { | 9079 | if (checkexit & status) |
8890 | exexit: | 9080 | raise_exception(EXEXIT); |
9081 | if (flags & EV_EXIT) | ||
8891 | raise_exception(EXEXIT); | 9082 | raise_exception(EXEXIT); |
8892 | } | ||
8893 | 9083 | ||
8894 | RESTORE_INT(int_level); | 9084 | RESTORE_INT(int_level); |
8895 | TRACE(("leaving evaltree (no interrupts)\n")); | 9085 | TRACE(("leaving evaltree (no interrupts)\n")); |
9086 | |||
9087 | return exitstatus; | ||
8896 | } | 9088 | } |
8897 | 9089 | ||
8898 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) | 9090 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) |
8899 | static | 9091 | static |
8900 | #endif | 9092 | #endif |
8901 | void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); | 9093 | int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); |
8902 | 9094 | ||
8903 | static void | 9095 | static int |
9096 | skiploop(void) | ||
9097 | { | ||
9098 | int skip = evalskip; | ||
9099 | |||
9100 | switch (skip) { | ||
9101 | case 0: | ||
9102 | break; | ||
9103 | case SKIPBREAK: | ||
9104 | case SKIPCONT: | ||
9105 | if (--skipcount <= 0) { | ||
9106 | evalskip = 0; | ||
9107 | break; | ||
9108 | } | ||
9109 | skip = SKIPBREAK; | ||
9110 | break; | ||
9111 | } | ||
9112 | return skip; | ||
9113 | } | ||
9114 | |||
9115 | static int | ||
8904 | evalloop(union node *n, int flags) | 9116 | evalloop(union node *n, int flags) |
8905 | { | 9117 | { |
9118 | int skip; | ||
8906 | int status; | 9119 | int status; |
8907 | 9120 | ||
8908 | loopnest++; | 9121 | loopnest++; |
8909 | status = 0; | 9122 | status = 0; |
8910 | flags &= EV_TESTED; | 9123 | flags &= EV_TESTED; |
8911 | for (;;) { | 9124 | do { |
8912 | int i; | 9125 | int i; |
8913 | 9126 | ||
8914 | evaltree(n->nbinary.ch1, EV_TESTED); | 9127 | i = evaltree(n->nbinary.ch1, EV_TESTED); |
8915 | if (evalskip) { | 9128 | skip = skiploop(); |
8916 | skipping: | 9129 | if (skip == SKIPFUNC) |
8917 | if (evalskip == SKIPCONT && --skipcount <= 0) { | 9130 | status = i; |
8918 | evalskip = 0; | 9131 | if (skip) |
8919 | continue; | 9132 | continue; |
8920 | } | ||
8921 | if (evalskip == SKIPBREAK && --skipcount <= 0) | ||
8922 | evalskip = 0; | ||
8923 | break; | ||
8924 | } | ||
8925 | i = exitstatus; | ||
8926 | if (n->type != NWHILE) | 9133 | if (n->type != NWHILE) |
8927 | i = !i; | 9134 | i = !i; |
8928 | if (i != 0) | 9135 | if (i != 0) |
8929 | break; | 9136 | break; |
8930 | evaltree(n->nbinary.ch2, flags); | 9137 | status = evaltree(n->nbinary.ch2, flags); |
8931 | status = exitstatus; | 9138 | skip = skiploop(); |
8932 | if (evalskip) | 9139 | } while (!(skip & ~SKIPCONT)); |
8933 | goto skipping; | ||
8934 | } | ||
8935 | loopnest--; | 9140 | loopnest--; |
8936 | exitstatus = status; | 9141 | |
9142 | return status; | ||
8937 | } | 9143 | } |
8938 | 9144 | ||
8939 | static void | 9145 | static int |
8940 | evalfor(union node *n, int flags) | 9146 | evalfor(union node *n, int flags) |
8941 | { | 9147 | { |
8942 | struct arglist arglist; | 9148 | struct arglist arglist; |
8943 | union node *argp; | 9149 | union node *argp; |
8944 | struct strlist *sp; | 9150 | struct strlist *sp; |
8945 | struct stackmark smark; | 9151 | struct stackmark smark; |
9152 | int status = 0; | ||
8946 | 9153 | ||
8947 | setstackmark(&smark); | 9154 | setstackmark(&smark); |
8948 | arglist.list = NULL; | 9155 | arglist.list = NULL; |
8949 | arglist.lastp = &arglist.list; | 9156 | arglist.lastp = &arglist.list; |
8950 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { | 9157 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { |
8951 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); | 9158 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); |
8952 | /* XXX */ | ||
8953 | if (evalskip) | ||
8954 | goto out; | ||
8955 | } | 9159 | } |
8956 | *arglist.lastp = NULL; | 9160 | *arglist.lastp = NULL; |
8957 | 9161 | ||
8958 | exitstatus = 0; | ||
8959 | loopnest++; | 9162 | loopnest++; |
8960 | flags &= EV_TESTED; | 9163 | flags &= EV_TESTED; |
8961 | for (sp = arglist.list; sp; sp = sp->next) { | 9164 | for (sp = arglist.list; sp; sp = sp->next) { |
8962 | setvar0(n->nfor.var, sp->text); | 9165 | setvar0(n->nfor.var, sp->text); |
8963 | evaltree(n->nfor.body, flags); | 9166 | status = evaltree(n->nfor.body, flags); |
8964 | if (evalskip) { | 9167 | if (skiploop() & ~SKIPCONT) |
8965 | if (evalskip == SKIPCONT && --skipcount <= 0) { | ||
8966 | evalskip = 0; | ||
8967 | continue; | ||
8968 | } | ||
8969 | if (evalskip == SKIPBREAK && --skipcount <= 0) | ||
8970 | evalskip = 0; | ||
8971 | break; | 9168 | break; |
8972 | } | ||
8973 | } | 9169 | } |
8974 | loopnest--; | 9170 | loopnest--; |
8975 | out: | ||
8976 | popstackmark(&smark); | 9171 | popstackmark(&smark); |
9172 | |||
9173 | return status; | ||
8977 | } | 9174 | } |
8978 | 9175 | ||
8979 | static void | 9176 | static int |
8980 | evalcase(union node *n, int flags) | 9177 | evalcase(union node *n, int flags) |
8981 | { | 9178 | { |
8982 | union node *cp; | 9179 | union node *cp; |
8983 | union node *patp; | 9180 | union node *patp; |
8984 | struct arglist arglist; | 9181 | struct arglist arglist; |
8985 | struct stackmark smark; | 9182 | struct stackmark smark; |
9183 | int status = 0; | ||
8986 | 9184 | ||
8987 | setstackmark(&smark); | 9185 | setstackmark(&smark); |
8988 | arglist.list = NULL; | 9186 | arglist.list = NULL; |
8989 | arglist.lastp = &arglist.list; | 9187 | arglist.lastp = &arglist.list; |
8990 | expandarg(n->ncase.expr, &arglist, EXP_TILDE); | 9188 | expandarg(n->ncase.expr, &arglist, EXP_TILDE); |
8991 | exitstatus = 0; | ||
8992 | for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { | 9189 | for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { |
8993 | for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { | 9190 | for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { |
8994 | if (casematch(patp, arglist.list->text)) { | 9191 | if (casematch(patp, arglist.list->text)) { |
8995 | if (evalskip == 0) { | 9192 | /* Ensure body is non-empty as otherwise |
8996 | evaltree(cp->nclist.body, flags); | 9193 | * EV_EXIT may prevent us from setting the |
9194 | * exit status. | ||
9195 | */ | ||
9196 | if (evalskip == 0 && cp->nclist.body) { | ||
9197 | status = evaltree(cp->nclist.body, flags); | ||
8997 | } | 9198 | } |
8998 | goto out; | 9199 | goto out; |
8999 | } | 9200 | } |
@@ -9001,12 +9202,14 @@ evalcase(union node *n, int flags) | |||
9001 | } | 9202 | } |
9002 | out: | 9203 | out: |
9003 | popstackmark(&smark); | 9204 | popstackmark(&smark); |
9205 | |||
9206 | return status; | ||
9004 | } | 9207 | } |
9005 | 9208 | ||
9006 | /* | 9209 | /* |
9007 | * Kick off a subshell to evaluate a tree. | 9210 | * Kick off a subshell to evaluate a tree. |
9008 | */ | 9211 | */ |
9009 | static void | 9212 | static int |
9010 | evalsubshell(union node *n, int flags) | 9213 | evalsubshell(union node *n, int flags) |
9011 | { | 9214 | { |
9012 | IF_PLATFORM_MINGW32(struct forkshell fs;) | 9215 | IF_PLATFORM_MINGW32(struct forkshell fs;) |
@@ -9043,8 +9246,8 @@ evalsubshell(union node *n, int flags) | |||
9043 | status = 0; | 9246 | status = 0; |
9044 | if (!backgnd) | 9247 | if (!backgnd) |
9045 | status = waitforjob(jp); | 9248 | status = waitforjob(jp); |
9046 | exitstatus = status; | ||
9047 | INT_ON; | 9249 | INT_ON; |
9250 | return status; | ||
9048 | } | 9251 | } |
9049 | 9252 | ||
9050 | /* | 9253 | /* |
@@ -9117,7 +9320,7 @@ expredir(union node *n) | |||
9117 | * of the shell, which make the last process in a pipeline the parent | 9320 | * of the shell, which make the last process in a pipeline the parent |
9118 | * of all the rest.) | 9321 | * of all the rest.) |
9119 | */ | 9322 | */ |
9120 | static void | 9323 | static int |
9121 | evalpipe(union node *n, int flags) | 9324 | evalpipe(union node *n, int flags) |
9122 | { | 9325 | { |
9123 | IF_PLATFORM_MINGW32(struct forkshell fs;) | 9326 | IF_PLATFORM_MINGW32(struct forkshell fs;) |
@@ -9126,6 +9329,7 @@ evalpipe(union node *n, int flags) | |||
9126 | int pipelen; | 9329 | int pipelen; |
9127 | int prevfd; | 9330 | int prevfd; |
9128 | int pip[2]; | 9331 | int pip[2]; |
9332 | int status = 0; | ||
9129 | 9333 | ||
9130 | TRACE(("evalpipe(0x%lx) called\n", (long)n)); | 9334 | TRACE(("evalpipe(0x%lx) called\n", (long)n)); |
9131 | pipelen = 0; | 9335 | pipelen = 0; |
@@ -9180,10 +9384,12 @@ evalpipe(union node *n, int flags) | |||
9180 | close(pip[1]); | 9384 | close(pip[1]); |
9181 | } | 9385 | } |
9182 | if (n->npipe.pipe_backgnd == 0) { | 9386 | if (n->npipe.pipe_backgnd == 0) { |
9183 | exitstatus = waitforjob(jp); | 9387 | status = waitforjob(jp); |
9184 | TRACE(("evalpipe: job done exit status %d\n", exitstatus)); | 9388 | TRACE(("evalpipe: job done exit status %d\n", status)); |
9185 | } | 9389 | } |
9186 | INT_ON; | 9390 | INT_ON; |
9391 | |||
9392 | return status; | ||
9187 | } | 9393 | } |
9188 | 9394 | ||
9189 | /* | 9395 | /* |
@@ -9282,12 +9488,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9282 | 9488 | ||
9283 | saveparam = shellparam; | 9489 | saveparam = shellparam; |
9284 | savelocalvars = localvars; | 9490 | savelocalvars = localvars; |
9491 | savehandler = exception_handler; | ||
9285 | e = setjmp(jmploc.loc); | 9492 | e = setjmp(jmploc.loc); |
9286 | if (e) { | 9493 | if (e) { |
9287 | goto funcdone; | 9494 | goto funcdone; |
9288 | } | 9495 | } |
9289 | INT_OFF; | 9496 | INT_OFF; |
9290 | savehandler = exception_handler; | ||
9291 | exception_handler = &jmploc; | 9497 | exception_handler = &jmploc; |
9292 | localvars = NULL; | 9498 | localvars = NULL; |
9293 | shellparam.malloced = 0; | 9499 | shellparam.malloced = 0; |
@@ -9315,42 +9521,6 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9315 | return e; | 9521 | return e; |
9316 | } | 9522 | } |
9317 | 9523 | ||
9318 | #if ENABLE_ASH_CMDCMD | ||
9319 | static char ** | ||
9320 | parse_command_args(char **argv, const char **path) | ||
9321 | { | ||
9322 | char *cp, c; | ||
9323 | |||
9324 | for (;;) { | ||
9325 | cp = *++argv; | ||
9326 | if (!cp) | ||
9327 | return NULL; | ||
9328 | if (*cp++ != '-') | ||
9329 | break; | ||
9330 | c = *cp++; | ||
9331 | if (!c) | ||
9332 | break; | ||
9333 | if (c == '-' && !*cp) { | ||
9334 | if (!*++argv) | ||
9335 | return NULL; | ||
9336 | break; | ||
9337 | } | ||
9338 | do { | ||
9339 | switch (c) { | ||
9340 | case 'p': | ||
9341 | *path = bb_default_path; | ||
9342 | break; | ||
9343 | default: | ||
9344 | /* run 'typecmd' for other options */ | ||
9345 | return NULL; | ||
9346 | } | ||
9347 | c = *cp++; | ||
9348 | } while (c); | ||
9349 | } | ||
9350 | return argv; | ||
9351 | } | ||
9352 | #endif | ||
9353 | |||
9354 | /* | 9524 | /* |
9355 | * Make a variable a local variable. When a variable is made local, it's | 9525 | * Make a variable a local variable. When a variable is made local, it's |
9356 | * value and flags are saved in a localvar structure. The saved values | 9526 | * value and flags are saved in a localvar structure. The saved values |
@@ -9458,7 +9628,20 @@ execcmd(int argc UNUSED_PARAM, char **argv) | |||
9458 | iflag = 0; /* exit on error */ | 9628 | iflag = 0; /* exit on error */ |
9459 | mflag = 0; | 9629 | mflag = 0; |
9460 | optschanged(); | 9630 | optschanged(); |
9631 | /* We should set up signals for "exec CMD" | ||
9632 | * the same way as for "CMD" without "exec". | ||
9633 | * But optschanged->setinteractive->setsignal | ||
9634 | * still thought we are a root shell. Therefore, for example, | ||
9635 | * SIGQUIT is still set to IGN. Fix it: | ||
9636 | */ | ||
9637 | shlvl++; | ||
9638 | setsignal(SIGQUIT); | ||
9639 | /*setsignal(SIGTERM); - unnecessary because of iflag=0 */ | ||
9640 | /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */ | ||
9641 | /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */ | ||
9642 | |||
9461 | shellexec(argv + 1, pathval(), 0); | 9643 | shellexec(argv + 1, pathval(), 0); |
9644 | /* NOTREACHED */ | ||
9462 | } | 9645 | } |
9463 | return 0; | 9646 | return 0; |
9464 | } | 9647 | } |
@@ -9473,14 +9656,14 @@ returncmd(int argc UNUSED_PARAM, char **argv) | |||
9473 | * If called outside a function, do what ksh does; | 9656 | * If called outside a function, do what ksh does; |
9474 | * skip the rest of the file. | 9657 | * skip the rest of the file. |
9475 | */ | 9658 | */ |
9476 | evalskip = funcnest ? SKIPFUNC : SKIPFILE; | 9659 | evalskip = SKIPFUNC; |
9477 | return argv[1] ? number(argv[1]) : exitstatus; | 9660 | return argv[1] ? number(argv[1]) : exitstatus; |
9478 | } | 9661 | } |
9479 | 9662 | ||
9480 | /* Forward declarations for builtintab[] */ | 9663 | /* Forward declarations for builtintab[] */ |
9481 | static int breakcmd(int, char **) FAST_FUNC; | 9664 | static int breakcmd(int, char **) FAST_FUNC; |
9482 | static int dotcmd(int, char **) FAST_FUNC; | 9665 | static int dotcmd(int, char **) FAST_FUNC; |
9483 | static int evalcmd(int, char **) FAST_FUNC; | 9666 | static int evalcmd(int, char **, int) FAST_FUNC; |
9484 | static int exitcmd(int, char **) FAST_FUNC; | 9667 | static int exitcmd(int, char **) FAST_FUNC; |
9485 | static int exportcmd(int, char **) FAST_FUNC; | 9668 | static int exportcmd(int, char **) FAST_FUNC; |
9486 | #if ENABLE_ASH_GETOPTS | 9669 | #if ENABLE_ASH_GETOPTS |
@@ -9550,7 +9733,7 @@ static const struct builtincmd builtintab[] = { | |||
9550 | #if ENABLE_ASH_BUILTIN_ECHO | 9733 | #if ENABLE_ASH_BUILTIN_ECHO |
9551 | { BUILTIN_REGULAR "echo" , echocmd }, | 9734 | { BUILTIN_REGULAR "echo" , echocmd }, |
9552 | #endif | 9735 | #endif |
9553 | { BUILTIN_SPEC_REG "eval" , evalcmd }, | 9736 | { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/ |
9554 | { BUILTIN_SPEC_REG "exec" , execcmd }, | 9737 | { BUILTIN_SPEC_REG "exec" , execcmd }, |
9555 | { BUILTIN_SPEC_REG "exit" , exitcmd }, | 9738 | { BUILTIN_SPEC_REG "exit" , exitcmd }, |
9556 | { BUILTIN_SPEC_REG_ASSG "export" , exportcmd }, | 9739 | { BUILTIN_SPEC_REG_ASSG "export" , exportcmd }, |
@@ -9606,27 +9789,28 @@ static const struct builtincmd builtintab[] = { | |||
9606 | 9789 | ||
9607 | /* Should match the above table! */ | 9790 | /* Should match the above table! */ |
9608 | #define COMMANDCMD (builtintab + \ | 9791 | #define COMMANDCMD (builtintab + \ |
9609 | 2 + \ | 9792 | /* . : */ 2 + \ |
9610 | 1 * ENABLE_ASH_BUILTIN_TEST + \ | 9793 | /* [ */ 1 * ENABLE_ASH_BUILTIN_TEST + \ |
9611 | 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ | 9794 | /* [[ */ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ |
9612 | 1 * ENABLE_ASH_ALIAS + \ | 9795 | /* alias */ 1 * ENABLE_ASH_ALIAS + \ |
9613 | 1 * ENABLE_ASH_JOB_CONTROL + \ | 9796 | /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ |
9614 | 3) | 9797 | /* break cd cddir */ 3) |
9615 | #define EXECCMD (builtintab + \ | 9798 | #define EVALCMD (COMMANDCMD + \ |
9616 | 2 + \ | 9799 | /* command */ 1 * ENABLE_ASH_CMDCMD + \ |
9617 | 1 * ENABLE_ASH_BUILTIN_TEST + \ | 9800 | /* continue */ 1 + \ |
9618 | 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ | 9801 | /* echo */ 1 * ENABLE_ASH_BUILTIN_ECHO + \ |
9619 | 1 * ENABLE_ASH_ALIAS + \ | 9802 | 0) |
9620 | 1 * ENABLE_ASH_JOB_CONTROL + \ | 9803 | #define EXECCMD (EVALCMD + \ |
9621 | 3 + \ | 9804 | /* eval */ 1) |
9622 | 1 * ENABLE_ASH_CMDCMD + \ | ||
9623 | 1 + \ | ||
9624 | ENABLE_ASH_BUILTIN_ECHO + \ | ||
9625 | 1) | ||
9626 | 9805 | ||
9627 | /* | 9806 | /* |
9628 | * Search the table of builtin commands. | 9807 | * Search the table of builtin commands. |
9629 | */ | 9808 | */ |
9809 | static int | ||
9810 | pstrcmp1(const void *a, const void *b) | ||
9811 | { | ||
9812 | return strcmp((char*)a, *(char**)b + 1); | ||
9813 | } | ||
9630 | static struct builtincmd * | 9814 | static struct builtincmd * |
9631 | find_builtin(const char *name) | 9815 | find_builtin(const char *name) |
9632 | { | 9816 | { |
@@ -9634,7 +9818,7 @@ find_builtin(const char *name) | |||
9634 | 9818 | ||
9635 | bp = bsearch( | 9819 | bp = bsearch( |
9636 | name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), | 9820 | name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), |
9637 | pstrcmp | 9821 | pstrcmp1 |
9638 | ); | 9822 | ); |
9639 | return bp; | 9823 | return bp; |
9640 | } | 9824 | } |
@@ -9658,7 +9842,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
9658 | return back_exitstatus; | 9842 | return back_exitstatus; |
9659 | } | 9843 | } |
9660 | 9844 | ||
9661 | static void | 9845 | static int |
9662 | evalcommand(union node *cmd, int flags) | 9846 | evalcommand(union node *cmd, int flags) |
9663 | { | 9847 | { |
9664 | static const struct builtincmd null_bltin = { | 9848 | static const struct builtincmd null_bltin = { |
@@ -9794,6 +9978,9 @@ evalcommand(union node *cmd, int flags) | |||
9794 | nargv = parse_command_args(argv, &path); | 9978 | nargv = parse_command_args(argv, &path); |
9795 | if (!nargv) | 9979 | if (!nargv) |
9796 | break; | 9980 | break; |
9981 | /* It's "command [-p] PROG ARGS" (that is, no -Vv). | ||
9982 | * nargv => "PROG". path is updated if -p. | ||
9983 | */ | ||
9797 | argc -= nargv - argv; | 9984 | argc -= nargv - argv; |
9798 | argv = nargv; | 9985 | argv = nargv; |
9799 | cmd_flag |= DO_NOFUNC; | 9986 | cmd_flag |= DO_NOFUNC; |
@@ -9862,9 +10049,9 @@ evalcommand(union node *cmd, int flags) | |||
9862 | jp = makejob(/*cmd,*/ 1); | 10049 | jp = makejob(/*cmd,*/ 1); |
9863 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 10050 | if (forkshell(jp, cmd, FORK_FG) != 0) { |
9864 | /* parent */ | 10051 | /* parent */ |
9865 | exitstatus = waitforjob(jp); | 10052 | status = waitforjob(jp); |
9866 | INT_ON; | 10053 | INT_ON; |
9867 | TRACE(("forked child exited with %d\n", exitstatus)); | 10054 | TRACE(("forked child exited with %d\n", status)); |
9868 | break; | 10055 | break; |
9869 | } | 10056 | } |
9870 | /* child */ | 10057 | /* child */ |
@@ -9894,7 +10081,7 @@ evalcommand(union node *cmd, int flags) | |||
9894 | * to reap the zombie and make kill detect that it's gone: */ | 10081 | * to reap the zombie and make kill detect that it's gone: */ |
9895 | dowait(DOWAIT_NONBLOCK, NULL); | 10082 | dowait(DOWAIT_NONBLOCK, NULL); |
9896 | 10083 | ||
9897 | if (evalbltin(cmdentry.u.cmd, argc, argv)) { | 10084 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { |
9898 | int exit_status; | 10085 | int exit_status; |
9899 | int i = exception_type; | 10086 | int i = exception_type; |
9900 | if (i == EXEXIT || i == EXEXEC) | 10087 | if (i == EXEXIT || i == EXEXEC) |
@@ -9911,7 +10098,7 @@ evalcommand(union node *cmd, int flags) | |||
9911 | } | 10098 | } |
9912 | FORCE_INT_ON; | 10099 | FORCE_INT_ON; |
9913 | } | 10100 | } |
9914 | break; | 10101 | goto readstatus; |
9915 | 10102 | ||
9916 | case CMDFUNCTION: | 10103 | case CMDFUNCTION: |
9917 | listsetvar(varlist.list, 0); | 10104 | listsetvar(varlist.list, 0); |
@@ -9919,6 +10106,8 @@ evalcommand(union node *cmd, int flags) | |||
9919 | dowait(DOWAIT_NONBLOCK, NULL); | 10106 | dowait(DOWAIT_NONBLOCK, NULL); |
9920 | if (evalfun(cmdentry.u.func, argc, argv, flags)) | 10107 | if (evalfun(cmdentry.u.func, argc, argv, flags)) |
9921 | goto raise; | 10108 | goto raise; |
10109 | readstatus: | ||
10110 | status = exitstatus; | ||
9922 | break; | 10111 | break; |
9923 | } /* switch */ | 10112 | } /* switch */ |
9924 | 10113 | ||
@@ -9932,29 +10121,36 @@ evalcommand(union node *cmd, int flags) | |||
9932 | setvar0("_", lastarg); | 10121 | setvar0("_", lastarg); |
9933 | } | 10122 | } |
9934 | popstackmark(&smark); | 10123 | popstackmark(&smark); |
10124 | |||
10125 | return status; | ||
9935 | } | 10126 | } |
9936 | 10127 | ||
9937 | static int | 10128 | static int |
9938 | evalbltin(const struct builtincmd *cmd, int argc, char **argv) | 10129 | evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags) |
9939 | { | 10130 | { |
9940 | char *volatile savecmdname; | 10131 | char *volatile savecmdname; |
9941 | struct jmploc *volatile savehandler; | 10132 | struct jmploc *volatile savehandler; |
9942 | struct jmploc jmploc; | 10133 | struct jmploc jmploc; |
10134 | int status; | ||
9943 | int i; | 10135 | int i; |
9944 | 10136 | ||
9945 | savecmdname = commandname; | 10137 | savecmdname = commandname; |
10138 | savehandler = exception_handler; | ||
9946 | i = setjmp(jmploc.loc); | 10139 | i = setjmp(jmploc.loc); |
9947 | if (i) | 10140 | if (i) |
9948 | goto cmddone; | 10141 | goto cmddone; |
9949 | savehandler = exception_handler; | ||
9950 | exception_handler = &jmploc; | 10142 | exception_handler = &jmploc; |
9951 | commandname = argv[0]; | 10143 | commandname = argv[0]; |
9952 | argptr = argv + 1; | 10144 | argptr = argv + 1; |
9953 | optptr = NULL; /* initialize nextopt */ | 10145 | optptr = NULL; /* initialize nextopt */ |
9954 | exitstatus = (*cmd->builtin)(argc, argv); | 10146 | if (cmd == EVALCMD) |
10147 | status = evalcmd(argc, argv, flags); | ||
10148 | else | ||
10149 | status = (*cmd->builtin)(argc, argv); | ||
9955 | flush_stdout_stderr(); | 10150 | flush_stdout_stderr(); |
10151 | status |= ferror(stdout); | ||
10152 | exitstatus = status; | ||
9956 | cmddone: | 10153 | cmddone: |
9957 | exitstatus |= ferror(stdout); | ||
9958 | clearerr(stdout); | 10154 | clearerr(stdout); |
9959 | commandname = savecmdname; | 10155 | commandname = savecmdname; |
9960 | exception_handler = savehandler; | 10156 | exception_handler = savehandler; |
@@ -10058,6 +10254,8 @@ pushstring(char *s, struct alias *ap) | |||
10058 | g_parsefile->strpush = sp; | 10254 | g_parsefile->strpush = sp; |
10059 | sp->prev_string = g_parsefile->next_to_pgetc; | 10255 | sp->prev_string = g_parsefile->next_to_pgetc; |
10060 | sp->prev_left_in_line = g_parsefile->left_in_line; | 10256 | sp->prev_left_in_line = g_parsefile->left_in_line; |
10257 | sp->unget = g_parsefile->unget; | ||
10258 | memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc)); | ||
10061 | #if ENABLE_ASH_ALIAS | 10259 | #if ENABLE_ASH_ALIAS |
10062 | sp->ap = ap; | 10260 | sp->ap = ap; |
10063 | if (ap) { | 10261 | if (ap) { |
@@ -10067,6 +10265,7 @@ pushstring(char *s, struct alias *ap) | |||
10067 | #endif | 10265 | #endif |
10068 | g_parsefile->next_to_pgetc = s; | 10266 | g_parsefile->next_to_pgetc = s; |
10069 | g_parsefile->left_in_line = len; | 10267 | g_parsefile->left_in_line = len; |
10268 | g_parsefile->unget = 0; | ||
10070 | INT_ON; | 10269 | INT_ON; |
10071 | } | 10270 | } |
10072 | 10271 | ||
@@ -10094,17 +10293,14 @@ popstring(void) | |||
10094 | #endif | 10293 | #endif |
10095 | g_parsefile->next_to_pgetc = sp->prev_string; | 10294 | g_parsefile->next_to_pgetc = sp->prev_string; |
10096 | g_parsefile->left_in_line = sp->prev_left_in_line; | 10295 | g_parsefile->left_in_line = sp->prev_left_in_line; |
10296 | g_parsefile->unget = sp->unget; | ||
10297 | memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); | ||
10097 | g_parsefile->strpush = sp->prev; | 10298 | g_parsefile->strpush = sp->prev; |
10098 | if (sp != &(g_parsefile->basestrpush)) | 10299 | if (sp != &(g_parsefile->basestrpush)) |
10099 | free(sp); | 10300 | free(sp); |
10100 | INT_ON; | 10301 | INT_ON; |
10101 | } | 10302 | } |
10102 | 10303 | ||
10103 | //FIXME: BASH_COMPAT with "...&" does TWO pungetc(): | ||
10104 | //it peeks whether it is &>, and then pushes back both chars. | ||
10105 | //This function needs to save last *next_to_pgetc to buf[0] | ||
10106 | //to make two pungetc() reliable. Currently, | ||
10107 | // pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... | ||
10108 | static int | 10304 | static int |
10109 | preadfd(void) | 10305 | preadfd(void) |
10110 | { | 10306 | { |
@@ -10189,13 +10385,14 @@ preadfd(void) | |||
10189 | */ | 10385 | */ |
10190 | //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) | 10386 | //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) |
10191 | #define pgetc_debug(...) ((void)0) | 10387 | #define pgetc_debug(...) ((void)0) |
10388 | static int pgetc(void); | ||
10192 | static int | 10389 | static int |
10193 | preadbuffer(void) | 10390 | preadbuffer(void) |
10194 | { | 10391 | { |
10195 | char *q; | 10392 | char *q; |
10196 | int more; | 10393 | int more; |
10197 | 10394 | ||
10198 | while (g_parsefile->strpush) { | 10395 | if (g_parsefile->strpush) { |
10199 | #if ENABLE_ASH_ALIAS | 10396 | #if ENABLE_ASH_ALIAS |
10200 | if (g_parsefile->left_in_line == -1 | 10397 | if (g_parsefile->left_in_line == -1 |
10201 | && g_parsefile->strpush->ap | 10398 | && g_parsefile->strpush->ap |
@@ -10207,13 +10404,7 @@ preadbuffer(void) | |||
10207 | } | 10404 | } |
10208 | #endif | 10405 | #endif |
10209 | popstring(); | 10406 | popstring(); |
10210 | /* try "pgetc" now: */ | 10407 | return pgetc(); |
10211 | pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'", | ||
10212 | g_parsefile->left_in_line, | ||
10213 | g_parsefile->next_to_pgetc, | ||
10214 | g_parsefile->next_to_pgetc); | ||
10215 | if (--g_parsefile->left_in_line >= 0) | ||
10216 | return (unsigned char)(*g_parsefile->next_to_pgetc++); | ||
10217 | } | 10408 | } |
10218 | /* on both branches above g_parsefile->left_in_line < 0. | 10409 | /* on both branches above g_parsefile->left_in_line < 0. |
10219 | * "pgetc" needs refilling. | 10410 | * "pgetc" needs refilling. |
@@ -10292,27 +10483,41 @@ preadbuffer(void) | |||
10292 | return (unsigned char)*g_parsefile->next_to_pgetc++; | 10483 | return (unsigned char)*g_parsefile->next_to_pgetc++; |
10293 | } | 10484 | } |
10294 | 10485 | ||
10295 | #define pgetc_as_macro() \ | 10486 | static void |
10296 | (--g_parsefile->left_in_line >= 0 \ | 10487 | nlprompt(void) |
10297 | ? (unsigned char)*g_parsefile->next_to_pgetc++ \ | 10488 | { |
10298 | : preadbuffer() \ | 10489 | g_parsefile->linno++; |
10299 | ) | 10490 | setprompt_if(doprompt, 2); |
10491 | } | ||
10492 | static void | ||
10493 | nlnoprompt(void) | ||
10494 | { | ||
10495 | g_parsefile->linno++; | ||
10496 | needprompt = doprompt; | ||
10497 | } | ||
10300 | 10498 | ||
10301 | static int | 10499 | static int |
10302 | pgetc(void) | 10500 | pgetc(void) |
10303 | { | 10501 | { |
10304 | pgetc_debug("pgetc_fast at %d:%p'%s'", | 10502 | int c; |
10503 | |||
10504 | pgetc_debug("pgetc at %d:%p'%s'", | ||
10305 | g_parsefile->left_in_line, | 10505 | g_parsefile->left_in_line, |
10306 | g_parsefile->next_to_pgetc, | 10506 | g_parsefile->next_to_pgetc, |
10307 | g_parsefile->next_to_pgetc); | 10507 | g_parsefile->next_to_pgetc); |
10308 | return pgetc_as_macro(); | 10508 | if (g_parsefile->unget) |
10309 | } | 10509 | return g_parsefile->lastc[--g_parsefile->unget]; |
10310 | 10510 | ||
10311 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE | 10511 | if (--g_parsefile->left_in_line >= 0) |
10312 | # define pgetc_fast() pgetc() | 10512 | c = (signed char)*g_parsefile->next_to_pgetc++; |
10313 | #else | 10513 | else |
10314 | # define pgetc_fast() pgetc_as_macro() | 10514 | c = preadbuffer(); |
10315 | #endif | 10515 | |
10516 | g_parsefile->lastc[1] = g_parsefile->lastc[0]; | ||
10517 | g_parsefile->lastc[0] = c; | ||
10518 | |||
10519 | return c; | ||
10520 | } | ||
10316 | 10521 | ||
10317 | #if ENABLE_ASH_ALIAS | 10522 | #if ENABLE_ASH_ALIAS |
10318 | static int | 10523 | static int |
@@ -10320,11 +10525,11 @@ pgetc_without_PEOA(void) | |||
10320 | { | 10525 | { |
10321 | int c; | 10526 | int c; |
10322 | do { | 10527 | do { |
10323 | pgetc_debug("pgetc_fast at %d:%p'%s'", | 10528 | pgetc_debug("pgetc at %d:%p'%s'", |
10324 | g_parsefile->left_in_line, | 10529 | g_parsefile->left_in_line, |
10325 | g_parsefile->next_to_pgetc, | 10530 | g_parsefile->next_to_pgetc, |
10326 | g_parsefile->next_to_pgetc); | 10531 | g_parsefile->next_to_pgetc); |
10327 | c = pgetc_fast(); | 10532 | c = pgetc(); |
10328 | } while (c == PEOA); | 10533 | } while (c == PEOA); |
10329 | return c; | 10534 | return c; |
10330 | } | 10535 | } |
@@ -10358,18 +10563,31 @@ pfgets(char *line, int len) | |||
10358 | } | 10563 | } |
10359 | 10564 | ||
10360 | /* | 10565 | /* |
10361 | * Undo the last call to pgetc. Only one character may be pushed back. | 10566 | * Undo a call to pgetc. Only two characters may be pushed back. |
10362 | * PEOF may be pushed back. | 10567 | * PEOF may be pushed back. |
10363 | */ | 10568 | */ |
10364 | static void | 10569 | static void |
10365 | pungetc(void) | 10570 | pungetc(void) |
10366 | { | 10571 | { |
10367 | g_parsefile->left_in_line++; | 10572 | g_parsefile->unget++; |
10368 | g_parsefile->next_to_pgetc--; | 10573 | } |
10369 | pgetc_debug("pushed back to %d:%p'%s'", | 10574 | |
10370 | g_parsefile->left_in_line, | 10575 | /* This one eats backslash+newline */ |
10371 | g_parsefile->next_to_pgetc, | 10576 | static int |
10372 | g_parsefile->next_to_pgetc); | 10577 | pgetc_eatbnl(void) |
10578 | { | ||
10579 | int c; | ||
10580 | |||
10581 | while ((c = pgetc()) == '\\') { | ||
10582 | if (pgetc() != '\n') { | ||
10583 | pungetc(); | ||
10584 | break; | ||
10585 | } | ||
10586 | |||
10587 | nlprompt(); | ||
10588 | } | ||
10589 | |||
10590 | return c; | ||
10373 | } | 10591 | } |
10374 | 10592 | ||
10375 | /* | 10593 | /* |
@@ -10386,6 +10604,7 @@ pushfile(void) | |||
10386 | pf->pf_fd = -1; | 10604 | pf->pf_fd = -1; |
10387 | /*pf->strpush = NULL; - ckzalloc did it */ | 10605 | /*pf->strpush = NULL; - ckzalloc did it */ |
10388 | /*pf->basestrpush.prev = NULL;*/ | 10606 | /*pf->basestrpush.prev = NULL;*/ |
10607 | /*pf->unget = 0;*/ | ||
10389 | g_parsefile = pf; | 10608 | g_parsefile = pf; |
10390 | } | 10609 | } |
10391 | 10610 | ||
@@ -10959,8 +11178,8 @@ static const char * | |||
10959 | tokname(char *buf, int tok) | 11178 | tokname(char *buf, int tok) |
10960 | { | 11179 | { |
10961 | if (tok < TSEMI) | 11180 | if (tok < TSEMI) |
10962 | return tokname_array[tok] + 1; | 11181 | return tokname_array[tok]; |
10963 | sprintf(buf, "\"%s\"", tokname_array[tok] + 1); | 11182 | sprintf(buf, "\"%s\"", tokname_array[tok]); |
10964 | return buf; | 11183 | return buf; |
10965 | } | 11184 | } |
10966 | 11185 | ||
@@ -11017,7 +11236,7 @@ list(int nlflag) | |||
11017 | } | 11236 | } |
11018 | 11237 | ||
11019 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | 11238 | checkkwd = CHKNL | CHKKWD | CHKALIAS; |
11020 | if (nlflag == 2 && tokname_array[peektoken()][0]) | 11239 | if (nlflag == 2 && ((1 << peektoken()) & tokendlist)) |
11021 | return n1; | 11240 | return n1; |
11022 | nlflag |= 2; | 11241 | nlflag |= 2; |
11023 | 11242 | ||
@@ -11399,7 +11618,7 @@ parse_command(void) | |||
11399 | n1->nbinary.ch1 = list(0); | 11618 | n1->nbinary.ch1 = list(0); |
11400 | got = readtoken(); | 11619 | got = readtoken(); |
11401 | if (got != TDO) { | 11620 | if (got != TDO) { |
11402 | TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1, | 11621 | TRACE(("expecting DO got '%s' %s\n", tokname_array[got], |
11403 | got == TWORD ? wordtext : "")); | 11622 | got == TWORD ? wordtext : "")); |
11404 | raise_error_unexpected_syntax(TDO); | 11623 | raise_error_unexpected_syntax(TDO); |
11405 | } | 11624 | } |
@@ -11545,7 +11764,8 @@ parse_command(void) | |||
11545 | } | 11764 | } |
11546 | 11765 | ||
11547 | #if ENABLE_ASH_BASH_COMPAT | 11766 | #if ENABLE_ASH_BASH_COMPAT |
11548 | static int decode_dollar_squote(void) | 11767 | static int |
11768 | decode_dollar_squote(void) | ||
11549 | { | 11769 | { |
11550 | static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; | 11770 | static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; |
11551 | int c, cnt; | 11771 | int c, cnt; |
@@ -11613,7 +11833,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11613 | /* NB: syntax parameter fits into smallint */ | 11833 | /* NB: syntax parameter fits into smallint */ |
11614 | /* c parameter is an unsigned char or PEOF or PEOA */ | 11834 | /* c parameter is an unsigned char or PEOF or PEOA */ |
11615 | char *out; | 11835 | char *out; |
11616 | int len; | 11836 | size_t len; |
11617 | char line[EOFMARKLEN + 1]; | 11837 | char line[EOFMARKLEN + 1]; |
11618 | struct nodelist *bqlist; | 11838 | struct nodelist *bqlist; |
11619 | smallint quotef; | 11839 | smallint quotef; |
@@ -11656,25 +11876,31 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11656 | if (syntax == BASESYNTAX) | 11876 | if (syntax == BASESYNTAX) |
11657 | goto endword; /* exit outer loop */ | 11877 | goto endword; /* exit outer loop */ |
11658 | USTPUTC(c, out); | 11878 | USTPUTC(c, out); |
11659 | g_parsefile->linno++; | 11879 | nlprompt(); |
11660 | setprompt_if(doprompt, 2); | ||
11661 | c = pgetc(); | 11880 | c = pgetc(); |
11662 | goto loop; /* continue outer loop */ | 11881 | goto loop; /* continue outer loop */ |
11663 | case CWORD: | 11882 | case CWORD: |
11664 | USTPUTC(c, out); | 11883 | USTPUTC(c, out); |
11665 | break; | 11884 | break; |
11666 | case CCTL: | 11885 | case CCTL: |
11667 | if (eofmark == NULL || dblquote) | ||
11668 | USTPUTC(CTLESC, out); | ||
11669 | #if ENABLE_ASH_BASH_COMPAT | 11886 | #if ENABLE_ASH_BASH_COMPAT |
11670 | if (c == '\\' && bash_dollar_squote) { | 11887 | if (c == '\\' && bash_dollar_squote) { |
11671 | c = decode_dollar_squote(); | 11888 | c = decode_dollar_squote(); |
11889 | if (c == '\0') { | ||
11890 | /* skip $'\000', $'\x00' (like bash) */ | ||
11891 | break; | ||
11892 | } | ||
11672 | if (c & 0x100) { | 11893 | if (c & 0x100) { |
11673 | USTPUTC('\\', out); | 11894 | /* Unknown escape. Encode as '\z' */ |
11674 | c = (unsigned char)c; | 11895 | c = (unsigned char)c; |
11896 | if (eofmark == NULL || dblquote) | ||
11897 | USTPUTC(CTLESC, out); | ||
11898 | USTPUTC('\\', out); | ||
11675 | } | 11899 | } |
11676 | } | 11900 | } |
11677 | #endif | 11901 | #endif |
11902 | if (eofmark == NULL || dblquote) | ||
11903 | USTPUTC(CTLESC, out); | ||
11678 | USTPUTC(c, out); | 11904 | USTPUTC(c, out); |
11679 | break; | 11905 | break; |
11680 | case CBACK: /* backslash */ | 11906 | case CBACK: /* backslash */ |
@@ -11684,7 +11910,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11684 | USTPUTC('\\', out); | 11910 | USTPUTC('\\', out); |
11685 | pungetc(); | 11911 | pungetc(); |
11686 | } else if (c == '\n') { | 11912 | } else if (c == '\n') { |
11687 | setprompt_if(doprompt, 2); | 11913 | nlprompt(); |
11688 | } else { | 11914 | } else { |
11689 | #if ENABLE_ASH_EXPAND_PRMT | 11915 | #if ENABLE_ASH_EXPAND_PRMT |
11690 | if (c == '$' && pssyntax) { | 11916 | if (c == '$' && pssyntax) { |
@@ -11752,7 +11978,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11752 | if (parenlevel > 0) { | 11978 | if (parenlevel > 0) { |
11753 | parenlevel--; | 11979 | parenlevel--; |
11754 | } else { | 11980 | } else { |
11755 | if (pgetc() == ')') { | 11981 | if (pgetc_eatbnl() == ')') { |
11756 | c = CTLENDARI; | 11982 | c = CTLENDARI; |
11757 | if (--arinest == 0) { | 11983 | if (--arinest == 0) { |
11758 | syntax = prevsyntax; | 11984 | syntax = prevsyntax; |
@@ -11779,6 +12005,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11779 | if (varnest == 0) { | 12005 | if (varnest == 0) { |
11780 | #if ENABLE_ASH_BASH_COMPAT | 12006 | #if ENABLE_ASH_BASH_COMPAT |
11781 | if (c == '&') { | 12007 | if (c == '&') { |
12008 | //Can't call pgetc_eatbnl() here, this requires three-deep pungetc() | ||
11782 | if (pgetc() == '>') | 12009 | if (pgetc() == '>') |
11783 | c = 0x100 + '>'; /* flag &> */ | 12010 | c = 0x100 + '>'; /* flag &> */ |
11784 | pungetc(); | 12011 | pungetc(); |
@@ -11789,7 +12016,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11789 | IF_ASH_ALIAS(if (c != PEOA)) | 12016 | IF_ASH_ALIAS(if (c != PEOA)) |
11790 | USTPUTC(c, out); | 12017 | USTPUTC(c, out); |
11791 | } | 12018 | } |
11792 | c = pgetc_fast(); | 12019 | c = pgetc(); |
11793 | } /* for (;;) */ | 12020 | } /* for (;;) */ |
11794 | endword: | 12021 | endword: |
11795 | 12022 | ||
@@ -11854,8 +12081,7 @@ checkend: { | |||
11854 | continue; | 12081 | continue; |
11855 | if (*p == '\n' && *q == '\0') { | 12082 | if (*p == '\n' && *q == '\0') { |
11856 | c = PEOF; | 12083 | c = PEOF; |
11857 | g_parsefile->linno++; | 12084 | nlnoprompt(); |
11858 | needprompt = doprompt; | ||
11859 | } else { | 12085 | } else { |
11860 | pushstring(line, NULL); | 12086 | pushstring(line, NULL); |
11861 | } | 12087 | } |
@@ -11954,7 +12180,7 @@ parsesub: { | |||
11954 | int typeloc; | 12180 | int typeloc; |
11955 | int flags; | 12181 | int flags; |
11956 | 12182 | ||
11957 | c = pgetc(); | 12183 | c = pgetc_eatbnl(); |
11958 | if (c > 255 /* PEOA or PEOF */ | 12184 | if (c > 255 /* PEOA or PEOF */ |
11959 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) | 12185 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
11960 | ) { | 12186 | ) { |
@@ -11967,7 +12193,7 @@ parsesub: { | |||
11967 | pungetc(); | 12193 | pungetc(); |
11968 | } else if (c == '(') { | 12194 | } else if (c == '(') { |
11969 | /* $(command) or $((arith)) */ | 12195 | /* $(command) or $((arith)) */ |
11970 | if (pgetc() == '(') { | 12196 | if (pgetc_eatbnl() == '(') { |
11971 | #if ENABLE_SH_MATH_SUPPORT | 12197 | #if ENABLE_SH_MATH_SUPPORT |
11972 | PARSEARITH(); | 12198 | PARSEARITH(); |
11973 | #else | 12199 | #else |
@@ -11984,9 +12210,9 @@ parsesub: { | |||
11984 | USTPUTC(VSNORMAL, out); | 12210 | USTPUTC(VSNORMAL, out); |
11985 | subtype = VSNORMAL; | 12211 | subtype = VSNORMAL; |
11986 | if (c == '{') { | 12212 | if (c == '{') { |
11987 | c = pgetc(); | 12213 | c = pgetc_eatbnl(); |
11988 | if (c == '#') { | 12214 | if (c == '#') { |
11989 | c = pgetc(); | 12215 | c = pgetc_eatbnl(); |
11990 | if (c == '}') | 12216 | if (c == '}') |
11991 | c = '#'; /* ${#} - same as $# */ | 12217 | c = '#'; /* ${#} - same as $# */ |
11992 | else | 12218 | else |
@@ -11999,18 +12225,18 @@ parsesub: { | |||
11999 | /* $[{[#]]NAME[}] */ | 12225 | /* $[{[#]]NAME[}] */ |
12000 | do { | 12226 | do { |
12001 | STPUTC(c, out); | 12227 | STPUTC(c, out); |
12002 | c = pgetc(); | 12228 | c = pgetc_eatbnl(); |
12003 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); | 12229 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); |
12004 | } else if (isdigit(c)) { | 12230 | } else if (isdigit(c)) { |
12005 | /* $[{[#]]NUM[}] */ | 12231 | /* $[{[#]]NUM[}] */ |
12006 | do { | 12232 | do { |
12007 | STPUTC(c, out); | 12233 | STPUTC(c, out); |
12008 | c = pgetc(); | 12234 | c = pgetc_eatbnl(); |
12009 | } while (isdigit(c)); | 12235 | } while (isdigit(c)); |
12010 | } else if (is_special(c)) { | 12236 | } else if (is_special(c)) { |
12011 | /* $[{[#]]<specialchar>[}] */ | 12237 | /* $[{[#]]<specialchar>[}] */ |
12012 | USTPUTC(c, out); | 12238 | USTPUTC(c, out); |
12013 | c = pgetc(); | 12239 | c = pgetc_eatbnl(); |
12014 | } else { | 12240 | } else { |
12015 | badsub: | 12241 | badsub: |
12016 | raise_error_syntax("bad substitution"); | 12242 | raise_error_syntax("bad substitution"); |
@@ -12023,15 +12249,18 @@ parsesub: { | |||
12023 | STPUTC('=', out); | 12249 | STPUTC('=', out); |
12024 | flags = 0; | 12250 | flags = 0; |
12025 | if (subtype == 0) { | 12251 | if (subtype == 0) { |
12252 | static const char types[] ALIGN1 = "}-+?="; | ||
12026 | /* ${VAR...} but not $VAR or ${#VAR} */ | 12253 | /* ${VAR...} but not $VAR or ${#VAR} */ |
12027 | /* c == first char after VAR */ | 12254 | /* c == first char after VAR */ |
12028 | switch (c) { | 12255 | switch (c) { |
12029 | case ':': | 12256 | case ':': |
12030 | c = pgetc(); | 12257 | c = pgetc_eatbnl(); |
12031 | #if ENABLE_ASH_BASH_COMPAT | 12258 | #if ENABLE_ASH_BASH_COMPAT |
12032 | if (c == ':' || c == '$' || isdigit(c)) { | 12259 | /* This check is only needed to not misinterpret |
12033 | //TODO: support more general format ${v:EXPR:EXPR}, | 12260 | * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} |
12034 | // where EXPR follows $(()) rules | 12261 | * constructs. |
12262 | */ | ||
12263 | if (!strchr(types, c)) { | ||
12035 | subtype = VSSUBSTR; | 12264 | subtype = VSSUBSTR; |
12036 | pungetc(); | 12265 | pungetc(); |
12037 | break; /* "goto do_pungetc" is bigger (!) */ | 12266 | break; /* "goto do_pungetc" is bigger (!) */ |
@@ -12040,7 +12269,6 @@ parsesub: { | |||
12040 | flags = VSNUL; | 12269 | flags = VSNUL; |
12041 | /*FALLTHROUGH*/ | 12270 | /*FALLTHROUGH*/ |
12042 | default: { | 12271 | default: { |
12043 | static const char types[] ALIGN1 = "}-+?="; | ||
12044 | const char *p = strchr(types, c); | 12272 | const char *p = strchr(types, c); |
12045 | if (p == NULL) | 12273 | if (p == NULL) |
12046 | goto badsub; | 12274 | goto badsub; |
@@ -12051,7 +12279,7 @@ parsesub: { | |||
12051 | case '#': { | 12279 | case '#': { |
12052 | int cc = c; | 12280 | int cc = c; |
12053 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); | 12281 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); |
12054 | c = pgetc(); | 12282 | c = pgetc_eatbnl(); |
12055 | if (c != cc) | 12283 | if (c != cc) |
12056 | goto do_pungetc; | 12284 | goto do_pungetc; |
12057 | subtype++; | 12285 | subtype++; |
@@ -12063,7 +12291,7 @@ parsesub: { | |||
12063 | //TODO: encode pattern and repl separately. | 12291 | //TODO: encode pattern and repl separately. |
12064 | // Currently ${v/$var_with_slash/repl} is horribly broken | 12292 | // Currently ${v/$var_with_slash/repl} is horribly broken |
12065 | subtype = VSREPLACE; | 12293 | subtype = VSREPLACE; |
12066 | c = pgetc(); | 12294 | c = pgetc_eatbnl(); |
12067 | if (c != '/') | 12295 | if (c != '/') |
12068 | goto do_pungetc; | 12296 | goto do_pungetc; |
12069 | subtype++; /* VSREPLACEALL */ | 12297 | subtype++; /* VSREPLACEALL */ |
@@ -12101,9 +12329,18 @@ parsebackq: { | |||
12101 | str = NULL; | 12329 | str = NULL; |
12102 | savelen = out - (char *)stackblock(); | 12330 | savelen = out - (char *)stackblock(); |
12103 | if (savelen > 0) { | 12331 | if (savelen > 0) { |
12332 | /* | ||
12333 | * FIXME: this can allocate very large block on stack and SEGV. | ||
12334 | * Example: | ||
12335 | * echo "..<100kbytes>..`true` $(true) `true` ..." | ||
12336 | * allocates 100kb for every command subst. With about | ||
12337 | * a hundred command substitutions stack overflows. | ||
12338 | * With larger prepended string, SEGV happens sooner. | ||
12339 | */ | ||
12104 | str = alloca(savelen); | 12340 | str = alloca(savelen); |
12105 | memcpy(str, stackblock(), savelen); | 12341 | memcpy(str, stackblock(), savelen); |
12106 | } | 12342 | } |
12343 | |||
12107 | if (oldstyle) { | 12344 | if (oldstyle) { |
12108 | /* We must read until the closing backquote, giving special | 12345 | /* We must read until the closing backquote, giving special |
12109 | * treatment to some slashes, and then push the string and | 12346 | * treatment to some slashes, and then push the string and |
@@ -12126,8 +12363,7 @@ parsebackq: { | |||
12126 | case '\\': | 12363 | case '\\': |
12127 | pc = pgetc(); | 12364 | pc = pgetc(); |
12128 | if (pc == '\n') { | 12365 | if (pc == '\n') { |
12129 | g_parsefile->linno++; | 12366 | nlprompt(); |
12130 | setprompt_if(doprompt, 2); | ||
12131 | /* | 12367 | /* |
12132 | * If eating a newline, avoid putting | 12368 | * If eating a newline, avoid putting |
12133 | * the newline into the new character | 12369 | * the newline into the new character |
@@ -12152,8 +12388,7 @@ parsebackq: { | |||
12152 | raise_error_syntax("EOF in backquote substitution"); | 12388 | raise_error_syntax("EOF in backquote substitution"); |
12153 | 12389 | ||
12154 | case '\n': | 12390 | case '\n': |
12155 | g_parsefile->linno++; | 12391 | nlnoprompt(); |
12156 | needprompt = doprompt; | ||
12157 | break; | 12392 | break; |
12158 | 12393 | ||
12159 | default: | 12394 | default: |
@@ -12272,7 +12507,7 @@ xxreadtoken(void) | |||
12272 | setprompt_if(needprompt, 2); | 12507 | setprompt_if(needprompt, 2); |
12273 | startlinno = g_parsefile->linno; | 12508 | startlinno = g_parsefile->linno; |
12274 | for (;;) { /* until token or start of word found */ | 12509 | for (;;) { /* until token or start of word found */ |
12275 | c = pgetc_fast(); | 12510 | c = pgetc(); |
12276 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) | 12511 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) |
12277 | continue; | 12512 | continue; |
12278 | 12513 | ||
@@ -12285,16 +12520,14 @@ xxreadtoken(void) | |||
12285 | pungetc(); | 12520 | pungetc(); |
12286 | break; /* return readtoken1(...) */ | 12521 | break; /* return readtoken1(...) */ |
12287 | } | 12522 | } |
12288 | startlinno = ++g_parsefile->linno; | 12523 | nlprompt(); |
12289 | setprompt_if(doprompt, 2); | ||
12290 | } else { | 12524 | } else { |
12291 | const char *p; | 12525 | const char *p; |
12292 | 12526 | ||
12293 | p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; | 12527 | p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; |
12294 | if (c != PEOF) { | 12528 | if (c != PEOF) { |
12295 | if (c == '\n') { | 12529 | if (c == '\n') { |
12296 | g_parsefile->linno++; | 12530 | nlnoprompt(); |
12297 | needprompt = doprompt; | ||
12298 | } | 12531 | } |
12299 | 12532 | ||
12300 | p = strchr(xxreadtoken_chars, c); | 12533 | p = strchr(xxreadtoken_chars, c); |
@@ -12335,7 +12568,7 @@ xxreadtoken(void) | |||
12335 | setprompt_if(needprompt, 2); | 12568 | setprompt_if(needprompt, 2); |
12336 | startlinno = g_parsefile->linno; | 12569 | startlinno = g_parsefile->linno; |
12337 | for (;;) { /* until token or start of word found */ | 12570 | for (;;) { /* until token or start of word found */ |
12338 | c = pgetc_fast(); | 12571 | c = pgetc(); |
12339 | switch (c) { | 12572 | switch (c) { |
12340 | case ' ': case '\t': | 12573 | case ' ': case '\t': |
12341 | IF_ASH_ALIAS(case PEOA:) | 12574 | IF_ASH_ALIAS(case PEOA:) |
@@ -12347,15 +12580,13 @@ xxreadtoken(void) | |||
12347 | continue; | 12580 | continue; |
12348 | case '\\': | 12581 | case '\\': |
12349 | if (pgetc() == '\n') { | 12582 | if (pgetc() == '\n') { |
12350 | startlinno = ++g_parsefile->linno; | 12583 | nlprompt(); |
12351 | setprompt_if(doprompt, 2); | ||
12352 | continue; | 12584 | continue; |
12353 | } | 12585 | } |
12354 | pungetc(); | 12586 | pungetc(); |
12355 | goto breakloop; | 12587 | goto breakloop; |
12356 | case '\n': | 12588 | case '\n': |
12357 | g_parsefile->linno++; | 12589 | nlnoprompt(); |
12358 | needprompt = doprompt; | ||
12359 | RETURN(TNL); | 12590 | RETURN(TNL); |
12360 | case PEOF: | 12591 | case PEOF: |
12361 | RETURN(TEOF); | 12592 | RETURN(TEOF); |
@@ -12426,7 +12657,7 @@ readtoken(void) | |||
12426 | pp = findkwd(wordtext); | 12657 | pp = findkwd(wordtext); |
12427 | if (pp) { | 12658 | if (pp) { |
12428 | lasttoken = t = pp - tokname_array; | 12659 | lasttoken = t = pp - tokname_array; |
12429 | TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1)); | 12660 | TRACE(("keyword '%s' recognized\n", tokname_array[t])); |
12430 | goto out; | 12661 | goto out; |
12431 | } | 12662 | } |
12432 | } | 12663 | } |
@@ -12447,9 +12678,9 @@ readtoken(void) | |||
12447 | checkkwd = 0; | 12678 | checkkwd = 0; |
12448 | #if DEBUG | 12679 | #if DEBUG |
12449 | if (!alreadyseen) | 12680 | if (!alreadyseen) |
12450 | TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); | 12681 | TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : "")); |
12451 | else | 12682 | else |
12452 | TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); | 12683 | TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : "")); |
12453 | #endif | 12684 | #endif |
12454 | return t; | 12685 | return t; |
12455 | } | 12686 | } |
@@ -12536,35 +12767,39 @@ expandstr(const char *ps) | |||
12536 | * Execute a command or commands contained in a string. | 12767 | * Execute a command or commands contained in a string. |
12537 | */ | 12768 | */ |
12538 | static int | 12769 | static int |
12539 | evalstring(char *s, int mask) | 12770 | evalstring(char *s, int flags) |
12540 | { | 12771 | { |
12541 | union node *n; | 12772 | union node *n; |
12542 | struct stackmark smark; | 12773 | struct stackmark smark; |
12543 | int skip; | 12774 | int status; |
12544 | 12775 | ||
12776 | s = sstrdup(s); | ||
12545 | setinputstring(s); | 12777 | setinputstring(s); |
12546 | setstackmark(&smark); | 12778 | setstackmark(&smark); |
12547 | 12779 | ||
12548 | skip = 0; | 12780 | status = 0; |
12549 | while ((n = parsecmd(0)) != NODE_EOF) { | 12781 | while ((n = parsecmd(0)) != NODE_EOF) { |
12550 | evaltree(n, 0); | 12782 | int i; |
12783 | |||
12784 | i = evaltree(n, flags); | ||
12785 | if (n) | ||
12786 | status = i; | ||
12551 | popstackmark(&smark); | 12787 | popstackmark(&smark); |
12552 | skip = evalskip; | 12788 | if (evalskip) |
12553 | if (skip) | ||
12554 | break; | 12789 | break; |
12555 | } | 12790 | } |
12791 | popstackmark(&smark); | ||
12556 | popfile(); | 12792 | popfile(); |
12793 | stunalloc(s); | ||
12557 | 12794 | ||
12558 | skip &= mask; | 12795 | return status; |
12559 | evalskip = skip; | ||
12560 | return skip; | ||
12561 | } | 12796 | } |
12562 | 12797 | ||
12563 | /* | 12798 | /* |
12564 | * The eval command. | 12799 | * The eval command. |
12565 | */ | 12800 | */ |
12566 | static int FAST_FUNC | 12801 | static int FAST_FUNC |
12567 | evalcmd(int argc UNUSED_PARAM, char **argv) | 12802 | evalcmd(int argc UNUSED_PARAM, char **argv, int flags) |
12568 | { | 12803 | { |
12569 | char *p; | 12804 | char *p; |
12570 | char *concat; | 12805 | char *concat; |
@@ -12584,9 +12819,9 @@ evalcmd(int argc UNUSED_PARAM, char **argv) | |||
12584 | STPUTC('\0', concat); | 12819 | STPUTC('\0', concat); |
12585 | p = grabstackstr(concat); | 12820 | p = grabstackstr(concat); |
12586 | } | 12821 | } |
12587 | evalstring(p, ~SKIPEVAL); | 12822 | return evalstring(p, flags & EV_TESTED); |
12588 | } | 12823 | } |
12589 | return exitstatus; | 12824 | return 0; |
12590 | } | 12825 | } |
12591 | 12826 | ||
12592 | /* | 12827 | /* |
@@ -12600,6 +12835,7 @@ cmdloop(int top) | |||
12600 | union node *n; | 12835 | union node *n; |
12601 | struct stackmark smark; | 12836 | struct stackmark smark; |
12602 | int inter; | 12837 | int inter; |
12838 | int status = 0; | ||
12603 | int numeof = 0; | 12839 | int numeof = 0; |
12604 | 12840 | ||
12605 | TRACE(("cmdloop(%d) called\n", top)); | 12841 | TRACE(("cmdloop(%d) called\n", top)); |
@@ -12631,20 +12867,24 @@ cmdloop(int top) | |||
12631 | } | 12867 | } |
12632 | numeof++; | 12868 | numeof++; |
12633 | } else if (nflag == 0) { | 12869 | } else if (nflag == 0) { |
12870 | int i; | ||
12871 | |||
12634 | /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ | 12872 | /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ |
12635 | job_warning >>= 1; | 12873 | job_warning >>= 1; |
12636 | numeof = 0; | 12874 | numeof = 0; |
12637 | evaltree(n, 0); | 12875 | i = evaltree(n, 0); |
12876 | if (n) | ||
12877 | status = i; | ||
12638 | } | 12878 | } |
12639 | popstackmark(&smark); | 12879 | popstackmark(&smark); |
12640 | skip = evalskip; | 12880 | skip = evalskip; |
12641 | 12881 | ||
12642 | if (skip) { | 12882 | if (skip) { |
12643 | evalskip = 0; | 12883 | evalskip &= ~SKIPFUNC; |
12644 | return skip & SKIPEVAL; | 12884 | break; |
12645 | } | 12885 | } |
12646 | } | 12886 | } |
12647 | return 0; | 12887 | return status; |
12648 | } | 12888 | } |
12649 | 12889 | ||
12650 | /* | 12890 | /* |
@@ -12689,32 +12929,38 @@ find_dot_file(char *name) | |||
12689 | } | 12929 | } |
12690 | 12930 | ||
12691 | static int FAST_FUNC | 12931 | static int FAST_FUNC |
12692 | dotcmd(int argc, char **argv) | 12932 | dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM) |
12693 | { | 12933 | { |
12934 | /* "false; . empty_file; echo $?" should print 0, not 1: */ | ||
12935 | int status = 0; | ||
12694 | char *fullname; | 12936 | char *fullname; |
12937 | char **argv; | ||
12695 | struct strlist *sp; | 12938 | struct strlist *sp; |
12696 | volatile struct shparam saveparam; | 12939 | volatile struct shparam saveparam; |
12697 | 12940 | ||
12698 | for (sp = cmdenviron; sp; sp = sp->next) | 12941 | for (sp = cmdenviron; sp; sp = sp->next) |
12699 | setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); | 12942 | setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); |
12700 | 12943 | ||
12701 | if (!argv[1]) { | 12944 | nextopt(nullstr); /* handle possible "--" */ |
12945 | argv = argptr; | ||
12946 | |||
12947 | if (!argv[0]) { | ||
12702 | /* bash says: "bash: .: filename argument required" */ | 12948 | /* bash says: "bash: .: filename argument required" */ |
12703 | return 2; /* bash compat */ | 12949 | return 2; /* bash compat */ |
12704 | } | 12950 | } |
12705 | 12951 | ||
12706 | /* "false; . empty_file; echo $?" should print 0, not 1: */ | ||
12707 | exitstatus = 0; | ||
12708 | |||
12709 | /* This aborts if file isn't found, which is POSIXly correct. | 12952 | /* This aborts if file isn't found, which is POSIXly correct. |
12710 | * bash returns exitcode 1 instead. | 12953 | * bash returns exitcode 1 instead. |
12711 | */ | 12954 | */ |
12712 | fullname = find_dot_file(argv[1]); | 12955 | fullname = find_dot_file(argv[0]); |
12713 | argv += 2; | 12956 | argv++; |
12714 | argc -= 2; | 12957 | if (argv[0]) { /* . FILE ARGS, ARGS exist */ |
12715 | if (argc) { /* argc > 0, argv[0] != NULL */ | 12958 | int argc; |
12716 | saveparam = shellparam; | 12959 | saveparam = shellparam; |
12717 | shellparam.malloced = 0; | 12960 | shellparam.malloced = 0; |
12961 | argc = 1; | ||
12962 | while (argv[argc]) | ||
12963 | argc++; | ||
12718 | shellparam.nparam = argc; | 12964 | shellparam.nparam = argc; |
12719 | shellparam.p = argv; | 12965 | shellparam.p = argv; |
12720 | }; | 12966 | }; |
@@ -12724,15 +12970,15 @@ dotcmd(int argc, char **argv) | |||
12724 | */ | 12970 | */ |
12725 | setinputfile(fullname, INPUT_PUSH_FILE); | 12971 | setinputfile(fullname, INPUT_PUSH_FILE); |
12726 | commandname = fullname; | 12972 | commandname = fullname; |
12727 | cmdloop(0); | 12973 | status = cmdloop(0); |
12728 | popfile(); | 12974 | popfile(); |
12729 | 12975 | ||
12730 | if (argc) { | 12976 | if (argv[0]) { |
12731 | freeparam(&shellparam); | 12977 | freeparam(&shellparam); |
12732 | shellparam = saveparam; | 12978 | shellparam = saveparam; |
12733 | }; | 12979 | }; |
12734 | 12980 | ||
12735 | return exitstatus; | 12981 | return status; |
12736 | } | 12982 | } |
12737 | 12983 | ||
12738 | static int FAST_FUNC | 12984 | static int FAST_FUNC |
@@ -12994,8 +13240,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
12994 | } | 13240 | } |
12995 | 13241 | ||
12996 | 13242 | ||
12997 | /* ============ trap.c */ | ||
12998 | |||
12999 | /* | 13243 | /* |
13000 | * The trap builtin. | 13244 | * The trap builtin. |
13001 | */ | 13245 | */ |
@@ -13101,7 +13345,7 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13101 | } | 13345 | } |
13102 | } | 13346 | } |
13103 | # endif | 13347 | # endif |
13104 | out1fmt("\n\n"); | 13348 | newline_and_flush(stdout); |
13105 | return EXIT_SUCCESS; | 13349 | return EXIT_SUCCESS; |
13106 | } | 13350 | } |
13107 | #endif | 13351 | #endif |
@@ -13421,15 +13665,10 @@ exitshell(void) | |||
13421 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 13665 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
13422 | save_history(line_input_state); | 13666 | save_history(line_input_state); |
13423 | #endif | 13667 | #endif |
13424 | |||
13425 | status = exitstatus; | 13668 | status = exitstatus; |
13426 | TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); | 13669 | TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); |
13427 | if (setjmp(loc.loc)) { | 13670 | if (setjmp(loc.loc)) { |
13428 | if (exception_type == EXEXIT) | 13671 | if (exception_type == EXEXIT) |
13429 | /* dash bug: it just does _exit(exitstatus) here | ||
13430 | * but we have to do setjobctl(0) first! | ||
13431 | * (bug is still not fixed in dash-0.5.3 - if you run dash | ||
13432 | * under Midnight Commander, on exit from dash MC is backgrounded) */ | ||
13433 | status = exitstatus; | 13672 | status = exitstatus; |
13434 | goto out; | 13673 | goto out; |
13435 | } | 13674 | } |
@@ -13437,11 +13676,15 @@ exitshell(void) | |||
13437 | p = trap[0]; | 13676 | p = trap[0]; |
13438 | if (p) { | 13677 | if (p) { |
13439 | trap[0] = NULL; | 13678 | trap[0] = NULL; |
13679 | evalskip = 0; | ||
13440 | evalstring(p, 0); | 13680 | evalstring(p, 0); |
13441 | free(p); | 13681 | /*free(p); - we'll exit soon */ |
13442 | } | 13682 | } |
13443 | flush_stdout_stderr(); | 13683 | flush_stdout_stderr(); |
13444 | out: | 13684 | out: |
13685 | /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". | ||
13686 | * our setjobctl(0) does not panic if tcsetpgrp fails inside it. | ||
13687 | */ | ||
13445 | setjobctl(0); | 13688 | setjobctl(0); |
13446 | _exit(status); | 13689 | _exit(status); |
13447 | /* NOTREACHED */ | 13690 | /* NOTREACHED */ |
@@ -13454,18 +13697,15 @@ init(int xp) | |||
13454 | init(void) | 13697 | init(void) |
13455 | #endif | 13698 | #endif |
13456 | { | 13699 | { |
13457 | /* from input.c: */ | ||
13458 | /* we will never free this */ | 13700 | /* we will never free this */ |
13459 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); | 13701 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); |
13460 | 13702 | ||
13461 | /* from trap.c: */ | ||
13462 | signal(SIGCHLD, SIG_DFL); | 13703 | signal(SIGCHLD, SIG_DFL); |
13463 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | 13704 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. |
13464 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | 13705 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" |
13465 | */ | 13706 | */ |
13466 | signal(SIGHUP, SIG_DFL); | 13707 | signal(SIGHUP, SIG_DFL); |
13467 | 13708 | ||
13468 | /* from var.c: */ | ||
13469 | { | 13709 | { |
13470 | char **envp; | 13710 | char **envp; |
13471 | const char *p; | 13711 | const char *p; |
@@ -13553,11 +13793,14 @@ init(void) | |||
13553 | } | 13793 | } |
13554 | #endif | 13794 | #endif |
13555 | for (envp = environ; envp && *envp; envp++) { | 13795 | for (envp = environ; envp && *envp; envp++) { |
13556 | if (strchr(*envp, '=')) { | 13796 | p = endofname(*envp); |
13797 | if (p != *envp && *p == '=') { | ||
13557 | setvareq(*envp, VEXPORT|VTEXTFIXED); | 13798 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
13558 | } | 13799 | } |
13559 | } | 13800 | } |
13560 | 13801 | ||
13802 | setvareq((char*)defoptindvar, VTEXTFIXED); | ||
13803 | |||
13561 | setvar0("PPID", utoa(getppid())); | 13804 | setvar0("PPID", utoa(getppid())); |
13562 | #if ENABLE_ASH_BASH_COMPAT | 13805 | #if ENABLE_ASH_BASH_COMPAT |
13563 | p = lookupvar("SHLVL"); | 13806 | p = lookupvar("SHLVL"); |
@@ -13570,10 +13813,10 @@ init(void) | |||
13570 | #endif | 13813 | #endif |
13571 | p = lookupvar("PWD"); | 13814 | p = lookupvar("PWD"); |
13572 | if (p) { | 13815 | if (p) { |
13573 | if (*p != '/' || stat(p, &st1) || stat(".", &st2) | 13816 | if (p[0] != '/' || stat(p, &st1) || stat(".", &st2) |
13574 | || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino | 13817 | || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino |
13575 | ) { | 13818 | ) { |
13576 | p = '\0'; | 13819 | p = NULL; |
13577 | } | 13820 | } |
13578 | } | 13821 | } |
13579 | setpwd(p, 0); | 13822 | setpwd(p, 0); |
@@ -13664,14 +13907,10 @@ procargs(char **argv) | |||
13664 | static void | 13907 | static void |
13665 | read_profile(const char *name) | 13908 | read_profile(const char *name) |
13666 | { | 13909 | { |
13667 | int skip; | ||
13668 | |||
13669 | if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) | 13910 | if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) |
13670 | return; | 13911 | return; |
13671 | skip = cmdloop(0); | 13912 | cmdloop(0); |
13672 | popfile(); | 13913 | popfile(); |
13673 | if (skip) | ||
13674 | exitshell(); | ||
13675 | } | 13914 | } |
13676 | 13915 | ||
13677 | /* | 13916 | /* |
diff --git a/shell/ash_test/ash-glob/glob_dir.right b/shell/ash_test/ash-glob/glob_dir.right new file mode 100644 index 000000000..aa90514d5 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_dir.right | |||
@@ -0,0 +1,19 @@ | |||
1 | dirtest/z.tmp | ||
2 | dirtest/z.tmp | ||
3 | dirtest/z.tmp | ||
4 | dirtest/z.tmp | ||
5 | dirtest/z.tmp | ||
6 | dirtest/z.tmp | ||
7 | dirtest/z.tmp | ||
8 | dirtest/z.tmp | ||
9 | dirtest/z.tmp | ||
10 | |||
11 | */z.tmp | ||
12 | */z.* | ||
13 | */?.* | ||
14 | */z*p | ||
15 | d*r*e*t/z*p | ||
16 | *\/z.tmp | ||
17 | */z.* | ||
18 | */z*p | ||
19 | d*r*e*t/z*p | ||
diff --git a/shell/ash_test/ash-glob/glob_dir.tests b/shell/ash_test/ash-glob/glob_dir.tests new file mode 100755 index 000000000..dc4c4fdb5 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_dir.tests | |||
@@ -0,0 +1,25 @@ | |||
1 | mkdir dirtest | ||
2 | >dirtest/z.tmp | ||
3 | |||
4 | echo */z.tmp | ||
5 | echo */z.* | ||
6 | echo */?.* | ||
7 | echo */z*p | ||
8 | echo d*r*e*t/z*p | ||
9 | echo *"/z.t"mp | ||
10 | echo */z"."* | ||
11 | echo *"/z"*"p" | ||
12 | echo "d"*r*e*t"/"z*p | ||
13 | echo | ||
14 | echo \*/z.tmp | ||
15 | echo "*"/z.* | ||
16 | echo */"?".* | ||
17 | echo */z"*p" | ||
18 | echo d*r*e\*t/z*p | ||
19 | echo *"\\/z.t"mp | ||
20 | echo */z".*" | ||
21 | echo *"/z"\*"p" | ||
22 | echo "d*"r*e*t"/"z*p | ||
23 | |||
24 | rm dirtest/z.tmp | ||
25 | rmdir dirtest | ||
diff --git a/shell/hush_test/hush-misc/heredoc2.right b/shell/ash_test/ash-heredoc/heredoc5.right index 74110e3b5..74110e3b5 100644 --- a/shell/hush_test/hush-misc/heredoc2.right +++ b/shell/ash_test/ash-heredoc/heredoc5.right | |||
diff --git a/shell/hush_test/hush-misc/heredoc2.tests b/shell/ash_test/ash-heredoc/heredoc5.tests index e619bded1..e619bded1 100755 --- a/shell/hush_test/hush-misc/heredoc2.tests +++ b/shell/ash_test/ash-heredoc/heredoc5.tests | |||
diff --git a/shell/hush_test/hush-misc/heredoc_huge.right b/shell/ash_test/ash-heredoc/heredoc_huge.right index 11740f674..11740f674 100644 --- a/shell/hush_test/hush-misc/heredoc_huge.right +++ b/shell/ash_test/ash-heredoc/heredoc_huge.right | |||
diff --git a/shell/hush_test/hush-misc/heredoc_huge.tests b/shell/ash_test/ash-heredoc/heredoc_huge.tests index c2ec2817b..c2ec2817b 100755 --- a/shell/hush_test/hush-misc/heredoc_huge.tests +++ b/shell/ash_test/ash-heredoc/heredoc_huge.tests | |||
diff --git a/shell/ash_test/ash-misc/and-or.right b/shell/ash_test/ash-misc/and-or.right new file mode 100644 index 000000000..f9fa5fbf8 --- /dev/null +++ b/shell/ash_test/ash-misc/and-or.right | |||
@@ -0,0 +1,18 @@ | |||
1 | a1 | ||
2 | a4 | ||
3 | b1 | ||
4 | b3 | ||
5 | b4 | ||
6 | b6 | ||
7 | c4 | ||
8 | c5 | ||
9 | c7 | ||
10 | c8 | ||
11 | ff1 | ||
12 | ff3 | ||
13 | ft2 | ||
14 | ft3 | ||
15 | tf2 | ||
16 | tf3 | ||
17 | tt2 | ||
18 | tt4 | ||
diff --git a/shell/ash_test/ash-misc/and-or.tests b/shell/ash_test/ash-misc/and-or.tests new file mode 100755 index 000000000..485458abe --- /dev/null +++ b/shell/ash_test/ash-misc/and-or.tests | |||
@@ -0,0 +1,34 @@ | |||
1 | false || echo a1 | ||
2 | false && echo a2 | ||
3 | true || echo a3 | ||
4 | true && echo a4 | ||
5 | |||
6 | false || echo b1 || echo b2 | ||
7 | false || echo b3 && echo b4 | ||
8 | false && echo b5 || echo b6 | ||
9 | false && echo b7 && echo b8 | ||
10 | |||
11 | true || echo c1 || echo c2 | ||
12 | true || echo c3 && echo c4 | ||
13 | true && echo c5 || echo c6 | ||
14 | true && echo c7 && echo c8 | ||
15 | |||
16 | false || false || echo ff1 | ||
17 | false || false && echo ff2 | ||
18 | false && false || echo ff3 | ||
19 | false && false && echo ff4 | ||
20 | |||
21 | false || true || echo ft1 | ||
22 | false || true && echo ft2 | ||
23 | false && true || echo ft3 | ||
24 | false && true && echo ft4 | ||
25 | |||
26 | true || false || echo tf1 | ||
27 | true || false && echo tf2 | ||
28 | true && false || echo tf3 | ||
29 | true && false && echo tf4 | ||
30 | |||
31 | true || true || echo tt1 | ||
32 | true || true && echo tt2 | ||
33 | true && true || echo tt3 | ||
34 | true && true && echo tt4 | ||
diff --git a/shell/ash_test/ash-misc/assignment1.right b/shell/ash_test/ash-misc/assignment1.right new file mode 100644 index 000000000..d0a13d3d8 --- /dev/null +++ b/shell/ash_test/ash-misc/assignment1.right | |||
@@ -0,0 +1,9 @@ | |||
1 | if1:0 | ||
2 | while1:0 | ||
3 | until1:0 | ||
4 | if2:0 | ||
5 | while2:0 | ||
6 | until2:0 | ||
7 | if3:0 | ||
8 | while3:0 | ||
9 | until3:0 | ||
diff --git a/shell/ash_test/ash-misc/assignment1.tests b/shell/ash_test/ash-misc/assignment1.tests new file mode 100755 index 000000000..033b35250 --- /dev/null +++ b/shell/ash_test/ash-misc/assignment1.tests | |||
@@ -0,0 +1,42 @@ | |||
1 | # Assignments after some keywords should still work | ||
2 | |||
3 | if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi | ||
4 | echo if1:$? | ||
5 | while a=1 true; do a=1 true; break; done | ||
6 | echo while1:$? | ||
7 | until a=1 false; do a=1 true; break; done | ||
8 | echo until1:$? | ||
9 | |||
10 | if a=1 true | ||
11 | then a=1 true | ||
12 | elif a=1 true | ||
13 | then a=1 true | ||
14 | else a=1 true | ||
15 | fi | ||
16 | echo if2:$? | ||
17 | while a=1 true | ||
18 | do a=1 true | ||
19 | break | ||
20 | done | ||
21 | echo while2:$? | ||
22 | until a=1 false | ||
23 | do a=1 true | ||
24 | break | ||
25 | done | ||
26 | echo until2:$? | ||
27 | |||
28 | if | ||
29 | a=1 true; then | ||
30 | a=1 true; elif | ||
31 | a=1 true; then | ||
32 | a=1 true; else | ||
33 | a=1 true; fi | ||
34 | echo if3:$? | ||
35 | while | ||
36 | a=1 true; do | ||
37 | a=1 true; break; done | ||
38 | echo while3:$? | ||
39 | until | ||
40 | a=1 false; do | ||
41 | a=1 true; break; done | ||
42 | echo until3:$? | ||
diff --git a/shell/ash_test/ash-misc/assignment3.right b/shell/ash_test/ash-misc/assignment3.right new file mode 100644 index 000000000..0f02d7cbc --- /dev/null +++ b/shell/ash_test/ash-misc/assignment3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Done:0 | ||
2 | abc=123 | ||
diff --git a/shell/ash_test/ash-misc/assignment3.tests b/shell/ash_test/ash-misc/assignment3.tests new file mode 100755 index 000000000..790129be1 --- /dev/null +++ b/shell/ash_test/ash-misc/assignment3.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | # This must be interpreted as assignments | ||
2 | a=1 b\ | ||
3 | =2 c=3 | ||
4 | echo Done:$? | ||
5 | echo abc=$a$b$c | ||
diff --git a/shell/ash_test/ash-misc/assignment4.right b/shell/ash_test/ash-misc/assignment4.right new file mode 100644 index 000000000..31c896f62 --- /dev/null +++ b/shell/ash_test/ash-misc/assignment4.right | |||
@@ -0,0 +1 @@ | |||
Done:0 | |||
diff --git a/shell/ash_test/ash-misc/assignment4.tests b/shell/ash_test/ash-misc/assignment4.tests new file mode 100755 index 000000000..6f46d0a33 --- /dev/null +++ b/shell/ash_test/ash-misc/assignment4.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # There was a bug where we misinterpreted assignments after 'do': | ||
2 | for i in 1; do eval b=; done | ||
3 | echo Done:$? | ||
diff --git a/shell/ash_test/ash-misc/break1.right b/shell/ash_test/ash-misc/break1.right new file mode 100644 index 000000000..04a4b1757 --- /dev/null +++ b/shell/ash_test/ash-misc/break1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | A | ||
2 | OK:0 | ||
diff --git a/shell/ash_test/ash-misc/break1.tests b/shell/ash_test/ash-misc/break1.tests new file mode 100755 index 000000000..3a6b060d9 --- /dev/null +++ b/shell/ash_test/ash-misc/break1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | while true; do echo A; break; echo B; done | ||
2 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/break2.right b/shell/ash_test/ash-misc/break2.right new file mode 100644 index 000000000..8a15cb95f --- /dev/null +++ b/shell/ash_test/ash-misc/break2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | A | ||
2 | AA | ||
3 | OK:0 | ||
diff --git a/shell/ash_test/ash-misc/break2.tests b/shell/ash_test/ash-misc/break2.tests new file mode 100755 index 000000000..7da9faf34 --- /dev/null +++ b/shell/ash_test/ash-misc/break2.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | while true; do | ||
2 | echo A | ||
3 | while true; do echo AA; break 2; echo BB; done | ||
4 | echo B | ||
5 | done | ||
6 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/break3.right b/shell/ash_test/ash-misc/break3.right new file mode 100644 index 000000000..04a4b1757 --- /dev/null +++ b/shell/ash_test/ash-misc/break3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | A | ||
2 | OK:0 | ||
diff --git a/shell/ash_test/ash-misc/break3.tests b/shell/ash_test/ash-misc/break3.tests new file mode 100755 index 000000000..d138dcac5 --- /dev/null +++ b/shell/ash_test/ash-misc/break3.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v=break; while true; do echo A; $v; echo B; break; echo C; done | ||
2 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/break4.right b/shell/ash_test/ash-misc/break4.right new file mode 100644 index 000000000..6f41c141d --- /dev/null +++ b/shell/ash_test/ash-misc/break4.right | |||
@@ -0,0 +1,6 @@ | |||
1 | A | ||
2 | AA | ||
3 | TRUE | ||
4 | A | ||
5 | AA | ||
6 | OK:0 | ||
diff --git a/shell/ash_test/ash-misc/break4.tests b/shell/ash_test/ash-misc/break4.tests new file mode 100755 index 000000000..67da2889c --- /dev/null +++ b/shell/ash_test/ash-misc/break4.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | cond=true | ||
2 | while $cond; do | ||
3 | echo A | ||
4 | if test "$cond" = true; then | ||
5 | cond='echo TRUE' | ||
6 | else | ||
7 | cond=false | ||
8 | fi | ||
9 | while true; do echo AA; continue 2; echo BB; done | ||
10 | echo B | ||
11 | done | ||
12 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/break5.right b/shell/ash_test/ash-misc/break5.right new file mode 100644 index 000000000..0b9df2a4f --- /dev/null +++ b/shell/ash_test/ash-misc/break5.right | |||
@@ -0,0 +1,13 @@ | |||
1 | A | ||
2 | B | ||
3 | 0 | ||
4 | A:a | ||
5 | B | ||
6 | D | ||
7 | A:b | ||
8 | B | ||
9 | D | ||
10 | A:c | ||
11 | B | ||
12 | D | ||
13 | 0 | ||
diff --git a/shell/ash_test/ash-misc/break5.tests b/shell/ash_test/ash-misc/break5.tests new file mode 100755 index 000000000..273e040ec --- /dev/null +++ b/shell/ash_test/ash-misc/break5.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | while true; do echo A; { echo B; break; echo C; }; echo D; done | ||
2 | echo $? | ||
3 | for v in a b c; do echo A:$v; (echo B; break; echo C); echo D; done | ||
4 | echo $? | ||
diff --git a/shell/ash_test/ash-misc/builtin1.right b/shell/ash_test/ash-misc/builtin1.right new file mode 100644 index 000000000..2e55ecb09 --- /dev/null +++ b/shell/ash_test/ash-misc/builtin1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | VARIABLE=export | ||
2 | OK:0 | ||
diff --git a/shell/ash_test/ash-misc/builtin1.tests b/shell/ash_test/ash-misc/builtin1.tests new file mode 100755 index 000000000..1a2941faa --- /dev/null +++ b/shell/ash_test/ash-misc/builtin1.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # builtins, unlike keywords like "while", can be constructed | ||
2 | # with substitutions | ||
3 | VARIABLE=export | ||
4 | $VARIABLE VARIABLE | ||
5 | env | grep ^VARIABLE | ||
6 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/case1.right b/shell/ash_test/ash-misc/case1.right new file mode 100644 index 000000000..4afb2f51c --- /dev/null +++ b/shell/ash_test/ash-misc/case1.right | |||
@@ -0,0 +1,22 @@ | |||
1 | OK_1 | ||
2 | OK_1 | ||
3 | OK_21 | ||
4 | OK_22 | ||
5 | OK_23 | ||
6 | OK_31 | ||
7 | OK_32 | ||
8 | OK_41 | ||
9 | OK_42 | ||
10 | OK_43 | ||
11 | OK_44 | ||
12 | OK_51 | ||
13 | OK_52 | ||
14 | OK_53 | ||
15 | OK_sub1 | ||
16 | OK_sub2 | ||
17 | OK_sub3 | ||
18 | OK_sub4 | ||
19 | OK_sub5 | ||
20 | OK_sub6 | ||
21 | OK_esac1 | ||
22 | Done | ||
diff --git a/shell/ash_test/ash-misc/case1.tests b/shell/ash_test/ash-misc/case1.tests new file mode 100755 index 000000000..d72b57f53 --- /dev/null +++ b/shell/ash_test/ash-misc/case1.tests | |||
@@ -0,0 +1,40 @@ | |||
1 | case w in a) echo SKIP;; w) echo OK_1;; w) echo WRONG;; esac | ||
2 | |||
3 | case w in | ||
4 | a) echo SKIP;; | ||
5 | w)echo OK_1 ;; | ||
6 | w) | ||
7 | echo WRONG | ||
8 | ;; | ||
9 | esac | ||
10 | |||
11 | t=w | ||
12 | case $t in a) echo SKIP;; w) echo OK_21;; w) echo WRONG;; esac; | ||
13 | case "$t" in a) echo SKIP;; w) echo OK_22;; w) echo WRONG;; esac; | ||
14 | case w in a) echo SKIP;; $t) echo OK_23;; "$t") echo WRONG;; esac; | ||
15 | |||
16 | case '' in a) echo SKIP;; w) echo WRONG;; *) echo OK_31;; esac; | ||
17 | case '' in a) echo SKIP;; '') echo OK_32;; *) echo WRONG;; esac; | ||
18 | |||
19 | case `echo w` in a) echo SKIP;; w) echo OK_41;; w) echo WRONG;; esac; | ||
20 | case "`echo w`" in a) echo SKIP;; w) echo OK_42;; w) echo WRONG;; esac; | ||
21 | case `echo w w` in a) echo SKIP;; w) echo WRONG;; 'w w') echo OK_43;; esac; | ||
22 | case `echo w w` in a) echo SKIP;; w) echo WRONG;; w*) echo OK_44;; esac; | ||
23 | |||
24 | case w in `echo w`) echo OK_51;; `echo WRONG >&2`w) echo WRONG;; esac; | ||
25 | case w in `echo OK_52 >&2`) echo SKIP;; `echo`w) echo OK_53;; esac; | ||
26 | |||
27 | # parsing cases in subshells can easily get messy | ||
28 | case m in m) echo OK_sub1;; esac | ||
29 | case m in (m) echo OK_sub2;; esac | ||
30 | (case m in m) echo OK_sub3;; esac) | ||
31 | (case m in (m) echo OK_sub4;; esac) | ||
32 | ( | ||
33 | case m in m) echo OK_sub5;; esac | ||
34 | ) | ||
35 | ( | ||
36 | case m in (m) echo OK_sub6;; esac | ||
37 | ) | ||
38 | (case esac in "esac") echo OK_esac1;; esac) | ||
39 | |||
40 | echo Done | ||
diff --git a/shell/ash_test/ash-misc/colon.right b/shell/ash_test/ash-misc/colon.right new file mode 100644 index 000000000..2a87d0293 --- /dev/null +++ b/shell/ash_test/ash-misc/colon.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 0 | ||
2 | OK: 0 | ||
diff --git a/shell/ash_test/ash-misc/colon.tests b/shell/ash_test/ash-misc/colon.tests new file mode 100755 index 000000000..cb8ab5306 --- /dev/null +++ b/shell/ash_test/ash-misc/colon.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | false | ||
2 | : | ||
3 | echo $? | ||
4 | (while :; do exit; done) | ||
5 | echo OK: $? | ||
diff --git a/shell/ash_test/ash-misc/compound.right b/shell/ash_test/ash-misc/compound.right new file mode 100644 index 000000000..757d42fe4 --- /dev/null +++ b/shell/ash_test/ash-misc/compound.right | |||
@@ -0,0 +1,14 @@ | |||
1 | new group | ||
2 | 0 | ||
3 | 1 | ||
4 | 2 | ||
5 | 3 | ||
6 | 4 | ||
7 | 5 | ||
8 | 6 | ||
9 | new group | ||
10 | new group | ||
11 | 0 | ||
12 | 1 | ||
13 | 2 | ||
14 | 3 | ||
diff --git a/shell/ash_test/ash-misc/compound.tests b/shell/ash_test/ash-misc/compound.tests new file mode 100755 index 000000000..a5e85c3d5 --- /dev/null +++ b/shell/ash_test/ash-misc/compound.tests | |||
@@ -0,0 +1,21 @@ | |||
1 | echo new group | ||
2 | echo 0; { :; } | ||
3 | echo 1; { : ;} | ||
4 | echo 2; ({ :; }) | ||
5 | echo 3; ({ : ;}) | ||
6 | echo 4; ( : ) | ||
7 | echo 5; ( :; ) | ||
8 | echo 6; ( : ;) | ||
9 | # not sure if POSIX requires these, but bash accepts them ... | ||
10 | #echo 7; {( : )} | ||
11 | #echo 8; {( :; )} | ||
12 | #echo 9; {( : ;)} | ||
13 | |||
14 | echo new group | ||
15 | #echo 0; {(:);} | ||
16 | |||
17 | echo new group | ||
18 | echo 0; (:) | ||
19 | echo 1; (:;) | ||
20 | echo 2; (:); | ||
21 | echo 3; (:;); | ||
diff --git a/shell/ash_test/ash-misc/continue1.right b/shell/ash_test/ash-misc/continue1.right new file mode 100644 index 000000000..c4a5565bc --- /dev/null +++ b/shell/ash_test/ash-misc/continue1.right | |||
@@ -0,0 +1,8 @@ | |||
1 | A:a | ||
2 | A:b | ||
3 | A:c | ||
4 | OK1 | ||
5 | A:a | ||
6 | A:b | ||
7 | A:c | ||
8 | OK2 | ||
diff --git a/shell/ash_test/ash-misc/continue1.tests b/shell/ash_test/ash-misc/continue1.tests new file mode 100755 index 000000000..72d356660 --- /dev/null +++ b/shell/ash_test/ash-misc/continue1.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | for v in a b c; do echo A:$v; continue 666; done | ||
2 | echo OK1 | ||
3 | for v in a b c; do echo A:$v; continue 666; done | ||
4 | echo OK2 | ||
diff --git a/shell/ash_test/ash-misc/continue2.right b/shell/ash_test/ash-misc/continue2.right new file mode 100644 index 000000000..49d3ebd3a --- /dev/null +++ b/shell/ash_test/ash-misc/continue2.right | |||
@@ -0,0 +1 @@ | |||
Ok:1 | |||
diff --git a/shell/ash_test/ash-misc/continue2.tests b/shell/ash_test/ash-misc/continue2.tests new file mode 100755 index 000000000..c2df07195 --- /dev/null +++ b/shell/ash_test/ash-misc/continue2.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | e='' | ||
2 | (while test $e && exit 1; true; do e=1; continue; done) | ||
3 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-misc/continue3.right b/shell/ash_test/ash-misc/continue3.right new file mode 100644 index 000000000..aa47d0d46 --- /dev/null +++ b/shell/ash_test/ash-misc/continue3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 0 | ||
2 | 0 | ||
diff --git a/shell/ash_test/ash-misc/continue3.tests b/shell/ash_test/ash-misc/continue3.tests new file mode 100755 index 000000000..0aff867cd --- /dev/null +++ b/shell/ash_test/ash-misc/continue3.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # Test that "continue" does affect exitcode (sets to 0) | ||
2 | e='' | ||
3 | while echo $?; test $e && exit; true; do e=1; false; continue; done | ||
diff --git a/shell/ash_test/ash-misc/empty_for.right b/shell/ash_test/ash-misc/empty_for.right new file mode 100644 index 000000000..290d39b7e --- /dev/null +++ b/shell/ash_test/ash-misc/empty_for.right | |||
@@ -0,0 +1 @@ | |||
OK: 0 | |||
diff --git a/shell/ash_test/ash-misc/empty_for.tests b/shell/ash_test/ash-misc/empty_for.tests new file mode 100755 index 000000000..0cb52e849 --- /dev/null +++ b/shell/ash_test/ash-misc/empty_for.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | false | ||
2 | for a in; do echo "HELLO"; done | ||
3 | echo OK: $? | ||
diff --git a/shell/ash_test/ash-misc/empty_for2.right b/shell/ash_test/ash-misc/empty_for2.right new file mode 100644 index 000000000..1acee9eb8 --- /dev/null +++ b/shell/ash_test/ash-misc/empty_for2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | PARAM:abc | ||
2 | PARAM:d e | ||
3 | PARAM:123 | ||
4 | OK: 0 | ||
diff --git a/shell/ash_test/ash-misc/empty_for2.tests b/shell/ash_test/ash-misc/empty_for2.tests new file mode 100755 index 000000000..2b12ec2c1 --- /dev/null +++ b/shell/ash_test/ash-misc/empty_for2.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" $0 abc "d e" 123 | ||
3 | fi | ||
4 | false | ||
5 | for v; do echo "PARAM:$v"; done | ||
6 | echo OK: $? | ||
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/ash_test/ash-misc/errexit1.right index d86bac9de..d86bac9de 100644 --- a/shell/msh_test/msh-execution/many_continues.right +++ b/shell/ash_test/ash-misc/errexit1.right | |||
diff --git a/shell/ash_test/ash-misc/errexit1.tests b/shell/ash_test/ash-misc/errexit1.tests new file mode 100755 index 000000000..7b4a15634 --- /dev/null +++ b/shell/ash_test/ash-misc/errexit1.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | set -e | ||
2 | (true) | ||
3 | echo OK | ||
4 | (false) | ||
5 | echo FAIL | ||
diff --git a/shell/ash_test/ash-misc/eval1.right b/shell/ash_test/ash-misc/eval1.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/ash_test/ash-misc/eval1.right | |||
@@ -0,0 +1 @@ | |||
Ok:0 | |||
diff --git a/shell/ash_test/ash-misc/eval1.tests b/shell/ash_test/ash-misc/eval1.tests new file mode 100755 index 000000000..b78c6cc94 --- /dev/null +++ b/shell/ash_test/ash-misc/eval1.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # empty eval nevertheless sets $? = 0 | ||
2 | false | ||
3 | eval | ||
4 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-misc/eval2.right b/shell/ash_test/ash-misc/eval2.right new file mode 100644 index 000000000..a7ce6ccc7 --- /dev/null +++ b/shell/ash_test/ash-misc/eval2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Zero:0 | ||
2 | Zero:0 | ||
3 | Zero:0 | ||
diff --git a/shell/ash_test/ash-misc/eval2.tests b/shell/ash_test/ash-misc/eval2.tests new file mode 100755 index 000000000..6bfb87aa7 --- /dev/null +++ b/shell/ash_test/ash-misc/eval2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | false; eval; echo Zero:$? | ||
2 | false; eval ""; echo Zero:$? | ||
3 | false; eval " | ||
4 | "; echo Zero:$? | ||
diff --git a/shell/ash_test/ash-misc/exit1.right b/shell/ash_test/ash-misc/exit1.right new file mode 100644 index 000000000..dd2cfc279 --- /dev/null +++ b/shell/ash_test/ash-misc/exit1.right | |||
@@ -0,0 +1 @@ | |||
Once | |||
diff --git a/shell/ash_test/ash-misc/exit1.tests b/shell/ash_test/ash-misc/exit1.tests new file mode 100755 index 000000000..41e0d092d --- /dev/null +++ b/shell/ash_test/ash-misc/exit1.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | trap "echo Not shown" EXIT | ||
2 | (exit) # must be silent | ||
3 | trap "echo Once; exit" EXIT | ||
4 | { exit; } | ||
diff --git a/shell/ash_test/ash-misc/exitcode1.right b/shell/ash_test/ash-misc/exitcode1.right new file mode 100644 index 000000000..e5fefefda --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | One:1 | ||
2 | Zero:0 | ||
diff --git a/shell/ash_test/ash-misc/exitcode1.tests b/shell/ash_test/ash-misc/exitcode1.tests new file mode 100755 index 000000000..dc8619d8b --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | false || case a in a) echo One:$?;; esac | ||
2 | echo Zero:$? | ||
diff --git a/shell/ash_test/ash-misc/exitcode_EACCES.right b/shell/ash_test/ash-misc/exitcode_EACCES.right new file mode 100644 index 000000000..0a332f56c --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode_EACCES.right | |||
@@ -0,0 +1,2 @@ | |||
1 | exitcode_EACCES.tests: line 1: ./: Permission denied | ||
2 | 126 | ||
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.tests b/shell/ash_test/ash-misc/exitcode_EACCES.tests index 26b5c6116..26b5c6116 100755 --- a/shell/msh_test/msh-execution/exitcode_EACCES.tests +++ b/shell/ash_test/ash-misc/exitcode_EACCES.tests | |||
diff --git a/shell/ash_test/ash-misc/exitcode_ENOENT.right b/shell/ash_test/ash-misc/exitcode_ENOENT.right new file mode 100644 index 000000000..45b27b32e --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode_ENOENT.right | |||
@@ -0,0 +1,2 @@ | |||
1 | exitcode_ENOENT.tests: line 1: ./does_not_exist_for_sure: not found | ||
2 | 127 | ||
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.tests b/shell/ash_test/ash-misc/exitcode_ENOENT.tests index 7f1b88a99..7f1b88a99 100755 --- a/shell/msh_test/msh-execution/exitcode_ENOENT.tests +++ b/shell/ash_test/ash-misc/exitcode_ENOENT.tests | |||
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.right b/shell/ash_test/ash-misc/for_with_bslashes.right new file mode 100644 index 000000000..02d96692c --- /dev/null +++ b/shell/ash_test/ash-misc/for_with_bslashes.right | |||
@@ -0,0 +1,8 @@ | |||
1 | a | ||
2 | b\c | ||
3 | b\\c | ||
4 | b"c | ||
5 | b'c | ||
6 | b$c | ||
7 | b`true`c | ||
8 | Zero:0 | ||
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.tests b/shell/ash_test/ash-misc/for_with_bslashes.tests new file mode 100755 index 000000000..363f3d85b --- /dev/null +++ b/shell/ash_test/ash-misc/for_with_bslashes.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | # UNFIXED BUG. | ||
2 | # commented-out words contain ^C character. | ||
3 | # It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it. | ||
4 | # When it is fixed, update this test. | ||
5 | |||
6 | for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c' | ||
7 | do | ||
8 | echo $a | ||
9 | done | ||
10 | echo Zero:$? | ||
diff --git a/shell/ash_test/ash-misc/for_with_keywords.right b/shell/ash_test/ash-misc/for_with_keywords.right new file mode 100644 index 000000000..eb04e9af9 --- /dev/null +++ b/shell/ash_test/ash-misc/for_with_keywords.right | |||
@@ -0,0 +1,4 @@ | |||
1 | do | ||
2 | done | ||
3 | then | ||
4 | OK: 0 | ||
diff --git a/shell/ash_test/ash-misc/for_with_keywords.tests b/shell/ash_test/ash-misc/for_with_keywords.tests new file mode 100755 index 000000000..a8b8e4264 --- /dev/null +++ b/shell/ash_test/ash-misc/for_with_keywords.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | for if in do done then; do echo $if; done | ||
2 | echo OK: $? | ||
diff --git a/shell/ash_test/ash-misc/func6.right b/shell/ash_test/ash-misc/func6.right new file mode 100644 index 000000000..0ebd8e5a3 --- /dev/null +++ b/shell/ash_test/ash-misc/func6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Two:2 | ||
2 | Two:2 | ||
diff --git a/shell/ash_test/ash-misc/func6.tests b/shell/ash_test/ash-misc/func6.tests new file mode 100755 index 000000000..029c3e85e --- /dev/null +++ b/shell/ash_test/ash-misc/func6.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | f1() { | ||
2 | while return 2; do :; done | ||
3 | } | ||
4 | f1 | ||
5 | echo Two:$? | ||
6 | |||
7 | f2() { | ||
8 | while :; do return 2; done | ||
9 | } | ||
10 | f2 | ||
11 | echo Two:$? | ||
diff --git a/shell/ash_test/ash-misc/if_false_exitcode.right b/shell/ash_test/ash-misc/if_false_exitcode.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/ash_test/ash-misc/if_false_exitcode.right | |||
@@ -0,0 +1 @@ | |||
Ok:0 | |||
diff --git a/shell/ash_test/ash-misc/if_false_exitcode.tests b/shell/ash_test/ash-misc/if_false_exitcode.tests new file mode 100755 index 000000000..01b36b100 --- /dev/null +++ b/shell/ash_test/ash-misc/if_false_exitcode.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | if false; then echo Bad; fi | ||
2 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-misc/nommu1.right b/shell/ash_test/ash-misc/nommu1.right new file mode 100644 index 000000000..d206a854b --- /dev/null +++ b/shell/ash_test/ash-misc/nommu1.right | |||
@@ -0,0 +1,7 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Ok | ||
4 | Ok | ||
5 | Ok | ||
6 | Ok | ||
7 | Done | ||
diff --git a/shell/ash_test/ash-misc/nommu1.tests b/shell/ash_test/ash-misc/nommu1.tests new file mode 100755 index 000000000..e14ada5f9 --- /dev/null +++ b/shell/ash_test/ash-misc/nommu1.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | (echo \ | ||
2 | Ok) | ||
3 | ( (echo \ | ||
4 | Ok) ) | ||
5 | ( ( (echo \ | ||
6 | Ok) ) ) | ||
7 | |||
8 | (echo \Ok) | ||
9 | ( (echo \Ok) ) | ||
10 | ( ( (echo \Ok) ) ) | ||
11 | |||
12 | echo Done | ||
diff --git a/shell/ash_test/ash-misc/nommu2.right b/shell/ash_test/ash-misc/nommu2.right new file mode 100644 index 000000000..fb8ba8b5c --- /dev/null +++ b/shell/ash_test/ash-misc/nommu2.right | |||
@@ -0,0 +1,5 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Ok | ||
4 | Ok | ||
5 | Done | ||
diff --git a/shell/ash_test/ash-misc/nommu2.tests b/shell/ash_test/ash-misc/nommu2.tests new file mode 100755 index 000000000..61ed5ce5b --- /dev/null +++ b/shell/ash_test/ash-misc/nommu2.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | echo Not shown | if true; then echo $(echo Ok); fi | ||
2 | echo Not shown | if true; then echo `echo Ok`; fi | ||
3 | echo Not shown | ( if true; then echo $(echo Ok); fi ) | ||
4 | echo Not shown | ( if true; then echo `echo Ok`; fi ) | ||
5 | echo Done | ||
diff --git a/shell/ash_test/ash-misc/nommu3.right b/shell/ash_test/ash-misc/nommu3.right new file mode 100644 index 000000000..da1534bef --- /dev/null +++ b/shell/ash_test/ash-misc/nommu3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok | ||
2 | 0 | ||
diff --git a/shell/ash_test/ash-misc/nommu3.tests b/shell/ash_test/ash-misc/nommu3.tests new file mode 100755 index 000000000..ac82a6a11 --- /dev/null +++ b/shell/ash_test/ash-misc/nommu3.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | func() | ||
4 | { | ||
5 | while read p; do echo "$p"; done | ||
6 | } | ||
7 | |||
8 | pipe_to_func() | ||
9 | { | ||
10 | # We had a NOMMU bug which caused "echo Ok |" part to be lost | ||
11 | echo Ok | func | ||
12 | } | ||
13 | |||
14 | pipe_to_func | cat | ||
15 | echo $? | ||
diff --git a/shell/ash_test/ash-misc/opts1.right b/shell/ash_test/ash-misc/opts1.right new file mode 100644 index 000000000..4da75737d --- /dev/null +++ b/shell/ash_test/ash-misc/opts1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Param1: >-10qwertyuiop< | ||
2 | Done | ||
diff --git a/shell/ash_test/ash-misc/opts1.tests b/shell/ash_test/ash-misc/opts1.tests new file mode 100755 index 000000000..45a23d66b --- /dev/null +++ b/shell/ash_test/ash-misc/opts1.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" $0 -10qwertyuiop | ||
3 | fi | ||
4 | echo "Param1: >$1<" | ||
5 | echo Done | ||
diff --git a/shell/ash_test/ash-misc/pid.right b/shell/ash_test/ash-misc/pid.right new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/shell/ash_test/ash-misc/pid.right | |||
@@ -0,0 +1 @@ | |||
0 | |||
diff --git a/shell/ash_test/ash-misc/pid.tests b/shell/ash_test/ash-misc/pid.tests new file mode 100755 index 000000000..eaeaa717b --- /dev/null +++ b/shell/ash_test/ash-misc/pid.tests | |||
@@ -0,0 +1 @@ | |||
test `(echo $$)` = `echo $$`; echo $? | |||
diff --git a/shell/ash_test/ash-misc/pipefail.right b/shell/ash_test/ash-misc/pipefail.right new file mode 100644 index 000000000..5845d8939 --- /dev/null +++ b/shell/ash_test/ash-misc/pipefail.right | |||
@@ -0,0 +1,40 @@ | |||
1 | Default: | ||
2 | true | true: | ||
3 | 0 | ||
4 | 1 | ||
5 | true | false: | ||
6 | 1 | ||
7 | 0 | ||
8 | false | true: | ||
9 | 0 | ||
10 | 1 | ||
11 | exit 2 | exit 3 | exit 4: | ||
12 | 4 | ||
13 | 0 | ||
14 | Pipefail on: | ||
15 | true | true: | ||
16 | 0 | ||
17 | 1 | ||
18 | true | false: | ||
19 | 1 | ||
20 | 0 | ||
21 | false | true: | ||
22 | 1 | ||
23 | 0 | ||
24 | exit 2 | exit 3 | exit 4: | ||
25 | 4 | ||
26 | 0 | ||
27 | Pipefail off: | ||
28 | true | true: | ||
29 | 0 | ||
30 | 1 | ||
31 | true | false: | ||
32 | 1 | ||
33 | 0 | ||
34 | false | true: | ||
35 | 0 | ||
36 | 1 | ||
37 | exit 2 | exit 3 | exit 4: | ||
38 | 4 | ||
39 | 0 | ||
40 | Done | ||
diff --git a/shell/ash_test/ash-misc/pipefail.tests b/shell/ash_test/ash-misc/pipefail.tests new file mode 100755 index 000000000..9df841861 --- /dev/null +++ b/shell/ash_test/ash-misc/pipefail.tests | |||
@@ -0,0 +1,45 @@ | |||
1 | echo Default: | ||
2 | echo "true | true:" | ||
3 | true | true; echo $? | ||
4 | ! true | true; echo $? | ||
5 | echo "true | false:" | ||
6 | true | false; echo $? | ||
7 | ! true | false; echo $? | ||
8 | echo "false | true:" | ||
9 | false | true; echo $? | ||
10 | ! false | true; echo $? | ||
11 | echo "exit 2 | exit 3 | exit 4:" | ||
12 | exit 2 | exit 3 | exit 4; echo $? | ||
13 | ! exit 2 | exit 3 | exit 4; echo $? | ||
14 | |||
15 | echo Pipefail on: | ||
16 | set -o pipefail | ||
17 | echo "true | true:" | ||
18 | true | true; echo $? | ||
19 | ! true | true; echo $? | ||
20 | echo "true | false:" | ||
21 | true | false; echo $? | ||
22 | ! true | false; echo $? | ||
23 | echo "false | true:" | ||
24 | false | true; echo $? | ||
25 | ! false | true; echo $? | ||
26 | echo "exit 2 | exit 3 | exit 4:" | ||
27 | exit 2 | exit 3 | exit 4; echo $? | ||
28 | ! exit 2 | exit 3 | exit 4; echo $? | ||
29 | |||
30 | echo Pipefail off: | ||
31 | set +o pipefail | ||
32 | echo "true | true:" | ||
33 | true | true; echo $? | ||
34 | ! true | true; echo $? | ||
35 | echo "true | false:" | ||
36 | true | false; echo $? | ||
37 | ! true | false; echo $? | ||
38 | echo "false | true:" | ||
39 | false | true; echo $? | ||
40 | ! false | true; echo $? | ||
41 | echo "exit 2 | exit 3 | exit 4:" | ||
42 | exit 2 | exit 3 | exit 4; echo $? | ||
43 | ! exit 2 | exit 3 | exit 4; echo $? | ||
44 | |||
45 | echo Done | ||
diff --git a/shell/msh_test/msh-bugs/read.right b/shell/ash_test/ash-misc/read.right index 0e50e2a23..0e50e2a23 100644 --- a/shell/msh_test/msh-bugs/read.right +++ b/shell/ash_test/ash-misc/read.right | |||
diff --git a/shell/msh_test/msh-bugs/read.tests b/shell/ash_test/ash-misc/read.tests index ff1acbde1..ff1acbde1 100755 --- a/shell/msh_test/msh-bugs/read.tests +++ b/shell/ash_test/ash-misc/read.tests | |||
diff --git a/shell/ash_test/ash-misc/return1.right b/shell/ash_test/ash-misc/return1.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/ash_test/ash-misc/return1.right | |||
@@ -0,0 +1 @@ | |||
Ok:0 | |||
diff --git a/shell/ash_test/ash-misc/return1.tests b/shell/ash_test/ash-misc/return1.tests new file mode 100755 index 000000000..eeb92ef3f --- /dev/null +++ b/shell/ash_test/ash-misc/return1.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | echo "true && return; echo Should not be printed" >return_sourced | ||
2 | . ./return_sourced | ||
3 | rm return_sourced | ||
4 | echo Ok:$? | ||
diff --git a/shell/msh_test/msh-bugs/shift.right b/shell/ash_test/ash-misc/shift.right index d281e358c..d281e358c 100644 --- a/shell/msh_test/msh-bugs/shift.right +++ b/shell/ash_test/ash-misc/shift.right | |||
diff --git a/shell/msh_test/msh-bugs/shift.tests b/shell/ash_test/ash-misc/shift.tests index 53ef249f2..53ef249f2 100755 --- a/shell/msh_test/msh-bugs/shift.tests +++ b/shell/ash_test/ash-misc/shift.tests | |||
diff --git a/shell/ash_test/ash-misc/sigint1.right b/shell/ash_test/ash-misc/sigint1.right new file mode 100644 index 000000000..a9094b056 --- /dev/null +++ b/shell/ash_test/ash-misc/sigint1.right | |||
@@ -0,0 +1 @@ | |||
Sending SIGINT to main shell PID | |||
diff --git a/shell/ash_test/ash-misc/sigint1.tests b/shell/ash_test/ash-misc/sigint1.tests new file mode 100755 index 000000000..3d483d32a --- /dev/null +++ b/shell/ash_test/ash-misc/sigint1.tests | |||
@@ -0,0 +1,41 @@ | |||
1 | # What should happen if non-interactive shell gets SIGINT? | ||
2 | |||
3 | (sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & | ||
4 | |||
5 | # We create a child which exits with 0 even on SIGINT | ||
6 | # (The complex command is necessary only if SIGINT is generated by ^C, | ||
7 | # in this testcase even bare "sleep 2" would do because | ||
8 | # in the testcase we don't send SIGINT *to the child*...) | ||
9 | $THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' | ||
10 | |||
11 | # In one second, we (main shell) get SIGINT here. | ||
12 | # The question is whether we should, or should not, exit. | ||
13 | |||
14 | # bash will not stop here. It will execute next command(s). | ||
15 | |||
16 | # The rationale for this is described here: | ||
17 | # http://www.cons.org/cracauer/sigint.html | ||
18 | # | ||
19 | # Basically, bash will not exit on SIGINT immediately if it waits | ||
20 | # for a child. It will wait for the child to exit. | ||
21 | # If child exits NOT by dying on SIGINT, then bash will not exit. | ||
22 | # | ||
23 | # The idea is that the following script: | ||
24 | # | emacs file.txt | ||
25 | # | more cmds | ||
26 | # User may use ^C to interrupt editor's ops like search. But then | ||
27 | # emacs exits normally. User expects that script doesn't stop. | ||
28 | # | ||
29 | # This is a nice idea, but detecting "did process really exit | ||
30 | # with SIGINT?" is racy. Consider: | ||
31 | # | bash -c 'while true; do /bin/true; done' | ||
32 | # When ^C is pressed while bash waits for /bin/true to exit, | ||
33 | # it may happen that /bin/true exits with exitcode 0 before | ||
34 | # ^C is delivered to it as SIGINT. bash will see SIGINT, then | ||
35 | # it will see that child exited with 0, and bash will NOT EXIT. | ||
36 | |||
37 | # Therefore we do not implement bash behavior. | ||
38 | # I'd say that emacs need to put itself into a separate pgrp | ||
39 | # to isolate shell from getting stray SIGINTs from ^C. | ||
40 | |||
41 | echo Next command after SIGINT was executed | ||
diff --git a/shell/ash_test/ash-misc/source3.right b/shell/ash_test/ash-misc/source3.right new file mode 100644 index 000000000..bdf9001a5 --- /dev/null +++ b/shell/ash_test/ash-misc/source3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Zero:0 | ||
2 | Zero:0 | ||
diff --git a/shell/ash_test/ash-misc/source3.tests b/shell/ash_test/ash-misc/source3.tests new file mode 100755 index 000000000..1abf156ed --- /dev/null +++ b/shell/ash_test/ash-misc/source3.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # Test both empty file, and one-empty-line file | ||
2 | echo >sourced1 | ||
3 | true >sourced2 | ||
4 | false; . ./sourced1; echo Zero:$? | ||
5 | false; . ./sourced2; echo Zero:$? | ||
6 | rm sourced1 sourced2 | ||
diff --git a/shell/ash_test/ash-misc/source5.right b/shell/ash_test/ash-misc/source5.right new file mode 100644 index 000000000..0587bad67 --- /dev/null +++ b/shell/ash_test/ash-misc/source5.right | |||
@@ -0,0 +1,4 @@ | |||
1 | 0:arg0 1:arg1 2:arg2 | ||
2 | Ok1:0 | ||
3 | 0:arg0 1:q 2:w | ||
4 | Ok2:0 | ||
diff --git a/shell/ash_test/ash-misc/source5.tests b/shell/ash_test/ash-misc/source5.tests new file mode 100755 index 000000000..40b6b83cd --- /dev/null +++ b/shell/ash_test/ash-misc/source5.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | echo 'echo "0:$0 1:$1 2:$2"' >sourced1 | ||
2 | set -- 1 2 3 | ||
3 | "$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2 | ||
4 | echo Ok1:$? | ||
5 | "$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2 | ||
6 | echo Ok2:$? | ||
7 | |||
8 | rm sourced1 | ||
diff --git a/shell/msh_test/msh-misc/tick.right b/shell/ash_test/ash-misc/unicode1.right index 6ed281c75..d3bbbf697 100644 --- a/shell/msh_test/msh-misc/tick.right +++ b/shell/ash_test/ash-misc/unicode1.right | |||
@@ -1,2 +1,3 @@ | |||
1 | 1 | 1 | 1 |
2 | 1 | 2 | 1 |
3 | Ok | ||
diff --git a/shell/ash_test/ash-misc/unicode1.tests b/shell/ash_test/ash-misc/unicode1.tests new file mode 100755 index 000000000..8788ba910 --- /dev/null +++ b/shell/ash_test/ash-misc/unicode1.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | LANG=en_US.UTF-8 | ||
2 | |||
3 | # A combining character U+300 | ||
4 | a=`printf "\xcc\x80"` | ||
5 | # Should print 1 | ||
6 | echo ${#a} | ||
7 | |||
8 | # A Japanese katakana charachter U+30a3 | ||
9 | a=`printf "\xe3\x82\xa3"` | ||
10 | # Should print 1 | ||
11 | echo ${#a} | ||
12 | |||
13 | echo Ok | ||
diff --git a/shell/ash_test/ash-misc/until1.right b/shell/ash_test/ash-misc/until1.right new file mode 100644 index 000000000..be2daad95 --- /dev/null +++ b/shell/ash_test/ash-misc/until1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | 1 | ||
2 | 1 | ||
3 | Ok:0 | ||
diff --git a/shell/ash_test/ash-misc/until1.tests b/shell/ash_test/ash-misc/until1.tests new file mode 100755 index 000000000..10ab28381 --- /dev/null +++ b/shell/ash_test/ash-misc/until1.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | x=1 | ||
2 | until test "$x" = 4; do echo $x; x=4; done | ||
3 | |||
4 | # We had a bug in multi-line form | ||
5 | x=1 | ||
6 | until test "$x" = 4; do | ||
7 | echo $x | ||
8 | x=4 | ||
9 | done | ||
10 | |||
11 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-misc/while1.right b/shell/ash_test/ash-misc/while1.right new file mode 100644 index 000000000..7c4d7beb0 --- /dev/null +++ b/shell/ash_test/ash-misc/while1.right | |||
@@ -0,0 +1 @@ | |||
OK:0 | |||
diff --git a/shell/ash_test/ash-misc/while1.tests b/shell/ash_test/ash-misc/while1.tests new file mode 100755 index 000000000..11e201e6a --- /dev/null +++ b/shell/ash_test/ash-misc/while1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | while false; do echo NOT SHOWN; done | ||
2 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/while2.right b/shell/ash_test/ash-misc/while2.right new file mode 100644 index 000000000..07207cc84 --- /dev/null +++ b/shell/ash_test/ash-misc/while2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Hello | ||
2 | OK:0 | ||
diff --git a/shell/ash_test/ash-misc/while2.tests b/shell/ash_test/ash-misc/while2.tests new file mode 100755 index 000000000..2247adc74 --- /dev/null +++ b/shell/ash_test/ash-misc/while2.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | while echo Hello; false; do echo NOT SHOWN; done | ||
2 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-misc/while4.right b/shell/ash_test/ash-misc/while4.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/ash_test/ash-misc/while4.right | |||
@@ -0,0 +1 @@ | |||
Ok:0 | |||
diff --git a/shell/ash_test/ash-misc/while4.tests b/shell/ash_test/ash-misc/while4.tests new file mode 100755 index 000000000..ba80e603a --- /dev/null +++ b/shell/ash_test/ash-misc/while4.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | false | ||
2 | while false && echo Not reached; do | ||
3 | echo BUG | ||
4 | break | ||
5 | done | ||
6 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-misc/while_in_subshell.right b/shell/ash_test/ash-misc/while_in_subshell.right new file mode 100644 index 000000000..290d39b7e --- /dev/null +++ b/shell/ash_test/ash-misc/while_in_subshell.right | |||
@@ -0,0 +1 @@ | |||
OK: 0 | |||
diff --git a/shell/ash_test/ash-misc/while_in_subshell.tests b/shell/ash_test/ash-misc/while_in_subshell.tests new file mode 100755 index 000000000..def8e092b --- /dev/null +++ b/shell/ash_test/ash-misc/while_in_subshell.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | (while true; do exit; done) | ||
2 | echo OK: $? | ||
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests index 3fa2f186d..74dca1cc0 100755 --- a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests +++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests | |||
@@ -1,5 +1,5 @@ | |||
1 | # The bug here was triggered by: | 1 | # The bug here was triggered by: |
2 | # * performin pathname expansion because we see [ | 2 | # * performing pathname expansion because we see [ |
3 | # * replace operator did not escape \ in replace string | 3 | # * replace operator did not escape \ in replace string |
4 | 4 | ||
5 | IP=192.168.0.1 | 5 | IP=192.168.0.1 |
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash2.right b/shell/ash_test/ash-quoting/dollar_squote_bash2.right new file mode 100644 index 000000000..f7a1731dd --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_squote_bash2.right | |||
@@ -0,0 +1,6 @@ | |||
1 | strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr | ||
2 | strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr | ||
3 | 80:\€ | ||
4 | 81:\ | ||
5 | 82:\‚ | ||
6 | Done:0 | ||
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash2.tests b/shell/ash_test/ash-quoting/dollar_squote_bash2.tests new file mode 100755 index 000000000..449772813 --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_squote_bash2.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | # Embedded NULs | ||
2 | echo $'str\x00'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr | ||
3 | echo $'str\000'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr | ||
4 | |||
5 | # The chars after '\' are hex 0x80,81,82... | ||
6 | echo 80:$'\€' | ||
7 | echo 81:$'\' | ||
8 | echo 82:$'\‚' | ||
9 | |||
10 | echo Done:$? | ||
diff --git a/shell/ash_test/ash-read/read_r.tests b/shell/ash_test/ash-read/read_r.tests index 2c4cc6106..1f0a18afc 100755 --- a/shell/ash_test/ash-read/read_r.tests +++ b/shell/ash_test/ash-read/read_r.tests | |||
@@ -1,2 +1,4 @@ | |||
1 | echo -e 'test\\\nbest' | (read reply; echo "$reply") | 1 | echo 'test\ |
2 | echo -e 'test\\\nbest' | (read -r reply; echo "$reply") | 2 | best' | (read reply; echo "$reply") |
3 | echo 'test\ | ||
4 | best' | (read -r reply; echo "$reply") | ||
diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/ash_test/ash-redir/redir1.right index 15515d1af..15515d1af 100644 --- a/shell/hush_test/hush-misc/redir1.right +++ b/shell/ash_test/ash-redir/redir1.right | |||
diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/ash_test/ash-redir/redir1.tests index ef2fbfb77..ef2fbfb77 100755 --- a/shell/hush_test/hush-misc/redir1.tests +++ b/shell/ash_test/ash-redir/redir1.tests | |||
diff --git a/shell/ash_test/ash-redir/redir7.tests b/shell/ash_test/ash-redir/redir7.tests index ca3979a81..e873a4622 100755 --- a/shell/ash_test/ash-redir/redir7.tests +++ b/shell/ash_test/ash-redir/redir7.tests | |||
@@ -4,9 +4,9 @@ | |||
4 | # was out of sync for redirect filenames. | 4 | # was out of sync for redirect filenames. |
5 | 5 | ||
6 | >unicode.sh | 6 | >unicode.sh |
7 | echo -e 'echo Ok >uni\x81code' >>unicode.sh | 7 | printf 'echo Ok >uni\x81code\n' >>unicode.sh |
8 | echo -e 'cat uni\x81code' >>unicode.sh | 8 | printf 'cat uni\x81code\n' >>unicode.sh |
9 | echo -e 'cat uni?code' >>unicode.sh | 9 | printf 'cat uni?code\n' >>unicode.sh |
10 | . ./unicode.sh | 10 | . ./unicode.sh |
11 | rm uni*code* | 11 | rm uni*code* |
12 | echo Done | 12 | echo Done |
diff --git a/shell/ash_test/ash-redir/redir8.tests b/shell/ash_test/ash-redir/redir8.tests index 8cb42c0d3..2bd386749 100755 --- a/shell/ash_test/ash-redir/redir8.tests +++ b/shell/ash_test/ash-redir/redir8.tests | |||
@@ -6,10 +6,10 @@ | |||
6 | # Subcase when redirect filename is specified in a variable. | 6 | # Subcase when redirect filename is specified in a variable. |
7 | 7 | ||
8 | >unicode.sh | 8 | >unicode.sh |
9 | echo -e 'v=uni\x81code' >>unicode.sh | 9 | printf 'v=uni\x81code\n' >>unicode.sh |
10 | echo -e 'echo Ok >"$v"' >>unicode.sh | 10 | printf 'echo Ok >"$v"\n' >>unicode.sh |
11 | echo -e 'cat uni\x81code' >>unicode.sh | 11 | printf 'cat uni\x81code\n' >>unicode.sh |
12 | echo -e 'cat uni?code' >>unicode.sh | 12 | printf 'cat uni?code\n' >>unicode.sh |
13 | . ./unicode.sh | 13 | . ./unicode.sh |
14 | rm uni*code* | 14 | rm uni*code* |
15 | echo Done | 15 | echo Done |
diff --git a/shell/hush_test/hush-misc/redir2.right b/shell/ash_test/ash-redir/redir_escapednum.right index 7326d9603..7326d9603 100644 --- a/shell/hush_test/hush-misc/redir2.right +++ b/shell/ash_test/ash-redir/redir_escapednum.right | |||
diff --git a/shell/hush_test/hush-misc/redir2.tests b/shell/ash_test/ash-redir/redir_escapednum.tests index 81983cae2..81983cae2 100755 --- a/shell/hush_test/hush-misc/redir2.tests +++ b/shell/ash_test/ash-redir/redir_escapednum.tests | |||
diff --git a/shell/hush_test/hush-misc/redir4.right b/shell/ash_test/ash-redir/redir_expand.right index ead25f603..ead25f603 100644 --- a/shell/hush_test/hush-misc/redir4.right +++ b/shell/ash_test/ash-redir/redir_expand.right | |||
diff --git a/shell/hush_test/hush-misc/redir4.tests b/shell/ash_test/ash-redir/redir_expand.tests index c50b8cedf..c50b8cedf 100755 --- a/shell/hush_test/hush-misc/redir4.tests +++ b/shell/ash_test/ash-redir/redir_expand.tests | |||
diff --git a/shell/hush_test/hush-misc/redir6.right b/shell/ash_test/ash-redir/redir_multi.right index a97c4bdf1..a97c4bdf1 100644 --- a/shell/hush_test/hush-misc/redir6.right +++ b/shell/ash_test/ash-redir/redir_multi.right | |||
diff --git a/shell/hush_test/hush-misc/redir6.tests b/shell/ash_test/ash-redir/redir_multi.tests index c639ebb2d..c639ebb2d 100755 --- a/shell/hush_test/hush-misc/redir6.tests +++ b/shell/ash_test/ash-redir/redir_multi.tests | |||
diff --git a/shell/ash_test/ash-redir/redir_script.right b/shell/ash_test/ash-redir/redir_script.right new file mode 100644 index 000000000..6694ed334 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_script.right | |||
@@ -0,0 +1 @@ | |||
Ok: script fd is not closed | |||
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests new file mode 100755 index 000000000..ccc497d7b --- /dev/null +++ b/shell/ash_test/ash-redir/redir_script.tests | |||
@@ -0,0 +1,29 @@ | |||
1 | # Builds a " 3>&- 4>&-" string. | ||
2 | # Note: one of these fds is a directory opened to /proc/self/fd | ||
3 | # for globbing. It is unwanted, but I don't know how to filter it out. | ||
4 | find_fds() { | ||
5 | fds="" | ||
6 | for f in /proc/self/fd/*; do | ||
7 | test "$f" = "/proc/self/fd/0" && continue | ||
8 | test "$f" = "/proc/self/fd/1" && continue | ||
9 | test "$f" = "/proc/self/fd/2" && continue | ||
10 | fds="$fds ${f##*/}>&-" | ||
11 | done | ||
12 | } | ||
13 | |||
14 | find_fds | ||
15 | fds1="$fds" | ||
16 | |||
17 | # One of the fds is open to the script body | ||
18 | # Close it while executing something. | ||
19 | eval "find_fds $fds" | ||
20 | |||
21 | # Shell should not lose that fd. Did it? | ||
22 | find_fds | ||
23 | test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } | ||
24 | |||
25 | echo "Bug: script fd is closed" | ||
26 | echo "fds1:$fds1" | ||
27 | echo "fds2:$fds" | ||
28 | exit 1 | ||
29 | |||
diff --git a/shell/hush_test/hush-parsing/redir_space.right b/shell/ash_test/ash-redir/redir_space.right index 084295204..084295204 100644 --- a/shell/hush_test/hush-parsing/redir_space.right +++ b/shell/ash_test/ash-redir/redir_space.right | |||
diff --git a/shell/hush_test/hush-parsing/redir_space.tests b/shell/ash_test/ash-redir/redir_space.tests index c0b543098..c0b543098 100755 --- a/shell/hush_test/hush-parsing/redir_space.tests +++ b/shell/ash_test/ash-redir/redir_space.tests | |||
diff --git a/shell/ash_test/ash-signals/continue_and_trap1.right b/shell/ash_test/ash-signals/continue_and_trap1.right new file mode 100644 index 000000000..d2dd0af72 --- /dev/null +++ b/shell/ash_test/ash-signals/continue_and_trap1.right | |||
@@ -0,0 +1 @@ | |||
Exiting | |||
diff --git a/shell/ash_test/ash-signals/continue_and_trap1.tests b/shell/ash_test/ash-signals/continue_and_trap1.tests new file mode 100755 index 000000000..2a5c147b1 --- /dev/null +++ b/shell/ash_test/ash-signals/continue_and_trap1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | trap "echo Exiting; exit" INT | ||
2 | |||
3 | (sleep 1; kill -s INT $$) & | ||
4 | |||
5 | while continue; do | ||
6 | continue; | ||
7 | done | ||
diff --git a/shell/ash_test/ash-signals/return_in_trap1.right b/shell/ash_test/ash-signals/return_in_trap1.right new file mode 100644 index 000000000..a6e637885 --- /dev/null +++ b/shell/ash_test/ash-signals/return_in_trap1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | a:2 | ||
2 | b:0 | ||
3 | Trap | ||
4 | d:3 | ||
diff --git a/shell/ash_test/ash-signals/return_in_trap1.tests b/shell/ash_test/ash-signals/return_in_trap1.tests new file mode 100755 index 000000000..f2498024f --- /dev/null +++ b/shell/ash_test/ash-signals/return_in_trap1.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | a() { | ||
2 | (exit 2) | ||
3 | echo a:$? | ||
4 | (kill -s USR1 $$; echo b:$?; exit 3) | ||
5 | echo c:$? # does not execute | ||
6 | (exit 4) | ||
7 | } | ||
8 | |||
9 | trap "echo Trap; return" USR1 | ||
10 | a | ||
11 | |||
12 | echo d:$? | ||
13 | # It's debatable what is the correct value above. | ||
14 | # Does 'return' in trap sees $? == 2 or $? == 3? | ||
15 | # IOW: after (kill..), does shell first wait for its completion | ||
16 | # and sets $?, then checks pending signals and runs a trap handler, | ||
17 | # or does it first checks pending signals and runs handler? | ||
18 | # hush does the former, and prints 3. | ||
diff --git a/shell/hush_test/hush-trap/save-ret.right b/shell/ash_test/ash-signals/save-ret.right index a3e12ce5e..a3e12ce5e 100644 --- a/shell/hush_test/hush-trap/save-ret.right +++ b/shell/ash_test/ash-signals/save-ret.right | |||
diff --git a/shell/hush_test/hush-trap/save-ret.tests b/shell/ash_test/ash-signals/save-ret.tests index 0786b6d96..0786b6d96 100755 --- a/shell/hush_test/hush-trap/save-ret.tests +++ b/shell/ash_test/ash-signals/save-ret.tests | |||
diff --git a/shell/ash_test/ash-signals/signal1.tests b/shell/ash_test/ash-signals/signal1.tests index 28bfc6a46..61943467a 100755 --- a/shell/ash_test/ash-signals/signal1.tests +++ b/shell/ash_test/ash-signals/signal1.tests | |||
@@ -19,7 +19,7 @@ while $sleeping; do | |||
19 | if wait %%; then | 19 | if wait %%; then |
20 | echo "sleep completed" | 20 | echo "sleep completed" |
21 | sleeping=false | 21 | sleeping=false |
22 | elif [ $? == 127 ]; then | 22 | elif [ $? = 127 ]; then |
23 | echo "BUG: no processes to wait for?!" | 23 | echo "BUG: no processes to wait for?!" |
24 | sleeping=false | 24 | sleeping=false |
25 | else | 25 | else |
diff --git a/shell/ash_test/ash-signals/sigquit_exec.right b/shell/ash_test/ash-signals/sigquit_exec.right new file mode 100644 index 000000000..a8041929a --- /dev/null +++ b/shell/ash_test/ash-signals/sigquit_exec.right | |||
@@ -0,0 +1,2 @@ | |||
1 | SigIgn: 0000000000000000 | ||
2 | SigIgn: 0000000000000000 | ||
diff --git a/shell/ash_test/ash-signals/sigquit_exec.tests b/shell/ash_test/ash-signals/sigquit_exec.tests new file mode 100755 index 000000000..24bda6921 --- /dev/null +++ b/shell/ash_test/ash-signals/sigquit_exec.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # Should show no masked signals in both cases. | ||
2 | # We had a bug where SIGQUIT was masked on exec. | ||
3 | grep SigIgn: /proc/self/status | ||
4 | exec grep SigIgn: /proc/self/status | ||
diff --git a/shell/ash_test/ash-vars/empty.right b/shell/ash_test/ash-vars/empty.right new file mode 100644 index 000000000..2cb3c70f2 --- /dev/null +++ b/shell/ash_test/ash-vars/empty.right | |||
@@ -0,0 +1,3 @@ | |||
1 | a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F | ||
2 | a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F | ||
3 | a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F | ||
diff --git a/shell/ash_test/ash-vars/empty.tests b/shell/ash_test/ash-vars/empty.tests new file mode 100755 index 000000000..a9c247e31 --- /dev/null +++ b/shell/ash_test/ash-vars/empty.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | e= | ||
2 | |||
3 | echo a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F | ||
4 | echo a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F | ||
5 | echo $e a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F | ||
diff --git a/shell/ash_test/ash-vars/glob_and_vars.right b/shell/ash_test/ash-vars/glob_and_vars.right new file mode 100644 index 000000000..3ac7ec5ff --- /dev/null +++ b/shell/ash_test/ash-vars/glob_and_vars.right | |||
@@ -0,0 +1 @@ | |||
./glob_and_vars.right ./glob_and_vars.tests | |||
diff --git a/shell/ash_test/ash-vars/glob_and_vars.tests b/shell/ash_test/ash-vars/glob_and_vars.tests new file mode 100755 index 000000000..482cf9d8a --- /dev/null +++ b/shell/ash_test/ash-vars/glob_and_vars.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v=. | ||
2 | echo $v/glob_and_vars.[tr]* | ||
diff --git a/shell/ash_test/ash-vars/param_expand_len.right b/shell/ash_test/ash-vars/param_expand_len.right new file mode 100644 index 000000000..96e8cb59b --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_len.right | |||
@@ -0,0 +1,9 @@ | |||
1 | 0 | ||
2 | 0 | ||
3 | 1 | ||
4 | Make sure len parsing doesnt break arg count | ||
5 | 0 0 | ||
6 | 4 4 | ||
7 | Testing len op | ||
8 | 4 3 2 1 0 0 | ||
9 | 0 3 0 | ||
diff --git a/shell/ash_test/ash-vars/param_expand_len.tests b/shell/ash_test/ash-vars/param_expand_len.tests new file mode 100755 index 000000000..fe20a45e9 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_len.tests | |||
@@ -0,0 +1,17 @@ | |||
1 | "$THIS_SH" -c 'echo $#' | ||
2 | "$THIS_SH" -c 'echo $#' arg0 | ||
3 | "$THIS_SH" -c 'echo $#' arg0 arg1 | ||
4 | |||
5 | echo Make sure len parsing doesnt break arg count | ||
6 | set -- | ||
7 | echo $# ${#} | ||
8 | set -- aaaa bbb cc d | ||
9 | echo $# ${#} | ||
10 | |||
11 | echo Testing len op | ||
12 | echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} | ||
13 | |||
14 | unset e | ||
15 | f=abc | ||
16 | g= | ||
17 | echo ${#e} ${#f} ${#g} | ||
diff --git a/shell/ash_test/ash-vars/param_glob.right b/shell/ash_test/ash-vars/param_glob.right new file mode 100644 index 000000000..bdee8fe0f --- /dev/null +++ b/shell/ash_test/ash-vars/param_glob.right | |||
@@ -0,0 +1,4 @@ | |||
1 | param_glob.tests | ||
2 | param_glob.tests | ||
3 | param_glob.t* | ||
4 | param_glob.t* | ||
diff --git a/shell/ash_test/ash-vars/param_glob.tests b/shell/ash_test/ash-vars/param_glob.tests new file mode 100755 index 000000000..4d74fee02 --- /dev/null +++ b/shell/ash_test/ash-vars/param_glob.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" "$0" 'param_glob.t*' | ||
3 | echo NOT SHOWN | ||
4 | exit | ||
5 | fi | ||
6 | echo $* | ||
7 | echo $@ | ||
8 | echo "$*" | ||
9 | echo "$@" | ||
diff --git a/shell/ash_test/ash-vars/param_subshell.right b/shell/ash_test/ash-vars/param_subshell.right new file mode 100644 index 000000000..f3c3767de --- /dev/null +++ b/shell/ash_test/ash-vars/param_subshell.right | |||
@@ -0,0 +1,7 @@ | |||
1 | 1=1 | ||
2 | 2=2 | ||
3 | 3=3 | ||
4 | 4=4 | ||
5 | 5=5 | ||
6 | 6=6 | ||
7 | 7=7 | ||
diff --git a/shell/ash_test/ash-vars/param_subshell.tests b/shell/ash_test/ash-vars/param_subshell.tests new file mode 100755 index 000000000..27fdc5b9b --- /dev/null +++ b/shell/ash_test/ash-vars/param_subshell.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | if test $# = 0; then | ||
2 | "$THIS_SH" "$0" 1 2 3 4 5 6 7 8 9 | ||
3 | exit | ||
4 | fi | ||
5 | echo 1=$1 | ||
6 | { echo 2=$2; } | ||
7 | { echo 3=$3; } & | ||
8 | # cant use usleep as it isnt standard in $PATH -- | ||
9 | # we fail when testing busybox compiled solely as "hush" | ||
10 | wait | ||
11 | ( echo 4=$4 ) | ||
12 | ( echo 5=$5 ) & | ||
13 | wait | ||
14 | true | echo 6=$6 | cat | ||
15 | true | { echo 7=$7; } | cat | ||
diff --git a/shell/msh_test/msh-vars/star.right b/shell/ash_test/ash-vars/star.right index 0ecc55bc2..0ecc55bc2 100644 --- a/shell/msh_test/msh-vars/star.right +++ b/shell/ash_test/ash-vars/star.right | |||
diff --git a/shell/msh_test/msh-vars/star.tests b/shell/ash_test/ash-vars/star.tests index 5554c4090..5554c4090 100755 --- a/shell/msh_test/msh-vars/star.tests +++ b/shell/ash_test/ash-vars/star.tests | |||
diff --git a/shell/ash_test/ash-vars/var1.right b/shell/ash_test/ash-vars/var1.right index 2a01291bb..194e7dbe2 100644 --- a/shell/ash_test/ash-vars/var1.right +++ b/shell/ash_test/ash-vars/var1.right | |||
@@ -1,6 +1,4 @@ | |||
1 | a=a A=a | 1 | http://busybox.net |
2 | a=a A=a | 2 | http://busybox.net_abc |
3 | a= A= | 3 | 1 1 |
4 | a= A= | 4 | 1 1 |
5 | a=a A=a | ||
6 | a=a A=a | ||
diff --git a/shell/ash_test/ash-vars/var1.tests b/shell/ash_test/ash-vars/var1.tests index 802e489bd..48a6782ba 100755 --- a/shell/ash_test/ash-vars/var1.tests +++ b/shell/ash_test/ash-vars/var1.tests | |||
@@ -1,14 +1,9 @@ | |||
1 | # check that first assignment has proper effect on second one | 1 | URL=http://busybox.net |
2 | 2 | ||
3 | ( | 3 | echo $URL |
4 | a=a A=$a | 4 | echo ${URL}_abc |
5 | echo a=$a A=$A | ||
6 | ) | ||
7 | (a=a A=$a; echo a=$a A=$A) | ||
8 | (a=a A=$a echo a=$a A=$A) | ||
9 | (a=a A=$a /bin/echo a=$a A=$A) | ||
10 | 5 | ||
11 | f() { echo a=$a A=$A; } | 6 | true |
12 | 7 | false; echo $? ${?} | |
13 | (a=a A=$a f) | 8 | true |
14 | (a=a A=$a; f) | 9 | { false; echo $? ${?}; } |
diff --git a/shell/ash_test/ash-vars/var2.right b/shell/ash_test/ash-vars/var2.right index 8fed138eb..40bf4bff4 100644 --- a/shell/ash_test/ash-vars/var2.right +++ b/shell/ash_test/ash-vars/var2.right | |||
@@ -1 +1,2 @@ | |||
1 | bus/usb/1/2 | 1 | http://busybox.net |
2 | http://busybox.net_abc | ||
diff --git a/shell/ash_test/ash-vars/var2.tests b/shell/ash_test/ash-vars/var2.tests index 07feaeb8b..1292410f3 100755 --- a/shell/ash_test/ash-vars/var2.tests +++ b/shell/ash_test/ash-vars/var2.tests | |||
@@ -1 +1,4 @@ | |||
1 | X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D | 1 | _1=http://busybox.net |
2 | |||
3 | echo $_1 | ||
4 | echo ${_1}_abc | ||
diff --git a/shell/ash_test/ash-vars/var4.right b/shell/ash_test/ash-vars/var4.right new file mode 100644 index 000000000..8fed138eb --- /dev/null +++ b/shell/ash_test/ash-vars/var4.right | |||
@@ -0,0 +1 @@ | |||
bus/usb/1/2 | |||
diff --git a/shell/ash_test/ash-vars/var4.tests b/shell/ash_test/ash-vars/var4.tests new file mode 100755 index 000000000..07feaeb8b --- /dev/null +++ b/shell/ash_test/ash-vars/var4.tests | |||
@@ -0,0 +1 @@ | |||
X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D | |||
diff --git a/shell/ash_test/ash-vars/var5.right b/shell/ash_test/ash-vars/var5.right new file mode 100644 index 000000000..2a01291bb --- /dev/null +++ b/shell/ash_test/ash-vars/var5.right | |||
@@ -0,0 +1,6 @@ | |||
1 | a=a A=a | ||
2 | a=a A=a | ||
3 | a= A= | ||
4 | a= A= | ||
5 | a=a A=a | ||
6 | a=a A=a | ||
diff --git a/shell/ash_test/ash-vars/var5.tests b/shell/ash_test/ash-vars/var5.tests new file mode 100755 index 000000000..802e489bd --- /dev/null +++ b/shell/ash_test/ash-vars/var5.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | # check that first assignment has proper effect on second one | ||
2 | |||
3 | ( | ||
4 | a=a A=$a | ||
5 | echo a=$a A=$A | ||
6 | ) | ||
7 | (a=a A=$a; echo a=$a A=$A) | ||
8 | (a=a A=$a echo a=$a A=$A) | ||
9 | (a=a A=$a /bin/echo a=$a A=$A) | ||
10 | |||
11 | f() { echo a=$a A=$A; } | ||
12 | |||
13 | (a=a A=$a f) | ||
14 | (a=a A=$a; f) | ||
diff --git a/shell/ash_test/ash-vars/var_bash1a.right b/shell/ash_test/ash-vars/var_bash1a.right new file mode 100644 index 000000000..1965b5c6c --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash1a.right | |||
@@ -0,0 +1,6 @@ | |||
1 | parameter 'abcdef' | ||
2 | varoffset2 'cdef' | ||
3 | varoffset-2 'ef' | ||
4 | literal '2' 'cdef' | ||
5 | literal '-2' 'abcdef' | ||
6 | literal ' -2' 'ef' | ||
diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests new file mode 100755 index 000000000..551dd9acc --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash1a.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | parameter=abcdef | ||
2 | offset=2 | ||
3 | noffset=-2 | ||
4 | echo "parameter '${parameter}'" | ||
5 | echo "varoffset2 '${parameter:${offset}}'" | ||
6 | echo "varoffset-2 '${parameter:${noffset}}'" | ||
7 | echo "literal '2' '${parameter:2}'" | ||
8 | # This is not inrpreted as ${VAR:POS{:LEN}}, | ||
9 | # but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD | ||
10 | echo "literal '-2' '${parameter:-2}'" | ||
11 | echo "literal ' -2' '${parameter: -2}'" | ||
diff --git a/shell/ash_test/ash-vars/var_bash4.right b/shell/ash_test/ash-vars/var_bash4.right index 600e8532f..0ef1bf661 100644 --- a/shell/ash_test/ash-vars/var_bash4.right +++ b/shell/ash_test/ash-vars/var_bash4.right | |||
@@ -1,23 +1,40 @@ | |||
1 | Source: a*b\*c | 1 | Source: a*b\*c |
2 | Replace str: _\\_\z_ | 2 | Replace str: _\\_\z_ |
3 | Pattern: single backslash and star: "replace literal star" | 3 | Pattern: single backslash and star: "replace literal star" |
4 | In assignment: a_\_z_b\*c | ||
5 | Unquoted: a_\_z_b\*c | 4 | Unquoted: a_\_z_b\*c |
5 | Unquoted =: a_\_z_b\*c | ||
6 | Quoted: a_\_\z_b\*c | 6 | Quoted: a_\_\z_b\*c |
7 | Quoted =: a_\_\z_b\*c | ||
7 | Pattern: double backslash and star: "replace backslash and everything after it" | 8 | Pattern: double backslash and star: "replace backslash and everything after it" |
8 | In assignment: a*b_\_z_ | ||
9 | Unquoted: a*b_\_z_ | 9 | Unquoted: a*b_\_z_ |
10 | Unquoted =: a*b_\_z_ | ||
10 | Quoted: a*b_\_\z_ | 11 | Quoted: a*b_\_\z_ |
12 | Quoted =: a*b_\_\z_ | ||
11 | 13 | ||
12 | Source: a\bc | 14 | Source: a\bc |
13 | Replace str: _\\_\z_ | 15 | Replace str: _\\_\z_ |
14 | Pattern: single backslash and b: "replace b" | 16 | Pattern: single backslash and b: "replace b" |
15 | In assignment: a\_\_z_c | ||
16 | Unquoted: a\_\_z_c | 17 | Unquoted: a\_\_z_c |
18 | Unquoted =: a\_\_z_c | ||
17 | Quoted: a\_\_\z_c | 19 | Quoted: a\_\_\z_c |
20 | Quoted =: a\_\_\z_c | ||
18 | Pattern: double backslash and b: "replace backslash and b" | 21 | Pattern: double backslash and b: "replace backslash and b" |
19 | In assignment: a_\_z_c | ||
20 | Unquoted: a_\_z_c | 22 | Unquoted: a_\_z_c |
23 | Unquoted =: a_\_z_c | ||
21 | Quoted: a_\_\z_c | 24 | Quoted: a_\_\z_c |
25 | Quoted =: a_\_\z_c | ||
26 | |||
27 | Source: a\bc | ||
28 | Replace str: _\\_\z_ (as variable $s) | ||
29 | Pattern: single backslash and b: "replace b" | ||
30 | Unquoted: a\_\\_\z_c | ||
31 | Unquoted =: a\_\\_\z_c | ||
32 | Quoted: a\_\\_\z_c | ||
33 | Quoted =: a\_\\_\z_c | ||
34 | Pattern: double backslash and b: "replace backslash and b" | ||
35 | Unquoted: a_\\_\z_c | ||
36 | Unquoted =: a_\\_\z_c | ||
37 | Quoted: a_\\_\z_c | ||
38 | Quoted =: a_\\_\z_c | ||
22 | 39 | ||
23 | Done: 0 | 40 | Done: 0 |
diff --git a/shell/ash_test/ash-vars/var_bash4.tests b/shell/ash_test/ash-vars/var_bash4.tests index d5470614b..32aa2b34c 100755 --- a/shell/ash_test/ash-vars/var_bash4.tests +++ b/shell/ash_test/ash-vars/var_bash4.tests | |||
@@ -6,23 +6,30 @@ | |||
6 | # even in quotes. | 6 | # even in quotes. |
7 | # | 7 | # |
8 | # bash4 (and probably bash3 too): "Quoted:" results are different from | 8 | # bash4 (and probably bash3 too): "Quoted:" results are different from |
9 | # unquoted and assignment expansions - they have a backslash before z. | 9 | # unquoted expansions - they have a backslash before z. |
10 | # | ||
11 | # The difference only exists if repl is a literal. If it is a variable: | ||
12 | # ${v/.../$s}, then all backslashes are preserved in both cases. | ||
10 | 13 | ||
11 | v='a*b\*c' | 14 | v='a*b\*c' |
12 | echo 'Source: ' "$v" | 15 | echo 'Source: ' "$v" |
13 | echo 'Replace str: ' '_\\_\z_' | 16 | echo 'Replace str: ' '_\\_\z_' |
14 | 17 | ||
15 | echo 'Pattern: ' 'single backslash and star: "replace literal star"' | 18 | echo 'Pattern: ' 'single backslash and star: "replace literal star"' |
16 | r=${v/\*/_\\_\z_} | ||
17 | echo 'In assignment:' "$r" | ||
18 | echo 'Unquoted: ' ${v/\*/_\\_\z_} | 19 | echo 'Unquoted: ' ${v/\*/_\\_\z_} |
20 | r=${v/\*/_\\_\z_} | ||
21 | echo 'Unquoted =: ' "$r" | ||
19 | echo 'Quoted: ' "${v/\*/_\\_\z_}" | 22 | echo 'Quoted: ' "${v/\*/_\\_\z_}" |
23 | r="${v/\*/_\\_\z_}" | ||
24 | echo 'Quoted =: ' "$r" | ||
20 | 25 | ||
21 | echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' | 26 | echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' |
22 | r=${v/\\*/_\\_\z_} | ||
23 | echo 'In assignment:' "$r" | ||
24 | echo 'Unquoted: ' ${v/\\*/_\\_\z_} | 27 | echo 'Unquoted: ' ${v/\\*/_\\_\z_} |
28 | r=${v/\\*/_\\_\z_} | ||
29 | echo 'Unquoted =: ' "$r" | ||
25 | echo 'Quoted: ' "${v/\\*/_\\_\z_}" | 30 | echo 'Quoted: ' "${v/\\*/_\\_\z_}" |
31 | r="${v/\\*/_\\_\z_}" | ||
32 | echo 'Quoted =: ' "$r" | ||
26 | 33 | ||
27 | echo | 34 | echo |
28 | 35 | ||
@@ -31,16 +38,43 @@ echo 'Source: ' "$v" | |||
31 | echo 'Replace str: ' '_\\_\z_' | 38 | echo 'Replace str: ' '_\\_\z_' |
32 | 39 | ||
33 | echo 'Pattern: ' 'single backslash and b: "replace b"' | 40 | echo 'Pattern: ' 'single backslash and b: "replace b"' |
34 | r=${v/\b/_\\_\z_} | ||
35 | echo 'In assignment:' "$r" | ||
36 | echo 'Unquoted: ' ${v/\b/_\\_\z_} | 41 | echo 'Unquoted: ' ${v/\b/_\\_\z_} |
42 | r=${v/\b/_\\_\z_} | ||
43 | echo 'Unquoted =: ' "$r" | ||
37 | echo 'Quoted: ' "${v/\b/_\\_\z_}" | 44 | echo 'Quoted: ' "${v/\b/_\\_\z_}" |
45 | r="${v/\b/_\\_\z_}" | ||
46 | echo 'Quoted =: ' "$r" | ||
38 | 47 | ||
39 | echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' | 48 | echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' |
40 | r=${v/\\b/_\\_\z_} | ||
41 | echo 'In assignment:' "$r" | ||
42 | echo 'Unquoted: ' ${v/\\b/_\\_\z_} | 49 | echo 'Unquoted: ' ${v/\\b/_\\_\z_} |
50 | r=${v/\\b/_\\_\z_} | ||
51 | echo 'Unquoted =: ' "$r" | ||
43 | echo 'Quoted: ' "${v/\\b/_\\_\z_}" | 52 | echo 'Quoted: ' "${v/\\b/_\\_\z_}" |
53 | r="${v/\\b/_\\_\z_}" | ||
54 | echo 'Quoted =: ' "$r" | ||
55 | |||
56 | echo | ||
57 | |||
58 | v='a\bc' | ||
59 | s='_\\_\z_' | ||
60 | echo 'Source: ' "$v" | ||
61 | echo 'Replace str: ' "$s" '(as variable $s)' | ||
62 | |||
63 | echo 'Pattern: ' 'single backslash and b: "replace b"' | ||
64 | echo 'Unquoted: ' ${v/\b/$s} | ||
65 | r=${v/\b/$s} | ||
66 | echo 'Unquoted =: ' "$r" | ||
67 | echo 'Quoted: ' "${v/\b/$s}" | ||
68 | r="${v/\b/$s}" | ||
69 | echo 'Quoted =: ' "$r" | ||
70 | |||
71 | echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' | ||
72 | echo 'Unquoted: ' ${v/\\b/$s} | ||
73 | r=${v/\\b/$s} | ||
74 | echo 'Unquoted =: ' "$r" | ||
75 | echo 'Quoted: ' "${v/\\b/$s}" | ||
76 | r="${v/\\b/$s}" | ||
77 | echo 'Quoted =: ' "$r" | ||
44 | 78 | ||
45 | echo | 79 | echo |
46 | 80 | ||
diff --git a/shell/ash_test/ash-vars/var_bash5.right b/shell/ash_test/ash-vars/var_bash5.right index 278ed3228..1990902b2 100644 --- a/shell/ash_test/ash-vars/var_bash5.right +++ b/shell/ash_test/ash-vars/var_bash5.right | |||
@@ -1,4 +1,11 @@ | |||
1 | a/ | 1 | 1 a/ |
2 | a/d | 2 | 2 a/d |
3 | a/e/f | 3 | 3 a/e/f |
4 | 4 a\ | ||
5 | 5 a\d | ||
6 | 6 a\e\f | ||
7 | 7 a\\ | ||
8 | 8 a\\d | ||
9 | 9 a\\e\\f | ||
10 | a ab | ||
4 | Done: 0 | 11 | Done: 0 |
diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests index 7f482a554..5748b4ac7 100755 --- a/shell/ash_test/ash-vars/var_bash5.tests +++ b/shell/ash_test/ash-vars/var_bash5.tests | |||
@@ -4,8 +4,26 @@ | |||
4 | v='a/b/c' | 4 | v='a/b/c' |
5 | s='b/c' | 5 | s='b/c' |
6 | r='e/f' | 6 | r='e/f' |
7 | echo "${v/$s}" | 7 | echo "1 ${v/$s}" |
8 | echo "${v/$s/d}" | 8 | echo "2 ${v/$s/d}" |
9 | echo "${v/$s/$r}" | 9 | echo "3 ${v/$s/$r}" |
10 | |||
11 | v='a\b\c' | ||
12 | s='b\\c' | ||
13 | r='e\f' | ||
14 | echo "4 ${v/$s}" | ||
15 | echo "5 ${v/$s/d}" | ||
16 | echo "6 ${v/$s/$r}" | ||
17 | |||
18 | v='a\\b\\c' | ||
19 | s='b\\\\c' | ||
20 | r='e\\f' | ||
21 | echo "7 ${v/$s}" | ||
22 | echo "8 ${v/$s/d}" | ||
23 | echo "9 ${v/$s/$r}" | ||
24 | |||
25 | v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b' | ||
26 | s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-' | ||
27 | echo "a ${v/$s}" | ||
10 | 28 | ||
11 | echo Done: $? | 29 | echo Done: $? |
diff --git a/shell/msh_test/msh-bugs/var_expand_in_assign.right b/shell/ash_test/ash-vars/var_expand_in_assign.right index 352210d7e..352210d7e 100644 --- a/shell/msh_test/msh-bugs/var_expand_in_assign.right +++ b/shell/ash_test/ash-vars/var_expand_in_assign.right | |||
diff --git a/shell/msh_test/msh-bugs/var_expand_in_assign.tests b/shell/ash_test/ash-vars/var_expand_in_assign.tests index 18cdc74c0..18cdc74c0 100755 --- a/shell/msh_test/msh-bugs/var_expand_in_assign.tests +++ b/shell/ash_test/ash-vars/var_expand_in_assign.tests | |||
diff --git a/shell/msh_test/msh-bugs/var_expand_in_redir.right b/shell/ash_test/ash-vars/var_expand_in_redir.right index 423299c97..423299c97 100644 --- a/shell/msh_test/msh-bugs/var_expand_in_redir.right +++ b/shell/ash_test/ash-vars/var_expand_in_redir.right | |||
diff --git a/shell/msh_test/msh-bugs/var_expand_in_redir.tests b/shell/ash_test/ash-vars/var_expand_in_redir.tests index bda6bdd7f..bda6bdd7f 100755 --- a/shell/msh_test/msh-bugs/var_expand_in_redir.tests +++ b/shell/ash_test/ash-vars/var_expand_in_redir.tests | |||
diff --git a/shell/ash_test/ash-vars/var_expand_on_ifs.right b/shell/ash_test/ash-vars/var_expand_on_ifs.right new file mode 100644 index 000000000..2ed2069f7 --- /dev/null +++ b/shell/ash_test/ash-vars/var_expand_on_ifs.right | |||
@@ -0,0 +1,9 @@ | |||
1 | 1 a b c | ||
2 | 2 a + b c | ||
3 | 3 a b c | ||
4 | 4 a b c | ||
5 | 5 a b c | ||
6 | 6 a b + c | ||
7 | 7 a b c | ||
8 | 8 a b c | ||
9 | 9 a b c | ||
diff --git a/shell/ash_test/ash-vars/var_expand_on_ifs.tests b/shell/ash_test/ash-vars/var_expand_on_ifs.tests new file mode 100755 index 000000000..a12ff8ec8 --- /dev/null +++ b/shell/ash_test/ash-vars/var_expand_on_ifs.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | b=' b ' | ||
2 | e='' | ||
3 | echo 1 a $b c | ||
4 | echo 2 a +$b c | ||
5 | echo 3 a $e$b c | ||
6 | echo 4 a "$e"$b c | ||
7 | echo 5 a ""$b c | ||
8 | echo 6 a $b+ c | ||
9 | echo 7 a $b$e c | ||
10 | echo 8 a $b"$e" c | ||
11 | echo 9 a $b"" c | ||
diff --git a/shell/ash_test/ash-vars/var_in_pipes.right b/shell/ash_test/ash-vars/var_in_pipes.right new file mode 100644 index 000000000..faf65bed4 --- /dev/null +++ b/shell/ash_test/ash-vars/var_in_pipes.right | |||
@@ -0,0 +1,6 @@ | |||
1 | b=1 | ||
2 | b=2 | ||
3 | b=3 | ||
4 | b=4 | ||
5 | b=5 | ||
6 | b=6 | ||
diff --git a/shell/ash_test/ash-vars/var_in_pipes.tests b/shell/ash_test/ash-vars/var_in_pipes.tests new file mode 100755 index 000000000..3f8cd2729 --- /dev/null +++ b/shell/ash_test/ash-vars/var_in_pipes.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | b=1 env | grep ^b= | ||
2 | true | b=2 env | grep ^b= | ||
3 | a=1 true | b=3 env | grep ^b= | ||
4 | |||
5 | (b=4 env) | grep ^b= | ||
6 | (true | b=5 env) | grep ^b= | ||
7 | (a=1 true | b=6 env) | grep ^b= | ||
diff --git a/shell/msh_test/msh-parsing/argv0.right b/shell/ash_test/ash-vars/var_leaks.right index d86bac9de..d86bac9de 100644 --- a/shell/msh_test/msh-parsing/argv0.right +++ b/shell/ash_test/ash-vars/var_leaks.right | |||
diff --git a/shell/ash_test/ash-vars/var_leaks.tests b/shell/ash_test/ash-vars/var_leaks.tests new file mode 100755 index 000000000..27c8c6504 --- /dev/null +++ b/shell/ash_test/ash-vars/var_leaks.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | # external program | ||
2 | a=b /bin/true | ||
3 | env | grep ^a= | ||
4 | |||
5 | # builtin | ||
6 | a=b true | ||
7 | env | grep ^a= | ||
8 | |||
9 | # exec with redirection only | ||
10 | # in bash, this leaks! | ||
11 | a=b exec 1>&1 | ||
12 | env | grep ^a= | ||
13 | |||
14 | echo OK | ||
diff --git a/shell/ash_test/ash-vars/var_posix1.right b/shell/ash_test/ash-vars/var_posix1.right index 55f35798a..7ff618ad0 100644 --- a/shell/ash_test/ash-vars/var_posix1.right +++ b/shell/ash_test/ash-vars/var_posix1.right | |||
@@ -1,3 +1,19 @@ | |||
1 | Empty: | ||
2 | Empty: | ||
3 | Empty: | ||
4 | Empty: | ||
5 | Empty: | ||
6 | Empty: | ||
7 | Empty: | ||
8 | Empty: | ||
9 | Empty: | ||
10 | Empty: | ||
11 | Empty: | ||
12 | Empty: | ||
13 | Empty: | ||
14 | Empty: | ||
15 | Empty: | ||
16 | Empty: | ||
1 | abcdcd | 17 | abcdcd |
2 | abcdcd | 18 | abcdcd |
3 | abcdcd | 19 | abcdcd |
@@ -5,7 +21,9 @@ cdcd | |||
5 | babcdcd | 21 | babcdcd |
6 | babcdcd | 22 | babcdcd |
7 | ababcdcd | 23 | ababcdcd |
8 | 24 | Empty: | |
25 | ababcdcd}_tail | ||
26 | ababcdcd_tail | ||
9 | ababcd | 27 | ababcd |
10 | ababcd | 28 | ababcd |
11 | ababcd | 29 | ababcd |
@@ -13,5 +31,11 @@ abab | |||
13 | ababcdc | 31 | ababcdc |
14 | ababcdc | 32 | ababcdc |
15 | ababcdcd | 33 | ababcdcd |
16 | 34 | Empty: | |
17 | end | 35 | ababcdcd}_tail |
36 | ababcdcd_tail | ||
37 | ababcdcd | ||
38 | ab | ||
39 | ab | ||
40 | ab | ||
41 | End | ||
diff --git a/shell/ash_test/ash-vars/var_posix1.tests b/shell/ash_test/ash-vars/var_posix1.tests index 4139e2cc3..82abe8198 100755 --- a/shell/ash_test/ash-vars/var_posix1.tests +++ b/shell/ash_test/ash-vars/var_posix1.tests | |||
@@ -1,3 +1,25 @@ | |||
1 | unset var | ||
2 | |||
3 | echo Empty:${var#} | ||
4 | echo Empty:${var##} | ||
5 | echo Empty:${var#*} | ||
6 | echo Empty:${var##*} | ||
7 | echo Empty:${var%} | ||
8 | echo Empty:${var%%} | ||
9 | echo Empty:${var%*} | ||
10 | echo Empty:${var%%*} | ||
11 | |||
12 | var= | ||
13 | |||
14 | echo Empty:${var#} | ||
15 | echo Empty:${var##} | ||
16 | echo Empty:${var#*} | ||
17 | echo Empty:${var##*} | ||
18 | echo Empty:${var%} | ||
19 | echo Empty:${var%%} | ||
20 | echo Empty:${var%*} | ||
21 | echo Empty:${var%%*} | ||
22 | |||
1 | var=ababcdcd | 23 | var=ababcdcd |
2 | 24 | ||
3 | echo ${var#ab} | 25 | echo ${var#ab} |
@@ -7,7 +29,9 @@ echo ${var##a*b} | |||
7 | echo ${var#?} | 29 | echo ${var#?} |
8 | echo ${var##?} | 30 | echo ${var##?} |
9 | echo ${var#*} | 31 | echo ${var#*} |
10 | echo ${var##*} | 32 | echo Empty:${var##*} |
33 | echo ${var#}}_tail | ||
34 | echo ${var#\}}_tail | ||
11 | 35 | ||
12 | echo ${var%cd} | 36 | echo ${var%cd} |
13 | echo ${var%%cd} | 37 | echo ${var%%cd} |
@@ -16,6 +40,13 @@ echo ${var%%c*d} | |||
16 | echo ${var%?} | 40 | echo ${var%?} |
17 | echo ${var%%?} | 41 | echo ${var%%?} |
18 | echo ${var%*} | 42 | echo ${var%*} |
19 | echo ${var%%*} | 43 | echo Empty:${var%%*} |
44 | echo ${var#}}_tail | ||
45 | echo ${var#\}}_tail | ||
46 | echo ${var%\\*} | ||
47 | |||
48 | a=ab}; echo ${a%\}}; | ||
49 | a=abc; c=c; echo ${a%${c}} | ||
50 | a=ab{{c; echo ${a%`echo {{c`} | ||
20 | 51 | ||
21 | echo end | 52 | echo End |
diff --git a/shell/ash_test/ash-vars/var_serial.right b/shell/ash_test/ash-vars/var_serial.right new file mode 100644 index 000000000..42aa33057 --- /dev/null +++ b/shell/ash_test/ash-vars/var_serial.right | |||
@@ -0,0 +1,5 @@ | |||
1 | Assignments only: c=a | ||
2 | Assignments and a command: c=a | ||
3 | Assignments and a builtin: c=a | ||
4 | Assignments and a function: c=a | ||
5 | Done | ||
diff --git a/shell/ash_test/ash-vars/var_serial.tests b/shell/ash_test/ash-vars/var_serial.tests new file mode 100755 index 000000000..6b4a4cdf7 --- /dev/null +++ b/shell/ash_test/ash-vars/var_serial.tests | |||
@@ -0,0 +1,22 @@ | |||
1 | a=a | ||
2 | |||
3 | b=b | ||
4 | c=c | ||
5 | # Second assignment depends on the first: | ||
6 | b=$a c=$b | ||
7 | echo Assignments only: c=$c | ||
8 | |||
9 | b=b | ||
10 | c=c | ||
11 | b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c' | ||
12 | |||
13 | b=b | ||
14 | c=c | ||
15 | b=$a c=$b eval 'echo Assignments and a builtin: c=$c' | ||
16 | |||
17 | b=b | ||
18 | c=c | ||
19 | f() { echo Assignments and a function: c=$c; } | ||
20 | b=$a c=$b f | ||
21 | |||
22 | echo Done | ||
diff --git a/shell/msh_test/msh-vars/var_subst_in_for.right b/shell/ash_test/ash-vars/var_subst_in_for.right index c8aca1c12..c8aca1c12 100644 --- a/shell/msh_test/msh-vars/var_subst_in_for.right +++ b/shell/ash_test/ash-vars/var_subst_in_for.right | |||
diff --git a/shell/msh_test/msh-vars/var_subst_in_for.tests b/shell/ash_test/ash-vars/var_subst_in_for.tests index 4d1c11201..433c60627 100755 --- a/shell/msh_test/msh-vars/var_subst_in_for.tests +++ b/shell/ash_test/ash-vars/var_subst_in_for.tests | |||
@@ -1,5 +1,5 @@ | |||
1 | if test $# = 0; then | 1 | if test $# = 0; then |
2 | exec "$THIS_SH" var_subst_in_for.tests abc "d e" | 2 | exec "$THIS_SH" "$0" abc "d e" |
3 | fi | 3 | fi |
4 | 4 | ||
5 | echo 'Testing: in x y z' | 5 | echo 'Testing: in x y z' |
diff --git a/shell/ash_test/ash-vars/var_unbackslash.right b/shell/ash_test/ash-vars/var_unbackslash.right new file mode 100644 index 000000000..8bc834711 --- /dev/null +++ b/shell/ash_test/ash-vars/var_unbackslash.right | |||
@@ -0,0 +1,11 @@ | |||
1 | b1=-qwerty-t-\-"-`---z-*-?- | ||
2 | b1=-qwerty-t-\-"-`---z-*-?- | ||
3 | b2=-qwerty-\t-\-"-`-\--\z-\*-\?- | ||
4 | b2=-qwerty-\t-\-"-`-\--\z-\*-\?- | ||
5 | b3=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
6 | b3=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
7 | c=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
8 | c=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
9 | c=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
10 | c=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
11 | Done: 0 | ||
diff --git a/shell/ash_test/ash-vars/var_unbackslash.tests b/shell/ash_test/ash-vars/var_unbackslash.tests new file mode 100755 index 000000000..bb52af3d0 --- /dev/null +++ b/shell/ash_test/ash-vars/var_unbackslash.tests | |||
@@ -0,0 +1,23 @@ | |||
1 | # Test for correct handling of backslashes | ||
2 | a=qwerty | ||
3 | |||
4 | b=-$a-\t-\\-\"-\`-\--\z-\*-\?- | ||
5 | echo b1=$b | ||
6 | echo "b1=$b" | ||
7 | b="-$a-\t-\\-\"-\`-\--\z-\*-\?-" | ||
8 | echo b2=$b | ||
9 | echo "b2=$b" | ||
10 | b='-$a-\t-\\-\"-\`-\--\z-\*-\?-' | ||
11 | echo b3=$b | ||
12 | echo "b3=$b" | ||
13 | |||
14 | c=$b | ||
15 | echo "c=$c" | ||
16 | c=${b} | ||
17 | echo "c=$c" | ||
18 | c="$b" | ||
19 | echo "c=$c" | ||
20 | c="${b}" | ||
21 | echo "c=$c" | ||
22 | |||
23 | echo "Done: $?" | ||
diff --git a/shell/ash_test/ash-vars/var_unbackslash1.right b/shell/ash_test/ash-vars/var_unbackslash1.right new file mode 100644 index 000000000..3e0c0e2af --- /dev/null +++ b/shell/ash_test/ash-vars/var_unbackslash1.right | |||
@@ -0,0 +1,7 @@ | |||
1 | Ok | ||
2 | Ba d | ||
3 | Ok | ||
4 | Ok | ||
5 | Ok | ||
6 | Forty two:42 | ||
7 | Forty two:42 | ||
diff --git a/shell/ash_test/ash-vars/var_unbackslash1.tests b/shell/ash_test/ash-vars/var_unbackslash1.tests new file mode 100755 index 000000000..a4665e423 --- /dev/null +++ b/shell/ash_test/ash-vars/var_unbackslash1.tests | |||
@@ -0,0 +1,35 @@ | |||
1 | ad="Ok" | ||
2 | a="Ba" | ||
3 | |||
4 | # "Ok" | ||
5 | echo $a\ | ||
6 | d | ||
7 | |||
8 | # This variable contains backslash+newline! | ||
9 | e='echo $a\ | ||
10 | d' | ||
11 | |||
12 | # "Ba d" | ||
13 | eval $e | ||
14 | # "Ok" | ||
15 | eval "$e" | ||
16 | |||
17 | echo $\ | ||
18 | (echo Ok\ | ||
19 | ) | ||
20 | echo "$\ | ||
21 | (echo Ok\ | ||
22 | )" | ||
23 | |||
24 | echo Forty two:$\ | ||
25 | (\ | ||
26 | (\ | ||
27 | 42\ | ||
28 | )\ | ||
29 | ) | ||
30 | echo "Forty two:$\ | ||
31 | (\ | ||
32 | (\ | ||
33 | 42\ | ||
34 | )\ | ||
35 | )" | ||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.right b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right new file mode 100644 index 000000000..cf583d0aa --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right | |||
@@ -0,0 +1,41 @@ | |||
1 | Testing: !IFS $* | ||
2 | .abc. | ||
3 | .d. | ||
4 | .e. | ||
5 | Testing: !IFS $@ | ||
6 | .abc. | ||
7 | .d. | ||
8 | .e. | ||
9 | Testing: !IFS "$*" | ||
10 | .abc d e. | ||
11 | Testing: !IFS "$@" | ||
12 | .abc. | ||
13 | .d e. | ||
14 | Testing: IFS="" $* | ||
15 | .abc. | ||
16 | .d e. | ||
17 | Testing: IFS="" $@ | ||
18 | .abc. | ||
19 | .d e. | ||
20 | Testing: IFS="" "$*" | ||
21 | .abcd e. | ||
22 | Testing: IFS="" "$@" | ||
23 | .abc. | ||
24 | .d e. | ||
25 | Testing: !IFS v=$* | ||
26 | v='abc d e' | ||
27 | Testing: !IFS v=$@ | ||
28 | v='abc d e' | ||
29 | Testing: !IFS v="$*" | ||
30 | v='abc d e' | ||
31 | Testing: !IFS v="$@" | ||
32 | v='abc d e' | ||
33 | Testing: IFS="" v=$* | ||
34 | v='abcd e' | ||
35 | Testing: IFS="" v=$@ | ||
36 | v='abcd e' | ||
37 | Testing: IFS="" v="$*" | ||
38 | v='abcd e' | ||
39 | Testing: IFS="" v="$@" | ||
40 | v='abcd e' | ||
41 | Finished | ||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests new file mode 100755 index 000000000..a62afc6fd --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests | |||
@@ -0,0 +1,42 @@ | |||
1 | set -- abc "d e" | ||
2 | |||
3 | echo 'Testing: !IFS $*' | ||
4 | unset IFS; for a in $*; do echo ".$a."; done | ||
5 | echo 'Testing: !IFS $@' | ||
6 | unset IFS; for a in $@; do echo ".$a."; done | ||
7 | echo 'Testing: !IFS "$*"' | ||
8 | unset IFS; for a in "$*"; do echo ".$a."; done | ||
9 | echo 'Testing: !IFS "$@"' | ||
10 | unset IFS; for a in "$@"; do echo ".$a."; done | ||
11 | |||
12 | echo 'Testing: IFS="" $*' | ||
13 | IFS=""; for a in $*; do echo ".$a."; done | ||
14 | echo 'Testing: IFS="" $@' | ||
15 | IFS=""; for a in $@; do echo ".$a."; done | ||
16 | echo 'Testing: IFS="" "$*"' | ||
17 | IFS=""; for a in "$*"; do echo ".$a."; done | ||
18 | echo 'Testing: IFS="" "$@"' | ||
19 | IFS=""; for a in "$@"; do echo ".$a."; done | ||
20 | |||
21 | echo 'Testing: !IFS v=$*' | ||
22 | unset IFS; v=$*; echo "v='$v'" | ||
23 | echo 'Testing: !IFS v=$@' | ||
24 | unset IFS; v=$@; echo "v='$v'" | ||
25 | echo 'Testing: !IFS v="$*"' | ||
26 | unset IFS; v="$*"; echo "v='$v'" | ||
27 | echo 'Testing: !IFS v="$@"' | ||
28 | unset IFS; v="$@"; echo "v='$v'" | ||
29 | |||
30 | echo 'Testing: IFS="" v=$*' | ||
31 | IFS=""; v=$*; echo "v='$v'" | ||
32 | echo 'Testing: IFS="" v=$@' | ||
33 | IFS=""; v=$@; echo "v='$v'" | ||
34 | echo 'Testing: IFS="" v="$*"' | ||
35 | IFS=""; v="$*"; echo "v='$v'" | ||
36 | echo 'Testing: IFS="" v="$@"' | ||
37 | IFS=""; v="$@"; echo "v='$v'" | ||
38 | |||
39 | # Note: in IFS="" v=$@ and IFS="" v="$@" cases, bash produces "abc d e" | ||
40 | # We produce "abcd e" | ||
41 | |||
42 | echo Finished | ||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs2.right b/shell/ash_test/ash-vars/var_wordsplit_ifs2.right new file mode 100644 index 000000000..c234193fe --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Unquoted:<1> | ||
2 | Unquoted:<3> | ||
3 | Quoted:<123> | ||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests new file mode 100755 index 000000000..47523549c --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | # 123 chars long | ||
2 | a="\ | ||
3 | 01234567890123456789\ | ||
4 | 01234567890123456789\ | ||
5 | 01234567890123456789\ | ||
6 | 01234567890123456789\ | ||
7 | 01234567890123456789\ | ||
8 | 0123456789\ | ||
9 | 0123456789\ | ||
10 | 012" | ||
11 | |||
12 | IFS=2; for v in ${#a}; do echo Unquoted:"<$v>"; done | ||
13 | IFS=2; for v in "${#a}"; do echo Quoted:"<$v>"; done | ||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs3.right b/shell/ash_test/ash-vars/var_wordsplit_ifs3.right new file mode 100644 index 000000000..5ab72e1cd --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs3.right | |||
@@ -0,0 +1,12 @@ | |||
1 | Unquoted%:<q> | ||
2 | Unquoted%:<w> | ||
3 | Unquoted%:<e> | ||
4 | Unquoted%:<r> | ||
5 | Unquoted%:<t> | ||
6 | Unquoted#:<w> | ||
7 | Unquoted#:<e> | ||
8 | Unquoted#:<r> | ||
9 | Unquoted#:<t> | ||
10 | Unquoted#:<y> | ||
11 | Quoted%:<q w e r t > | ||
12 | Quoted#:< w e r t y> | ||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests new file mode 100755 index 000000000..4aa65574a --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | a="q w e r t y" | ||
2 | for v in ${a%y}; do echo Unquoted%:"<$v>"; done | ||
3 | for v in ${a#q}; do echo Unquoted#:"<$v>"; done | ||
4 | for v in "${a%y}"; do echo Quoted%:"<$v>"; done | ||
5 | for v in "${a#q}"; do echo Quoted#:"<$v>"; done | ||
diff --git a/shell/hush.c b/shell/hush.c index eabe83ac6..9b51f389e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -103,6 +103,9 @@ | |||
103 | #else | 103 | #else |
104 | # define CLEAR_RANDOM_T(rnd) ((void)0) | 104 | # define CLEAR_RANDOM_T(rnd) ((void)0) |
105 | #endif | 105 | #endif |
106 | #ifndef F_DUPFD_CLOEXEC | ||
107 | # define F_DUPFD_CLOEXEC F_DUPFD | ||
108 | #endif | ||
106 | #ifndef PIPE_BUF | 109 | #ifndef PIPE_BUF |
107 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ | 110 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ |
108 | #endif | 111 | #endif |
@@ -459,19 +462,17 @@ static const char *const assignment_flag[] = { | |||
459 | 462 | ||
460 | typedef struct in_str { | 463 | typedef struct in_str { |
461 | const char *p; | 464 | const char *p; |
462 | /* eof_flag=1: last char in ->p is really an EOF */ | ||
463 | char eof_flag; /* meaningless if ->p == NULL */ | ||
464 | char peek_buf[2]; | ||
465 | #if ENABLE_HUSH_INTERACTIVE | 465 | #if ENABLE_HUSH_INTERACTIVE |
466 | smallint promptmode; /* 0: PS1, 1: PS2 */ | 466 | smallint promptmode; /* 0: PS1, 1: PS2 */ |
467 | #endif | 467 | #endif |
468 | int peek_buf[2]; | ||
468 | int last_char; | 469 | int last_char; |
469 | FILE *file; | 470 | FILE *file; |
470 | int (*get) (struct in_str *) FAST_FUNC; | 471 | int (*get) (struct in_str *) FAST_FUNC; |
471 | int (*peek) (struct in_str *) FAST_FUNC; | 472 | int (*peek) (struct in_str *) FAST_FUNC; |
472 | } in_str; | 473 | } in_str; |
473 | #define i_getch(input) ((input)->get(input)) | 474 | #define i_getch(input) ((input)->get(input)) |
474 | #define i_peek(input) ((input)->peek(input)) | 475 | #define i_peek(input) ((input)->peek(input)) |
475 | 476 | ||
476 | /* The descrip member of this structure is only used to make | 477 | /* The descrip member of this structure is only used to make |
477 | * debugging output pretty */ | 478 | * debugging output pretty */ |
@@ -711,6 +712,13 @@ enum { | |||
711 | }; | 712 | }; |
712 | 713 | ||
713 | 714 | ||
715 | struct FILE_list { | ||
716 | struct FILE_list *next; | ||
717 | FILE *fp; | ||
718 | int fd; | ||
719 | }; | ||
720 | |||
721 | |||
714 | /* "Globals" within this file */ | 722 | /* "Globals" within this file */ |
715 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 723 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
716 | struct globals { | 724 | struct globals { |
@@ -769,6 +777,9 @@ struct globals { | |||
769 | * 1: return is invoked, skip all till end of func | 777 | * 1: return is invoked, skip all till end of func |
770 | */ | 778 | */ |
771 | smallint flag_return_in_progress; | 779 | smallint flag_return_in_progress; |
780 | # define G_flag_return_in_progress (G.flag_return_in_progress) | ||
781 | #else | ||
782 | # define G_flag_return_in_progress 0 | ||
772 | #endif | 783 | #endif |
773 | smallint exiting; /* used to prevent EXIT trap recursion */ | 784 | smallint exiting; /* used to prevent EXIT trap recursion */ |
774 | /* These four support $?, $#, and $1 */ | 785 | /* These four support $?, $#, and $1 */ |
@@ -802,6 +813,7 @@ struct globals { | |||
802 | unsigned handled_SIGCHLD; | 813 | unsigned handled_SIGCHLD; |
803 | smallint we_have_children; | 814 | smallint we_have_children; |
804 | #endif | 815 | #endif |
816 | struct FILE_list *FILE_list; | ||
805 | /* Which signals have non-DFL handler (even with no traps set)? | 817 | /* Which signals have non-DFL handler (even with no traps set)? |
806 | * Set at the start to: | 818 | * Set at the start to: |
807 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | 819 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) |
@@ -823,7 +835,9 @@ struct globals { | |||
823 | int debug_indent; | 835 | int debug_indent; |
824 | #endif | 836 | #endif |
825 | struct sigaction sa; | 837 | struct sigaction sa; |
826 | char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; | 838 | #if ENABLE_FEATURE_EDITING |
839 | char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN]; | ||
840 | #endif | ||
827 | }; | 841 | }; |
828 | #define G (*ptr_to_globals) | 842 | #define G (*ptr_to_globals) |
829 | /* Not #defining name to G.name - this quickly gets unwieldy | 843 | /* Not #defining name to G.name - this quickly gets unwieldy |
@@ -1252,6 +1266,91 @@ static void free_strings(char **strings) | |||
1252 | } | 1266 | } |
1253 | 1267 | ||
1254 | 1268 | ||
1269 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) | ||
1270 | { | ||
1271 | /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */ | ||
1272 | int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10); | ||
1273 | if (newfd < 0) { | ||
1274 | /* fd was not open? */ | ||
1275 | if (errno == EBADF) | ||
1276 | return fd; | ||
1277 | xfunc_die(); | ||
1278 | } | ||
1279 | close(fd); | ||
1280 | return newfd; | ||
1281 | } | ||
1282 | |||
1283 | |||
1284 | /* Manipulating the list of open FILEs */ | ||
1285 | static FILE *remember_FILE(FILE *fp) | ||
1286 | { | ||
1287 | if (fp) { | ||
1288 | struct FILE_list *n = xmalloc(sizeof(*n)); | ||
1289 | n->next = G.FILE_list; | ||
1290 | G.FILE_list = n; | ||
1291 | n->fp = fp; | ||
1292 | n->fd = fileno(fp); | ||
1293 | close_on_exec_on(n->fd); | ||
1294 | } | ||
1295 | return fp; | ||
1296 | } | ||
1297 | static void fclose_and_forget(FILE *fp) | ||
1298 | { | ||
1299 | struct FILE_list **pp = &G.FILE_list; | ||
1300 | while (*pp) { | ||
1301 | struct FILE_list *cur = *pp; | ||
1302 | if (cur->fp == fp) { | ||
1303 | *pp = cur->next; | ||
1304 | free(cur); | ||
1305 | break; | ||
1306 | } | ||
1307 | pp = &cur->next; | ||
1308 | } | ||
1309 | fclose(fp); | ||
1310 | } | ||
1311 | static int save_FILEs_on_redirect(int fd) | ||
1312 | { | ||
1313 | struct FILE_list *fl = G.FILE_list; | ||
1314 | while (fl) { | ||
1315 | if (fd == fl->fd) { | ||
1316 | /* We use it only on script files, they are all CLOEXEC */ | ||
1317 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC); | ||
1318 | return 1; | ||
1319 | } | ||
1320 | fl = fl->next; | ||
1321 | } | ||
1322 | return 0; | ||
1323 | } | ||
1324 | static void restore_redirected_FILEs(void) | ||
1325 | { | ||
1326 | struct FILE_list *fl = G.FILE_list; | ||
1327 | while (fl) { | ||
1328 | int should_be = fileno(fl->fp); | ||
1329 | if (fl->fd != should_be) { | ||
1330 | xmove_fd(fl->fd, should_be); | ||
1331 | fl->fd = should_be; | ||
1332 | } | ||
1333 | fl = fl->next; | ||
1334 | } | ||
1335 | } | ||
1336 | #if ENABLE_FEATURE_SH_STANDALONE | ||
1337 | static void close_all_FILE_list(void) | ||
1338 | { | ||
1339 | struct FILE_list *fl = G.FILE_list; | ||
1340 | while (fl) { | ||
1341 | /* fclose would also free FILE object. | ||
1342 | * It is disastrous if we share memory with a vforked parent. | ||
1343 | * I'm not sure we never come here after vfork. | ||
1344 | * Therefore just close fd, nothing more. | ||
1345 | */ | ||
1346 | /*fclose(fl->fp); - unsafe */ | ||
1347 | close(fl->fd); | ||
1348 | fl = fl->next; | ||
1349 | } | ||
1350 | } | ||
1351 | #endif | ||
1352 | |||
1353 | |||
1255 | /* Helpers for setting new $n and restoring them back | 1354 | /* Helpers for setting new $n and restoring them back |
1256 | */ | 1355 | */ |
1257 | typedef struct save_arg_t { | 1356 | typedef struct save_arg_t { |
@@ -1477,19 +1576,50 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler) | |||
1477 | return old_sa.sa_handler; | 1576 | return old_sa.sa_handler; |
1478 | } | 1577 | } |
1479 | 1578 | ||
1579 | static void hush_exit(int exitcode) NORETURN; | ||
1580 | static void fflush_and__exit(void) NORETURN; | ||
1581 | static void restore_ttypgrp_and__exit(void) NORETURN; | ||
1582 | |||
1583 | static void restore_ttypgrp_and__exit(void) | ||
1584 | { | ||
1585 | /* xfunc has failed! die die die */ | ||
1586 | /* no EXIT traps, this is an escape hatch! */ | ||
1587 | G.exiting = 1; | ||
1588 | hush_exit(xfunc_error_retval); | ||
1589 | } | ||
1590 | |||
1591 | /* Needed only on some libc: | ||
1592 | * It was observed that on exit(), fgetc'ed buffered data | ||
1593 | * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). | ||
1594 | * With the net effect that even after fork(), not vfork(), | ||
1595 | * exit() in NOEXECed applet in "sh SCRIPT": | ||
1596 | * noexec_applet_here | ||
1597 | * echo END_OF_SCRIPT | ||
1598 | * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". | ||
1599 | * This makes "echo END_OF_SCRIPT" executed twice. | ||
1600 | * Similar problems can be seen with die_if_script() -> xfunc_die() | ||
1601 | * and in `cmd` handling. | ||
1602 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): | ||
1603 | */ | ||
1604 | static void fflush_and__exit(void) | ||
1605 | { | ||
1606 | fflush_all(); | ||
1607 | _exit(xfunc_error_retval); | ||
1608 | } | ||
1609 | |||
1480 | #if ENABLE_HUSH_JOB | 1610 | #if ENABLE_HUSH_JOB |
1481 | 1611 | ||
1482 | static void xfunc_has_died(void); | ||
1483 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ | 1612 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ |
1484 | # define disable_restore_tty_pgrp_on_exit() (die_func = NULL) | 1613 | # define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit) |
1485 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ | 1614 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ |
1486 | # define enable_restore_tty_pgrp_on_exit() (die_func = xfunc_has_died) | 1615 | # define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit) |
1487 | 1616 | ||
1488 | /* Restores tty foreground process group, and exits. | 1617 | /* Restores tty foreground process group, and exits. |
1489 | * May be called as signal handler for fatal signal | 1618 | * May be called as signal handler for fatal signal |
1490 | * (will resend signal to itself, producing correct exit state) | 1619 | * (will resend signal to itself, producing correct exit state) |
1491 | * or called directly with -EXITCODE. | 1620 | * or called directly with -EXITCODE. |
1492 | * We also call it if xfunc is exiting. */ | 1621 | * We also call it if xfunc is exiting. |
1622 | */ | ||
1493 | static void sigexit(int sig) NORETURN; | 1623 | static void sigexit(int sig) NORETURN; |
1494 | static void sigexit(int sig) | 1624 | static void sigexit(int sig) |
1495 | { | 1625 | { |
@@ -1544,7 +1674,6 @@ static sighandler_t pick_sighandler(unsigned sig) | |||
1544 | } | 1674 | } |
1545 | 1675 | ||
1546 | /* Restores tty foreground process group, and exits. */ | 1676 | /* Restores tty foreground process group, and exits. */ |
1547 | static void hush_exit(int exitcode) NORETURN; | ||
1548 | static void hush_exit(int exitcode) | 1677 | static void hush_exit(int exitcode) |
1549 | { | 1678 | { |
1550 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 1679 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
@@ -1580,23 +1709,14 @@ static void hush_exit(int exitcode) | |||
1580 | } | 1709 | } |
1581 | #endif | 1710 | #endif |
1582 | 1711 | ||
1583 | #if ENABLE_HUSH_JOB | ||
1584 | fflush_all(); | 1712 | fflush_all(); |
1713 | #if ENABLE_HUSH_JOB | ||
1585 | sigexit(- (exitcode & 0xff)); | 1714 | sigexit(- (exitcode & 0xff)); |
1586 | #else | 1715 | #else |
1587 | exit(exitcode); | 1716 | _exit(exitcode); |
1588 | #endif | 1717 | #endif |
1589 | } | 1718 | } |
1590 | 1719 | ||
1591 | static void xfunc_has_died(void) NORETURN; | ||
1592 | static void xfunc_has_died(void) | ||
1593 | { | ||
1594 | /* xfunc has failed! die die die */ | ||
1595 | /* no EXIT traps, this is an escape hatch! */ | ||
1596 | G.exiting = 1; | ||
1597 | hush_exit(xfunc_error_retval); | ||
1598 | } | ||
1599 | |||
1600 | 1720 | ||
1601 | //TODO: return a mask of ALL handled sigs? | 1721 | //TODO: return a mask of ALL handled sigs? |
1602 | static int check_and_run_traps(void) | 1722 | static int check_and_run_traps(void) |
@@ -1619,6 +1739,7 @@ static int check_and_run_traps(void) | |||
1619 | break; | 1739 | break; |
1620 | got_sig: | 1740 | got_sig: |
1621 | if (G.traps && G.traps[sig]) { | 1741 | if (G.traps && G.traps[sig]) { |
1742 | debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]); | ||
1622 | if (G.traps[sig][0]) { | 1743 | if (G.traps[sig][0]) { |
1623 | /* We have user-defined handler */ | 1744 | /* We have user-defined handler */ |
1624 | smalluint save_rcode; | 1745 | smalluint save_rcode; |
@@ -1636,6 +1757,7 @@ static int check_and_run_traps(void) | |||
1636 | /* not a trap: special action */ | 1757 | /* not a trap: special action */ |
1637 | switch (sig) { | 1758 | switch (sig) { |
1638 | case SIGINT: | 1759 | case SIGINT: |
1760 | debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig); | ||
1639 | /* Builtin was ^C'ed, make it look prettier: */ | 1761 | /* Builtin was ^C'ed, make it look prettier: */ |
1640 | bb_putchar('\n'); | 1762 | bb_putchar('\n'); |
1641 | G.flag_SIGINT = 1; | 1763 | G.flag_SIGINT = 1; |
@@ -1644,6 +1766,7 @@ static int check_and_run_traps(void) | |||
1644 | #if ENABLE_HUSH_JOB | 1766 | #if ENABLE_HUSH_JOB |
1645 | case SIGHUP: { | 1767 | case SIGHUP: { |
1646 | struct pipe *job; | 1768 | struct pipe *job; |
1769 | debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); | ||
1647 | /* bash is observed to signal whole process groups, | 1770 | /* bash is observed to signal whole process groups, |
1648 | * not individual processes */ | 1771 | * not individual processes */ |
1649 | for (job = G.job_list; job; job = job->next) { | 1772 | for (job = G.job_list; job; job = job->next) { |
@@ -1658,6 +1781,7 @@ static int check_and_run_traps(void) | |||
1658 | #endif | 1781 | #endif |
1659 | #if ENABLE_HUSH_FAST | 1782 | #if ENABLE_HUSH_FAST |
1660 | case SIGCHLD: | 1783 | case SIGCHLD: |
1784 | debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig); | ||
1661 | G.count_SIGCHLD++; | 1785 | G.count_SIGCHLD++; |
1662 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | 1786 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); |
1663 | /* Note: | 1787 | /* Note: |
@@ -1667,6 +1791,7 @@ static int check_and_run_traps(void) | |||
1667 | break; | 1791 | break; |
1668 | #endif | 1792 | #endif |
1669 | default: /* ignored: */ | 1793 | default: /* ignored: */ |
1794 | debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig); | ||
1670 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | 1795 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ |
1671 | /* Note: | 1796 | /* Note: |
1672 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | 1797 | * We dont do 'last_sig = sig' here -> NOT returning this sig. |
@@ -1766,6 +1891,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1766 | { | 1891 | { |
1767 | struct variable **var_pp; | 1892 | struct variable **var_pp; |
1768 | struct variable *cur; | 1893 | struct variable *cur; |
1894 | char *free_me = NULL; | ||
1769 | char *eq_sign; | 1895 | char *eq_sign; |
1770 | int name_len; | 1896 | int name_len; |
1771 | 1897 | ||
@@ -1782,6 +1908,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1782 | var_pp = &cur->next; | 1908 | var_pp = &cur->next; |
1783 | continue; | 1909 | continue; |
1784 | } | 1910 | } |
1911 | |||
1785 | /* We found an existing var with this name */ | 1912 | /* We found an existing var with this name */ |
1786 | if (cur->flg_read_only) { | 1913 | if (cur->flg_read_only) { |
1787 | #if !BB_MMU | 1914 | #if !BB_MMU |
@@ -1830,12 +1957,17 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1830 | strcpy(cur->varstr, str); | 1957 | strcpy(cur->varstr, str); |
1831 | goto free_and_exp; | 1958 | goto free_and_exp; |
1832 | } | 1959 | } |
1833 | } else { | 1960 | /* Can't reuse */ |
1834 | /* max_len == 0 signifies "malloced" var, which we can | 1961 | cur->max_len = 0; |
1835 | * (and has to) free */ | 1962 | goto set_str_and_exp; |
1836 | free(cur->varstr); | 1963 | } |
1837 | } | 1964 | /* max_len == 0 signifies "malloced" var, which we can |
1838 | cur->max_len = 0; | 1965 | * (and have to) free. But we can't free(cur->varstr) here: |
1966 | * if cur->flg_export is 1, it is in the environment. | ||
1967 | * We should either unsetenv+free, or wait until putenv, | ||
1968 | * then putenv(new)+free(old). | ||
1969 | */ | ||
1970 | free_me = cur->varstr; | ||
1839 | goto set_str_and_exp; | 1971 | goto set_str_and_exp; |
1840 | } | 1972 | } |
1841 | 1973 | ||
@@ -1862,10 +1994,15 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1862 | cur->flg_export = 0; | 1994 | cur->flg_export = 0; |
1863 | /* unsetenv was already done */ | 1995 | /* unsetenv was already done */ |
1864 | } else { | 1996 | } else { |
1997 | int i; | ||
1865 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); | 1998 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); |
1866 | return putenv(cur->varstr); | 1999 | i = putenv(cur->varstr); |
2000 | /* only now we can free old exported malloced string */ | ||
2001 | free(free_me); | ||
2002 | return i; | ||
1867 | } | 2003 | } |
1868 | } | 2004 | } |
2005 | free(free_me); | ||
1869 | return 0; | 2006 | return 0; |
1870 | } | 2007 | } |
1871 | 2008 | ||
@@ -2005,28 +2142,19 @@ static void reinit_unicode_for_hush(void) | |||
2005 | } | 2142 | } |
2006 | } | 2143 | } |
2007 | 2144 | ||
2008 | |||
2009 | /* | 2145 | /* |
2010 | * in_str support | 2146 | * in_str support (strings, and "strings" read from files). |
2011 | */ | 2147 | */ |
2012 | static int FAST_FUNC static_get(struct in_str *i) | ||
2013 | { | ||
2014 | int ch = *i->p; | ||
2015 | if (ch != '\0') { | ||
2016 | i->p++; | ||
2017 | i->last_char = ch; | ||
2018 | return ch; | ||
2019 | } | ||
2020 | return EOF; | ||
2021 | } | ||
2022 | |||
2023 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2024 | { | ||
2025 | return *i->p; | ||
2026 | } | ||
2027 | 2148 | ||
2028 | #if ENABLE_HUSH_INTERACTIVE | 2149 | #if ENABLE_HUSH_INTERACTIVE |
2029 | 2150 | /* To test correct lineedit/interactive behavior, type from command line: | |
2151 | * echo $P\ | ||
2152 | * \ | ||
2153 | * AT\ | ||
2154 | * H\ | ||
2155 | * \ | ||
2156 | * It excercises a lot of corner cases. | ||
2157 | */ | ||
2030 | static void cmdedit_update_prompt(void) | 2158 | static void cmdedit_update_prompt(void) |
2031 | { | 2159 | { |
2032 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { | 2160 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { |
@@ -2040,7 +2168,6 @@ static void cmdedit_update_prompt(void) | |||
2040 | if (G.PS2 == NULL) | 2168 | if (G.PS2 == NULL) |
2041 | G.PS2 = "> "; | 2169 | G.PS2 = "> "; |
2042 | } | 2170 | } |
2043 | |||
2044 | static const char *setup_prompt_string(int promptmode) | 2171 | static const char *setup_prompt_string(int promptmode) |
2045 | { | 2172 | { |
2046 | const char *prompt_str; | 2173 | const char *prompt_str; |
@@ -2058,33 +2185,36 @@ static const char *setup_prompt_string(int promptmode) | |||
2058 | prompt_str = G.PS2; | 2185 | prompt_str = G.PS2; |
2059 | } else | 2186 | } else |
2060 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; | 2187 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; |
2061 | debug_printf("result '%s'\n", prompt_str); | 2188 | debug_printf("prompt_str '%s'\n", prompt_str); |
2062 | return prompt_str; | 2189 | return prompt_str; |
2063 | } | 2190 | } |
2064 | 2191 | static int get_user_input(struct in_str *i) | |
2065 | static void get_user_input(struct in_str *i) | ||
2066 | { | 2192 | { |
2067 | int r; | 2193 | int r; |
2068 | const char *prompt_str; | 2194 | const char *prompt_str; |
2069 | 2195 | ||
2070 | prompt_str = setup_prompt_string(i->promptmode); | 2196 | prompt_str = setup_prompt_string(i->promptmode); |
2071 | # if ENABLE_FEATURE_EDITING | 2197 | # if ENABLE_FEATURE_EDITING |
2072 | /* Enable command line editing only while a command line | ||
2073 | * is actually being read */ | ||
2074 | do { | 2198 | do { |
2075 | reinit_unicode_for_hush(); | 2199 | reinit_unicode_for_hush(); |
2076 | G.flag_SIGINT = 0; | 2200 | G.flag_SIGINT = 0; |
2077 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 2201 | /* buglet: SIGINT will not make new prompt to appear _at once_, |
2078 | * only after <Enter>. (^C will work) */ | 2202 | * only after <Enter>. (^C will work) */ |
2079 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); | 2203 | r = read_line_input(G.line_input_state, prompt_str, |
2204 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, | ||
2205 | /*timeout*/ -1 | ||
2206 | ); | ||
2080 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2207 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
2081 | check_and_run_traps(); | 2208 | check_and_run_traps(); |
2082 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2209 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
2083 | i->eof_flag = (r < 0); | 2210 | if (r < 0) { |
2084 | if (i->eof_flag) { /* EOF/error detected */ | 2211 | /* EOF/error detected */ |
2085 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | 2212 | i->p = NULL; |
2086 | G.user_input_buf[1] = '\0'; | 2213 | i->peek_buf[0] = r = EOF; |
2214 | return r; | ||
2087 | } | 2215 | } |
2216 | i->p = G.user_input_buf; | ||
2217 | return (unsigned char)*i->p++; | ||
2088 | # else | 2218 | # else |
2089 | do { | 2219 | do { |
2090 | G.flag_SIGINT = 0; | 2220 | G.flag_SIGINT = 0; |
@@ -2098,76 +2228,149 @@ static void get_user_input(struct in_str *i) | |||
2098 | fputs(prompt_str, stdout); | 2228 | fputs(prompt_str, stdout); |
2099 | } | 2229 | } |
2100 | fflush_all(); | 2230 | fflush_all(); |
2101 | G.user_input_buf[0] = r = fgetc(i->file); | 2231 | r = fgetc(i->file); |
2102 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | 2232 | } while (G.flag_SIGINT || r == '\0'); |
2103 | } while (G.flag_SIGINT); | 2233 | return r; |
2104 | i->eof_flag = (r == EOF); | ||
2105 | # endif | 2234 | # endif |
2106 | i->p = G.user_input_buf; | ||
2107 | } | 2235 | } |
2108 | |||
2109 | #endif /* INTERACTIVE */ | ||
2110 | |||
2111 | /* This is the magic location that prints prompts | 2236 | /* This is the magic location that prints prompts |
2112 | * and gets data back from the user */ | 2237 | * and gets data back from the user */ |
2238 | static int fgetc_interactive(struct in_str *i) | ||
2239 | { | ||
2240 | int ch; | ||
2241 | /* If it's interactive stdin, get new line. */ | ||
2242 | if (G_interactive_fd && i->file == stdin) { | ||
2243 | /* Returns first char (or EOF), the rest is in i->p[] */ | ||
2244 | ch = get_user_input(i); | ||
2245 | i->promptmode = 1; /* PS2 */ | ||
2246 | } else { | ||
2247 | /* Not stdin: script file, sourced file, etc */ | ||
2248 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2249 | } | ||
2250 | return ch; | ||
2251 | } | ||
2252 | #else | ||
2253 | static inline int fgetc_interactive(struct in_str *i) | ||
2254 | { | ||
2255 | int ch; | ||
2256 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2257 | return ch; | ||
2258 | } | ||
2259 | #endif /* INTERACTIVE */ | ||
2260 | |||
2113 | static int FAST_FUNC file_get(struct in_str *i) | 2261 | static int FAST_FUNC file_get(struct in_str *i) |
2114 | { | 2262 | { |
2115 | int ch; | 2263 | int ch; |
2116 | 2264 | ||
2117 | /* If there is data waiting, eat it up */ | 2265 | #if ENABLE_FEATURE_EDITING |
2118 | if (i->p && *i->p) { | 2266 | /* This can be stdin, check line editing char[] buffer */ |
2119 | #if ENABLE_HUSH_INTERACTIVE | 2267 | if (i->p && *i->p != '\0') { |
2120 | take_cached: | 2268 | ch = (unsigned char)*i->p++; |
2121 | #endif | 2269 | goto out; |
2122 | ch = *i->p++; | 2270 | } |
2123 | if (i->eof_flag && !*i->p) | ||
2124 | ch = EOF; | ||
2125 | /* note: ch is never NUL */ | ||
2126 | } else { | ||
2127 | /* need to double check i->file because we might be doing something | ||
2128 | * more complicated by now, like sourcing or substituting. */ | ||
2129 | #if ENABLE_HUSH_INTERACTIVE | ||
2130 | if (G_interactive_fd && i->file == stdin) { | ||
2131 | do { | ||
2132 | get_user_input(i); | ||
2133 | } while (!*i->p); /* need non-empty line */ | ||
2134 | i->promptmode = 1; /* PS2 */ | ||
2135 | goto take_cached; | ||
2136 | } | ||
2137 | #endif | 2271 | #endif |
2138 | do ch = fgetc(i->file); while (ch == '\0'); | 2272 | /* peek_buf[] is an int array, not char. Can contain EOF. */ |
2273 | ch = i->peek_buf[0]; | ||
2274 | if (ch != 0) { | ||
2275 | int ch2 = i->peek_buf[1]; | ||
2276 | i->peek_buf[0] = ch2; | ||
2277 | if (ch2 == 0) /* very likely, avoid redundant write */ | ||
2278 | goto out; | ||
2279 | i->peek_buf[1] = 0; | ||
2280 | goto out; | ||
2139 | } | 2281 | } |
2282 | |||
2283 | ch = fgetc_interactive(i); | ||
2284 | out: | ||
2140 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2285 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
2141 | i->last_char = ch; | 2286 | i->last_char = ch; |
2142 | return ch; | 2287 | return ch; |
2143 | } | 2288 | } |
2144 | 2289 | ||
2145 | /* All callers guarantee this routine will never | ||
2146 | * be used right after a newline, so prompting is not needed. | ||
2147 | */ | ||
2148 | static int FAST_FUNC file_peek(struct in_str *i) | 2290 | static int FAST_FUNC file_peek(struct in_str *i) |
2149 | { | 2291 | { |
2150 | int ch; | 2292 | int ch; |
2151 | if (i->p && *i->p) { | 2293 | |
2152 | if (i->eof_flag && !i->p[1]) | 2294 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE |
2153 | return EOF; | 2295 | /* This can be stdin, check line editing char[] buffer */ |
2154 | return *i->p; | 2296 | if (i->p && *i->p != '\0') |
2155 | /* note: ch is never NUL */ | 2297 | return (unsigned char)*i->p; |
2298 | #endif | ||
2299 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2300 | ch = i->peek_buf[0]; | ||
2301 | if (ch != 0) | ||
2302 | return ch; | ||
2303 | |||
2304 | /* Need to get a new char */ | ||
2305 | ch = fgetc_interactive(i); | ||
2306 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | ||
2307 | |||
2308 | /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */ | ||
2309 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE | ||
2310 | if (i->p) { | ||
2311 | i->p -= 1; | ||
2312 | return ch; | ||
2156 | } | 2313 | } |
2157 | do ch = fgetc(i->file); while (ch == '\0'); | 2314 | #endif |
2158 | i->eof_flag = (ch == EOF); | ||
2159 | i->peek_buf[0] = ch; | 2315 | i->peek_buf[0] = ch; |
2160 | i->peek_buf[1] = '\0'; | 2316 | /*i->peek_buf[1] = 0; - already is */ |
2161 | i->p = i->peek_buf; | 2317 | return ch; |
2162 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | 2318 | } |
2319 | |||
2320 | static int FAST_FUNC static_get(struct in_str *i) | ||
2321 | { | ||
2322 | int ch = (unsigned char)*i->p; | ||
2323 | if (ch != '\0') { | ||
2324 | i->p++; | ||
2325 | i->last_char = ch; | ||
2326 | return ch; | ||
2327 | } | ||
2328 | return EOF; | ||
2329 | } | ||
2330 | |||
2331 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2332 | { | ||
2333 | /* Doesn't report EOF on NUL. None of the callers care. */ | ||
2334 | return (unsigned char)*i->p; | ||
2335 | } | ||
2336 | |||
2337 | /* Only ever called if i_peek() was called, and did not return EOF. | ||
2338 | * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, | ||
2339 | * not end-of-line. Therefore we never need to read a new editing line here. | ||
2340 | */ | ||
2341 | static int i_peek2(struct in_str *i) | ||
2342 | { | ||
2343 | int ch; | ||
2344 | |||
2345 | /* There are two cases when i->p[] buffer exists. | ||
2346 | * (1) it's a string in_str. | ||
2347 | * (2) It's a file, and we have a saved line editing buffer. | ||
2348 | * In both cases, we know that i->p[0] exists and not NUL, and | ||
2349 | * the peek2 result is in i->p[1]. | ||
2350 | */ | ||
2351 | if (i->p) | ||
2352 | return (unsigned char)i->p[1]; | ||
2353 | |||
2354 | /* Now we know it is a file-based in_str. */ | ||
2355 | |||
2356 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2357 | /* Is there 2nd char? */ | ||
2358 | ch = i->peek_buf[1]; | ||
2359 | if (ch == 0) { | ||
2360 | /* We did not read it yet, get it now */ | ||
2361 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2362 | i->peek_buf[1] = ch; | ||
2363 | } | ||
2364 | |||
2365 | debug_printf("file_peek2: got '%c' %d\n", ch, ch); | ||
2163 | return ch; | 2366 | return ch; |
2164 | } | 2367 | } |
2165 | 2368 | ||
2166 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2369 | static void setup_file_in_str(struct in_str *i, FILE *f) |
2167 | { | 2370 | { |
2168 | memset(i, 0, sizeof(*i)); | 2371 | memset(i, 0, sizeof(*i)); |
2169 | i->peek = file_peek; | ||
2170 | i->get = file_get; | 2372 | i->get = file_get; |
2373 | i->peek = file_peek; | ||
2171 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2374 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2172 | i->file = f; | 2375 | i->file = f; |
2173 | /* i->p = NULL; */ | 2376 | /* i->p = NULL; */ |
@@ -2176,11 +2379,10 @@ static void setup_file_in_str(struct in_str *i, FILE *f) | |||
2176 | static void setup_string_in_str(struct in_str *i, const char *s) | 2379 | static void setup_string_in_str(struct in_str *i, const char *s) |
2177 | { | 2380 | { |
2178 | memset(i, 0, sizeof(*i)); | 2381 | memset(i, 0, sizeof(*i)); |
2179 | i->peek = static_peek; | ||
2180 | i->get = static_get; | 2382 | i->get = static_get; |
2383 | i->peek = static_peek; | ||
2181 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2384 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2182 | i->p = s; | 2385 | i->p = s; |
2183 | /* i->eof_flag = 0; */ | ||
2184 | } | 2386 | } |
2185 | 2387 | ||
2186 | 2388 | ||
@@ -2211,7 +2413,7 @@ static ALWAYS_INLINE void o_free_unsafe(o_string *o) | |||
2211 | static void o_grow_by(o_string *o, int len) | 2413 | static void o_grow_by(o_string *o, int len) |
2212 | { | 2414 | { |
2213 | if (o->length + len > o->maxlen) { | 2415 | if (o->length + len > o->maxlen) { |
2214 | o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); | 2416 | o->maxlen += (2 * len) | (B_CHUNK-1); |
2215 | o->data = xrealloc(o->data, 1 + o->maxlen); | 2417 | o->data = xrealloc(o->data, 1 + o->maxlen); |
2216 | } | 2418 | } |
2217 | } | 2419 | } |
@@ -2219,11 +2421,26 @@ static void o_grow_by(o_string *o, int len) | |||
2219 | static void o_addchr(o_string *o, int ch) | 2421 | static void o_addchr(o_string *o, int ch) |
2220 | { | 2422 | { |
2221 | debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); | 2423 | debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); |
2424 | if (o->length < o->maxlen) { | ||
2425 | /* likely. avoid o_grow_by() call */ | ||
2426 | add: | ||
2427 | o->data[o->length] = ch; | ||
2428 | o->length++; | ||
2429 | o->data[o->length] = '\0'; | ||
2430 | return; | ||
2431 | } | ||
2222 | o_grow_by(o, 1); | 2432 | o_grow_by(o, 1); |
2223 | o->data[o->length] = ch; | 2433 | goto add; |
2224 | o->length++; | 2434 | } |
2435 | |||
2436 | #if 0 | ||
2437 | /* Valid only if we know o_string is not empty */ | ||
2438 | static void o_delchr(o_string *o) | ||
2439 | { | ||
2440 | o->length--; | ||
2225 | o->data[o->length] = '\0'; | 2441 | o->data[o->length] = '\0'; |
2226 | } | 2442 | } |
2443 | #endif | ||
2227 | 2444 | ||
2228 | static void o_addblock(o_string *o, const char *str, int len) | 2445 | static void o_addblock(o_string *o, const char *str, int len) |
2229 | { | 2446 | { |
@@ -2937,6 +3154,14 @@ static int done_command(struct parse_context *ctx) | |||
2937 | struct pipe *pi = ctx->pipe; | 3154 | struct pipe *pi = ctx->pipe; |
2938 | struct command *command = ctx->command; | 3155 | struct command *command = ctx->command; |
2939 | 3156 | ||
3157 | #if 0 /* Instead we emit error message at run time */ | ||
3158 | if (ctx->pending_redirect) { | ||
3159 | /* For example, "cmd >" (no filename to redirect to) */ | ||
3160 | die_if_script("syntax error: %s", "invalid redirect"); | ||
3161 | ctx->pending_redirect = NULL; | ||
3162 | } | ||
3163 | #endif | ||
3164 | |||
2940 | if (command) { | 3165 | if (command) { |
2941 | if (IS_NULL_CMD(command)) { | 3166 | if (IS_NULL_CMD(command)) { |
2942 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | 3167 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); |
@@ -3459,6 +3684,12 @@ static int parse_redirect(struct parse_context *ctx, | |||
3459 | debug_printf_parse("duplicating redirect '%d>&%d'\n", | 3684 | debug_printf_parse("duplicating redirect '%d>&%d'\n", |
3460 | redir->rd_fd, redir->rd_dup); | 3685 | redir->rd_fd, redir->rd_dup); |
3461 | } else { | 3686 | } else { |
3687 | #if 0 /* Instead we emit error message at run time */ | ||
3688 | if (ctx->pending_redirect) { | ||
3689 | /* For example, "cmd > <file" */ | ||
3690 | die_if_script("syntax error: %s", "invalid redirect"); | ||
3691 | } | ||
3692 | #endif | ||
3462 | /* Set ctx->pending_redirect, so we know what to do at the | 3693 | /* Set ctx->pending_redirect, so we know what to do at the |
3463 | * end of the next parsed word. */ | 3694 | * end of the next parsed word. */ |
3464 | ctx->pending_redirect = redir; | 3695 | ctx->pending_redirect = redir; |
@@ -3724,6 +3955,39 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
3724 | /* command remains "open", available for possible redirects */ | 3955 | /* command remains "open", available for possible redirects */ |
3725 | } | 3956 | } |
3726 | 3957 | ||
3958 | static int i_getch_and_eat_bkslash_nl(struct in_str *input) | ||
3959 | { | ||
3960 | for (;;) { | ||
3961 | int ch, ch2; | ||
3962 | |||
3963 | ch = i_getch(input); | ||
3964 | if (ch != '\\') | ||
3965 | return ch; | ||
3966 | ch2 = i_peek(input); | ||
3967 | if (ch2 != '\n') | ||
3968 | return ch; | ||
3969 | /* backslash+newline, skip it */ | ||
3970 | i_getch(input); | ||
3971 | } | ||
3972 | } | ||
3973 | |||
3974 | static int i_peek_and_eat_bkslash_nl(struct in_str *input) | ||
3975 | { | ||
3976 | for (;;) { | ||
3977 | int ch, ch2; | ||
3978 | |||
3979 | ch = i_peek(input); | ||
3980 | if (ch != '\\') | ||
3981 | return ch; | ||
3982 | ch2 = i_peek2(input); | ||
3983 | if (ch2 != '\n') | ||
3984 | return ch; | ||
3985 | /* backslash+newline, skip it */ | ||
3986 | i_getch(input); | ||
3987 | i_getch(input); | ||
3988 | } | ||
3989 | } | ||
3990 | |||
3727 | #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS | 3991 | #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS |
3728 | /* Subroutines for copying $(...) and `...` things */ | 3992 | /* Subroutines for copying $(...) and `...` things */ |
3729 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); | 3993 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); |
@@ -3841,7 +4105,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
3841 | if (!dbl) | 4105 | if (!dbl) |
3842 | break; | 4106 | break; |
3843 | /* we look for closing )) of $((EXPR)) */ | 4107 | /* we look for closing )) of $((EXPR)) */ |
3844 | if (i_peek(input) == end_ch) { | 4108 | if (i_peek_and_eat_bkslash_nl(input) == end_ch) { |
3845 | i_getch(input); /* eat second ')' */ | 4109 | i_getch(input); /* eat second ')' */ |
3846 | break; | 4110 | break; |
3847 | } | 4111 | } |
@@ -3879,6 +4143,13 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
3879 | syntax_error_unterm_ch(')'); | 4143 | syntax_error_unterm_ch(')'); |
3880 | return 0; | 4144 | return 0; |
3881 | } | 4145 | } |
4146 | #if 0 | ||
4147 | if (ch == '\n') { | ||
4148 | /* "backslash+newline", ignore both */ | ||
4149 | o_delchr(dest); /* undo insertion of '\' */ | ||
4150 | continue; | ||
4151 | } | ||
4152 | #endif | ||
3882 | o_addchr(dest, ch); | 4153 | o_addchr(dest, ch); |
3883 | continue; | 4154 | continue; |
3884 | } | 4155 | } |
@@ -3897,7 +4168,7 @@ static int parse_dollar(o_string *as_string, | |||
3897 | o_string *dest, | 4168 | o_string *dest, |
3898 | struct in_str *input, unsigned char quote_mask) | 4169 | struct in_str *input, unsigned char quote_mask) |
3899 | { | 4170 | { |
3900 | int ch = i_peek(input); /* first character after the $ */ | 4171 | int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ |
3901 | 4172 | ||
3902 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); | 4173 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); |
3903 | if (isalpha(ch)) { | 4174 | if (isalpha(ch)) { |
@@ -3909,9 +4180,11 @@ static int parse_dollar(o_string *as_string, | |||
3909 | debug_printf_parse(": '%c'\n", ch); | 4180 | debug_printf_parse(": '%c'\n", ch); |
3910 | o_addchr(dest, ch | quote_mask); | 4181 | o_addchr(dest, ch | quote_mask); |
3911 | quote_mask = 0; | 4182 | quote_mask = 0; |
3912 | ch = i_peek(input); | 4183 | ch = i_peek_and_eat_bkslash_nl(input); |
3913 | if (!isalnum(ch) && ch != '_') | 4184 | if (!isalnum(ch) && ch != '_') { |
4185 | /* End of variable name reached */ | ||
3914 | break; | 4186 | break; |
4187 | } | ||
3915 | ch = i_getch(input); | 4188 | ch = i_getch(input); |
3916 | nommu_addchr(as_string, ch); | 4189 | nommu_addchr(as_string, ch); |
3917 | } | 4190 | } |
@@ -3938,7 +4211,7 @@ static int parse_dollar(o_string *as_string, | |||
3938 | ch = i_getch(input); /* eat '{' */ | 4211 | ch = i_getch(input); /* eat '{' */ |
3939 | nommu_addchr(as_string, ch); | 4212 | nommu_addchr(as_string, ch); |
3940 | 4213 | ||
3941 | ch = i_getch(input); /* first char after '{' */ | 4214 | ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */ |
3942 | /* It should be ${?}, or ${#var}, | 4215 | /* It should be ${?}, or ${#var}, |
3943 | * or even ${?+subst} - operator acting on a special variable, | 4216 | * or even ${?+subst} - operator acting on a special variable, |
3944 | * or the beginning of variable name. | 4217 | * or the beginning of variable name. |
@@ -4044,7 +4317,7 @@ static int parse_dollar(o_string *as_string, | |||
4044 | ch = i_getch(input); | 4317 | ch = i_getch(input); |
4045 | nommu_addchr(as_string, ch); | 4318 | nommu_addchr(as_string, ch); |
4046 | # if ENABLE_SH_MATH_SUPPORT | 4319 | # if ENABLE_SH_MATH_SUPPORT |
4047 | if (i_peek(input) == '(') { | 4320 | if (i_peek_and_eat_bkslash_nl(input) == '(') { |
4048 | ch = i_getch(input); | 4321 | ch = i_getch(input); |
4049 | nommu_addchr(as_string, ch); | 4322 | nommu_addchr(as_string, ch); |
4050 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4323 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
@@ -4081,7 +4354,7 @@ static int parse_dollar(o_string *as_string, | |||
4081 | case '_': | 4354 | case '_': |
4082 | ch = i_getch(input); | 4355 | ch = i_getch(input); |
4083 | nommu_addchr(as_string, ch); | 4356 | nommu_addchr(as_string, ch); |
4084 | ch = i_peek(input); | 4357 | ch = i_peek_and_eat_bkslash_nl(input); |
4085 | if (isalnum(ch)) { /* it's $_name or $_123 */ | 4358 | if (isalnum(ch)) { /* it's $_name or $_123 */ |
4086 | ch = '_'; | 4359 | ch = '_'; |
4087 | goto make_var; | 4360 | goto make_var; |
@@ -5548,7 +5821,7 @@ static char* expand_strvec_to_string(char **argv) | |||
5548 | n++; | 5821 | n++; |
5549 | } | 5822 | } |
5550 | } | 5823 | } |
5551 | overlapping_strcpy((char*)list, list[0]); | 5824 | overlapping_strcpy((char*)list, list[0] ? list[0] : ""); |
5552 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | 5825 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); |
5553 | return (char*)list; | 5826 | return (char*)list; |
5554 | } | 5827 | } |
@@ -5825,10 +6098,8 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) | |||
5825 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); | 6098 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); |
5826 | run_and_free_list(pipe_list); | 6099 | run_and_free_list(pipe_list); |
5827 | empty = 0; | 6100 | empty = 0; |
5828 | #if ENABLE_HUSH_FUNCTIONS | 6101 | if (G_flag_return_in_progress == 1) |
5829 | if (G.flag_return_in_progress == 1) | ||
5830 | break; | 6102 | break; |
5831 | #endif | ||
5832 | } | 6103 | } |
5833 | } | 6104 | } |
5834 | 6105 | ||
@@ -5913,7 +6184,8 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
5913 | ) { | 6184 | ) { |
5914 | static const char *const argv[] = { NULL, NULL }; | 6185 | static const char *const argv[] = { NULL, NULL }; |
5915 | builtin_trap((char**)argv); | 6186 | builtin_trap((char**)argv); |
5916 | exit(0); /* not _exit() - we need to fflush */ | 6187 | fflush_all(); /* important */ |
6188 | _exit(0); | ||
5917 | } | 6189 | } |
5918 | # if BB_MMU | 6190 | # if BB_MMU |
5919 | reset_traps_to_defaults(); | 6191 | reset_traps_to_defaults(); |
@@ -5946,8 +6218,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
5946 | free(to_free); | 6218 | free(to_free); |
5947 | # endif | 6219 | # endif |
5948 | close(channel[1]); | 6220 | close(channel[1]); |
5949 | close_on_exec_on(channel[0]); | 6221 | return remember_FILE(xfdopen_for_read(channel[0])); |
5950 | return xfdopen_for_read(channel[0]); | ||
5951 | } | 6222 | } |
5952 | 6223 | ||
5953 | /* Return code is exit status of the process that is run. */ | 6224 | /* Return code is exit status of the process that is run. */ |
@@ -5976,7 +6247,7 @@ static int process_command_subs(o_string *dest, const char *s) | |||
5976 | } | 6247 | } |
5977 | 6248 | ||
5978 | debug_printf("done reading from `cmd` pipe, closing it\n"); | 6249 | debug_printf("done reading from `cmd` pipe, closing it\n"); |
5979 | fclose(fp); | 6250 | fclose_and_forget(fp); |
5980 | /* We need to extract exitcode. Test case | 6251 | /* We need to extract exitcode. Test case |
5981 | * "true; echo `sleep 1; false` $?" | 6252 | * "true; echo `sleep 1; false` $?" |
5982 | * should print 1 */ | 6253 | * should print 1 */ |
@@ -6068,6 +6339,74 @@ static void setup_heredoc(struct redir_struct *redir) | |||
6068 | wait(NULL); /* wait till child has died */ | 6339 | wait(NULL); /* wait till child has died */ |
6069 | } | 6340 | } |
6070 | 6341 | ||
6342 | /* fd: redirect wants this fd to be used (e.g. 3>file). | ||
6343 | * Move all conflicting internally used fds, | ||
6344 | * and remember them so that we can restore them later. | ||
6345 | */ | ||
6346 | static int save_fds_on_redirect(int fd, int squirrel[3]) | ||
6347 | { | ||
6348 | if (squirrel) { | ||
6349 | /* Handle redirects of fds 0,1,2 */ | ||
6350 | |||
6351 | /* If we collide with an already moved stdio fd... */ | ||
6352 | if (fd == squirrel[0]) { | ||
6353 | squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD); | ||
6354 | return 1; | ||
6355 | } | ||
6356 | if (fd == squirrel[1]) { | ||
6357 | squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD); | ||
6358 | return 1; | ||
6359 | } | ||
6360 | if (fd == squirrel[2]) { | ||
6361 | squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD); | ||
6362 | return 1; | ||
6363 | } | ||
6364 | /* If we are about to redirect stdio fd, and did not yet move it... */ | ||
6365 | if (fd <= 2 && squirrel[fd] < 0) { | ||
6366 | /* We avoid taking stdio fds */ | ||
6367 | squirrel[fd] = fcntl(fd, F_DUPFD, 10); | ||
6368 | if (squirrel[fd] < 0 && errno != EBADF) | ||
6369 | xfunc_die(); | ||
6370 | return 0; /* "we did not close fd" */ | ||
6371 | } | ||
6372 | } | ||
6373 | |||
6374 | #if ENABLE_HUSH_INTERACTIVE | ||
6375 | if (fd != 0 && fd == G.interactive_fd) { | ||
6376 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC); | ||
6377 | return 1; | ||
6378 | } | ||
6379 | #endif | ||
6380 | |||
6381 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: | ||
6382 | * (1) Redirect in a forked child. No need to save FILEs' fds, | ||
6383 | * we aren't going to use them anymore, ok to trash. | ||
6384 | * (2) "exec 3>FILE". Bummer. We can save FILEs' fds, | ||
6385 | * but how are we doing to use them? | ||
6386 | * "fileno(fd) = new_fd" can't be done. | ||
6387 | */ | ||
6388 | if (!squirrel) | ||
6389 | return 0; | ||
6390 | |||
6391 | return save_FILEs_on_redirect(fd); | ||
6392 | } | ||
6393 | |||
6394 | static void restore_redirects(int squirrel[3]) | ||
6395 | { | ||
6396 | int i, fd; | ||
6397 | for (i = 0; i <= 2; i++) { | ||
6398 | fd = squirrel[i]; | ||
6399 | if (fd != -1) { | ||
6400 | /* We simply die on error */ | ||
6401 | xmove_fd(fd, i); | ||
6402 | } | ||
6403 | } | ||
6404 | |||
6405 | /* Moved G.interactive_fd stays on new fd, not doing anything for it */ | ||
6406 | |||
6407 | restore_redirected_FILEs(); | ||
6408 | } | ||
6409 | |||
6071 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 6410 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
6072 | * and stderr if they are redirected. */ | 6411 | * and stderr if they are redirected. */ |
6073 | static int setup_redirects(struct command *prog, int squirrel[]) | 6412 | static int setup_redirects(struct command *prog, int squirrel[]) |
@@ -6077,12 +6416,8 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6077 | 6416 | ||
6078 | for (redir = prog->redirects; redir; redir = redir->next) { | 6417 | for (redir = prog->redirects; redir; redir = redir->next) { |
6079 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 6418 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
6080 | /* rd_fd<<HERE case */ | 6419 | /* "rd_fd<<HERE" case */ |
6081 | if (squirrel && redir->rd_fd < 3 | 6420 | save_fds_on_redirect(redir->rd_fd, squirrel); |
6082 | && squirrel[redir->rd_fd] < 0 | ||
6083 | ) { | ||
6084 | squirrel[redir->rd_fd] = dup(redir->rd_fd); | ||
6085 | } | ||
6086 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 6421 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
6087 | * of the heredoc */ | 6422 | * of the heredoc */ |
6088 | debug_printf_parse("set heredoc '%s'\n", | 6423 | debug_printf_parse("set heredoc '%s'\n", |
@@ -6092,12 +6427,15 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6092 | } | 6427 | } |
6093 | 6428 | ||
6094 | if (redir->rd_dup == REDIRFD_TO_FILE) { | 6429 | if (redir->rd_dup == REDIRFD_TO_FILE) { |
6095 | /* rd_fd<*>file case (<*> is <,>,>>,<>) */ | 6430 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ |
6096 | char *p; | 6431 | char *p; |
6097 | if (redir->rd_filename == NULL) { | 6432 | if (redir->rd_filename == NULL) { |
6098 | /* Something went wrong in the parse. | 6433 | /* |
6099 | * Pretend it didn't happen */ | 6434 | * Examples: |
6100 | bb_error_msg("bug in redirect parse"); | 6435 | * "cmd >" (no filename) |
6436 | * "cmd > <file" (2nd redirect starts too early) | ||
6437 | */ | ||
6438 | die_if_script("syntax error: %s", "invalid redirect"); | ||
6101 | continue; | 6439 | continue; |
6102 | } | 6440 | } |
6103 | mode = redir_table[redir->rd_type].mode; | 6441 | mode = redir_table[redir->rd_type].mode; |
@@ -6105,47 +6443,39 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6105 | openfd = open_or_warn(p, mode); | 6443 | openfd = open_or_warn(p, mode); |
6106 | free(p); | 6444 | free(p); |
6107 | if (openfd < 0) { | 6445 | if (openfd < 0) { |
6108 | /* this could get lost if stderr has been redirected, but | 6446 | /* Error message from open_or_warn can be lost |
6109 | * bash and ash both lose it as well (though zsh doesn't!) */ | 6447 | * if stderr has been redirected, but bash |
6110 | //what the above comment tries to say? | 6448 | * and ash both lose it as well |
6449 | * (though zsh doesn't!) | ||
6450 | */ | ||
6111 | return 1; | 6451 | return 1; |
6112 | } | 6452 | } |
6113 | } else { | 6453 | } else { |
6114 | /* rd_fd<*>rd_dup or rd_fd<*>- cases */ | 6454 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ |
6115 | openfd = redir->rd_dup; | 6455 | openfd = redir->rd_dup; |
6116 | } | 6456 | } |
6117 | 6457 | ||
6118 | if (openfd != redir->rd_fd) { | 6458 | if (openfd != redir->rd_fd) { |
6119 | if (squirrel && redir->rd_fd < 3 | 6459 | int closed = save_fds_on_redirect(redir->rd_fd, squirrel); |
6120 | && squirrel[redir->rd_fd] < 0 | ||
6121 | ) { | ||
6122 | squirrel[redir->rd_fd] = dup(redir->rd_fd); | ||
6123 | } | ||
6124 | if (openfd == REDIRFD_CLOSE) { | 6460 | if (openfd == REDIRFD_CLOSE) { |
6125 | /* "n>-" means "close me" */ | 6461 | /* "rd_fd >&-" means "close me" */ |
6126 | close(redir->rd_fd); | 6462 | if (!closed) { |
6463 | /* ^^^ optimization: saving may already | ||
6464 | * have closed it. If not... */ | ||
6465 | close(redir->rd_fd); | ||
6466 | } | ||
6127 | } else { | 6467 | } else { |
6128 | xdup2(openfd, redir->rd_fd); | 6468 | xdup2(openfd, redir->rd_fd); |
6129 | if (redir->rd_dup == REDIRFD_TO_FILE) | 6469 | if (redir->rd_dup == REDIRFD_TO_FILE) |
6470 | /* "rd_fd > FILE" */ | ||
6130 | close(openfd); | 6471 | close(openfd); |
6472 | /* else: "rd_fd > rd_dup" */ | ||
6131 | } | 6473 | } |
6132 | } | 6474 | } |
6133 | } | 6475 | } |
6134 | return 0; | 6476 | return 0; |
6135 | } | 6477 | } |
6136 | 6478 | ||
6137 | static void restore_redirects(int squirrel[]) | ||
6138 | { | ||
6139 | int i, fd; | ||
6140 | for (i = 0; i < 3; i++) { | ||
6141 | fd = squirrel[i]; | ||
6142 | if (fd != -1) { | ||
6143 | /* We simply die on error */ | ||
6144 | xmove_fd(fd, i); | ||
6145 | } | ||
6146 | } | ||
6147 | } | ||
6148 | |||
6149 | static char *find_in_path(const char *arg) | 6479 | static char *find_in_path(const char *arg) |
6150 | { | 6480 | { |
6151 | char *ret = NULL; | 6481 | char *ret = NULL; |
@@ -6329,8 +6659,8 @@ static int run_function(const struct function *funcp, char **argv) | |||
6329 | save_and_replace_G_args(&sv, argv); | 6659 | save_and_replace_G_args(&sv, argv); |
6330 | 6660 | ||
6331 | /* "we are in function, ok to use return" */ | 6661 | /* "we are in function, ok to use return" */ |
6332 | sv_flg = G.flag_return_in_progress; | 6662 | sv_flg = G_flag_return_in_progress; |
6333 | G.flag_return_in_progress = -1; | 6663 | G_flag_return_in_progress = -1; |
6334 | # if ENABLE_HUSH_LOCAL | 6664 | # if ENABLE_HUSH_LOCAL |
6335 | G.func_nest_level++; | 6665 | G.func_nest_level++; |
6336 | # endif | 6666 | # endif |
@@ -6371,7 +6701,7 @@ static int run_function(const struct function *funcp, char **argv) | |||
6371 | G.func_nest_level--; | 6701 | G.func_nest_level--; |
6372 | } | 6702 | } |
6373 | # endif | 6703 | # endif |
6374 | G.flag_return_in_progress = sv_flg; | 6704 | G_flag_return_in_progress = sv_flg; |
6375 | 6705 | ||
6376 | restore_G_args(&sv, argv); | 6706 | restore_G_args(&sv, argv); |
6377 | 6707 | ||
@@ -6417,13 +6747,17 @@ static void exec_builtin(char ***to_free, | |||
6417 | static void execvp_or_die(char **argv) NORETURN; | 6747 | static void execvp_or_die(char **argv) NORETURN; |
6418 | static void execvp_or_die(char **argv) | 6748 | static void execvp_or_die(char **argv) |
6419 | { | 6749 | { |
6750 | int e; | ||
6420 | debug_printf_exec("execing '%s'\n", argv[0]); | 6751 | debug_printf_exec("execing '%s'\n", argv[0]); |
6421 | /* Don't propagate SIG_IGN to the child */ | 6752 | /* Don't propagate SIG_IGN to the child */ |
6422 | if (SPECIAL_JOBSTOP_SIGS != 0) | 6753 | if (SPECIAL_JOBSTOP_SIGS != 0) |
6423 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | 6754 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); |
6424 | execvp(argv[0], argv); | 6755 | execvp(argv[0], argv); |
6756 | e = 2; | ||
6757 | if (errno == EACCES) e = 126; | ||
6758 | if (errno == ENOENT) e = 127; | ||
6425 | bb_perror_msg("can't execute '%s'", argv[0]); | 6759 | bb_perror_msg("can't execute '%s'", argv[0]); |
6426 | _exit(127); /* bash compat */ | 6760 | _exit(e); |
6427 | } | 6761 | } |
6428 | 6762 | ||
6429 | #if ENABLE_HUSH_MODE_X | 6763 | #if ENABLE_HUSH_MODE_X |
@@ -6466,7 +6800,8 @@ static void dump_cmd_in_x_mode(char **argv) | |||
6466 | * Never returns. | 6800 | * Never returns. |
6467 | * Don't exit() here. If you don't exec, use _exit instead. | 6801 | * Don't exit() here. If you don't exec, use _exit instead. |
6468 | * The at_exit handlers apparently confuse the calling process, | 6802 | * The at_exit handlers apparently confuse the calling process, |
6469 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ | 6803 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) |
6804 | */ | ||
6470 | static void pseudo_exec_argv(nommu_save_t *nommu_save, | 6805 | static void pseudo_exec_argv(nommu_save_t *nommu_save, |
6471 | char **argv, int assignment_cnt, | 6806 | char **argv, int assignment_cnt, |
6472 | char **argv_expanded) NORETURN; | 6807 | char **argv_expanded) NORETURN; |
@@ -6546,6 +6881,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
6546 | if (a >= 0) { | 6881 | if (a >= 0) { |
6547 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 6882 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
6548 | if (APPLET_IS_NOEXEC(a)) { | 6883 | if (APPLET_IS_NOEXEC(a)) { |
6884 | /* Do not leak open fds from opened script files etc */ | ||
6885 | close_all_FILE_list(); | ||
6549 | debug_printf_exec("running applet '%s'\n", argv[0]); | 6886 | debug_printf_exec("running applet '%s'\n", argv[0]); |
6550 | run_applet_no_and_exit(a, argv); | 6887 | run_applet_no_and_exit(a, argv); |
6551 | } | 6888 | } |
@@ -7121,6 +7458,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7121 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { | 7458 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { |
7122 | debug_printf("exec with redirects only\n"); | 7459 | debug_printf("exec with redirects only\n"); |
7123 | rcode = setup_redirects(command, NULL); | 7460 | rcode = setup_redirects(command, NULL); |
7461 | /* rcode=1 can be if redir file can't be opened */ | ||
7124 | goto clean_up_and_ret1; | 7462 | goto clean_up_and_ret1; |
7125 | } | 7463 | } |
7126 | } | 7464 | } |
@@ -7247,9 +7585,20 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7247 | if (pipefds.rd > 1) | 7585 | if (pipefds.rd > 1) |
7248 | close(pipefds.rd); | 7586 | close(pipefds.rd); |
7249 | /* Like bash, explicit redirects override pipes, | 7587 | /* Like bash, explicit redirects override pipes, |
7250 | * and the pipe fd is available for dup'ing. */ | 7588 | * and the pipe fd (fd#1) is available for dup'ing: |
7251 | if (setup_redirects(command, NULL)) | 7589 | * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr |
7590 | * of cmd1 goes into pipe. | ||
7591 | */ | ||
7592 | if (setup_redirects(command, NULL)) { | ||
7593 | /* Happens when redir file can't be opened: | ||
7594 | * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ' | ||
7595 | * FOO | ||
7596 | * hush: can't open '/qwe/rty': No such file or directory | ||
7597 | * BAZ | ||
7598 | * (echo BAR is not executed, it hits _exit(1) below) | ||
7599 | */ | ||
7252 | _exit(1); | 7600 | _exit(1); |
7601 | } | ||
7253 | 7602 | ||
7254 | /* Stores to nommu_save list of env vars putenv'ed | 7603 | /* Stores to nommu_save list of env vars putenv'ed |
7255 | * (NOMMU, on MMU we don't need that) */ | 7604 | * (NOMMU, on MMU we don't need that) */ |
@@ -7380,6 +7729,8 @@ static int run_list(struct pipe *pi) | |||
7380 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { | 7729 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { |
7381 | if (G.flag_SIGINT) | 7730 | if (G.flag_SIGINT) |
7382 | break; | 7731 | break; |
7732 | if (G_flag_return_in_progress == 1) | ||
7733 | break; | ||
7383 | 7734 | ||
7384 | IF_HAS_KEYWORDS(rword = pi->res_word;) | 7735 | IF_HAS_KEYWORDS(rword = pi->res_word;) |
7385 | debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", | 7736 | debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", |
@@ -7550,12 +7901,10 @@ static int run_list(struct pipe *pi) | |||
7550 | continue; | 7901 | continue; |
7551 | } | 7902 | } |
7552 | #endif | 7903 | #endif |
7553 | #if ENABLE_HUSH_FUNCTIONS | 7904 | if (G_flag_return_in_progress == 1) { |
7554 | if (G.flag_return_in_progress == 1) { | ||
7555 | checkjobs(NULL); | 7905 | checkjobs(NULL); |
7556 | break; | 7906 | break; |
7557 | } | 7907 | } |
7558 | #endif | ||
7559 | } else if (pi->followup == PIPE_BG) { | 7908 | } else if (pi->followup == PIPE_BG) { |
7560 | /* What does bash do with attempts to background builtins? */ | 7909 | /* What does bash do with attempts to background builtins? */ |
7561 | /* even bash 3.2 doesn't do that well with nested bg: | 7910 | /* even bash 3.2 doesn't do that well with nested bg: |
@@ -7787,6 +8136,7 @@ int hush_main(int argc, char **argv) | |||
7787 | INIT_G(); | 8136 | INIT_G(); |
7788 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ | 8137 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ |
7789 | G.last_exitcode = EXIT_SUCCESS; | 8138 | G.last_exitcode = EXIT_SUCCESS; |
8139 | |||
7790 | #if ENABLE_HUSH_FAST | 8140 | #if ENABLE_HUSH_FAST |
7791 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | 8141 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ |
7792 | #endif | 8142 | #endif |
@@ -7876,7 +8226,7 @@ int hush_main(int argc, char **argv) | |||
7876 | /* Initialize some more globals to non-zero values */ | 8226 | /* Initialize some more globals to non-zero values */ |
7877 | cmdedit_update_prompt(); | 8227 | cmdedit_update_prompt(); |
7878 | 8228 | ||
7879 | die_func = xfunc_has_died; | 8229 | die_func = restore_ttypgrp_and__exit; |
7880 | 8230 | ||
7881 | /* Shell is non-interactive at first. We need to call | 8231 | /* Shell is non-interactive at first. We need to call |
7882 | * install_special_sighandlers() if we are going to execute "sh <script>", | 8232 | * install_special_sighandlers() if we are going to execute "sh <script>", |
@@ -8035,10 +8385,10 @@ int hush_main(int argc, char **argv) | |||
8035 | debug_printf("sourcing /etc/profile\n"); | 8385 | debug_printf("sourcing /etc/profile\n"); |
8036 | input = fopen_for_read("/etc/profile"); | 8386 | input = fopen_for_read("/etc/profile"); |
8037 | if (input != NULL) { | 8387 | if (input != NULL) { |
8038 | close_on_exec_on(fileno(input)); | 8388 | remember_FILE(input); |
8039 | install_special_sighandlers(); | 8389 | install_special_sighandlers(); |
8040 | parse_and_run_file(input); | 8390 | parse_and_run_file(input); |
8041 | fclose(input); | 8391 | fclose_and_forget(input); |
8042 | } | 8392 | } |
8043 | /* bash: after sourcing /etc/profile, | 8393 | /* bash: after sourcing /etc/profile, |
8044 | * tries to source (in the given order): | 8394 | * tries to source (in the given order): |
@@ -8060,11 +8410,11 @@ int hush_main(int argc, char **argv) | |||
8060 | G.global_argv++; | 8410 | G.global_argv++; |
8061 | debug_printf("running script '%s'\n", G.global_argv[0]); | 8411 | debug_printf("running script '%s'\n", G.global_argv[0]); |
8062 | input = xfopen_for_read(G.global_argv[0]); | 8412 | input = xfopen_for_read(G.global_argv[0]); |
8063 | close_on_exec_on(fileno(input)); | 8413 | remember_FILE(input); |
8064 | install_special_sighandlers(); | 8414 | install_special_sighandlers(); |
8065 | parse_and_run_file(input); | 8415 | parse_and_run_file(input); |
8066 | #if ENABLE_FEATURE_CLEAN_UP | 8416 | #if ENABLE_FEATURE_CLEAN_UP |
8067 | fclose(input); | 8417 | fclose_and_forget(input); |
8068 | #endif | 8418 | #endif |
8069 | goto final_return; | 8419 | goto final_return; |
8070 | } | 8420 | } |
@@ -8205,7 +8555,7 @@ int hush_main(int argc, char **argv) | |||
8205 | int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 8555 | int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
8206 | int msh_main(int argc, char **argv) | 8556 | int msh_main(int argc, char **argv) |
8207 | { | 8557 | { |
8208 | //bb_error_msg("msh is deprecated, please use hush instead"); | 8558 | bb_error_msg("msh is deprecated, please use hush instead"); |
8209 | return hush_main(argc, argv); | 8559 | return hush_main(argc, argv); |
8210 | } | 8560 | } |
8211 | #endif | 8561 | #endif |
@@ -8399,6 +8749,14 @@ static void helper_export_local(char **argv, int exp, int lvl) | |||
8399 | continue; | 8749 | continue; |
8400 | } | 8750 | } |
8401 | } | 8751 | } |
8752 | #if ENABLE_HUSH_LOCAL | ||
8753 | if (exp == 0 /* local? */ | ||
8754 | && var && var->func_nest_level == lvl | ||
8755 | ) { | ||
8756 | /* "local x=abc; ...; local x" - ignore second local decl */ | ||
8757 | continue; | ||
8758 | } | ||
8759 | #endif | ||
8402 | /* Exporting non-existing variable. | 8760 | /* Exporting non-existing variable. |
8403 | * bash does not put it in environment, | 8761 | * bash does not put it in environment, |
8404 | * but remembers that it is exported, | 8762 | * but remembers that it is exported, |
@@ -8473,6 +8831,212 @@ static int FAST_FUNC builtin_local(char **argv) | |||
8473 | } | 8831 | } |
8474 | #endif | 8832 | #endif |
8475 | 8833 | ||
8834 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ | ||
8835 | static int FAST_FUNC builtin_unset(char **argv) | ||
8836 | { | ||
8837 | int ret; | ||
8838 | unsigned opts; | ||
8839 | |||
8840 | /* "!": do not abort on errors */ | ||
8841 | /* "+": stop at 1st non-option */ | ||
8842 | opts = getopt32(argv, "!+vf"); | ||
8843 | if (opts == (unsigned)-1) | ||
8844 | return EXIT_FAILURE; | ||
8845 | if (opts == 3) { | ||
8846 | bb_error_msg("unset: -v and -f are exclusive"); | ||
8847 | return EXIT_FAILURE; | ||
8848 | } | ||
8849 | argv += optind; | ||
8850 | |||
8851 | ret = EXIT_SUCCESS; | ||
8852 | while (*argv) { | ||
8853 | if (!(opts & 2)) { /* not -f */ | ||
8854 | if (unset_local_var(*argv)) { | ||
8855 | /* unset <nonexistent_var> doesn't fail. | ||
8856 | * Error is when one tries to unset RO var. | ||
8857 | * Message was printed by unset_local_var. */ | ||
8858 | ret = EXIT_FAILURE; | ||
8859 | } | ||
8860 | } | ||
8861 | #if ENABLE_HUSH_FUNCTIONS | ||
8862 | else { | ||
8863 | unset_func(*argv); | ||
8864 | } | ||
8865 | #endif | ||
8866 | argv++; | ||
8867 | } | ||
8868 | return ret; | ||
8869 | } | ||
8870 | |||
8871 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set | ||
8872 | * built-in 'set' handler | ||
8873 | * SUSv3 says: | ||
8874 | * set [-abCefhmnuvx] [-o option] [argument...] | ||
8875 | * set [+abCefhmnuvx] [+o option] [argument...] | ||
8876 | * set -- [argument...] | ||
8877 | * set -o | ||
8878 | * set +o | ||
8879 | * Implementations shall support the options in both their hyphen and | ||
8880 | * plus-sign forms. These options can also be specified as options to sh. | ||
8881 | * Examples: | ||
8882 | * Write out all variables and their values: set | ||
8883 | * Set $1, $2, and $3 and set "$#" to 3: set c a b | ||
8884 | * Turn on the -x and -v options: set -xv | ||
8885 | * Unset all positional parameters: set -- | ||
8886 | * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" | ||
8887 | * Set the positional parameters to the expansion of x, even if x expands | ||
8888 | * with a leading '-' or '+': set -- $x | ||
8889 | * | ||
8890 | * So far, we only support "set -- [argument...]" and some of the short names. | ||
8891 | */ | ||
8892 | static int FAST_FUNC builtin_set(char **argv) | ||
8893 | { | ||
8894 | int n; | ||
8895 | char **pp, **g_argv; | ||
8896 | char *arg = *++argv; | ||
8897 | |||
8898 | if (arg == NULL) { | ||
8899 | struct variable *e; | ||
8900 | for (e = G.top_var; e; e = e->next) | ||
8901 | puts(e->varstr); | ||
8902 | return EXIT_SUCCESS; | ||
8903 | } | ||
8904 | |||
8905 | do { | ||
8906 | if (strcmp(arg, "--") == 0) { | ||
8907 | ++argv; | ||
8908 | goto set_argv; | ||
8909 | } | ||
8910 | if (arg[0] != '+' && arg[0] != '-') | ||
8911 | break; | ||
8912 | for (n = 1; arg[n]; ++n) { | ||
8913 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) | ||
8914 | goto error; | ||
8915 | if (arg[n] == 'o' && argv[1]) | ||
8916 | argv++; | ||
8917 | } | ||
8918 | } while ((arg = *++argv) != NULL); | ||
8919 | /* Now argv[0] is 1st argument */ | ||
8920 | |||
8921 | if (arg == NULL) | ||
8922 | return EXIT_SUCCESS; | ||
8923 | set_argv: | ||
8924 | |||
8925 | /* NB: G.global_argv[0] ($0) is never freed/changed */ | ||
8926 | g_argv = G.global_argv; | ||
8927 | if (G.global_args_malloced) { | ||
8928 | pp = g_argv; | ||
8929 | while (*++pp) | ||
8930 | free(*pp); | ||
8931 | g_argv[1] = NULL; | ||
8932 | } else { | ||
8933 | G.global_args_malloced = 1; | ||
8934 | pp = xzalloc(sizeof(pp[0]) * 2); | ||
8935 | pp[0] = g_argv[0]; /* retain $0 */ | ||
8936 | g_argv = pp; | ||
8937 | } | ||
8938 | /* This realloc's G.global_argv */ | ||
8939 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); | ||
8940 | |||
8941 | n = 1; | ||
8942 | while (*++pp) | ||
8943 | n++; | ||
8944 | G.global_argc = n; | ||
8945 | |||
8946 | return EXIT_SUCCESS; | ||
8947 | |||
8948 | /* Nothing known, so abort */ | ||
8949 | error: | ||
8950 | bb_error_msg("set: %s: invalid option", arg); | ||
8951 | return EXIT_FAILURE; | ||
8952 | } | ||
8953 | |||
8954 | static int FAST_FUNC builtin_shift(char **argv) | ||
8955 | { | ||
8956 | int n = 1; | ||
8957 | argv = skip_dash_dash(argv); | ||
8958 | if (argv[0]) { | ||
8959 | n = atoi(argv[0]); | ||
8960 | } | ||
8961 | if (n >= 0 && n < G.global_argc) { | ||
8962 | if (G.global_args_malloced) { | ||
8963 | int m = 1; | ||
8964 | while (m <= n) | ||
8965 | free(G.global_argv[m++]); | ||
8966 | } | ||
8967 | G.global_argc -= n; | ||
8968 | memmove(&G.global_argv[1], &G.global_argv[n+1], | ||
8969 | G.global_argc * sizeof(G.global_argv[0])); | ||
8970 | return EXIT_SUCCESS; | ||
8971 | } | ||
8972 | return EXIT_FAILURE; | ||
8973 | } | ||
8974 | |||
8975 | /* Interruptibility of read builtin in bash | ||
8976 | * (tested on bash-4.2.8 by sending signals (not by ^C)): | ||
8977 | * | ||
8978 | * Empty trap makes read ignore corresponding signal, for any signal. | ||
8979 | * | ||
8980 | * SIGINT: | ||
8981 | * - terminates non-interactive shell; | ||
8982 | * - interrupts read in interactive shell; | ||
8983 | * if it has non-empty trap: | ||
8984 | * - executes trap and returns to command prompt in interactive shell; | ||
8985 | * - executes trap and returns to read in non-interactive shell; | ||
8986 | * SIGTERM: | ||
8987 | * - is ignored (does not interrupt) read in interactive shell; | ||
8988 | * - terminates non-interactive shell; | ||
8989 | * if it has non-empty trap: | ||
8990 | * - executes trap and returns to read; | ||
8991 | * SIGHUP: | ||
8992 | * - terminates shell (regardless of interactivity); | ||
8993 | * if it has non-empty trap: | ||
8994 | * - executes trap and returns to read; | ||
8995 | */ | ||
8996 | static int FAST_FUNC builtin_read(char **argv) | ||
8997 | { | ||
8998 | const char *r; | ||
8999 | char *opt_n = NULL; | ||
9000 | char *opt_p = NULL; | ||
9001 | char *opt_t = NULL; | ||
9002 | char *opt_u = NULL; | ||
9003 | const char *ifs; | ||
9004 | int read_flags; | ||
9005 | |||
9006 | /* "!": do not abort on errors. | ||
9007 | * Option string must start with "sr" to match BUILTIN_READ_xxx | ||
9008 | */ | ||
9009 | read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); | ||
9010 | if (read_flags == (uint32_t)-1) | ||
9011 | return EXIT_FAILURE; | ||
9012 | argv += optind; | ||
9013 | ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
9014 | |||
9015 | again: | ||
9016 | r = shell_builtin_read(set_local_var_from_halves, | ||
9017 | argv, | ||
9018 | ifs, | ||
9019 | read_flags, | ||
9020 | opt_n, | ||
9021 | opt_p, | ||
9022 | opt_t, | ||
9023 | opt_u | ||
9024 | ); | ||
9025 | |||
9026 | if ((uintptr_t)r == 1 && errno == EINTR) { | ||
9027 | unsigned sig = check_and_run_traps(); | ||
9028 | if (sig && sig != SIGINT) | ||
9029 | goto again; | ||
9030 | } | ||
9031 | |||
9032 | if ((uintptr_t)r > 1) { | ||
9033 | bb_error_msg("%s", r); | ||
9034 | r = (char*)(uintptr_t)1; | ||
9035 | } | ||
9036 | |||
9037 | return (uintptr_t)r; | ||
9038 | } | ||
9039 | |||
8476 | static int FAST_FUNC builtin_trap(char **argv) | 9040 | static int FAST_FUNC builtin_trap(char **argv) |
8477 | { | 9041 | { |
8478 | int sig; | 9042 | int sig; |
@@ -8671,7 +9235,6 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) | |||
8671 | if (x->b_descr) | 9235 | if (x->b_descr) |
8672 | printf("%-10s%s\n", x->b_cmd, x->b_descr); | 9236 | printf("%-10s%s\n", x->b_cmd, x->b_descr); |
8673 | } | 9237 | } |
8674 | bb_putchar('\n'); | ||
8675 | return EXIT_SUCCESS; | 9238 | return EXIT_SUCCESS; |
8676 | } | 9239 | } |
8677 | #endif | 9240 | #endif |
@@ -8721,6 +9284,15 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) | |||
8721 | if (l < (unsigned long)p) l = (unsigned long)p; | 9284 | if (l < (unsigned long)p) l = (unsigned long)p; |
8722 | free(p); | 9285 | free(p); |
8723 | 9286 | ||
9287 | |||
9288 | # if 0 /* debug */ | ||
9289 | { | ||
9290 | struct mallinfo mi = mallinfo(); | ||
9291 | printf("top alloc:0x%lx malloced:%d+%d=%d\n", l, | ||
9292 | mi.arena, mi.hblkhd, mi.arena + mi.hblkhd); | ||
9293 | } | ||
9294 | # endif | ||
9295 | |||
8724 | if (!G.memleak_value) | 9296 | if (!G.memleak_value) |
8725 | G.memleak_value = l; | 9297 | G.memleak_value = l; |
8726 | 9298 | ||
@@ -8742,175 +9314,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | |||
8742 | return EXIT_SUCCESS; | 9314 | return EXIT_SUCCESS; |
8743 | } | 9315 | } |
8744 | 9316 | ||
8745 | /* Interruptibility of read builtin in bash | ||
8746 | * (tested on bash-4.2.8 by sending signals (not by ^C)): | ||
8747 | * | ||
8748 | * Empty trap makes read ignore corresponding signal, for any signal. | ||
8749 | * | ||
8750 | * SIGINT: | ||
8751 | * - terminates non-interactive shell; | ||
8752 | * - interrupts read in interactive shell; | ||
8753 | * if it has non-empty trap: | ||
8754 | * - executes trap and returns to command prompt in interactive shell; | ||
8755 | * - executes trap and returns to read in non-interactive shell; | ||
8756 | * SIGTERM: | ||
8757 | * - is ignored (does not interrupt) read in interactive shell; | ||
8758 | * - terminates non-interactive shell; | ||
8759 | * if it has non-empty trap: | ||
8760 | * - executes trap and returns to read; | ||
8761 | * SIGHUP: | ||
8762 | * - terminates shell (regardless of interactivity); | ||
8763 | * if it has non-empty trap: | ||
8764 | * - executes trap and returns to read; | ||
8765 | */ | ||
8766 | static int FAST_FUNC builtin_read(char **argv) | ||
8767 | { | ||
8768 | const char *r; | ||
8769 | char *opt_n = NULL; | ||
8770 | char *opt_p = NULL; | ||
8771 | char *opt_t = NULL; | ||
8772 | char *opt_u = NULL; | ||
8773 | const char *ifs; | ||
8774 | int read_flags; | ||
8775 | |||
8776 | /* "!": do not abort on errors. | ||
8777 | * Option string must start with "sr" to match BUILTIN_READ_xxx | ||
8778 | */ | ||
8779 | read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); | ||
8780 | if (read_flags == (uint32_t)-1) | ||
8781 | return EXIT_FAILURE; | ||
8782 | argv += optind; | ||
8783 | ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
8784 | |||
8785 | again: | ||
8786 | r = shell_builtin_read(set_local_var_from_halves, | ||
8787 | argv, | ||
8788 | ifs, | ||
8789 | read_flags, | ||
8790 | opt_n, | ||
8791 | opt_p, | ||
8792 | opt_t, | ||
8793 | opt_u | ||
8794 | ); | ||
8795 | |||
8796 | if ((uintptr_t)r == 1 && errno == EINTR) { | ||
8797 | unsigned sig = check_and_run_traps(); | ||
8798 | if (sig && sig != SIGINT) | ||
8799 | goto again; | ||
8800 | } | ||
8801 | |||
8802 | if ((uintptr_t)r > 1) { | ||
8803 | bb_error_msg("%s", r); | ||
8804 | r = (char*)(uintptr_t)1; | ||
8805 | } | ||
8806 | |||
8807 | return (uintptr_t)r; | ||
8808 | } | ||
8809 | |||
8810 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set | ||
8811 | * built-in 'set' handler | ||
8812 | * SUSv3 says: | ||
8813 | * set [-abCefhmnuvx] [-o option] [argument...] | ||
8814 | * set [+abCefhmnuvx] [+o option] [argument...] | ||
8815 | * set -- [argument...] | ||
8816 | * set -o | ||
8817 | * set +o | ||
8818 | * Implementations shall support the options in both their hyphen and | ||
8819 | * plus-sign forms. These options can also be specified as options to sh. | ||
8820 | * Examples: | ||
8821 | * Write out all variables and their values: set | ||
8822 | * Set $1, $2, and $3 and set "$#" to 3: set c a b | ||
8823 | * Turn on the -x and -v options: set -xv | ||
8824 | * Unset all positional parameters: set -- | ||
8825 | * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" | ||
8826 | * Set the positional parameters to the expansion of x, even if x expands | ||
8827 | * with a leading '-' or '+': set -- $x | ||
8828 | * | ||
8829 | * So far, we only support "set -- [argument...]" and some of the short names. | ||
8830 | */ | ||
8831 | static int FAST_FUNC builtin_set(char **argv) | ||
8832 | { | ||
8833 | int n; | ||
8834 | char **pp, **g_argv; | ||
8835 | char *arg = *++argv; | ||
8836 | |||
8837 | if (arg == NULL) { | ||
8838 | struct variable *e; | ||
8839 | for (e = G.top_var; e; e = e->next) | ||
8840 | puts(e->varstr); | ||
8841 | return EXIT_SUCCESS; | ||
8842 | } | ||
8843 | |||
8844 | do { | ||
8845 | if (strcmp(arg, "--") == 0) { | ||
8846 | ++argv; | ||
8847 | goto set_argv; | ||
8848 | } | ||
8849 | if (arg[0] != '+' && arg[0] != '-') | ||
8850 | break; | ||
8851 | for (n = 1; arg[n]; ++n) { | ||
8852 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) | ||
8853 | goto error; | ||
8854 | if (arg[n] == 'o' && argv[1]) | ||
8855 | argv++; | ||
8856 | } | ||
8857 | } while ((arg = *++argv) != NULL); | ||
8858 | /* Now argv[0] is 1st argument */ | ||
8859 | |||
8860 | if (arg == NULL) | ||
8861 | return EXIT_SUCCESS; | ||
8862 | set_argv: | ||
8863 | |||
8864 | /* NB: G.global_argv[0] ($0) is never freed/changed */ | ||
8865 | g_argv = G.global_argv; | ||
8866 | if (G.global_args_malloced) { | ||
8867 | pp = g_argv; | ||
8868 | while (*++pp) | ||
8869 | free(*pp); | ||
8870 | g_argv[1] = NULL; | ||
8871 | } else { | ||
8872 | G.global_args_malloced = 1; | ||
8873 | pp = xzalloc(sizeof(pp[0]) * 2); | ||
8874 | pp[0] = g_argv[0]; /* retain $0 */ | ||
8875 | g_argv = pp; | ||
8876 | } | ||
8877 | /* This realloc's G.global_argv */ | ||
8878 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); | ||
8879 | |||
8880 | n = 1; | ||
8881 | while (*++pp) | ||
8882 | n++; | ||
8883 | G.global_argc = n; | ||
8884 | |||
8885 | return EXIT_SUCCESS; | ||
8886 | |||
8887 | /* Nothing known, so abort */ | ||
8888 | error: | ||
8889 | bb_error_msg("set: %s: invalid option", arg); | ||
8890 | return EXIT_FAILURE; | ||
8891 | } | ||
8892 | |||
8893 | static int FAST_FUNC builtin_shift(char **argv) | ||
8894 | { | ||
8895 | int n = 1; | ||
8896 | argv = skip_dash_dash(argv); | ||
8897 | if (argv[0]) { | ||
8898 | n = atoi(argv[0]); | ||
8899 | } | ||
8900 | if (n >= 0 && n < G.global_argc) { | ||
8901 | if (G.global_args_malloced) { | ||
8902 | int m = 1; | ||
8903 | while (m <= n) | ||
8904 | free(G.global_argv[m++]); | ||
8905 | } | ||
8906 | G.global_argc -= n; | ||
8907 | memmove(&G.global_argv[1], &G.global_argv[n+1], | ||
8908 | G.global_argc * sizeof(G.global_argv[0])); | ||
8909 | return EXIT_SUCCESS; | ||
8910 | } | ||
8911 | return EXIT_FAILURE; | ||
8912 | } | ||
8913 | |||
8914 | static int FAST_FUNC builtin_source(char **argv) | 9317 | static int FAST_FUNC builtin_source(char **argv) |
8915 | { | 9318 | { |
8916 | char *arg_path, *filename; | 9319 | char *arg_path, *filename; |
@@ -8932,7 +9335,7 @@ static int FAST_FUNC builtin_source(char **argv) | |||
8932 | if (arg_path) | 9335 | if (arg_path) |
8933 | filename = arg_path; | 9336 | filename = arg_path; |
8934 | } | 9337 | } |
8935 | input = fopen_or_warn(filename, "r"); | 9338 | input = remember_FILE(fopen_or_warn(filename, "r")); |
8936 | free(arg_path); | 9339 | free(arg_path); |
8937 | if (!input) { | 9340 | if (!input) { |
8938 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ | 9341 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ |
@@ -8941,23 +9344,24 @@ static int FAST_FUNC builtin_source(char **argv) | |||
8941 | */ | 9344 | */ |
8942 | return EXIT_FAILURE; | 9345 | return EXIT_FAILURE; |
8943 | } | 9346 | } |
8944 | close_on_exec_on(fileno(input)); | ||
8945 | 9347 | ||
8946 | #if ENABLE_HUSH_FUNCTIONS | 9348 | #if ENABLE_HUSH_FUNCTIONS |
8947 | sv_flg = G.flag_return_in_progress; | 9349 | sv_flg = G_flag_return_in_progress; |
8948 | /* "we are inside sourced file, ok to use return" */ | 9350 | /* "we are inside sourced file, ok to use return" */ |
8949 | G.flag_return_in_progress = -1; | 9351 | G_flag_return_in_progress = -1; |
8950 | #endif | 9352 | #endif |
8951 | if (argv[1]) | 9353 | if (argv[1]) |
8952 | save_and_replace_G_args(&sv, argv); | 9354 | save_and_replace_G_args(&sv, argv); |
8953 | 9355 | ||
9356 | /* "false; . ./empty_line; echo Zero:$?" should print 0 */ | ||
9357 | G.last_exitcode = 0; | ||
8954 | parse_and_run_file(input); | 9358 | parse_and_run_file(input); |
8955 | fclose(input); | 9359 | fclose_and_forget(input); |
8956 | 9360 | ||
8957 | if (argv[1]) | 9361 | if (argv[1]) |
8958 | restore_G_args(&sv, argv); | 9362 | restore_G_args(&sv, argv); |
8959 | #if ENABLE_HUSH_FUNCTIONS | 9363 | #if ENABLE_HUSH_FUNCTIONS |
8960 | G.flag_return_in_progress = sv_flg; | 9364 | G_flag_return_in_progress = sv_flg; |
8961 | #endif | 9365 | #endif |
8962 | 9366 | ||
8963 | return G.last_exitcode; | 9367 | return G.last_exitcode; |
@@ -9000,43 +9404,6 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
9000 | return !rc; /* rc != 0 - success */ | 9404 | return !rc; /* rc != 0 - success */ |
9001 | } | 9405 | } |
9002 | 9406 | ||
9003 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ | ||
9004 | static int FAST_FUNC builtin_unset(char **argv) | ||
9005 | { | ||
9006 | int ret; | ||
9007 | unsigned opts; | ||
9008 | |||
9009 | /* "!": do not abort on errors */ | ||
9010 | /* "+": stop at 1st non-option */ | ||
9011 | opts = getopt32(argv, "!+vf"); | ||
9012 | if (opts == (unsigned)-1) | ||
9013 | return EXIT_FAILURE; | ||
9014 | if (opts == 3) { | ||
9015 | bb_error_msg("unset: -v and -f are exclusive"); | ||
9016 | return EXIT_FAILURE; | ||
9017 | } | ||
9018 | argv += optind; | ||
9019 | |||
9020 | ret = EXIT_SUCCESS; | ||
9021 | while (*argv) { | ||
9022 | if (!(opts & 2)) { /* not -f */ | ||
9023 | if (unset_local_var(*argv)) { | ||
9024 | /* unset <nonexistent_var> doesn't fail. | ||
9025 | * Error is when one tries to unset RO var. | ||
9026 | * Message was printed by unset_local_var. */ | ||
9027 | ret = EXIT_FAILURE; | ||
9028 | } | ||
9029 | } | ||
9030 | #if ENABLE_HUSH_FUNCTIONS | ||
9031 | else { | ||
9032 | unset_func(*argv); | ||
9033 | } | ||
9034 | #endif | ||
9035 | argv++; | ||
9036 | } | ||
9037 | return ret; | ||
9038 | } | ||
9039 | |||
9040 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ | 9407 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ |
9041 | static int FAST_FUNC builtin_wait(char **argv) | 9408 | static int FAST_FUNC builtin_wait(char **argv) |
9042 | { | 9409 | { |
@@ -9153,9 +9520,11 @@ static int FAST_FUNC builtin_break(char **argv) | |||
9153 | unsigned depth; | 9520 | unsigned depth; |
9154 | if (G.depth_of_loop == 0) { | 9521 | if (G.depth_of_loop == 0) { |
9155 | bb_error_msg("%s: only meaningful in a loop", argv[0]); | 9522 | bb_error_msg("%s: only meaningful in a loop", argv[0]); |
9523 | /* if we came from builtin_continue(), need to undo "= 1" */ | ||
9524 | G.flag_break_continue = 0; | ||
9156 | return EXIT_SUCCESS; /* bash compat */ | 9525 | return EXIT_SUCCESS; /* bash compat */ |
9157 | } | 9526 | } |
9158 | G.flag_break_continue++; /* BC_BREAK = 1 */ | 9527 | G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */ |
9159 | 9528 | ||
9160 | G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); | 9529 | G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); |
9161 | if (depth == UINT_MAX) | 9530 | if (depth == UINT_MAX) |
@@ -9178,12 +9547,12 @@ static int FAST_FUNC builtin_return(char **argv) | |||
9178 | { | 9547 | { |
9179 | int rc; | 9548 | int rc; |
9180 | 9549 | ||
9181 | if (G.flag_return_in_progress != -1) { | 9550 | if (G_flag_return_in_progress != -1) { |
9182 | bb_error_msg("%s: not in a function or sourced script", argv[0]); | 9551 | bb_error_msg("%s: not in a function or sourced script", argv[0]); |
9183 | return EXIT_FAILURE; /* bash compat */ | 9552 | return EXIT_FAILURE; /* bash compat */ |
9184 | } | 9553 | } |
9185 | 9554 | ||
9186 | G.flag_return_in_progress = 1; | 9555 | G_flag_return_in_progress = 1; |
9187 | 9556 | ||
9188 | /* bash: | 9557 | /* bash: |
9189 | * out of range: wraps around at 256, does not error out | 9558 | * out of range: wraps around at 256, does not error out |
diff --git a/shell/hush_test/hush-bugs/var3.right b/shell/hush_test/hush-bugs/var3.right deleted file mode 100644 index 8eb0e3337..000000000 --- a/shell/hush_test/hush-bugs/var3.right +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | 1 | ||
2 | 1 | ||
3 | |||
4 | |||
5 | 0 | ||
diff --git a/shell/hush_test/hush-bugs/var3.tests b/shell/hush_test/hush-bugs/var3.tests deleted file mode 100755 index 97b102cbe..000000000 --- a/shell/hush_test/hush-bugs/var3.tests +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x | ||
diff --git a/shell/hush_test/hush-glob/bash_brace1.tests b/shell/hush_test/hush-glob/bash_brace1.tests index eb2f0e974..eae2e2a84 100755 --- a/shell/hush_test/hush-glob/bash_brace1.tests +++ b/shell/hush_test/hush-glob/bash_brace1.tests | |||
@@ -4,7 +4,7 @@ v='*brace1.t*'; echo $v | |||
4 | # ...but not brace expanded: | 4 | # ...but not brace expanded: |
5 | v='*{b,b}race1.t*'; echo $v | 5 | v='*{b,b}race1.t*'; echo $v |
6 | 6 | ||
7 | # whereas direct brces are expanded: | 7 | # whereas direct braces are expanded: |
8 | echo *{b,b}race1.t* | 8 | echo *{b,b}race1.t* |
9 | 9 | ||
10 | echo Done: $? | 10 | echo Done: $? |
diff --git a/shell/hush_test/hush-glob/glob_dir.right b/shell/hush_test/hush-glob/glob_dir.right new file mode 100644 index 000000000..aa90514d5 --- /dev/null +++ b/shell/hush_test/hush-glob/glob_dir.right | |||
@@ -0,0 +1,19 @@ | |||
1 | dirtest/z.tmp | ||
2 | dirtest/z.tmp | ||
3 | dirtest/z.tmp | ||
4 | dirtest/z.tmp | ||
5 | dirtest/z.tmp | ||
6 | dirtest/z.tmp | ||
7 | dirtest/z.tmp | ||
8 | dirtest/z.tmp | ||
9 | dirtest/z.tmp | ||
10 | |||
11 | */z.tmp | ||
12 | */z.* | ||
13 | */?.* | ||
14 | */z*p | ||
15 | d*r*e*t/z*p | ||
16 | *\/z.tmp | ||
17 | */z.* | ||
18 | */z*p | ||
19 | d*r*e*t/z*p | ||
diff --git a/shell/hush_test/hush-glob/glob_dir.tests b/shell/hush_test/hush-glob/glob_dir.tests new file mode 100755 index 000000000..dc4c4fdb5 --- /dev/null +++ b/shell/hush_test/hush-glob/glob_dir.tests | |||
@@ -0,0 +1,25 @@ | |||
1 | mkdir dirtest | ||
2 | >dirtest/z.tmp | ||
3 | |||
4 | echo */z.tmp | ||
5 | echo */z.* | ||
6 | echo */?.* | ||
7 | echo */z*p | ||
8 | echo d*r*e*t/z*p | ||
9 | echo *"/z.t"mp | ||
10 | echo */z"."* | ||
11 | echo *"/z"*"p" | ||
12 | echo "d"*r*e*t"/"z*p | ||
13 | echo | ||
14 | echo \*/z.tmp | ||
15 | echo "*"/z.* | ||
16 | echo */"?".* | ||
17 | echo */z"*p" | ||
18 | echo d*r*e\*t/z*p | ||
19 | echo *"\\/z.t"mp | ||
20 | echo */z".*" | ||
21 | echo *"/z"\*"p" | ||
22 | echo "d*"r*e*t"/"z*p | ||
23 | |||
24 | rm dirtest/z.tmp | ||
25 | rmdir dirtest | ||
diff --git a/shell/hush_test/hush-misc/heredoc1.right b/shell/hush_test/hush-heredoc/heredoc1.right index 7fc68f3e1..7fc68f3e1 100644 --- a/shell/hush_test/hush-misc/heredoc1.right +++ b/shell/hush_test/hush-heredoc/heredoc1.right | |||
diff --git a/shell/hush_test/hush-misc/heredoc1.tests b/shell/hush_test/hush-heredoc/heredoc1.tests index 2eeb4726b..2eeb4726b 100755 --- a/shell/hush_test/hush-misc/heredoc1.tests +++ b/shell/hush_test/hush-heredoc/heredoc1.tests | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc2.right b/shell/hush_test/hush-heredoc/heredoc2.right new file mode 100644 index 000000000..a486f1ac4 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | bar | ||
2 | bar | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc2.tests b/shell/hush_test/hush-heredoc/heredoc2.tests new file mode 100755 index 000000000..6d9ccb6cc --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc2.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | foo () { | ||
2 | cat <<EOF && { echo "$1" ; } | ||
3 | $1 | ||
4 | EOF | ||
5 | } | ||
6 | |||
7 | foo "bar" | ||
diff --git a/shell/hush_test/hush-misc/heredoc3.right b/shell/hush_test/hush-heredoc/heredoc3.right index 6ed517f74..6ed517f74 100644 --- a/shell/hush_test/hush-misc/heredoc3.right +++ b/shell/hush_test/hush-heredoc/heredoc3.right | |||
diff --git a/shell/hush_test/hush-misc/heredoc3.tests b/shell/hush_test/hush-heredoc/heredoc3.tests index 938577a89..938577a89 100755 --- a/shell/hush_test/hush-misc/heredoc3.tests +++ b/shell/hush_test/hush-heredoc/heredoc3.tests | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc4.right b/shell/hush_test/hush-heredoc/heredoc4.right new file mode 100644 index 000000000..371b092e2 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc4.right | |||
@@ -0,0 +1 @@ | |||
'$' | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc4.tests b/shell/hush_test/hush-heredoc/heredoc4.tests new file mode 100755 index 000000000..642ddb324 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc4.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | cat <<EOF | ||
2 | '$' | ||
3 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc5.right b/shell/hush_test/hush-heredoc/heredoc5.right new file mode 100644 index 000000000..74110e3b5 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc5.right | |||
@@ -0,0 +1,9 @@ | |||
1 | exit EOF-f | ||
2 | " | ||
3 | echo 1 | ||
4 | echo Hello World | ||
5 | moo | ||
6 | EOF-f | ||
7 | EOF-f f | ||
8 | EOF-f | ||
9 | Ok | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc5.tests b/shell/hush_test/hush-heredoc/heredoc5.tests new file mode 100755 index 000000000..e619bded1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc5.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | f=1 | ||
2 | cat <<- EOF-f | ||
3 | exit EOF-f | ||
4 | " | ||
5 | echo $f | ||
6 | echo `echo Hello World` | ||
7 | moo | ||
8 | EOF-f | ||
9 | EOF-f f | ||
10 | EOF-f | ||
11 | EOF-f | ||
12 | echo Ok | ||
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-heredoc/heredoc_backslash1.right index 6a6114821..6a6114821 100644 --- a/shell/hush_test/hush-misc/heredoc_backslash1.right +++ b/shell/hush_test/hush-heredoc/heredoc_backslash1.right | |||
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-heredoc/heredoc_backslash1.tests index 501af5490..501af5490 100755 --- a/shell/hush_test/hush-misc/heredoc_backslash1.tests +++ b/shell/hush_test/hush-heredoc/heredoc_backslash1.tests | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_huge.right b/shell/hush_test/hush-heredoc/heredoc_huge.right new file mode 100644 index 000000000..11740f674 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_huge.right | |||
@@ -0,0 +1,3 @@ | |||
1 | 546ed3f5c81c780d3ab86ada14824237 - | ||
2 | 546ed3f5c81c780d3ab86ada14824237 - | ||
3 | End | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_huge.tests b/shell/hush_test/hush-heredoc/heredoc_huge.tests new file mode 100755 index 000000000..c2ec2817b --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_huge.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | # This creates 120k heredoc | ||
2 | echo 'cat <<HERE | md5sum' >"$0.tmp" | ||
3 | yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp" | ||
4 | echo 'HERE' >>"$0.tmp" | ||
5 | |||
6 | yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum | ||
7 | . "$0.tmp" | ||
8 | rm "$0.tmp" | ||
9 | echo End | ||
diff --git a/shell/hush_test/hush-invert/invert.right b/shell/hush_test/hush-invert/invert.right new file mode 100644 index 000000000..5a9239aa6 --- /dev/null +++ b/shell/hush_test/hush-invert/invert.right | |||
@@ -0,0 +1,10 @@ | |||
1 | 1 | ||
2 | 1 | ||
3 | 1 | ||
4 | 0 | ||
5 | 0 | ||
6 | 1 | ||
7 | 0 | ||
8 | 1 | ||
9 | 0 | ||
10 | 1 | ||
diff --git a/shell/hush_test/hush-invert/invert.tests b/shell/hush_test/hush-invert/invert.tests new file mode 100755 index 000000000..8393d95a4 --- /dev/null +++ b/shell/hush_test/hush-invert/invert.tests | |||
@@ -0,0 +1,19 @@ | |||
1 | # tests of return value inversion | ||
2 | # placeholder for future expansion | ||
3 | |||
4 | # user subshells (...) did this wrong in bash versions before 2.04 | ||
5 | |||
6 | ! ( echo hello | grep h >/dev/null 2>&1 ); echo $? | ||
7 | ! echo hello | grep h >/dev/null 2>&1 ; echo $? | ||
8 | |||
9 | ! true ; echo $? | ||
10 | ! false; echo $? | ||
11 | |||
12 | ! (false) ; echo $? | ||
13 | ! (true); echo $? | ||
14 | |||
15 | ! true | false ; echo $? | ||
16 | ! false | true ; echo $? | ||
17 | |||
18 | ! (true | false) ; echo $? | ||
19 | ! (false | true) ; echo $? | ||
diff --git a/shell/hush_test/hush-misc/assignment2.right b/shell/hush_test/hush-misc/assignment2.right new file mode 100644 index 000000000..9d1860be7 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: can't execute 'a=b': No such file or directory | ||
2 | 127 | ||
diff --git a/shell/hush_test/hush-misc/assignment2.rigth b/shell/hush_test/hush-misc/assignment2.rigth deleted file mode 100644 index 591552cde..000000000 --- a/shell/hush_test/hush-misc/assignment2.rigth +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | hush: can't exec 'a=b': No such file or directory | ||
2 | 1 | ||
diff --git a/shell/hush_test/hush-misc/assignment2.tests b/shell/hush_test/hush-misc/assignment2.tests index 540e01ec2..f6938434c 100755 --- a/shell/hush_test/hush-misc/assignment2.tests +++ b/shell/hush_test/hush-misc/assignment2.tests | |||
@@ -1,4 +1,3 @@ | |||
1 | # This must not be interpreted as an assignment | 1 | # This must not be interpreted as an assignment |
2 | a''=b true | 2 | a''=b true |
3 | echo $? | 3 | echo $? |
4 | # (buglet: $? should be 127. it is currently 1) | ||
diff --git a/shell/hush_test/hush-misc/eval1.right b/shell/hush_test/hush-misc/eval1.right new file mode 100644 index 000000000..7b24a35ff --- /dev/null +++ b/shell/hush_test/hush-misc/eval1.right | |||
@@ -0,0 +1 @@ | |||
Ok:0 | |||
diff --git a/shell/hush_test/hush-misc/eval1.tests b/shell/hush_test/hush-misc/eval1.tests new file mode 100755 index 000000000..b78c6cc94 --- /dev/null +++ b/shell/hush_test/hush-misc/eval1.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # empty eval nevertheless sets $? = 0 | ||
2 | false | ||
3 | eval | ||
4 | echo Ok:$? | ||
diff --git a/shell/hush_test/hush-misc/eval2.right b/shell/hush_test/hush-misc/eval2.right new file mode 100644 index 000000000..a7ce6ccc7 --- /dev/null +++ b/shell/hush_test/hush-misc/eval2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Zero:0 | ||
2 | Zero:0 | ||
3 | Zero:0 | ||
diff --git a/shell/hush_test/hush-misc/eval2.tests b/shell/hush_test/hush-misc/eval2.tests new file mode 100755 index 000000000..6bfb87aa7 --- /dev/null +++ b/shell/hush_test/hush-misc/eval2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | false; eval; echo Zero:$? | ||
2 | false; eval ""; echo Zero:$? | ||
3 | false; eval " | ||
4 | "; echo Zero:$? | ||
diff --git a/shell/hush_test/hush-misc/exitcode1.right b/shell/hush_test/hush-misc/exitcode1.right new file mode 100644 index 000000000..e5fefefda --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | One:1 | ||
2 | Zero:0 | ||
diff --git a/shell/hush_test/hush-misc/exitcode1.tests b/shell/hush_test/hush-misc/exitcode1.tests new file mode 100755 index 000000000..dc8619d8b --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | false || case a in a) echo One:$?;; esac | ||
2 | echo Zero:$? | ||
diff --git a/shell/hush_test/hush-misc/exitcode_EACCES.right b/shell/hush_test/hush-misc/exitcode_EACCES.right new file mode 100644 index 000000000..a80d55137 --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_EACCES.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: can't execute './': Permission denied | ||
2 | 126 | ||
diff --git a/shell/hush_test/hush-misc/exitcode_EACCES.tests b/shell/hush_test/hush-misc/exitcode_EACCES.tests new file mode 100755 index 000000000..26b5c6116 --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_EACCES.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | ./ | ||
2 | echo $? | ||
diff --git a/shell/hush_test/hush-misc/exitcode_ENOENT.right b/shell/hush_test/hush-misc/exitcode_ENOENT.right new file mode 100644 index 000000000..d8315503f --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_ENOENT.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: can't execute './does_not_exist_for_sure': No such file or directory | ||
2 | 127 | ||
diff --git a/shell/hush_test/hush-misc/exitcode_ENOENT.tests b/shell/hush_test/hush-misc/exitcode_ENOENT.tests new file mode 100755 index 000000000..7f1b88a99 --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_ENOENT.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | ./does_not_exist_for_sure | ||
2 | echo $? | ||
diff --git a/shell/hush_test/hush-misc/func6.right b/shell/hush_test/hush-misc/func6.right new file mode 100644 index 000000000..0ebd8e5a3 --- /dev/null +++ b/shell/hush_test/hush-misc/func6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Two:2 | ||
2 | Two:2 | ||
diff --git a/shell/hush_test/hush-misc/func6.tests b/shell/hush_test/hush-misc/func6.tests new file mode 100755 index 000000000..029c3e85e --- /dev/null +++ b/shell/hush_test/hush-misc/func6.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | f1() { | ||
2 | while return 2; do :; done | ||
3 | } | ||
4 | f1 | ||
5 | echo Two:$? | ||
6 | |||
7 | f2() { | ||
8 | while :; do return 2; done | ||
9 | } | ||
10 | f2 | ||
11 | echo Two:$? | ||
diff --git a/shell/hush_test/hush-misc/last_amp.right b/shell/hush_test/hush-misc/last_amp.right new file mode 100644 index 000000000..3da21aec2 --- /dev/null +++ b/shell/hush_test/hush-misc/last_amp.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 3 | ||
2 | End | ||
diff --git a/shell/hush_test/hush-misc/last_amp.tests b/shell/hush_test/hush-misc/last_amp.tests new file mode 100755 index 000000000..160937644 --- /dev/null +++ b/shell/hush_test/hush-misc/last_amp.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | $THIS_SH -c 'echo 3&' | ||
2 | d=`date` | ||
3 | while test "`date`" = "$d"; do true; done | ||
4 | d1=`date` | ||
5 | $THIS_SH -c 'sleep 1&' | ||
6 | d2=`date` | ||
7 | test "$d1" = "$d2" || echo BAD | ||
8 | echo End | ||
diff --git a/shell/hush_test/hush-misc/local1.right b/shell/hush_test/hush-misc/local1.right new file mode 100644 index 000000000..a2d121df6 --- /dev/null +++ b/shell/hush_test/hush-misc/local1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | A1:'A' | ||
2 | A2:'' | ||
3 | A3:'' | ||
4 | A4:'A' | ||
diff --git a/shell/hush_test/hush-misc/local1.tests b/shell/hush_test/hush-misc/local1.tests new file mode 100755 index 000000000..b1e675059 --- /dev/null +++ b/shell/hush_test/hush-misc/local1.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | a=A | ||
2 | f() { | ||
3 | local a | ||
4 | # the above line unsets $a | ||
5 | echo "A2:'$a'" | ||
6 | unset a | ||
7 | echo "A3:'$a'" | ||
8 | } | ||
9 | echo "A1:'$a'" | ||
10 | f | ||
11 | echo "A4:'$a'" | ||
diff --git a/shell/hush_test/hush-misc/nulltick1.right b/shell/hush_test/hush-misc/nulltick1.right new file mode 100644 index 000000000..f90b8209e --- /dev/null +++ b/shell/hush_test/hush-misc/nulltick1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Test 1 | ||
2 | Test 2 | ||
3 | Done | ||
diff --git a/shell/hush_test/hush-misc/nulltick1.tests b/shell/hush_test/hush-misc/nulltick1.tests new file mode 100755 index 000000000..f81923de3 --- /dev/null +++ b/shell/hush_test/hush-misc/nulltick1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | echo Test ` ` 1 | ||
2 | echo Test `</dev/null` 2 | ||
3 | echo Done | ||
diff --git a/shell/hush_test/hush-misc/source1.right b/shell/hush_test/hush-misc/source1.right index d4256034b..0ab7c548c 100644 --- a/shell/hush_test/hush-misc/source1.right +++ b/shell/hush_test/hush-misc/source1.right | |||
@@ -1,5 +1,2 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | Sourced ok |
2 | line2 | 2 | Done |
3 | Ok1:0 | ||
4 | hush: syntax error: unterminated ' | ||
5 | Ok2:1 | ||
diff --git a/shell/hush_test/hush-misc/source1.tests b/shell/hush_test/hush-misc/source1.tests index c13888359..e2e75b227 100755 --- a/shell/hush_test/hush-misc/source1.tests +++ b/shell/hush_test/hush-misc/source1.tests | |||
@@ -1,10 +1,5 @@ | |||
1 | echo 'echo ${^} | 1 | echo "echo Sourced ok" >../sourced.sh |
2 | echo line2' >sourced1 | 2 | PATH="..:$PATH" |
3 | . ./sourced1 | 3 | . sourced.sh |
4 | echo Ok1:$? | 4 | rm ../sourced.sh |
5 | 5 | echo Done | |
6 | echo "echo '" >sourced1 | ||
7 | . ./sourced1 | ||
8 | echo Ok2:$? | ||
9 | |||
10 | rm sourced1 | ||
diff --git a/shell/hush_test/hush-misc/source2.right b/shell/hush_test/hush-misc/source2.right index 0587bad67..ce7171c87 100644 --- a/shell/hush_test/hush-misc/source2.right +++ b/shell/hush_test/hush-misc/source2.right | |||
@@ -1,4 +1 @@ | |||
1 | 0:arg0 1:arg1 2:arg2 | Done: 0 | |
2 | Ok1:0 | ||
3 | 0:arg0 1:q 2:w | ||
4 | Ok2:0 | ||
diff --git a/shell/hush_test/hush-misc/source2.tests b/shell/hush_test/hush-misc/source2.tests index 40b6b83cd..1870cdf7e 100755 --- a/shell/hush_test/hush-misc/source2.tests +++ b/shell/hush_test/hush-misc/source2.tests | |||
@@ -1,8 +1,3 @@ | |||
1 | echo 'echo "0:$0 1:$1 2:$2"' >sourced1 | 1 | false |
2 | set -- 1 2 3 | 2 | . /dev/null |
3 | "$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2 | 3 | echo Done: $? |
4 | echo Ok1:$? | ||
5 | "$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2 | ||
6 | echo Ok2:$? | ||
7 | |||
8 | rm sourced1 | ||
diff --git a/shell/hush_test/hush-misc/source3.right b/shell/hush_test/hush-misc/source3.right new file mode 100644 index 000000000..bdf9001a5 --- /dev/null +++ b/shell/hush_test/hush-misc/source3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Zero:0 | ||
2 | Zero:0 | ||
diff --git a/shell/hush_test/hush-misc/source3.tests b/shell/hush_test/hush-misc/source3.tests new file mode 100755 index 000000000..1abf156ed --- /dev/null +++ b/shell/hush_test/hush-misc/source3.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # Test both empty file, and one-empty-line file | ||
2 | echo >sourced1 | ||
3 | true >sourced2 | ||
4 | false; . ./sourced1; echo Zero:$? | ||
5 | false; . ./sourced2; echo Zero:$? | ||
6 | rm sourced1 sourced2 | ||
diff --git a/shell/hush_test/hush-misc/source4.right b/shell/hush_test/hush-misc/source4.right new file mode 100644 index 000000000..d4256034b --- /dev/null +++ b/shell/hush_test/hush-misc/source4.right | |||
@@ -0,0 +1,5 @@ | |||
1 | hush: syntax error: unterminated ${name} | ||
2 | line2 | ||
3 | Ok1:0 | ||
4 | hush: syntax error: unterminated ' | ||
5 | Ok2:1 | ||
diff --git a/shell/hush_test/hush-misc/source4.tests b/shell/hush_test/hush-misc/source4.tests new file mode 100755 index 000000000..c13888359 --- /dev/null +++ b/shell/hush_test/hush-misc/source4.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | echo 'echo ${^} | ||
2 | echo line2' >sourced1 | ||
3 | . ./sourced1 | ||
4 | echo Ok1:$? | ||
5 | |||
6 | echo "echo '" >sourced1 | ||
7 | . ./sourced1 | ||
8 | echo Ok2:$? | ||
9 | |||
10 | rm sourced1 | ||
diff --git a/shell/hush_test/hush-misc/source5.right b/shell/hush_test/hush-misc/source5.right new file mode 100644 index 000000000..0587bad67 --- /dev/null +++ b/shell/hush_test/hush-misc/source5.right | |||
@@ -0,0 +1,4 @@ | |||
1 | 0:arg0 1:arg1 2:arg2 | ||
2 | Ok1:0 | ||
3 | 0:arg0 1:q 2:w | ||
4 | Ok2:0 | ||
diff --git a/shell/hush_test/hush-misc/source5.tests b/shell/hush_test/hush-misc/source5.tests new file mode 100755 index 000000000..40b6b83cd --- /dev/null +++ b/shell/hush_test/hush-misc/source5.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | echo 'echo "0:$0 1:$1 2:$2"' >sourced1 | ||
2 | set -- 1 2 3 | ||
3 | "$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2 | ||
4 | echo Ok1:$? | ||
5 | "$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2 | ||
6 | echo Ok2:$? | ||
7 | |||
8 | rm sourced1 | ||
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right new file mode 100644 index 000000000..b212c246c --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right | |||
@@ -0,0 +1,10 @@ | |||
1 | 192\.168\.0\.1 | ||
2 | 192\.168\.0\.1[ | ||
3 | 192\.168\.0\.1[ | ||
4 | 192\\.168\\.0\\.1[ | ||
5 | 192\.168\.0\.1[ | ||
6 | 192\.168\.0\.1 | ||
7 | 192\.168\.0\.1[ | ||
8 | 192\.168\.0\.1[ | ||
9 | 192\\.168\\.0\\.1[ | ||
10 | 192\.168\.0\.1[ | ||
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests new file mode 100755 index 000000000..74dca1cc0 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests | |||
@@ -0,0 +1,21 @@ | |||
1 | # The bug here was triggered by: | ||
2 | # * performing pathname expansion because we see [ | ||
3 | # * replace operator did not escape \ in replace string | ||
4 | |||
5 | IP=192.168.0.1 | ||
6 | |||
7 | rm -f '192.168.0.1[' | ||
8 | echo "${IP//./\\.}" | ||
9 | echo "${IP//./\\.}"'[' # bug was here | ||
10 | echo "${IP//./\\.}[" # bug was here | ||
11 | echo "${IP//./\\\\.}[" # bug was here | ||
12 | echo "192\.168\.0\.1[" | ||
13 | |||
14 | echo >'192.168.0.1[' | ||
15 | echo "${IP//./\\.}" | ||
16 | echo "${IP//./\\.}"'[' # bug was here | ||
17 | echo "${IP//./\\.}[" # bug was here | ||
18 | echo "${IP//./\\\\.}[" # bug was here | ||
19 | echo "192\.168\.0\.1[" | ||
20 | |||
21 | rm -f '192.168.0.1[' | ||
diff --git a/shell/hush_test/hush-read/read_r.tests b/shell/hush_test/hush-read/read_r.tests index 2c4cc6106..1f0a18afc 100755 --- a/shell/hush_test/hush-read/read_r.tests +++ b/shell/hush_test/hush-read/read_r.tests | |||
@@ -1,2 +1,4 @@ | |||
1 | echo -e 'test\\\nbest' | (read reply; echo "$reply") | 1 | echo 'test\ |
2 | echo -e 'test\\\nbest' | (read -r reply; echo "$reply") | 2 | best' | (read reply; echo "$reply") |
3 | echo 'test\ | ||
4 | best' | (read -r reply; echo "$reply") | ||
diff --git a/shell/hush_test/hush-redir/redir1.right b/shell/hush_test/hush-redir/redir1.right new file mode 100644 index 000000000..15515d1af --- /dev/null +++ b/shell/hush_test/hush-redir/redir1.right | |||
@@ -0,0 +1,12 @@ | |||
1 | Test 0: var:ok | ||
2 | File created:ok | ||
3 | Test 1: var:ok | ||
4 | File created:ok | ||
5 | Test 2: var:ok | ||
6 | File created:ok | ||
7 | Test 3: var:ok | ||
8 | File created:ok | ||
9 | Test 4: var:ok | ||
10 | File created:ok | ||
11 | Test 5: var:ok | ||
12 | File created:ok | ||
diff --git a/shell/hush_test/hush-redir/redir1.tests b/shell/hush_test/hush-redir/redir1.tests new file mode 100755 index 000000000..ef2fbfb77 --- /dev/null +++ b/shell/hush_test/hush-redir/redir1.tests | |||
@@ -0,0 +1,40 @@ | |||
1 | rm shell_test_$$ 2>/dev/null | ||
2 | var=bad | ||
3 | >shell_test_$$ var=ok | ||
4 | echo "Test 0: var:$var" | ||
5 | test -f shell_test_$$ && echo "File created:ok" | ||
6 | |||
7 | rm shell_test_$$ 2>/dev/null | ||
8 | var=bad | ||
9 | var=ok >shell_test_$$ | ||
10 | echo "Test 1: var:$var" | ||
11 | test -f shell_test_$$ && echo "File created:ok" | ||
12 | |||
13 | rm shell_test_$$ 2>/dev/null | ||
14 | var=ok | ||
15 | true | var=bad >shell_test_$$ | ||
16 | echo "Test 2: var:$var" | ||
17 | test -f shell_test_$$ && echo "File created:ok" | ||
18 | |||
19 | rm shell_test_$$ 2>/dev/null | ||
20 | var=bad | ||
21 | { var=ok >shell_test_$$; } | ||
22 | echo "Test 3: var:$var" | ||
23 | test -f shell_test_$$ && echo "File created:ok" | ||
24 | |||
25 | rm shell_test_$$ 2>/dev/null | ||
26 | var=ok | ||
27 | { var=bad >shell_test_$$; } & | ||
28 | # cant use usleep as it isnt standard in $PATH -- | ||
29 | # we fail when testing busybox compiled solely as "hush" | ||
30 | wait | ||
31 | echo "Test 4: var:$var" | ||
32 | test -f shell_test_$$ && echo "File created:ok" | ||
33 | |||
34 | rm shell_test_$$ 2>/dev/null | ||
35 | var=ok | ||
36 | ( var=bad >shell_test_$$ ) | ||
37 | echo "Test 5: var:$var" | ||
38 | test -f shell_test_$$ && echo "File created:ok" | ||
39 | |||
40 | rm shell_test_$$ 2>/dev/null | ||
diff --git a/shell/hush_test/hush-redir/redir2.right b/shell/hush_test/hush-redir/redir2.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/hush_test/hush-redir/redir2.right | |||
@@ -0,0 +1 @@ | |||
OK | |||
diff --git a/shell/hush_test/hush-redir/redir2.tests b/shell/hush_test/hush-redir/redir2.tests new file mode 100755 index 000000000..61ccea30c --- /dev/null +++ b/shell/hush_test/hush-redir/redir2.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | # ash once couldn't redirect above fd#9 | ||
2 | exec 1>/dev/null | ||
3 | (echo LOST1 >&22) 22>&1 | ||
4 | (echo LOST2 >&22) 22>&1 | ||
5 | (echo OK >&22) 22>&2 | ||
diff --git a/shell/hush_test/hush-misc/redir3.right b/shell/hush_test/hush-redir/redir3.right index 3d20bbf68..3d20bbf68 100644 --- a/shell/hush_test/hush-misc/redir3.right +++ b/shell/hush_test/hush-redir/redir3.right | |||
diff --git a/shell/hush_test/hush-misc/redir3.tests b/shell/hush_test/hush-redir/redir3.tests index 7c28e4324..7c28e4324 100755 --- a/shell/hush_test/hush-misc/redir3.tests +++ b/shell/hush_test/hush-redir/redir3.tests | |||
diff --git a/shell/hush_test/hush-redir/redir4.right b/shell/hush_test/hush-redir/redir4.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/hush_test/hush-redir/redir4.right | |||
@@ -0,0 +1 @@ | |||
OK | |||
diff --git a/shell/hush_test/hush-redir/redir4.tests b/shell/hush_test/hush-redir/redir4.tests new file mode 100755 index 000000000..4bdf5ae27 --- /dev/null +++ b/shell/hush_test/hush-redir/redir4.tests | |||
@@ -0,0 +1,72 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | exec 13>&- | ||
3 | exec 12>&- | ||
4 | exec 11>&- | ||
5 | exec 10>&- | ||
6 | # some amount of input is prefetched. | ||
7 | # make sure final echo is far enough to not be prefetched. | ||
8 | ############################################################### | ||
9 | ############################################################### | ||
10 | ############################################################### | ||
11 | ############################################################### | ||
12 | ############################################################### | ||
13 | ############################################################### | ||
14 | ############################################################### | ||
15 | ############################################################### | ||
16 | ############################################################### | ||
17 | ############################################################### | ||
18 | ############################################################### | ||
19 | ############################################################### | ||
20 | ############################################################### | ||
21 | ############################################################### | ||
22 | ############################################################### | ||
23 | ############################################################### | ||
24 | ############################################################### | ||
25 | ############################################################### | ||
26 | ############################################################### | ||
27 | ############################################################### | ||
28 | ############################################################### | ||
29 | ############################################################### | ||
30 | ############################################################### | ||
31 | ############################################################### | ||
32 | ############################################################### | ||
33 | ############################################################### | ||
34 | ############################################################### | ||
35 | ############################################################### | ||
36 | ############################################################### | ||
37 | ############################################################### | ||
38 | ############################################################### | ||
39 | ############################################################### | ||
40 | ############################################################### | ||
41 | ############################################################### | ||
42 | ############################################################### | ||
43 | ############################################################### | ||
44 | ############################################################### | ||
45 | ############################################################### | ||
46 | ############################################################### | ||
47 | ############################################################### | ||
48 | ############################################################### | ||
49 | ############################################################### | ||
50 | ############################################################### | ||
51 | ############################################################### | ||
52 | ############################################################### | ||
53 | ############################################################### | ||
54 | ############################################################### | ||
55 | ############################################################### | ||
56 | ############################################################### | ||
57 | ############################################################### | ||
58 | ############################################################### | ||
59 | ############################################################### | ||
60 | ############################################################### | ||
61 | ############################################################### | ||
62 | ############################################################### | ||
63 | ############################################################### | ||
64 | ############################################################### | ||
65 | ############################################################### | ||
66 | ############################################################### | ||
67 | ############################################################### | ||
68 | ############################################################### | ||
69 | ############################################################### | ||
70 | ############################################################### | ||
71 | ############################################################### | ||
72 | echo "OK" | ||
diff --git a/shell/hush_test/hush-misc/redir5.right b/shell/hush_test/hush-redir/redir5.right index 52cce4feb..52cce4feb 100644 --- a/shell/hush_test/hush-misc/redir5.right +++ b/shell/hush_test/hush-redir/redir5.right | |||
diff --git a/shell/hush_test/hush-misc/redir5.tests b/shell/hush_test/hush-redir/redir5.tests index 957f9c81f..957f9c81f 100755 --- a/shell/hush_test/hush-misc/redir5.tests +++ b/shell/hush_test/hush-redir/redir5.tests | |||
diff --git a/shell/hush_test/hush-redir/redir6.right b/shell/hush_test/hush-redir/redir6.right new file mode 100644 index 000000000..ed754df78 --- /dev/null +++ b/shell/hush_test/hush-redir/redir6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Hello | ||
2 | OK | ||
diff --git a/shell/hush_test/hush-redir/redir6.tests b/shell/hush_test/hush-redir/redir6.tests new file mode 100755 index 000000000..33b6d4cd4 --- /dev/null +++ b/shell/hush_test/hush-redir/redir6.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # we had a bug where this would hang | ||
2 | (head -n 1 <redir6.right) | ||
3 | echo OK | ||
diff --git a/shell/hush_test/hush-redir/redir7.right b/shell/hush_test/hush-redir/redir7.right new file mode 100644 index 000000000..6430b0211 --- /dev/null +++ b/shell/hush_test/hush-redir/redir7.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Done | ||
diff --git a/shell/hush_test/hush-redir/redir7.tests b/shell/hush_test/hush-redir/redir7.tests new file mode 100755 index 000000000..e873a4622 --- /dev/null +++ b/shell/hush_test/hush-redir/redir7.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | # Chars above 0x7f are used as special codes. | ||
2 | # 0x81 is CTLESC (see ash.c). | ||
3 | # The bug was that quoting and unquoting of them | ||
4 | # was out of sync for redirect filenames. | ||
5 | |||
6 | >unicode.sh | ||
7 | printf 'echo Ok >uni\x81code\n' >>unicode.sh | ||
8 | printf 'cat uni\x81code\n' >>unicode.sh | ||
9 | printf 'cat uni?code\n' >>unicode.sh | ||
10 | . ./unicode.sh | ||
11 | rm uni*code* | ||
12 | echo Done | ||
diff --git a/shell/hush_test/hush-redir/redir8.right b/shell/hush_test/hush-redir/redir8.right new file mode 100644 index 000000000..6430b0211 --- /dev/null +++ b/shell/hush_test/hush-redir/redir8.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Done | ||
diff --git a/shell/hush_test/hush-redir/redir8.tests b/shell/hush_test/hush-redir/redir8.tests new file mode 100755 index 000000000..2bd386749 --- /dev/null +++ b/shell/hush_test/hush-redir/redir8.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | # Chars above 0x7f are used as special codes. | ||
2 | # 0x81 is CTLESC (see ash.c). | ||
3 | # The bug was that quoting and unquoting of them | ||
4 | # was out of sync for redirect filenames. | ||
5 | |||
6 | # Subcase when redirect filename is specified in a variable. | ||
7 | |||
8 | >unicode.sh | ||
9 | printf 'v=uni\x81code\n' >>unicode.sh | ||
10 | printf 'echo Ok >"$v"\n' >>unicode.sh | ||
11 | printf 'cat uni\x81code\n' >>unicode.sh | ||
12 | printf 'cat uni?code\n' >>unicode.sh | ||
13 | . ./unicode.sh | ||
14 | rm uni*code* | ||
15 | echo Done | ||
diff --git a/shell/hush_test/hush-redir/redir9.right b/shell/hush_test/hush-redir/redir9.right new file mode 100644 index 000000000..34c2512e4 --- /dev/null +++ b/shell/hush_test/hush-redir/redir9.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok | ||
2 | Done:0 | ||
diff --git a/shell/hush_test/hush-redir/redir9.tests b/shell/hush_test/hush-redir/redir9.tests new file mode 100755 index 000000000..8befa611c --- /dev/null +++ b/shell/hush_test/hush-redir/redir9.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | echo Ok >file.tmp | ||
2 | cat 0<>file.tmp | ||
3 | echo Done:$? | ||
4 | rm file.tmp | ||
diff --git a/shell/hush_test/hush-redir/redirA.right b/shell/hush_test/hush-redir/redirA.right new file mode 100644 index 000000000..31406e336 --- /dev/null +++ b/shell/hush_test/hush-redir/redirA.right | |||
@@ -0,0 +1,2 @@ | |||
1 | tmp11 | ||
2 | tmp11 | ||
diff --git a/shell/hush_test/hush-redir/redirA.tests b/shell/hush_test/hush-redir/redirA.tests new file mode 100755 index 000000000..56833f938 --- /dev/null +++ b/shell/hush_test/hush-redir/redirA.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | x="tmp11:tmp22" | ||
2 | |||
3 | # Bug was incorrectly expanding variables in >redir | ||
4 | echo "${x%:*}" >"${x%:*}" | ||
5 | echo tmp1* | ||
6 | rm tmp1* | ||
7 | |||
8 | # Also try unquoted | ||
9 | echo "${x%:*}" >${x%:*} | ||
10 | echo tmp1* | ||
11 | rm tmp1* | ||
diff --git a/shell/hush_test/hush-redir/redir_escapednum.right b/shell/hush_test/hush-redir/redir_escapednum.right new file mode 100644 index 000000000..7326d9603 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_escapednum.right | |||
@@ -0,0 +1 @@ | |||
Ok | |||
diff --git a/shell/hush_test/hush-redir/redir_escapednum.tests b/shell/hush_test/hush-redir/redir_escapednum.tests new file mode 100755 index 000000000..81983cae2 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_escapednum.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | echo NOT SHOWN \2>/dev/null | ||
2 | echo Ok | ||
diff --git a/shell/hush_test/hush-redir/redir_expand.right b/shell/hush_test/hush-redir/redir_expand.right new file mode 100644 index 000000000..ead25f603 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_expand.right | |||
@@ -0,0 +1,18 @@ | |||
1 | shell_test | ||
2 | \shell_test | ||
3 | \shell_test | ||
4 | \shell_test | ||
5 | Here1 | ||
6 | Ok1 | ||
7 | Here2 | ||
8 | Ok2 | ||
9 | Here3 | ||
10 | Ok3 | ||
11 | Here4 | ||
12 | Ok4 | ||
13 | Now with variable refs | ||
14 | shell_test_1 | ||
15 | \shell_test_1 | ||
16 | \shell_test_1 | ||
17 | \shell_test_1 | ||
18 | Done | ||
diff --git a/shell/hush_test/hush-redir/redir_expand.tests b/shell/hush_test/hush-redir/redir_expand.tests new file mode 100755 index 000000000..c50b8cedf --- /dev/null +++ b/shell/hush_test/hush-redir/redir_expand.tests | |||
@@ -0,0 +1,85 @@ | |||
1 | rm *shell_test* 2>/dev/null | ||
2 | |||
3 | >\shell_test | ||
4 | echo *shell_test* | ||
5 | rm *shell_test* | ||
6 | |||
7 | >\\shell_test | ||
8 | echo *shell_test* | ||
9 | rm *shell_test* | ||
10 | |||
11 | >"\shell_test" | ||
12 | echo *shell_test* | ||
13 | rm *shell_test* | ||
14 | |||
15 | >"\\shell_test" | ||
16 | echo *shell_test* | ||
17 | rm *shell_test* | ||
18 | |||
19 | |||
20 | cat <<\shell_test | ||
21 | Here1 | ||
22 | shell_test | ||
23 | echo Ok1 | ||
24 | |||
25 | cat <<\\shell_test | ||
26 | Here2 | ||
27 | \shell_test | ||
28 | echo Ok2 | ||
29 | |||
30 | cat <<"\shell_test" | ||
31 | Here3 | ||
32 | \shell_test | ||
33 | echo Ok3 | ||
34 | |||
35 | cat <<"\\shell_test" | ||
36 | Here4 | ||
37 | \shell_test | ||
38 | echo Ok4 | ||
39 | |||
40 | |||
41 | echo Now with variable refs | ||
42 | i=1 | ||
43 | |||
44 | |||
45 | >\shell_test_$i | ||
46 | echo *shell_test* | ||
47 | rm *shell_test* | ||
48 | |||
49 | >\\shell_test_$i | ||
50 | echo *shell_test* | ||
51 | rm *shell_test* | ||
52 | |||
53 | >"\shell_test_$i" | ||
54 | echo *shell_test* | ||
55 | rm *shell_test* | ||
56 | |||
57 | >"\\shell_test_$i" | ||
58 | echo *shell_test* | ||
59 | rm *shell_test* | ||
60 | |||
61 | echo Done;exit | ||
62 | # UNFIXED BUG. bash apparently will expand $i even in terminating delimiter. | ||
63 | # http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | ||
64 | # does not mandate this behavior. | ||
65 | # This is not likely to be used much in real-world. | ||
66 | |||
67 | cat <<\shell_test_$i | ||
68 | Here1 | ||
69 | shell_test_$i | ||
70 | echo Ok1 | ||
71 | |||
72 | cat <<\\shell_test_$i | ||
73 | Here2 | ||
74 | \shell_test_$i | ||
75 | echo Ok2 | ||
76 | |||
77 | cat <<"\shell_test_$i" | ||
78 | Here3 | ||
79 | \shell_test_$i | ||
80 | echo Ok3 | ||
81 | |||
82 | cat <<"\\shell_test_$i" | ||
83 | Here4 | ||
84 | \shell_test_$i | ||
85 | echo Ok4 | ||
diff --git a/shell/hush_test/hush-redir/redir_multi.right b/shell/hush_test/hush-redir/redir_multi.right new file mode 100644 index 000000000..a97c4bdf1 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_multi.right | |||
@@ -0,0 +1,4 @@ | |||
1 | Testing multiple redirections to same fd | ||
2 | Hello | ||
3 | Done1 | ||
4 | Done2 | ||
diff --git a/shell/hush_test/hush-redir/redir_multi.tests b/shell/hush_test/hush-redir/redir_multi.tests new file mode 100755 index 000000000..c639ebb2d --- /dev/null +++ b/shell/hush_test/hush-redir/redir_multi.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | echo "Testing multiple redirections to same fd" | ||
2 | # bug was making us lose fd #1 after this: | ||
3 | echo Hello >/dev/null 1>&2 | ||
4 | echo Done1 | ||
5 | echo Done2 >&2 | ||
diff --git a/shell/hush_test/hush-redir/redir_script.right b/shell/hush_test/hush-redir/redir_script.right new file mode 100644 index 000000000..6694ed334 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_script.right | |||
@@ -0,0 +1 @@ | |||
Ok: script fd is not closed | |||
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests new file mode 100755 index 000000000..ccc497d7b --- /dev/null +++ b/shell/hush_test/hush-redir/redir_script.tests | |||
@@ -0,0 +1,29 @@ | |||
1 | # Builds a " 3>&- 4>&-" string. | ||
2 | # Note: one of these fds is a directory opened to /proc/self/fd | ||
3 | # for globbing. It is unwanted, but I don't know how to filter it out. | ||
4 | find_fds() { | ||
5 | fds="" | ||
6 | for f in /proc/self/fd/*; do | ||
7 | test "$f" = "/proc/self/fd/0" && continue | ||
8 | test "$f" = "/proc/self/fd/1" && continue | ||
9 | test "$f" = "/proc/self/fd/2" && continue | ||
10 | fds="$fds ${f##*/}>&-" | ||
11 | done | ||
12 | } | ||
13 | |||
14 | find_fds | ||
15 | fds1="$fds" | ||
16 | |||
17 | # One of the fds is open to the script body | ||
18 | # Close it while executing something. | ||
19 | eval "find_fds $fds" | ||
20 | |||
21 | # Shell should not lose that fd. Did it? | ||
22 | find_fds | ||
23 | test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } | ||
24 | |||
25 | echo "Bug: script fd is closed" | ||
26 | echo "fds1:$fds1" | ||
27 | echo "fds2:$fds" | ||
28 | exit 1 | ||
29 | |||
diff --git a/shell/hush_test/hush-redir/redir_space.right b/shell/hush_test/hush-redir/redir_space.right new file mode 100644 index 000000000..084295204 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_space.right | |||
@@ -0,0 +1,3 @@ | |||
1 | z1.tmp: 1 | ||
2 | z2.tmp: 1 | ||
3 | "z1.tmp z2.tmp": TEST 0 | ||
diff --git a/shell/hush_test/hush-redir/redir_space.tests b/shell/hush_test/hush-redir/redir_space.tests new file mode 100755 index 000000000..c0b543098 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_space.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | v='z1.tmp z2.tmp' | ||
2 | echo TEST >$v | ||
3 | echo 'z1.tmp:' `cat 'z1.tmp' 2>/dev/null; echo $?` | ||
4 | echo 'z2.tmp:' `cat 'z2.tmp' 2>/dev/null; echo $?` | ||
5 | echo '"z1.tmp z2.tmp":' `cat 'z1.tmp z2.tmp' 2>/dev/null; echo $?` | ||
6 | rm z*.tmp | ||
diff --git a/shell/hush_test/hush-trap/catch.right b/shell/hush_test/hush-signals/catch.right index 80a062c4b..80a062c4b 100644 --- a/shell/hush_test/hush-trap/catch.right +++ b/shell/hush_test/hush-signals/catch.right | |||
diff --git a/shell/hush_test/hush-trap/catch.tests b/shell/hush_test/hush-signals/catch.tests index d2a21d17e..d2a21d17e 100755 --- a/shell/hush_test/hush-trap/catch.tests +++ b/shell/hush_test/hush-signals/catch.tests | |||
diff --git a/shell/hush_test/hush-signals/continue_and_trap1.right b/shell/hush_test/hush-signals/continue_and_trap1.right new file mode 100644 index 000000000..d2dd0af72 --- /dev/null +++ b/shell/hush_test/hush-signals/continue_and_trap1.right | |||
@@ -0,0 +1 @@ | |||
Exiting | |||
diff --git a/shell/hush_test/hush-signals/continue_and_trap1.tests b/shell/hush_test/hush-signals/continue_and_trap1.tests new file mode 100755 index 000000000..2a5c147b1 --- /dev/null +++ b/shell/hush_test/hush-signals/continue_and_trap1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | trap "echo Exiting; exit" INT | ||
2 | |||
3 | (sleep 1; kill -s INT $$) & | ||
4 | |||
5 | while continue; do | ||
6 | continue; | ||
7 | done | ||
diff --git a/shell/hush_test/hush-trap/exit.right b/shell/hush_test/hush-signals/exit.right index 3d0072564..3d0072564 100644 --- a/shell/hush_test/hush-trap/exit.right +++ b/shell/hush_test/hush-signals/exit.right | |||
diff --git a/shell/hush_test/hush-trap/exit.tests b/shell/hush_test/hush-signals/exit.tests index 2061105dd..2061105dd 100755 --- a/shell/hush_test/hush-trap/exit.tests +++ b/shell/hush_test/hush-signals/exit.tests | |||
diff --git a/shell/hush_test/hush-signals/reap1.right b/shell/hush_test/hush-signals/reap1.right new file mode 100644 index 000000000..7326d9603 --- /dev/null +++ b/shell/hush_test/hush-signals/reap1.right | |||
@@ -0,0 +1 @@ | |||
Ok | |||
diff --git a/shell/hush_test/hush-signals/reap1.tests b/shell/hush_test/hush-signals/reap1.tests new file mode 100755 index 000000000..bf1a1f908 --- /dev/null +++ b/shell/hush_test/hush-signals/reap1.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | # Must not find us alive | ||
4 | { sleep 2; kill -9 $$; } 2>/dev/null & | ||
5 | |||
6 | sleep 1 & | ||
7 | PID=$! | ||
8 | |||
9 | # We must exit the loop in one second. | ||
10 | # We had bug 5304: builtins never waited for exited children | ||
11 | while kill -0 $PID >/dev/null 2>&1; do | ||
12 | true | ||
13 | done | ||
14 | echo Ok | ||
diff --git a/shell/hush_test/hush-signals/return_in_trap1.right b/shell/hush_test/hush-signals/return_in_trap1.right new file mode 100644 index 000000000..a6e637885 --- /dev/null +++ b/shell/hush_test/hush-signals/return_in_trap1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | a:2 | ||
2 | b:0 | ||
3 | Trap | ||
4 | d:3 | ||
diff --git a/shell/hush_test/hush-signals/return_in_trap1.tests b/shell/hush_test/hush-signals/return_in_trap1.tests new file mode 100755 index 000000000..f2498024f --- /dev/null +++ b/shell/hush_test/hush-signals/return_in_trap1.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | a() { | ||
2 | (exit 2) | ||
3 | echo a:$? | ||
4 | (kill -s USR1 $$; echo b:$?; exit 3) | ||
5 | echo c:$? # does not execute | ||
6 | (exit 4) | ||
7 | } | ||
8 | |||
9 | trap "echo Trap; return" USR1 | ||
10 | a | ||
11 | |||
12 | echo d:$? | ||
13 | # It's debatable what is the correct value above. | ||
14 | # Does 'return' in trap sees $? == 2 or $? == 3? | ||
15 | # IOW: after (kill..), does shell first wait for its completion | ||
16 | # and sets $?, then checks pending signals and runs a trap handler, | ||
17 | # or does it first checks pending signals and runs handler? | ||
18 | # hush does the former, and prints 3. | ||
diff --git a/shell/hush_test/hush-signals/save-ret.right b/shell/hush_test/hush-signals/save-ret.right new file mode 100644 index 000000000..a3e12ce5e --- /dev/null +++ b/shell/hush_test/hush-signals/save-ret.right | |||
@@ -0,0 +1,2 @@ | |||
1 | YEAH | ||
2 | 0 | ||
diff --git a/shell/hush_test/hush-signals/save-ret.tests b/shell/hush_test/hush-signals/save-ret.tests new file mode 100755 index 000000000..0786b6d96 --- /dev/null +++ b/shell/hush_test/hush-signals/save-ret.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # make sure we do not corrupt $? across traps | ||
2 | trap "echo YEAH; false" USR1 | ||
3 | kill -USR1 $$ | ||
4 | echo $? | ||
diff --git a/shell/hush_test/hush-trap/savetrap.right b/shell/hush_test/hush-signals/savetrap.right index a59225be3..a59225be3 100644 --- a/shell/hush_test/hush-trap/savetrap.right +++ b/shell/hush_test/hush-signals/savetrap.right | |||
diff --git a/shell/hush_test/hush-trap/savetrap.tests b/shell/hush_test/hush-signals/savetrap.tests index c2b312fb8..c2b312fb8 100755 --- a/shell/hush_test/hush-trap/savetrap.tests +++ b/shell/hush_test/hush-signals/savetrap.tests | |||
diff --git a/shell/hush_test/hush-signals/sigint1.right b/shell/hush_test/hush-signals/sigint1.right new file mode 100644 index 000000000..a9094b056 --- /dev/null +++ b/shell/hush_test/hush-signals/sigint1.right | |||
@@ -0,0 +1 @@ | |||
Sending SIGINT to main shell PID | |||
diff --git a/shell/hush_test/hush-signals/sigint1.tests b/shell/hush_test/hush-signals/sigint1.tests new file mode 100755 index 000000000..3d483d32a --- /dev/null +++ b/shell/hush_test/hush-signals/sigint1.tests | |||
@@ -0,0 +1,41 @@ | |||
1 | # What should happen if non-interactive shell gets SIGINT? | ||
2 | |||
3 | (sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) & | ||
4 | |||
5 | # We create a child which exits with 0 even on SIGINT | ||
6 | # (The complex command is necessary only if SIGINT is generated by ^C, | ||
7 | # in this testcase even bare "sleep 2" would do because | ||
8 | # in the testcase we don't send SIGINT *to the child*...) | ||
9 | $THIS_SH -c 'trap "exit 0" SIGINT; sleep 2' | ||
10 | |||
11 | # In one second, we (main shell) get SIGINT here. | ||
12 | # The question is whether we should, or should not, exit. | ||
13 | |||
14 | # bash will not stop here. It will execute next command(s). | ||
15 | |||
16 | # The rationale for this is described here: | ||
17 | # http://www.cons.org/cracauer/sigint.html | ||
18 | # | ||
19 | # Basically, bash will not exit on SIGINT immediately if it waits | ||
20 | # for a child. It will wait for the child to exit. | ||
21 | # If child exits NOT by dying on SIGINT, then bash will not exit. | ||
22 | # | ||
23 | # The idea is that the following script: | ||
24 | # | emacs file.txt | ||
25 | # | more cmds | ||
26 | # User may use ^C to interrupt editor's ops like search. But then | ||
27 | # emacs exits normally. User expects that script doesn't stop. | ||
28 | # | ||
29 | # This is a nice idea, but detecting "did process really exit | ||
30 | # with SIGINT?" is racy. Consider: | ||
31 | # | bash -c 'while true; do /bin/true; done' | ||
32 | # When ^C is pressed while bash waits for /bin/true to exit, | ||
33 | # it may happen that /bin/true exits with exitcode 0 before | ||
34 | # ^C is delivered to it as SIGINT. bash will see SIGINT, then | ||
35 | # it will see that child exited with 0, and bash will NOT EXIT. | ||
36 | |||
37 | # Therefore we do not implement bash behavior. | ||
38 | # I'd say that emacs need to put itself into a separate pgrp | ||
39 | # to isolate shell from getting stray SIGINTs from ^C. | ||
40 | |||
41 | echo Next command after SIGINT was executed | ||
diff --git a/shell/hush_test/hush-signals/signal2.right b/shell/hush_test/hush-signals/signal2.right new file mode 100644 index 000000000..a2af919c0 --- /dev/null +++ b/shell/hush_test/hush-signals/signal2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | child sleeps | ||
2 | child exits as expected | ||
3 | parent exits | ||
diff --git a/shell/hush_test/hush-signals/signal2.tests b/shell/hush_test/hush-signals/signal2.tests new file mode 100755 index 000000000..df639ca2c --- /dev/null +++ b/shell/hush_test/hush-signals/signal2.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | $THIS_SH -c ' | ||
4 | cleanup() { | ||
5 | echo "child exits as expected" | ||
6 | exit | ||
7 | } | ||
8 | trap cleanup HUP | ||
9 | echo "child sleeps" | ||
10 | sleep 1 | ||
11 | echo "BAD exit from child!" | ||
12 | ' & | ||
13 | |||
14 | child=$! | ||
15 | sleep 0.1 # let child install handler first | ||
16 | kill -HUP $child | ||
17 | wait | ||
18 | echo "parent exits" | ||
diff --git a/shell/hush_test/hush-signals/signal3.right b/shell/hush_test/hush-signals/signal3.right new file mode 100644 index 000000000..3113ba5cf --- /dev/null +++ b/shell/hush_test/hush-signals/signal3.right | |||
@@ -0,0 +1,4 @@ | |||
1 | child sleeps | ||
2 | child got HUP | ||
3 | child exits | ||
4 | parent exits | ||
diff --git a/shell/hush_test/hush-signals/signal3.tests b/shell/hush_test/hush-signals/signal3.tests new file mode 100755 index 000000000..b56c2d97e --- /dev/null +++ b/shell/hush_test/hush-signals/signal3.tests | |||
@@ -0,0 +1,17 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | $THIS_SH -c ' | ||
4 | hup() { | ||
5 | echo "child got HUP" | ||
6 | } | ||
7 | trap hup HUP | ||
8 | echo "child sleeps" | ||
9 | sleep 1 | ||
10 | echo "child exits" | ||
11 | ' & | ||
12 | |||
13 | child=$! | ||
14 | sleep 0.1 # let child install handler first | ||
15 | kill -HUP $child | ||
16 | wait | ||
17 | echo "parent exits" | ||
diff --git a/shell/hush_test/hush-signals/signal5.right b/shell/hush_test/hush-signals/signal5.right new file mode 100644 index 000000000..7cfd4110e --- /dev/null +++ b/shell/hush_test/hush-signals/signal5.right | |||
@@ -0,0 +1,12 @@ | |||
1 | Sleeping | ||
2 | Sleeping | ||
3 | Waiting | ||
4 | 2 sec passed, sending USR1 to parent | ||
5 | USR1 received | ||
6 | Wait exit code: 138 | ||
7 | Waiting | ||
8 | 3 sec passed, sending USR1 to parent | ||
9 | USR1 received | ||
10 | Wait exit code: 138 | ||
11 | Waiting | ||
12 | Wait returned 0 | ||
diff --git a/shell/hush_test/hush-signals/signal5.tests b/shell/hush_test/hush-signals/signal5.tests new file mode 100755 index 000000000..179bcdd80 --- /dev/null +++ b/shell/hush_test/hush-signals/signal5.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | trap "echo USR1 received" USR1 | ||
2 | stub() { | ||
3 | echo "Sleeping" | ||
4 | sleep $1 | ||
5 | echo "$1 sec passed, sending USR1 to parent" | ||
6 | kill -USR1 $$ | ||
7 | } | ||
8 | stub 3 & | ||
9 | stub 2 & | ||
10 | sleep 1 | ||
11 | until { echo "Waiting"; wait; } do | ||
12 | echo "Wait exit code: $?" | ||
13 | done | ||
14 | echo "Wait returned 0" | ||
diff --git a/shell/hush_test/hush-signals/signal6.right b/shell/hush_test/hush-signals/signal6.right new file mode 100644 index 000000000..df4d9306a --- /dev/null +++ b/shell/hush_test/hush-signals/signal6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | got TERM | ||
2 | Done: 0 | ||
diff --git a/shell/hush_test/hush-signals/signal6.tests b/shell/hush_test/hush-signals/signal6.tests new file mode 100755 index 000000000..3ce151060 --- /dev/null +++ b/shell/hush_test/hush-signals/signal6.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | { trap "echo got TERM" TERM; sleep 3; }& sleep 1; kill $!; wait | ||
2 | echo Done: $? | ||
diff --git a/shell/hush_test/hush-trap/signal7.right b/shell/hush_test/hush-signals/signal7.right index ba7453e42..ba7453e42 100644 --- a/shell/hush_test/hush-trap/signal7.right +++ b/shell/hush_test/hush-signals/signal7.right | |||
diff --git a/shell/hush_test/hush-trap/signal7.tests b/shell/hush_test/hush-signals/signal7.tests index c2b1381f9..c2b1381f9 100755 --- a/shell/hush_test/hush-trap/signal7.tests +++ b/shell/hush_test/hush-signals/signal7.tests | |||
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-signals/signal_read1.right index 2870a8e70..2870a8e70 100644 --- a/shell/hush_test/hush-trap/signal_read1.right +++ b/shell/hush_test/hush-signals/signal_read1.right | |||
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-signals/signal_read1.tests index 1105479a3..1105479a3 100755 --- a/shell/hush_test/hush-trap/signal_read1.tests +++ b/shell/hush_test/hush-signals/signal_read1.tests | |||
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-signals/signal_read2.right index 71a6bc16d..71a6bc16d 100644 --- a/shell/hush_test/hush-trap/signal_read2.right +++ b/shell/hush_test/hush-signals/signal_read2.right | |||
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests index eab5b9b5b..eab5b9b5b 100755 --- a/shell/hush_test/hush-trap/signal_read2.tests +++ b/shell/hush_test/hush-signals/signal_read2.tests | |||
diff --git a/shell/hush_test/hush-signals/sigquit_exec.right b/shell/hush_test/hush-signals/sigquit_exec.right new file mode 100644 index 000000000..a8041929a --- /dev/null +++ b/shell/hush_test/hush-signals/sigquit_exec.right | |||
@@ -0,0 +1,2 @@ | |||
1 | SigIgn: 0000000000000000 | ||
2 | SigIgn: 0000000000000000 | ||
diff --git a/shell/hush_test/hush-signals/sigquit_exec.tests b/shell/hush_test/hush-signals/sigquit_exec.tests new file mode 100755 index 000000000..24bda6921 --- /dev/null +++ b/shell/hush_test/hush-signals/sigquit_exec.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # Should show no masked signals in both cases. | ||
2 | # We had a bug where SIGQUIT was masked on exec. | ||
3 | grep SigIgn: /proc/self/status | ||
4 | exec grep SigIgn: /proc/self/status | ||
diff --git a/shell/hush_test/hush-trap/subshell.right b/shell/hush_test/hush-signals/subshell.right index f865b932b..f865b932b 100644 --- a/shell/hush_test/hush-trap/subshell.right +++ b/shell/hush_test/hush-signals/subshell.right | |||
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-signals/subshell.tests index d877f2b82..d877f2b82 100755 --- a/shell/hush_test/hush-trap/subshell.tests +++ b/shell/hush_test/hush-signals/subshell.tests | |||
diff --git a/shell/hush_test/hush-trap/usage.right b/shell/hush_test/hush-signals/usage.right index c0dbd6c3c..c0dbd6c3c 100644 --- a/shell/hush_test/hush-trap/usage.right +++ b/shell/hush_test/hush-signals/usage.right | |||
diff --git a/shell/hush_test/hush-trap/usage.tests b/shell/hush_test/hush-signals/usage.tests index d29c6e74a..d29c6e74a 100755 --- a/shell/hush_test/hush-trap/usage.tests +++ b/shell/hush_test/hush-signals/usage.tests | |||
diff --git a/shell/hush_test/hush-standalone/noexec_gets_no_env.right b/shell/hush_test/hush-standalone/noexec_gets_no_env.right new file mode 100644 index 000000000..8522dff31 --- /dev/null +++ b/shell/hush_test/hush-standalone/noexec_gets_no_env.right | |||
@@ -0,0 +1,4 @@ | |||
1 | VAR7=VAL | ||
2 | 0 | ||
3 | VAR8=VAL | ||
4 | 0 | ||
diff --git a/shell/hush_test/hush-standalone/noexec_gets_no_env.tests b/shell/hush_test/hush-standalone/noexec_gets_no_env.tests new file mode 100755 index 000000000..0d347bdcd --- /dev/null +++ b/shell/hush_test/hush-standalone/noexec_gets_no_env.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | export VAR7=VAL | ||
2 | env | grep ^VAR7= | ||
3 | echo $? | ||
4 | VAR8=VAL env | grep ^VAR8= | ||
5 | echo $? | ||
diff --git a/shell/hush_test/hush-standalone/nofork_trashes_getopt.right b/shell/hush_test/hush-standalone/nofork_trashes_getopt.right new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/shell/hush_test/hush-standalone/nofork_trashes_getopt.right | |||
@@ -0,0 +1 @@ | |||
0 | |||
diff --git a/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests b/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests new file mode 100755 index 000000000..f42c50730 --- /dev/null +++ b/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # In this test, rm is NOFORK and it modifies getopt internal state | ||
2 | rm -f non_existent_file | ||
3 | # Subsequent hexdump is run as NOEXEC, and thus still uses this state | ||
4 | hexdump </dev/null | ||
5 | # Did hexdump segfault etc? | ||
6 | echo $? | ||
diff --git a/shell/hush_test/hush-standalone/var_standalone1.right b/shell/hush_test/hush-standalone/var_standalone1.right new file mode 100644 index 000000000..37457fd74 --- /dev/null +++ b/shell/hush_test/hush-standalone/var_standalone1.right | |||
@@ -0,0 +1 @@ | |||
Done: 1 | |||
diff --git a/shell/hush_test/hush-standalone/var_standalone1.tests b/shell/hush_test/hush-standalone/var_standalone1.tests new file mode 100755 index 000000000..1e905ba75 --- /dev/null +++ b/shell/hush_test/hush-standalone/var_standalone1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | VAR=42 $THIS_SH -c 'unset VAR; env | grep ^VAR' | ||
2 | echo Done: $? | ||
diff --git a/shell/hush_test/hush-vars/var3.right b/shell/hush_test/hush-vars/var3.right index 40e67fdf5..8eb0e3337 100644 --- a/shell/hush_test/hush-vars/var3.right +++ b/shell/hush_test/hush-vars/var3.right | |||
@@ -1,2 +1,5 @@ | |||
1 | hush: invalid number '1q' | 1 | 1 |
2 | hush: syntax error: unterminated ${name} | 2 | 1 |
3 | |||
4 | |||
5 | 0 | ||
diff --git a/shell/hush_test/hush-vars/var3.tests b/shell/hush_test/hush-vars/var3.tests index aea36d62d..97b102cbe 100755 --- a/shell/hush_test/hush-vars/var3.tests +++ b/shell/hush_test/hush-vars/var3.tests | |||
@@ -1,4 +1 @@ | |||
1 | # reject invalid vars | x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x | |
2 | "$THIS_SH" -c 'echo ${1q}' | ||
3 | "$THIS_SH" -c 'echo ${&}' | ||
4 | #"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$ | ||
diff --git a/shell/hush_test/hush-vars/var4.right b/shell/hush_test/hush-vars/var4.right new file mode 100644 index 000000000..8fed138eb --- /dev/null +++ b/shell/hush_test/hush-vars/var4.right | |||
@@ -0,0 +1 @@ | |||
bus/usb/1/2 | |||
diff --git a/shell/hush_test/hush-vars/var4.tests b/shell/hush_test/hush-vars/var4.tests new file mode 100755 index 000000000..07feaeb8b --- /dev/null +++ b/shell/hush_test/hush-vars/var4.tests | |||
@@ -0,0 +1 @@ | |||
X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D | |||
diff --git a/shell/hush_test/hush-vars/var5.right b/shell/hush_test/hush-vars/var5.right new file mode 100644 index 000000000..2a01291bb --- /dev/null +++ b/shell/hush_test/hush-vars/var5.right | |||
@@ -0,0 +1,6 @@ | |||
1 | a=a A=a | ||
2 | a=a A=a | ||
3 | a= A= | ||
4 | a= A= | ||
5 | a=a A=a | ||
6 | a=a A=a | ||
diff --git a/shell/hush_test/hush-vars/var5.tests b/shell/hush_test/hush-vars/var5.tests new file mode 100755 index 000000000..802e489bd --- /dev/null +++ b/shell/hush_test/hush-vars/var5.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | # check that first assignment has proper effect on second one | ||
2 | |||
3 | ( | ||
4 | a=a A=$a | ||
5 | echo a=$a A=$A | ||
6 | ) | ||
7 | (a=a A=$a; echo a=$a A=$A) | ||
8 | (a=a A=$a echo a=$a A=$A) | ||
9 | (a=a A=$a /bin/echo a=$a A=$A) | ||
10 | |||
11 | f() { echo a=$a A=$A; } | ||
12 | |||
13 | (a=a A=$a f) | ||
14 | (a=a A=$a; f) | ||
diff --git a/shell/hush_test/hush-vars/var6.right b/shell/hush_test/hush-vars/var6.right new file mode 100644 index 000000000..40e67fdf5 --- /dev/null +++ b/shell/hush_test/hush-vars/var6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: invalid number '1q' | ||
2 | hush: syntax error: unterminated ${name} | ||
diff --git a/shell/hush_test/hush-vars/var6.tests b/shell/hush_test/hush-vars/var6.tests new file mode 100755 index 000000000..aea36d62d --- /dev/null +++ b/shell/hush_test/hush-vars/var6.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # reject invalid vars | ||
2 | "$THIS_SH" -c 'echo ${1q}' | ||
3 | "$THIS_SH" -c 'echo ${&}' | ||
4 | #"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$ | ||
diff --git a/shell/hush_test/hush-vars/var_unbackslash1.right b/shell/hush_test/hush-vars/var_unbackslash1.right new file mode 100644 index 000000000..3e0c0e2af --- /dev/null +++ b/shell/hush_test/hush-vars/var_unbackslash1.right | |||
@@ -0,0 +1,7 @@ | |||
1 | Ok | ||
2 | Ba d | ||
3 | Ok | ||
4 | Ok | ||
5 | Ok | ||
6 | Forty two:42 | ||
7 | Forty two:42 | ||
diff --git a/shell/hush_test/hush-vars/var_unbackslash1.tests b/shell/hush_test/hush-vars/var_unbackslash1.tests new file mode 100755 index 000000000..a4665e423 --- /dev/null +++ b/shell/hush_test/hush-vars/var_unbackslash1.tests | |||
@@ -0,0 +1,35 @@ | |||
1 | ad="Ok" | ||
2 | a="Ba" | ||
3 | |||
4 | # "Ok" | ||
5 | echo $a\ | ||
6 | d | ||
7 | |||
8 | # This variable contains backslash+newline! | ||
9 | e='echo $a\ | ||
10 | d' | ||
11 | |||
12 | # "Ba d" | ||
13 | eval $e | ||
14 | # "Ok" | ||
15 | eval "$e" | ||
16 | |||
17 | echo $\ | ||
18 | (echo Ok\ | ||
19 | ) | ||
20 | echo "$\ | ||
21 | (echo Ok\ | ||
22 | )" | ||
23 | |||
24 | echo Forty two:$\ | ||
25 | (\ | ||
26 | (\ | ||
27 | 42\ | ||
28 | )\ | ||
29 | ) | ||
30 | echo "Forty two:$\ | ||
31 | (\ | ||
32 | (\ | ||
33 | 42\ | ||
34 | )\ | ||
35 | )" | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.right b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right new file mode 100644 index 000000000..cf583d0aa --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right | |||
@@ -0,0 +1,41 @@ | |||
1 | Testing: !IFS $* | ||
2 | .abc. | ||
3 | .d. | ||
4 | .e. | ||
5 | Testing: !IFS $@ | ||
6 | .abc. | ||
7 | .d. | ||
8 | .e. | ||
9 | Testing: !IFS "$*" | ||
10 | .abc d e. | ||
11 | Testing: !IFS "$@" | ||
12 | .abc. | ||
13 | .d e. | ||
14 | Testing: IFS="" $* | ||
15 | .abc. | ||
16 | .d e. | ||
17 | Testing: IFS="" $@ | ||
18 | .abc. | ||
19 | .d e. | ||
20 | Testing: IFS="" "$*" | ||
21 | .abcd e. | ||
22 | Testing: IFS="" "$@" | ||
23 | .abc. | ||
24 | .d e. | ||
25 | Testing: !IFS v=$* | ||
26 | v='abc d e' | ||
27 | Testing: !IFS v=$@ | ||
28 | v='abc d e' | ||
29 | Testing: !IFS v="$*" | ||
30 | v='abc d e' | ||
31 | Testing: !IFS v="$@" | ||
32 | v='abc d e' | ||
33 | Testing: IFS="" v=$* | ||
34 | v='abcd e' | ||
35 | Testing: IFS="" v=$@ | ||
36 | v='abcd e' | ||
37 | Testing: IFS="" v="$*" | ||
38 | v='abcd e' | ||
39 | Testing: IFS="" v="$@" | ||
40 | v='abcd e' | ||
41 | Finished | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests new file mode 100755 index 000000000..a62afc6fd --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests | |||
@@ -0,0 +1,42 @@ | |||
1 | set -- abc "d e" | ||
2 | |||
3 | echo 'Testing: !IFS $*' | ||
4 | unset IFS; for a in $*; do echo ".$a."; done | ||
5 | echo 'Testing: !IFS $@' | ||
6 | unset IFS; for a in $@; do echo ".$a."; done | ||
7 | echo 'Testing: !IFS "$*"' | ||
8 | unset IFS; for a in "$*"; do echo ".$a."; done | ||
9 | echo 'Testing: !IFS "$@"' | ||
10 | unset IFS; for a in "$@"; do echo ".$a."; done | ||
11 | |||
12 | echo 'Testing: IFS="" $*' | ||
13 | IFS=""; for a in $*; do echo ".$a."; done | ||
14 | echo 'Testing: IFS="" $@' | ||
15 | IFS=""; for a in $@; do echo ".$a."; done | ||
16 | echo 'Testing: IFS="" "$*"' | ||
17 | IFS=""; for a in "$*"; do echo ".$a."; done | ||
18 | echo 'Testing: IFS="" "$@"' | ||
19 | IFS=""; for a in "$@"; do echo ".$a."; done | ||
20 | |||
21 | echo 'Testing: !IFS v=$*' | ||
22 | unset IFS; v=$*; echo "v='$v'" | ||
23 | echo 'Testing: !IFS v=$@' | ||
24 | unset IFS; v=$@; echo "v='$v'" | ||
25 | echo 'Testing: !IFS v="$*"' | ||
26 | unset IFS; v="$*"; echo "v='$v'" | ||
27 | echo 'Testing: !IFS v="$@"' | ||
28 | unset IFS; v="$@"; echo "v='$v'" | ||
29 | |||
30 | echo 'Testing: IFS="" v=$*' | ||
31 | IFS=""; v=$*; echo "v='$v'" | ||
32 | echo 'Testing: IFS="" v=$@' | ||
33 | IFS=""; v=$@; echo "v='$v'" | ||
34 | echo 'Testing: IFS="" v="$*"' | ||
35 | IFS=""; v="$*"; echo "v='$v'" | ||
36 | echo 'Testing: IFS="" v="$@"' | ||
37 | IFS=""; v="$@"; echo "v='$v'" | ||
38 | |||
39 | # Note: in IFS="" v=$@ and IFS="" v="$@" cases, bash produces "abc d e" | ||
40 | # We produce "abcd e" | ||
41 | |||
42 | echo Finished | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs2.right b/shell/hush_test/hush-vars/var_wordsplit_ifs2.right new file mode 100644 index 000000000..c234193fe --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Unquoted:<1> | ||
2 | Unquoted:<3> | ||
3 | Quoted:<123> | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests new file mode 100755 index 000000000..47523549c --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | # 123 chars long | ||
2 | a="\ | ||
3 | 01234567890123456789\ | ||
4 | 01234567890123456789\ | ||
5 | 01234567890123456789\ | ||
6 | 01234567890123456789\ | ||
7 | 01234567890123456789\ | ||
8 | 0123456789\ | ||
9 | 0123456789\ | ||
10 | 012" | ||
11 | |||
12 | IFS=2; for v in ${#a}; do echo Unquoted:"<$v>"; done | ||
13 | IFS=2; for v in "${#a}"; do echo Quoted:"<$v>"; done | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs3.right b/shell/hush_test/hush-vars/var_wordsplit_ifs3.right new file mode 100644 index 000000000..5ab72e1cd --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs3.right | |||
@@ -0,0 +1,12 @@ | |||
1 | Unquoted%:<q> | ||
2 | Unquoted%:<w> | ||
3 | Unquoted%:<e> | ||
4 | Unquoted%:<r> | ||
5 | Unquoted%:<t> | ||
6 | Unquoted#:<w> | ||
7 | Unquoted#:<e> | ||
8 | Unquoted#:<r> | ||
9 | Unquoted#:<t> | ||
10 | Unquoted#:<y> | ||
11 | Quoted%:<q w e r t > | ||
12 | Quoted#:< w e r t y> | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests new file mode 100755 index 000000000..4aa65574a --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | a="q w e r t y" | ||
2 | for v in ${a%y}; do echo Unquoted%:"<$v>"; done | ||
3 | for v in ${a#q}; do echo Unquoted#:"<$v>"; done | ||
4 | for v in "${a%y}"; do echo Quoted%:"<$v>"; done | ||
5 | for v in "${a#q}"; do echo Quoted#:"<$v>"; done | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 64a7abc47..837b3f7da 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
@@ -64,11 +64,12 @@ do_test() | |||
64 | echo -n "$1/$x:" | 64 | echo -n "$1/$x:" |
65 | ( | 65 | ( |
66 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 | 66 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 |
67 | r=$? | ||
67 | # filter C library differences | 68 | # filter C library differences |
68 | sed -i \ | 69 | sed -i \ |
69 | -e "/: invalid option /s:'::g" \ | 70 | -e "/: invalid option /s:'::g" \ |
70 | "$name.xx" | 71 | "$name.xx" |
71 | test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77 | 72 | test $r -eq 77 && rm -f "../$1-$x.fail" && exit 77 |
72 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" | 73 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" |
73 | ) | 74 | ) |
74 | case $? in | 75 | case $? in |
diff --git a/shell/msh_test/msh-bugs/noeol3.right b/shell/msh_test/msh-bugs/noeol3.right deleted file mode 100644 index 56f8515b7..000000000 --- a/shell/msh_test/msh-bugs/noeol3.right +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | hush: syntax error: unterminated " | ||
diff --git a/shell/msh_test/msh-bugs/noeol3.tests b/shell/msh_test/msh-bugs/noeol3.tests deleted file mode 100755 index ec958ed7a..000000000 --- a/shell/msh_test/msh-bugs/noeol3.tests +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | # last line has no EOL! | ||
2 | echo "unterminated \ No newline at end of file | ||
diff --git a/shell/msh_test/msh-bugs/process_subst.right b/shell/msh_test/msh-bugs/process_subst.right deleted file mode 100644 index 397bc8067..000000000 --- a/shell/msh_test/msh-bugs/process_subst.right +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | TESTzzBEST | ||
2 | TEST$(echo zz)BEST | ||
3 | TEST'BEST | ||
diff --git a/shell/msh_test/msh-bugs/process_subst.tests b/shell/msh_test/msh-bugs/process_subst.tests deleted file mode 100755 index 21996bc0e..000000000 --- a/shell/msh_test/msh-bugs/process_subst.tests +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | echo "TEST`echo zz;echo;echo`BEST" | ||
2 | echo "TEST`echo '$(echo zz)'`BEST" | ||
3 | echo "TEST`echo "'"`BEST" | ||
diff --git a/shell/msh_test/msh-bugs/starquoted.right b/shell/msh_test/msh-bugs/starquoted.right deleted file mode 100644 index b56323fe1..000000000 --- a/shell/msh_test/msh-bugs/starquoted.right +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | .1 abc d e f. | ||
2 | .1. | ||
3 | .abc. | ||
4 | .d e f. | ||
5 | .-1 abc d e f-. | ||
6 | .-1. | ||
7 | .abc. | ||
8 | .d e f-. | ||
diff --git a/shell/msh_test/msh-bugs/starquoted.tests b/shell/msh_test/msh-bugs/starquoted.tests deleted file mode 100755 index 2fe49b1cd..000000000 --- a/shell/msh_test/msh-bugs/starquoted.tests +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" "$0" 1 abc 'd e f' | ||
3 | fi | ||
4 | |||
5 | for a in "$*"; do echo ".$a."; done | ||
6 | for a in "$@"; do echo ".$a."; done | ||
7 | for a in "-$*-"; do echo ".$a."; done | ||
8 | for a in "-$@-"; do echo ".$a."; done | ||
diff --git a/shell/msh_test/msh-bugs/syntax_err.right b/shell/msh_test/msh-bugs/syntax_err.right deleted file mode 100644 index 08a270c31..000000000 --- a/shell/msh_test/msh-bugs/syntax_err.right +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | shown | ||
2 | hush: syntax error: unterminated ' | ||
diff --git a/shell/msh_test/msh-bugs/syntax_err.tests b/shell/msh_test/msh-bugs/syntax_err.tests deleted file mode 100755 index d10ed42e9..000000000 --- a/shell/msh_test/msh-bugs/syntax_err.tests +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | echo shown | ||
2 | echo test `echo 'aa` | ||
3 | echo not shown | ||
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.right b/shell/msh_test/msh-execution/exitcode_EACCES.right deleted file mode 100644 index 6e5480b9d..000000000 --- a/shell/msh_test/msh-execution/exitcode_EACCES.right +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | ./: can't execute | ||
2 | 126 | ||
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.right b/shell/msh_test/msh-execution/exitcode_ENOENT.right deleted file mode 100644 index dd49d2c0c..000000000 --- a/shell/msh_test/msh-execution/exitcode_ENOENT.right +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | ./does_not_exist_for_sure: not found | ||
2 | 127 | ||
diff --git a/shell/msh_test/msh-execution/many_continues.tests b/shell/msh_test/msh-execution/many_continues.tests deleted file mode 100755 index 86c729abc..000000000 --- a/shell/msh_test/msh-execution/many_continues.tests +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | if test $# = 0; then | ||
2 | # Child will kill us in 1 second | ||
3 | "$THIS_SH" "$0" $$ & | ||
4 | |||
5 | # Loop many, many times | ||
6 | trap 'echo OK; exit 0' 15 | ||
7 | while true; do | ||
8 | continue | ||
9 | done | ||
10 | echo BAD | ||
11 | exit 1 | ||
12 | fi | ||
13 | |||
14 | sleep 1 | ||
15 | kill $1 | ||
diff --git a/shell/msh_test/msh-execution/nested_break.right b/shell/msh_test/msh-execution/nested_break.right deleted file mode 100644 index 4e8b6b0f2..000000000 --- a/shell/msh_test/msh-execution/nested_break.right +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | A | ||
2 | B | ||
3 | iteration | ||
4 | C | ||
5 | A | ||
6 | B | ||
7 | iteration | ||
8 | D | ||
diff --git a/shell/msh_test/msh-execution/nested_break.tests b/shell/msh_test/msh-execution/nested_break.tests deleted file mode 100755 index 1a954d227..000000000 --- a/shell/msh_test/msh-execution/nested_break.tests +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | # Testcase for http://bugs.busybox.net/view.php?id=846 | ||
2 | |||
3 | n=0 | ||
4 | while : | ||
5 | do | ||
6 | echo A | ||
7 | while : | ||
8 | do | ||
9 | echo B | ||
10 | break | ||
11 | done | ||
12 | echo iteration | ||
13 | [ $n = 1 ] && break | ||
14 | echo C | ||
15 | n=`expr $n + 1` | ||
16 | done | ||
17 | echo D | ||
diff --git a/shell/msh_test/msh-misc/tick.tests b/shell/msh_test/msh-misc/tick.tests deleted file mode 100755 index 1f749a9cd..000000000 --- a/shell/msh_test/msh-misc/tick.tests +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | true | ||
2 | false; echo `echo $?` | ||
3 | true | ||
4 | { false; echo `echo $?`; } | ||
diff --git a/shell/msh_test/msh-parsing/argv0.tests b/shell/msh_test/msh-parsing/argv0.tests deleted file mode 100755 index f5c40f6fe..000000000 --- a/shell/msh_test/msh-parsing/argv0.tests +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" "$0" arg | ||
3 | fi | ||
4 | echo OK | ||
diff --git a/shell/msh_test/msh-parsing/noeol.right b/shell/msh_test/msh-parsing/noeol.right deleted file mode 100644 index e427984d4..000000000 --- a/shell/msh_test/msh-parsing/noeol.right +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | HELLO | ||
diff --git a/shell/msh_test/msh-parsing/noeol.tests b/shell/msh_test/msh-parsing/noeol.tests deleted file mode 100755 index a93113a03..000000000 --- a/shell/msh_test/msh-parsing/noeol.tests +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | # next line has no EOL! | ||
2 | echo HELLO \ No newline at end of file | ||
diff --git a/shell/msh_test/msh-parsing/noeol2.right b/shell/msh_test/msh-parsing/noeol2.right deleted file mode 100644 index d00491fd7..000000000 --- a/shell/msh_test/msh-parsing/noeol2.right +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | 1 | ||
diff --git a/shell/msh_test/msh-parsing/noeol2.tests b/shell/msh_test/msh-parsing/noeol2.tests deleted file mode 100755 index 1220f056f..000000000 --- a/shell/msh_test/msh-parsing/noeol2.tests +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | # last line has no EOL! | ||
2 | if true | ||
3 | then | ||
4 | echo 1 | ||
5 | else | ||
6 | echo 2 | ||
7 | fi \ No newline at end of file | ||
diff --git a/shell/msh_test/msh-parsing/quote1.right b/shell/msh_test/msh-parsing/quote1.right deleted file mode 100644 index cb382054c..000000000 --- a/shell/msh_test/msh-parsing/quote1.right +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | '1' | ||
diff --git a/shell/msh_test/msh-parsing/quote1.tests b/shell/msh_test/msh-parsing/quote1.tests deleted file mode 100755 index f55895466..000000000 --- a/shell/msh_test/msh-parsing/quote1.tests +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | a=1 | ||
2 | echo "'$a'" | ||
diff --git a/shell/msh_test/msh-parsing/quote2.right b/shell/msh_test/msh-parsing/quote2.right deleted file mode 100644 index 3bc9edcd6..000000000 --- a/shell/msh_test/msh-parsing/quote2.right +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | >1 | ||
diff --git a/shell/msh_test/msh-parsing/quote2.tests b/shell/msh_test/msh-parsing/quote2.tests deleted file mode 100755 index bd966f30b..000000000 --- a/shell/msh_test/msh-parsing/quote2.tests +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | a=1 | ||
2 | echo ">$a" | ||
diff --git a/shell/msh_test/msh-parsing/quote3.right b/shell/msh_test/msh-parsing/quote3.right deleted file mode 100644 index 069a46e8f..000000000 --- a/shell/msh_test/msh-parsing/quote3.right +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | Testing: in $empty"" | ||
2 | .. | ||
3 | Finished | ||
diff --git a/shell/msh_test/msh-parsing/quote3.tests b/shell/msh_test/msh-parsing/quote3.tests deleted file mode 100755 index 075e78570..000000000 --- a/shell/msh_test/msh-parsing/quote3.tests +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" quote3.tests abc "d e" | ||
3 | fi | ||
4 | |||
5 | echo 'Testing: in $empty""' | ||
6 | empty='' | ||
7 | for a in $empty""; do echo ".$a."; done | ||
8 | echo Finished | ||
diff --git a/shell/msh_test/msh-parsing/quote4.right b/shell/msh_test/msh-parsing/quote4.right deleted file mode 100644 index b2901ea97..000000000 --- a/shell/msh_test/msh-parsing/quote4.right +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | a b | ||
diff --git a/shell/msh_test/msh-parsing/quote4.tests b/shell/msh_test/msh-parsing/quote4.tests deleted file mode 100755 index f1dabfa54..000000000 --- a/shell/msh_test/msh-parsing/quote4.tests +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | a_b='a b' | ||
2 | echo "$a_b" | ||
diff --git a/shell/msh_test/msh-vars/var.right b/shell/msh_test/msh-vars/var.right deleted file mode 100644 index 14b2314d9..000000000 --- a/shell/msh_test/msh-vars/var.right +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | http://busybox.net | ||
2 | http://busybox.net_abc | ||
3 | 1 | ||
4 | 1 | ||
diff --git a/shell/msh_test/msh-vars/var.tests b/shell/msh_test/msh-vars/var.tests deleted file mode 100755 index 0a63696c9..000000000 --- a/shell/msh_test/msh-vars/var.tests +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | URL=http://busybox.net | ||
2 | |||
3 | echo $URL | ||
4 | echo ${URL}_abc | ||
5 | |||
6 | true | ||
7 | false; echo $? | ||
8 | true | ||
9 | { false; echo $?; } | ||
diff --git a/shell/msh_test/run-all b/shell/msh_test/run-all deleted file mode 100755 index 29f62a5e2..000000000 --- a/shell/msh_test/run-all +++ /dev/null | |||
@@ -1,64 +0,0 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | test -x msh || { | ||
4 | echo "No ./msh - creating a link to ../../busybox" | ||
5 | ln -s ../../busybox msh | ||
6 | } | ||
7 | |||
8 | PATH="$PWD:$PATH" # for msh | ||
9 | export PATH | ||
10 | |||
11 | THIS_SH="$PWD/msh" | ||
12 | export THIS_SH | ||
13 | |||
14 | do_test() | ||
15 | { | ||
16 | test -d "$1" || return 0 | ||
17 | # echo Running tests in directory "$1" | ||
18 | ( | ||
19 | cd "$1" || { echo "cannot cd $1!"; exit 1; } | ||
20 | for x in run-*; do | ||
21 | test -f "$x" || continue | ||
22 | case "$x" in | ||
23 | "$0"|run-minimal|run-gprof) ;; | ||
24 | *.orig|*~) ;; | ||
25 | #*) echo $x ; sh $x ;; | ||
26 | *) | ||
27 | sh "$x" >"../$1-$x.fail" 2>&1 && \ | ||
28 | { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; | ||
29 | ;; | ||
30 | esac | ||
31 | done | ||
32 | # Many bash run-XXX scripts just do this, | ||
33 | # no point in duplication it all over the place | ||
34 | for x in *.tests; do | ||
35 | test -x "$x" || continue | ||
36 | name="${x%%.tests}" | ||
37 | test -f "$name.right" || continue | ||
38 | # echo Running test: "$name.right" | ||
39 | { | ||
40 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 | ||
41 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" | ||
42 | } && echo "$1/$x: ok" || echo "$1/$x: fail" | ||
43 | done | ||
44 | ) | ||
45 | } | ||
46 | |||
47 | # Main part of this script | ||
48 | # Usage: run-all [directories] | ||
49 | |||
50 | if [ $# -lt 1 ]; then | ||
51 | # All sub directories | ||
52 | modules=`ls -d msh-*` | ||
53 | |||
54 | for module in $modules; do | ||
55 | do_test $module | ||
56 | done | ||
57 | else | ||
58 | while [ $# -ge 1 ]; do | ||
59 | if [ -d $1 ]; then | ||
60 | do_test $1 | ||
61 | fi | ||
62 | shift | ||
63 | done | ||
64 | fi | ||
diff --git a/shell/shell_common.c b/shell/shell_common.c index c3bff477d..5a5b1780d 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -25,6 +25,7 @@ const char defifsvar[] ALIGN1 = "IFS= \t\n"; | |||
25 | #else | 25 | #else |
26 | const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; | 26 | const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; |
27 | #endif | 27 | #endif |
28 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; | ||
28 | 29 | ||
29 | 30 | ||
30 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | 31 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) |
diff --git a/shell/shell_common.h b/shell/shell_common.h index 993ed5951..a82535c86 100644 --- a/shell/shell_common.h +++ b/shell/shell_common.h | |||
@@ -24,6 +24,8 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
24 | extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */ | 24 | extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */ |
25 | #define defifs (defifsvar + 4) | 25 | #define defifs (defifsvar + 4) |
26 | 26 | ||
27 | extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */ | ||
28 | |||
27 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); | 29 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); |
28 | 30 | ||
29 | /* Builtins */ | 31 | /* Builtins */ |
diff --git a/testsuite/gzip/gzip-compression-levels b/testsuite/gzip/gzip-compression-levels new file mode 100644 index 000000000..6d9a13d08 --- /dev/null +++ b/testsuite/gzip/gzip-compression-levels | |||
@@ -0,0 +1,5 @@ | |||
1 | # FEATURE: CONFIG_FEATURE_GZIP_LEVELS | ||
2 | |||
3 | level1=$(busybox gzip -c -1 $(which busybox) | wc -c) | ||
4 | level9=$(busybox gzip -c -9 $(which busybox) | wc -c) | ||
5 | test $level1 -gt $level9 | ||
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests index 6c75b6d1c..cca26dc64 100755 --- a/testsuite/md5sum.tests +++ b/testsuite/md5sum.tests | |||
@@ -23,6 +23,8 @@ test -f "$bindir/.config" && . "$bindir/.config" | |||
23 | test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \ | 23 | test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \ |
24 | && { echo "SKIPPED: $sum"; exit 0; } | 24 | && { echo "SKIPPED: $sum"; exit 0; } |
25 | 25 | ||
26 | FAILCOUNT=0 | ||
27 | |||
26 | text="The quick brown fox jumps over the lazy dog" | 28 | text="The quick brown fox jumps over the lazy dog" |
27 | text=`yes "$text" | head -c 9999` | 29 | text=`yes "$text" | head -c 9999` |
28 | 30 | ||
@@ -33,11 +35,21 @@ while test $n -le 999; do | |||
33 | n=$(($n+1)) | 35 | n=$(($n+1)) |
34 | done | "$sum" | 36 | done | "$sum" |
35 | )` | 37 | )` |
36 | 38 | if test x"$result" != x"$expected -"; then | |
37 | if test x"$result" = x"$expected -"; then | 39 | echo "FAIL: $sum (r:$result exp:$expected)" |
40 | : $((FAILCOUNT++)) | ||
41 | else | ||
38 | echo "PASS: $sum" | 42 | echo "PASS: $sum" |
39 | exit 0 | ||
40 | fi | 43 | fi |
41 | 44 | ||
42 | echo "FAIL: $sum (r:$result exp:$expected)" | 45 | # GNU compat: -c EMPTY must fail (exitcode 1)! |
43 | exit 1 | 46 | >EMPTY |
47 | if "$sum" -c EMPTY 2>/dev/null; then | ||
48 | echo "FAIL: $sum -c EMPTY" | ||
49 | : $((FAILCOUNT++)) | ||
50 | else | ||
51 | echo "PASS: $sum -c EMPTY" | ||
52 | fi | ||
53 | rm EMPTY | ||
54 | |||
55 | exit $FAILCOUNT | ||
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index a71f8b1f0..05c00a99b 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -73,13 +73,9 @@ testing "sed t (test/branch clears test bit)" "sed -e 's/a/b/;:loop;t loop'" \ | |||
73 | testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \ | 73 | testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \ |
74 | "1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n" | 74 | "1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n" |
75 | 75 | ||
76 | test x"$SKIP_KNOWN_BUGS" = x"" && { | ||
77 | # Normal sed end-of-script doesn't print "c" because n flushed the pattern | ||
78 | # space. If n hits EOF, pattern space is empty when script ends. | ||
79 | # Query: how does this interact with no newline at EOF? | ||
80 | testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \ | 76 | testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \ |
81 | "a\nb\nb\nc\n" "" "a\nb\nc\n" | 77 | "a\nb\nb\nc\n" "" "a\nb\nc\n" |
82 | } | 78 | |
83 | # non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end | 79 | # non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end |
84 | # GNU sed: N flushes pattern space, therefore c is printed too @ script end | 80 | # GNU sed: N flushes pattern space, therefore c is printed too @ script end |
85 | testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \ | 81 | testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \ |
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests index 82fada633..2cd8e3bf2 100755 --- a/testsuite/sha3sum.tests +++ b/testsuite/sha3sum.tests | |||
@@ -1,3 +1,3 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | 2 | ||
3 | . ./md5sum.tests sha3sum c29d77bc548fa2b20a04c861400a5360879c52156e2a54a3415b99a9a3123e1d5f36714a24eca8c1f05a8e2d8ba859c930d41141f64a255c6794436fc99c486a | 3 | . ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9 |
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 6391f9bd9..32a66d03d 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c | |||
@@ -363,7 +363,6 @@ struct globals { | |||
363 | 363 | ||
364 | jmp_buf listingbuf; | 364 | jmp_buf listingbuf; |
365 | char line_buffer[80]; | 365 | char line_buffer[80]; |
366 | char partname_buffer[80]; | ||
367 | /* Raw disk label. For DOS-type partition tables the MBR, | 366 | /* Raw disk label. For DOS-type partition tables the MBR, |
368 | * with descriptions of the primary partitions. */ | 367 | * with descriptions of the primary partitions. */ |
369 | char MBRbuffer[MAX_SECTOR_SIZE]; | 368 | char MBRbuffer[MAX_SECTOR_SIZE]; |
@@ -399,7 +398,6 @@ struct globals { | |||
399 | #define total_number_of_sectors (G.total_number_of_sectors) | 398 | #define total_number_of_sectors (G.total_number_of_sectors) |
400 | #define listingbuf (G.listingbuf ) | 399 | #define listingbuf (G.listingbuf ) |
401 | #define line_buffer (G.line_buffer ) | 400 | #define line_buffer (G.line_buffer ) |
402 | #define partname_buffer (G.partname_buffer) | ||
403 | #define MBRbuffer (G.MBRbuffer ) | 401 | #define MBRbuffer (G.MBRbuffer ) |
404 | #define ptes (G.ptes ) | 402 | #define ptes (G.ptes ) |
405 | #define INIT_G() do { \ | 403 | #define INIT_G() do { \ |
@@ -468,9 +466,6 @@ static sector_t bb_BLKGETSIZE_sectors(int fd) | |||
468 | 466 | ||
469 | #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) | 467 | #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) |
470 | 468 | ||
471 | #define hsc2sector(h,s,c) \ | ||
472 | (sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c))) | ||
473 | |||
474 | static void | 469 | static void |
475 | close_dev_fd(void) | 470 | close_dev_fd(void) |
476 | { | 471 | { |
@@ -478,9 +473,7 @@ close_dev_fd(void) | |||
478 | xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd); | 473 | xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd); |
479 | } | 474 | } |
480 | 475 | ||
481 | /* | 476 | /* Return partition name */ |
482 | * Return partition name - uses static storage | ||
483 | */ | ||
484 | static const char * | 477 | static const char * |
485 | partname(const char *dev, int pno, int lth) | 478 | partname(const char *dev, int pno, int lth) |
486 | { | 479 | { |
@@ -489,8 +482,8 @@ partname(const char *dev, int pno, int lth) | |||
489 | int bufsiz; | 482 | int bufsiz; |
490 | char *bufp; | 483 | char *bufp; |
491 | 484 | ||
492 | bufp = partname_buffer; | 485 | bufp = auto_string(xzalloc(80)); |
493 | bufsiz = sizeof(partname_buffer); | 486 | bufsiz = 80; |
494 | 487 | ||
495 | w = strlen(dev); | 488 | w = strlen(dev); |
496 | p = ""; | 489 | p = ""; |
@@ -1898,14 +1891,14 @@ check_consistency(const struct partition *p, int partition) | |||
1898 | return; /* do not check extended partitions */ | 1891 | return; /* do not check extended partitions */ |
1899 | 1892 | ||
1900 | /* physical beginning c, h, s */ | 1893 | /* physical beginning c, h, s */ |
1901 | pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300); | 1894 | pbc = cylinder(p->sector, p->cyl); |
1902 | pbh = p->head; | 1895 | pbh = p->head; |
1903 | pbs = p->sector & 0x3f; | 1896 | pbs = sector(p->sector); |
1904 | 1897 | ||
1905 | /* physical ending c, h, s */ | 1898 | /* physical ending c, h, s */ |
1906 | pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300); | 1899 | pec = cylinder(p->end_sector, p->end_cyl); |
1907 | peh = p->end_head; | 1900 | peh = p->end_head; |
1908 | pes = p->end_sector & 0x3f; | 1901 | pes = sector(p->end_sector); |
1909 | 1902 | ||
1910 | /* compute logical beginning (c, h, s) */ | 1903 | /* compute logical beginning (c, h, s) */ |
1911 | linear2chs(get_start_sect(p), &lbc, &lbh, &lbs); | 1904 | linear2chs(get_start_sect(p), &lbc, &lbh, &lbs); |
@@ -1916,17 +1909,17 @@ check_consistency(const struct partition *p, int partition) | |||
1916 | /* Same physical / logical beginning? */ | 1909 | /* Same physical / logical beginning? */ |
1917 | if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { | 1910 | if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { |
1918 | printf("Partition %u has different physical/logical " | 1911 | printf("Partition %u has different physical/logical " |
1919 | "beginnings (non-Linux?):\n", partition + 1); | 1912 | "start (non-Linux?):\n", partition + 1); |
1920 | printf(" phys=(%u, %u, %u) ", pbc, pbh, pbs); | 1913 | printf(" phys=(%u,%u,%u) ", pbc, pbh, pbs); |
1921 | printf("logical=(%u, %u, %u)\n", lbc, lbh, lbs); | 1914 | printf("logical=(%u,%u,%u)\n", lbc, lbh, lbs); |
1922 | } | 1915 | } |
1923 | 1916 | ||
1924 | /* Same physical / logical ending? */ | 1917 | /* Same physical / logical ending? */ |
1925 | if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { | 1918 | if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { |
1926 | printf("Partition %u has different physical/logical " | 1919 | printf("Partition %u has different physical/logical " |
1927 | "endings:\n", partition + 1); | 1920 | "end:\n", partition + 1); |
1928 | printf(" phys=(%u, %u, %u) ", pec, peh, pes); | 1921 | printf(" phys=(%u,%u,%u) ", pec, peh, pes); |
1929 | printf("logical=(%u, %u, %u)\n", lec, leh, les); | 1922 | printf("logical=(%u,%u,%u)\n", lec, leh, les); |
1930 | } | 1923 | } |
1931 | 1924 | ||
1932 | /* Ending on cylinder boundary? */ | 1925 | /* Ending on cylinder boundary? */ |
@@ -1940,22 +1933,23 @@ static void | |||
1940 | list_disk_geometry(void) | 1933 | list_disk_geometry(void) |
1941 | { | 1934 | { |
1942 | ullong bytes = ((ullong)total_number_of_sectors << 9); | 1935 | ullong bytes = ((ullong)total_number_of_sectors << 9); |
1943 | long megabytes = bytes / 1000000; | 1936 | ullong xbytes = bytes / (1024*1024); |
1944 | 1937 | char x = 'M'; | |
1945 | if (megabytes < 10000) | 1938 | |
1946 | printf("\nDisk %s: %lu MB, %llu bytes\n", | 1939 | if (xbytes >= 10000) { |
1947 | disk_device, megabytes, bytes); | 1940 | xbytes += 512; /* fdisk util-linux 2.28 does this */ |
1948 | else | 1941 | xbytes /= 1024; |
1949 | printf("\nDisk %s: %lu.%lu GB, %llu bytes\n", | 1942 | x = 'G'; |
1950 | disk_device, megabytes/1000, (megabytes/100)%10, bytes); | 1943 | } |
1951 | printf("%u heads, %u sectors/track, %u cylinders", | 1944 | printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n" |
1952 | g_heads, g_sectors, g_cylinders); | 1945 | "%u cylinders, %u heads, %u sectors/track\n" |
1953 | if (units_per_sector == 1) | 1946 | "Units: %s of %u * %u = %u bytes\n\n", |
1954 | printf(", total %"SECT_FMT"u sectors", | 1947 | disk_device, xbytes, x, |
1955 | total_number_of_sectors / (sector_size/512)); | 1948 | bytes, total_number_of_sectors, |
1956 | printf("\nUnits = %s of %u * %u = %u bytes\n\n", | 1949 | g_cylinders, g_heads, g_sectors, |
1957 | str_units(PLURAL), | 1950 | str_units(PLURAL), |
1958 | units_per_sector, sector_size, units_per_sector * sector_size); | 1951 | units_per_sector, sector_size, units_per_sector * sector_size |
1952 | ); | ||
1959 | } | 1953 | } |
1960 | 1954 | ||
1961 | /* | 1955 | /* |
@@ -2099,10 +2093,17 @@ fix_partition_table_order(void) | |||
2099 | } | 2093 | } |
2100 | #endif | 2094 | #endif |
2101 | 2095 | ||
2096 | static const char * | ||
2097 | chs_string11(unsigned cyl, unsigned head, unsigned sect) | ||
2098 | { | ||
2099 | char *buf = auto_string(xzalloc(sizeof(int)*3 * 3)); | ||
2100 | sprintf(buf, "%u,%u,%u", cylinder(sect,cyl), head, sector(sect)); | ||
2101 | return buf; | ||
2102 | } | ||
2103 | |||
2102 | static void | 2104 | static void |
2103 | list_table(int xtra) | 2105 | list_table(int xtra) |
2104 | { | 2106 | { |
2105 | const struct partition *p; | ||
2106 | int i, w; | 2107 | int i, w; |
2107 | 2108 | ||
2108 | if (LABEL_IS_SUN) { | 2109 | if (LABEL_IS_SUN) { |
@@ -2126,50 +2127,62 @@ list_table(int xtra) | |||
2126 | } | 2127 | } |
2127 | 2128 | ||
2128 | /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, | 2129 | /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, |
2129 | but if the device name ends in a digit, say /dev/foo1, | 2130 | * but if the device name ends in a digit, say /dev/foo1, |
2130 | then the partition is called /dev/foo1p3. */ | 2131 | * then the partition is called /dev/foo1p3. |
2132 | */ | ||
2131 | w = strlen(disk_device); | 2133 | w = strlen(disk_device); |
2132 | if (w && isdigit(disk_device[w-1])) | 2134 | if (w && isdigit(disk_device[w-1])) |
2133 | w++; | 2135 | w++; |
2134 | if (w < 5) | 2136 | if (w < 7) |
2135 | w = 5; | 2137 | w = 7; |
2136 | 2138 | ||
2137 | // 1 12345678901 12345678901 12345678901 12 | 2139 | printf("%-*s Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type\n", |
2138 | printf("%*s Boot Start End Blocks Id System\n", | 2140 | w-1, "Device"); |
2139 | w+1, "Device"); | ||
2140 | 2141 | ||
2141 | for (i = 0; i < g_partitions; i++) { | 2142 | for (i = 0; i < g_partitions; i++) { |
2143 | const struct partition *p; | ||
2142 | const struct pte *pe = &ptes[i]; | 2144 | const struct pte *pe = &ptes[i]; |
2143 | sector_t psects; | 2145 | char boot4[4]; |
2144 | sector_t pblocks; | 2146 | char numstr6[6]; |
2145 | unsigned podd; | 2147 | sector_t start_sect; |
2148 | sector_t end_sect; | ||
2149 | sector_t nr_sects; | ||
2146 | 2150 | ||
2147 | p = pe->part_table; | 2151 | p = pe->part_table; |
2148 | if (!p || is_cleared_partition(p)) | 2152 | if (!p || is_cleared_partition(p)) |
2149 | continue; | 2153 | continue; |
2150 | 2154 | ||
2151 | psects = get_nr_sects(p); | 2155 | sprintf(boot4, "%02x", p->boot_ind); |
2152 | pblocks = psects; | 2156 | if ((p->boot_ind & 0x7f) == 0) { |
2153 | podd = 0; | 2157 | /* 0x80 shown as '*', 0x00 is ' ' */ |
2154 | 2158 | boot4[0] = p->boot_ind ? '*' : ' '; | |
2155 | if (sector_size < 1024) { | 2159 | boot4[1] = ' '; |
2156 | pblocks /= (1024 / sector_size); | ||
2157 | podd = psects % (1024 / sector_size); | ||
2158 | } | 2160 | } |
2159 | if (sector_size > 1024) | ||
2160 | pblocks *= (sector_size / 1024); | ||
2161 | 2161 | ||
2162 | printf("%s %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n", | 2162 | start_sect = get_partition_start_from_dev_start(pe); |
2163 | partname(disk_device, i+1, w+2), | 2163 | end_sect = start_sect; |
2164 | !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */ | 2164 | nr_sects = get_nr_sects(p); |
2165 | ? '*' : '?', | 2165 | if (nr_sects != 0) |
2166 | cround(get_partition_start_from_dev_start(pe)), /* start */ | 2166 | end_sect += nr_sects - 1; |
2167 | cround(get_partition_start_from_dev_start(pe) + psects /* end */ | 2167 | |
2168 | - (psects ? 1 : 0)), | 2168 | smart_ulltoa5((ullong)nr_sects * sector_size, |
2169 | pblocks, podd ? '+' : ' ', /* odd flag on end */ | 2169 | numstr6, " KMGTPEZY")[0] = '\0'; |
2170 | p->sys_ind, /* type id */ | ||
2171 | partition_type(p->sys_ind)); /* type name */ | ||
2172 | 2170 | ||
2171 | #define SFMT SECT_FMT | ||
2172 | // Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type | ||
2173 | printf("%s%s %-11s"/**/" %-11s"/**/" %10"SFMT"u %10"SFMT"u %10"SFMT"u %s %2x %s\n", | ||
2174 | partname(disk_device, i+1, w+2), | ||
2175 | boot4, | ||
2176 | chs_string11(p->cyl, p->head, p->sector), | ||
2177 | chs_string11(p->end_cyl, p->end_head, p->end_sector), | ||
2178 | start_sect, | ||
2179 | end_sect, | ||
2180 | nr_sects, | ||
2181 | numstr6, | ||
2182 | p->sys_ind, | ||
2183 | partition_type(p->sys_ind) | ||
2184 | ); | ||
2185 | #undef SFMT | ||
2173 | check_consistency(p, i); | 2186 | check_consistency(p, i); |
2174 | } | 2187 | } |
2175 | 2188 | ||
@@ -2198,13 +2211,17 @@ x_list_table(int extend) | |||
2198 | p = (extend ? pe->ext_pointer : pe->part_table); | 2211 | p = (extend ? pe->ext_pointer : pe->part_table); |
2199 | if (p != NULL) { | 2212 | if (p != NULL) { |
2200 | printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n", | 2213 | printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n", |
2201 | i + 1, p->boot_ind, p->head, | 2214 | i + 1, p->boot_ind, |
2215 | p->head, | ||
2202 | sector(p->sector), | 2216 | sector(p->sector), |
2203 | cylinder(p->sector, p->cyl), p->end_head, | 2217 | cylinder(p->sector, p->cyl), |
2218 | p->end_head, | ||
2204 | sector(p->end_sector), | 2219 | sector(p->end_sector), |
2205 | cylinder(p->end_sector, p->end_cyl), | 2220 | cylinder(p->end_sector, p->end_cyl), |
2206 | get_start_sect(p), get_nr_sects(p), | 2221 | get_start_sect(p), |
2207 | p->sys_ind); | 2222 | get_nr_sects(p), |
2223 | p->sys_ind | ||
2224 | ); | ||
2208 | if (p->sys_ind) | 2225 | if (p->sys_ind) |
2209 | check_consistency(p, i); | 2226 | check_consistency(p, i); |
2210 | } | 2227 | } |
@@ -2261,6 +2278,7 @@ verify(void) | |||
2261 | { | 2278 | { |
2262 | int i, j; | 2279 | int i, j; |
2263 | sector_t total = 1; | 2280 | sector_t total = 1; |
2281 | sector_t chs_size; | ||
2264 | sector_t first[g_partitions], last[g_partitions]; | 2282 | sector_t first[g_partitions], last[g_partitions]; |
2265 | struct partition *p; | 2283 | struct partition *p; |
2266 | 2284 | ||
@@ -2322,11 +2340,14 @@ verify(void) | |||
2322 | } | 2340 | } |
2323 | } | 2341 | } |
2324 | 2342 | ||
2325 | if (total > g_heads * g_sectors * g_cylinders) | 2343 | chs_size = (sector_t)g_heads * g_sectors * g_cylinders; |
2326 | printf("Total allocated sectors %u greater than the maximum " | 2344 | if (total > chs_size) |
2327 | "%u\n", total, g_heads * g_sectors * g_cylinders); | 2345 | printf("Total allocated sectors %u" |
2346 | " greater than CHS size %"SECT_FMT"u\n", | ||
2347 | total, chs_size | ||
2348 | ); | ||
2328 | else { | 2349 | else { |
2329 | total = g_heads * g_sectors * g_cylinders - total; | 2350 | total = chs_size - total; |
2330 | if (total != 0) | 2351 | if (total != 0) |
2331 | printf("%"SECT_FMT"u unallocated sectors\n", total); | 2352 | printf("%"SECT_FMT"u unallocated sectors\n", total); |
2332 | } | 2353 | } |
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index 37514eb54..a59115dd4 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
@@ -543,8 +543,7 @@ static char *build_alias(char *alias, const char *device_name) | |||
543 | 543 | ||
544 | /* mknod in /dev based on a path like "/sys/block/hda/hda1" | 544 | /* mknod in /dev based on a path like "/sys/block/hda/hda1" |
545 | * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes | 545 | * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes |
546 | * after NUL, but we promise to not mangle (IOW: to restore NUL if needed) | 546 | * after NUL, but we promise to not mangle it (IOW: to restore NUL if needed). |
547 | * path string. | ||
548 | * NB2: "mdev -s" may call us many times, do not leak memory/fds! | 547 | * NB2: "mdev -s" may call us many times, do not leak memory/fds! |
549 | * | 548 | * |
550 | * device_name = $DEVNAME (may be NULL) | 549 | * device_name = $DEVNAME (may be NULL) |
@@ -810,41 +809,39 @@ static void make_device(char *device_name, char *path, int operation) | |||
810 | } /* for (;;) */ | 809 | } /* for (;;) */ |
811 | } | 810 | } |
812 | 811 | ||
813 | /* File callback for /sys/ traversal */ | 812 | /* File callback for /sys/ traversal. |
813 | * We act only on "/sys/.../dev" (pseudo)file | ||
814 | */ | ||
814 | static int FAST_FUNC fileAction(const char *fileName, | 815 | static int FAST_FUNC fileAction(const char *fileName, |
815 | struct stat *statbuf UNUSED_PARAM, | 816 | struct stat *statbuf UNUSED_PARAM, |
816 | void *userData, | 817 | void *userData, |
817 | int depth UNUSED_PARAM) | 818 | int depth UNUSED_PARAM) |
818 | { | 819 | { |
819 | size_t len = strlen(fileName) - 4; /* can't underflow */ | 820 | size_t len = strlen(fileName) - 4; /* can't underflow */ |
820 | char *scratch = userData; | 821 | char *path = userData; /* char array[PATH_MAX + SCRATCH_SIZE] */ |
821 | 822 | char subsys[PATH_MAX]; | |
822 | /* len check is for paranoid reasons */ | 823 | int res; |
823 | if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX) | 824 | |
824 | return FALSE; | 825 | /* Is it a ".../dev" file? (len check is for paranoid reasons) */ |
825 | 826 | if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32) | |
826 | strcpy(scratch, fileName); | 827 | return FALSE; /* not .../dev */ |
827 | scratch[len] = '\0'; | 828 | |
828 | make_device(/*DEVNAME:*/ NULL, scratch, OP_add); | 829 | strcpy(path, fileName); |
829 | 830 | path[len] = '\0'; | |
830 | return TRUE; | 831 | |
831 | } | 832 | /* Read ".../subsystem" symlink in the same directory where ".../dev" is */ |
832 | 833 | strcpy(subsys, path); | |
833 | /* Directory callback for /sys/ traversal */ | 834 | strcpy(subsys + len, "/subsystem"); |
834 | static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, | 835 | res = readlink(subsys, subsys, sizeof(subsys)-1); |
835 | struct stat *statbuf UNUSED_PARAM, | 836 | if (res > 0) { |
836 | void *userData UNUSED_PARAM, | 837 | subsys[res] = '\0'; |
837 | int depth) | ||
838 | { | ||
839 | /* Extract device subsystem -- the name of the directory | ||
840 | * under /sys/class/ */ | ||
841 | if (1 == depth) { | ||
842 | free(G.subsystem); | 838 | free(G.subsystem); |
843 | if (G.subsys_env) { | 839 | if (G.subsys_env) { |
844 | bb_unsetenv_and_free(G.subsys_env); | 840 | bb_unsetenv_and_free(G.subsys_env); |
845 | G.subsys_env = NULL; | 841 | G.subsys_env = NULL; |
846 | } | 842 | } |
847 | G.subsystem = strrchr(fileName, '/'); | 843 | /* Set G.subsystem and $SUBSYSTEM from symlink's last component */ |
844 | G.subsystem = strrchr(subsys, '/'); | ||
848 | if (G.subsystem) { | 845 | if (G.subsystem) { |
849 | G.subsystem = xstrdup(G.subsystem + 1); | 846 | G.subsystem = xstrdup(G.subsystem + 1); |
850 | G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); | 847 | G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); |
@@ -852,6 +849,17 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, | |||
852 | } | 849 | } |
853 | } | 850 | } |
854 | 851 | ||
852 | make_device(/*DEVNAME:*/ NULL, path, OP_add); | ||
853 | |||
854 | return TRUE; | ||
855 | } | ||
856 | |||
857 | /* Directory callback for /sys/ traversal */ | ||
858 | static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, | ||
859 | struct stat *statbuf UNUSED_PARAM, | ||
860 | void *userData UNUSED_PARAM, | ||
861 | int depth) | ||
862 | { | ||
855 | return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); | 863 | return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); |
856 | } | 864 | } |
857 | 865 | ||
@@ -872,8 +880,9 @@ static void load_firmware(const char *firmware, const char *sysfs_path) | |||
872 | int firmware_fd, loading_fd; | 880 | int firmware_fd, loading_fd; |
873 | 881 | ||
874 | /* check for /lib/firmware/$FIRMWARE */ | 882 | /* check for /lib/firmware/$FIRMWARE */ |
875 | xchdir("/lib/firmware"); | 883 | firmware_fd = -1; |
876 | firmware_fd = open(firmware, O_RDONLY); /* can fail */ | 884 | if (chdir("/lib/firmware") == 0) |
885 | firmware_fd = open(firmware, O_RDONLY); /* can fail */ | ||
877 | 886 | ||
878 | /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */ | 887 | /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */ |
879 | xchdir(sysfs_path); | 888 | xchdir(sysfs_path); |
@@ -1065,25 +1074,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
1065 | 1074 | ||
1066 | putenv((char*)"ACTION=add"); | 1075 | putenv((char*)"ACTION=add"); |
1067 | 1076 | ||
1068 | /* ACTION_FOLLOWLINKS is needed since in newer kernels | 1077 | /* Create all devices from /sys/dev hierarchy */ |
1069 | * /sys/block/loop* (for example) are symlinks to dirs, | 1078 | recursive_action("/sys/dev", |
1070 | * not real directories. | 1079 | ACTION_RECURSE | ACTION_FOLLOWLINKS, |
1071 | * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs, | 1080 | fileAction, dirAction, temp, 0); |
1072 | * but we can't enforce that on users) | ||
1073 | */ | ||
1074 | if (access("/sys/class/block", F_OK) != 0) { | ||
1075 | /* Scan obsolete /sys/block only if /sys/class/block | ||
1076 | * doesn't exist. Otherwise we'll have dupes. | ||
1077 | * Also, do not complain if it doesn't exist. | ||
1078 | * Some people configure kernel to have no blockdevs. | ||
1079 | */ | ||
1080 | recursive_action("/sys/block", | ||
1081 | ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET, | ||
1082 | fileAction, dirAction, temp, 0); | ||
1083 | } | ||
1084 | recursive_action("/sys/class", | ||
1085 | ACTION_RECURSE | ACTION_FOLLOWLINKS, | ||
1086 | fileAction, dirAction, temp, 0); | ||
1087 | } else { | 1081 | } else { |
1088 | char *fw; | 1082 | char *fw; |
1089 | char *seq; | 1083 | char *seq; |
diff --git a/util-linux/mount.c b/util-linux/mount.c index 13590ceb4..eb8b7ba7b 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c | |||
@@ -1931,7 +1931,6 @@ static int singlemount(struct mntent *mp, int ignore_busy) | |||
1931 | int len; | 1931 | int len; |
1932 | char c; | 1932 | char c; |
1933 | char *hostname, *share; | 1933 | char *hostname, *share; |
1934 | char *dotted, *ip; | ||
1935 | len_and_sockaddr *lsa; | 1934 | len_and_sockaddr *lsa; |
1936 | 1935 | ||
1937 | // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]" | 1936 | // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]" |
@@ -1971,13 +1970,26 @@ static int singlemount(struct mntent *mp, int ignore_busy) | |||
1971 | if (!lsa) | 1970 | if (!lsa) |
1972 | goto report_error; | 1971 | goto report_error; |
1973 | 1972 | ||
1974 | // Insert "ip=..." option into options | 1973 | // If there is no "ip=..." option yet |
1975 | dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); | 1974 | if (!is_prefixed_with(filteropts, ",ip="+1) |
1976 | if (ENABLE_FEATURE_CLEAN_UP) free(lsa); | 1975 | && !strstr(filteropts, ",ip=") |
1977 | ip = xasprintf("ip=%s", dotted); | 1976 | ) { |
1978 | if (ENABLE_FEATURE_CLEAN_UP) free(dotted); | 1977 | char *dotted, *ip; |
1979 | parse_mount_options(ip, &filteropts); | 1978 | // Insert "ip=..." option into options |
1980 | if (ENABLE_FEATURE_CLEAN_UP) free(ip); | 1979 | dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); |
1980 | if (ENABLE_FEATURE_CLEAN_UP) free(lsa); | ||
1981 | ip = xasprintf("ip=%s", dotted); | ||
1982 | if (ENABLE_FEATURE_CLEAN_UP) free(dotted); | ||
1983 | // Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be | ||
1984 | // handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()). | ||
1985 | // Currently, glibc does not support that (has no NI_NUMERICSCOPE), | ||
1986 | // musl apparently does. This results in "ip=numericIPv6%iface_name" | ||
1987 | // (instead of _numeric_ iface_id) with glibc. | ||
1988 | // This probably should be fixed in glibc, not here. | ||
1989 | // The workaround is to manually specify correct "ip=ADDR%n" option. | ||
1990 | parse_mount_options(ip, &filteropts); | ||
1991 | if (ENABLE_FEATURE_CLEAN_UP) free(ip); | ||
1992 | } | ||
1981 | 1993 | ||
1982 | mp->mnt_type = (char*)"cifs"; | 1994 | mp->mnt_type = (char*)"cifs"; |
1983 | rc = mount_it_now(mp, vfsflags, filteropts); | 1995 | rc = mount_it_now(mp, vfsflags, filteropts); |
diff --git a/util-linux/unshare.c b/util-linux/unshare.c index fa7086add..dcc59559a 100644 --- a/util-linux/unshare.c +++ b/util-linux/unshare.c | |||
@@ -103,7 +103,7 @@ enum { | |||
103 | OPT_mount = 1 << 0, | 103 | OPT_mount = 1 << 0, |
104 | OPT_uts = 1 << 1, | 104 | OPT_uts = 1 << 1, |
105 | OPT_ipc = 1 << 2, | 105 | OPT_ipc = 1 << 2, |
106 | OPT_network = 1 << 3, | 106 | OPT_net = 1 << 3, |
107 | OPT_pid = 1 << 4, | 107 | OPT_pid = 1 << 4, |
108 | OPT_user = 1 << 5, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */ | 108 | OPT_user = 1 << 5, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */ |
109 | OPT_fork = 1 << 6, | 109 | OPT_fork = 1 << 6, |
@@ -142,7 +142,7 @@ static const char unshare_longopts[] ALIGN1 = | |||
142 | "mount\0" Optional_argument "\xf0" | 142 | "mount\0" Optional_argument "\xf0" |
143 | "uts\0" Optional_argument "\xf1" | 143 | "uts\0" Optional_argument "\xf1" |
144 | "ipc\0" Optional_argument "\xf2" | 144 | "ipc\0" Optional_argument "\xf2" |
145 | "network\0" Optional_argument "\xf3" | 145 | "net\0" Optional_argument "\xf3" |
146 | "pid\0" Optional_argument "\xf4" | 146 | "pid\0" Optional_argument "\xf4" |
147 | "user\0" Optional_argument "\xf5" | 147 | "user\0" Optional_argument "\xf5" |
148 | "fork\0" No_argument "f" | 148 | "fork\0" No_argument "f" |
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c index 6b97df113..b64d28ceb 100644 --- a/util-linux/volume_id/get_devname.c +++ b/util-linux/volume_id/get_devname.c | |||
@@ -107,7 +107,11 @@ uuidcache_check_device(const char *device, | |||
107 | int depth UNUSED_PARAM) | 107 | int depth UNUSED_PARAM) |
108 | { | 108 | { |
109 | /* note: this check rejects links to devices, among other nodes */ | 109 | /* note: this check rejects links to devices, among other nodes */ |
110 | if (!S_ISBLK(statbuf->st_mode)) | 110 | if (!S_ISBLK(statbuf->st_mode) |
111 | #if ENABLE_FEATURE_VOLUMEID_UBIFS | ||
112 | && !(S_ISCHR(statbuf->st_mode) && strncmp(bb_basename(device), "ubi", 3) == 0) | ||
113 | #endif | ||
114 | ) | ||
111 | return TRUE; | 115 | return TRUE; |
112 | 116 | ||
113 | /* Users report that mucking with floppies (especially non-present | 117 | /* Users report that mucking with floppies (especially non-present |
diff --git a/util-linux/volume_id/ubifs.c b/util-linux/volume_id/ubifs.c new file mode 100644 index 000000000..13604ec35 --- /dev/null +++ b/util-linux/volume_id/ubifs.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * volume_id - reads filesystem label and uuid | ||
3 | * | ||
4 | * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org> | ||
5 | * | ||
6 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
7 | */ | ||
8 | |||
9 | //kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_UBIFS) += ubifs.o | ||
10 | |||
11 | //config: | ||
12 | //config:config FEATURE_VOLUMEID_UBIFS | ||
13 | //config: bool "UBIFS filesystem" | ||
14 | //config: default y | ||
15 | //config: depends on VOLUMEID | ||
16 | //config: help | ||
17 | //config: UBIFS (Unsorted Block Image File System) is a file | ||
18 | //config: system for use with raw flash memory media. | ||
19 | //config: | ||
20 | |||
21 | #include "volume_id_internal.h" | ||
22 | |||
23 | #define UBIFS_NODE_MAGIC 0x06101831 | ||
24 | |||
25 | /* | ||
26 | * struct ubifs_ch - common header node. | ||
27 | * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) | ||
28 | * @crc: CRC-32 checksum of the node header | ||
29 | * @sqnum: sequence number | ||
30 | * @len: full node length | ||
31 | * @node_type: node type | ||
32 | * @group_type: node group type | ||
33 | * @padding: reserved for future, zeroes | ||
34 | * | ||
35 | * Every UBIFS node starts with this common part. If the node has a key, the | ||
36 | * key always goes next. | ||
37 | */ | ||
38 | struct ubifs_ch { | ||
39 | uint32_t magic; | ||
40 | uint32_t crc; | ||
41 | uint64_t sqnum; | ||
42 | uint32_t len; | ||
43 | uint8_t node_type; | ||
44 | uint8_t group_type; | ||
45 | uint8_t padding[2]; | ||
46 | } PACKED; | ||
47 | |||
48 | /* | ||
49 | * struct ubifs_sb_node - superblock node. | ||
50 | * @ch: common header | ||
51 | * @padding: reserved for future, zeroes | ||
52 | * @key_hash: type of hash function used in keys | ||
53 | * @key_fmt: format of the key | ||
54 | * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) | ||
55 | * @min_io_size: minimal input/output unit size | ||
56 | * @leb_size: logical eraseblock size in bytes | ||
57 | * @leb_cnt: count of LEBs used by file-system | ||
58 | * @max_leb_cnt: maximum count of LEBs used by file-system | ||
59 | * @max_bud_bytes: maximum amount of data stored in buds | ||
60 | * @log_lebs: log size in logical eraseblocks | ||
61 | * @lpt_lebs: number of LEBs used for lprops table | ||
62 | * @orph_lebs: number of LEBs used for recording orphans | ||
63 | * @jhead_cnt: count of journal heads | ||
64 | * @fanout: tree fanout (max. number of links per indexing node) | ||
65 | * @lsave_cnt: number of LEB numbers in LPT's save table | ||
66 | * @fmt_version: UBIFS on-flash format version | ||
67 | * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc) | ||
68 | * @padding1: reserved for future, zeroes | ||
69 | * @rp_uid: reserve pool UID | ||
70 | * @rp_gid: reserve pool GID | ||
71 | * @rp_size: size of the reserved pool in bytes | ||
72 | * @padding2: reserved for future, zeroes | ||
73 | * @time_gran: time granularity in nanoseconds | ||
74 | * @uuid: UUID generated when the file system image was created | ||
75 | * @ro_compat_version: UBIFS R/O compatibility version | ||
76 | */ | ||
77 | struct ubifs_sb_node { | ||
78 | struct ubifs_ch ch; | ||
79 | uint8_t padding[2]; | ||
80 | uint8_t key_hash; | ||
81 | uint8_t key_fmt; | ||
82 | uint32_t flags; | ||
83 | uint32_t min_io_size; | ||
84 | uint32_t leb_size; | ||
85 | uint32_t leb_cnt; | ||
86 | uint32_t max_leb_cnt; | ||
87 | uint64_t max_bud_bytes; | ||
88 | uint32_t log_lebs; | ||
89 | uint32_t lpt_lebs; | ||
90 | uint32_t orph_lebs; | ||
91 | uint32_t jhead_cnt; | ||
92 | uint32_t fanout; | ||
93 | uint32_t lsave_cnt; | ||
94 | uint32_t fmt_version; | ||
95 | uint16_t default_compr; | ||
96 | uint8_t padding1[2]; | ||
97 | uint32_t rp_uid; | ||
98 | uint32_t rp_gid; | ||
99 | uint64_t rp_size; | ||
100 | uint32_t time_gran; | ||
101 | uint8_t uuid[16]; | ||
102 | uint32_t ro_compat_version; | ||
103 | /* | ||
104 | uint8_t padding2[3968]; | ||
105 | */ | ||
106 | } PACKED; | ||
107 | |||
108 | int FAST_FUNC volume_id_probe_ubifs(struct volume_id *id /*,uint64_t off*/) | ||
109 | { | ||
110 | #define off ((uint64_t)0) | ||
111 | struct ubifs_sb_node *sb; | ||
112 | |||
113 | dbg("UBIFS: probing at offset 0x%llx", (unsigned long long) off); | ||
114 | sb = volume_id_get_buffer(id, off, sizeof(struct ubifs_sb_node)); | ||
115 | if (!sb) | ||
116 | return -1; | ||
117 | |||
118 | if (le32_to_cpu(sb->ch.magic) != UBIFS_NODE_MAGIC) | ||
119 | return -1; | ||
120 | |||
121 | IF_FEATURE_BLKID_TYPE(id->type = "ubifs";) | ||
122 | volume_id_set_uuid(id, sb->uuid, UUID_DCE); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
diff --git a/util-linux/volume_id/volume_id.c b/util-linux/volume_id/volume_id.c index 3f71e0084..5bb95994b 100644 --- a/util-linux/volume_id/volume_id.c +++ b/util-linux/volume_id/volume_id.c | |||
@@ -168,6 +168,9 @@ static const probe_fptr fs2[] = { | |||
168 | #if ENABLE_FEATURE_VOLUMEID_OCFS2 | 168 | #if ENABLE_FEATURE_VOLUMEID_OCFS2 |
169 | volume_id_probe_ocfs2, | 169 | volume_id_probe_ocfs2, |
170 | #endif | 170 | #endif |
171 | #if ENABLE_FEATURE_VOLUMEID_UBIFS | ||
172 | volume_id_probe_ubifs, | ||
173 | #endif | ||
171 | }; | 174 | }; |
172 | 175 | ||
173 | int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size) | 176 | int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size) |
diff --git a/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h index 3061ac4d5..759a832e6 100644 --- a/util-linux/volume_id/volume_id_internal.h +++ b/util-linux/volume_id/volume_id_internal.h | |||
@@ -221,4 +221,6 @@ int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/); | |||
221 | 221 | ||
222 | int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/); | 222 | int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/); |
223 | 223 | ||
224 | int FAST_FUNC volume_id_probe_ubifs(struct volume_id *id /*,uint64_t off*/); | ||
225 | |||
224 | POP_SAVED_FUNCTION_VISIBILITY | 226 | POP_SAVED_FUNCTION_VISIBILITY |