summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 14:09:23 +1000
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 14:09:23 +1000
commitee7c9b2c212fc7db80cce945e094fc2601092283 (patch)
tree24e51b27dbc3e9ab0b00c5839a6822604c02187c
parentf28d4b20905b5b1f52ffa52060a0c6caf4b055ba (diff)
parent99862cbfad9c36b4f8f4378c3a7a9f077c239f20 (diff)
downloadbusybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.tar.gz
busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.tar.bz2
busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.zip
Merge remote branch 'origin/master'
-rw-r--r--TEST_config_nommu1
-rw-r--r--TEST_config_noprintf1
-rw-r--r--TEST_config_rh91
-rw-r--r--TODO2
-rw-r--r--archival/dpkg.c2
-rw-r--r--archival/lzop.c37
-rw-r--r--coreutils/ls.c3
-rw-r--r--coreutils/tee.c8
-rw-r--r--coreutils/uudecode.c53
-rw-r--r--editors/ed.c10
-rw-r--r--include/applets.src.h1
-rw-r--r--include/libbb.h4
-rw-r--r--include/usage.src.h2
-rw-r--r--libbb/Config.src4
-rw-r--r--libbb/appletlib.c1
-rw-r--r--libbb/default_error_retval.c2
-rw-r--r--libbb/lineedit.c742
-rw-r--r--libbb/unicode.c2
-rw-r--r--modutils/modutils-24.c2
-rw-r--r--networking/ifupdown.c6
-rw-r--r--networking/nc_bloaty.c2
-rw-r--r--networking/slattach.c2
-rw-r--r--procps/ps.c8
-rw-r--r--procps/smemcap.c2
-rw-r--r--runit/chpst.c1
-rw-r--r--runit/runit_lib.c18
-rw-r--r--runit/runit_lib.h12
-rw-r--r--runit/runsv.c3
-rw-r--r--shell/ash.c388
-rwxr-xr-xshell/ash_test/ash-vars/var_bash3.tests7
-rwxr-xr-xshell/ash_test/ash-vars/var_bash5.tests4
-rw-r--r--shell/bbsh.c223
-rw-r--r--shell/hush.c5151
-rw-r--r--[-rwxr-xr-x]shell/hush_test/hush-bugs/export_exp.tests.disabled (renamed from shell/hush_test/hush-bugs/export_exp.tests)3
-rw-r--r--shell/hush_test/hush-glob/glob2.right18
-rwxr-xr-xshell/hush_test/hush-glob/glob2.tests27
-rw-r--r--shell/hush_test/hush-misc/heredoc_backslash1.right43
-rwxr-xr-xshell/hush_test/hush-misc/heredoc_backslash1.tests70
-rw-r--r--shell/hush_test/hush-psubst/tick3.right2
-rwxr-xr-xshell/hush_test/hush-psubst/tick3.tests6
-rw-r--r--shell/hush_test/hush-trap/exit.right10
-rwxr-xr-xshell/hush_test/hush-trap/exit.tests31
-rwxr-xr-xshell/hush_test/hush-trap/subshell.tests11
-rw-r--r--shell/hush_test/hush-vars/var_bash1.right14
-rwxr-xr-xshell/hush_test/hush-vars/var_bash1.tests18
-rw-r--r--shell/hush_test/hush-vars/var_bash2.right10
-rwxr-xr-xshell/hush_test/hush-vars/var_bash2.tests24
-rw-r--r--shell/hush_test/hush-vars/var_bash3.right20
-rwxr-xr-xshell/hush_test/hush-vars/var_bash3.tests41
-rw-r--r--shell/hush_test/hush-vars/var_bash4.right40
-rwxr-xr-xshell/hush_test/hush-vars/var_bash4.tests81
-rw-r--r--shell/hush_test/hush-vars/var_bash5.right11
-rwxr-xr-xshell/hush_test/hush-vars/var_bash5.tests29
-rw-r--r--shell/hush_test/hush-vars/var_bash6.right5
-rwxr-xr-xshell/hush_test/hush-vars/var_bash6.tests9
-rw-r--r--shell/hush_test/hush-vars/var_unbackslash.right11
-rwxr-xr-xshell/hush_test/hush-vars/var_unbackslash.tests23
-rw-r--r--shell/match.c132
-rw-r--r--shell/match.h29
-rw-r--r--shell/math.c14
-rw-r--r--shell/math.h9
-rwxr-xr-xtestsuite/makedevs.tests5
-rwxr-xr-xtestsuite/printf.tests6
-rwxr-xr-xtestsuite/tar.tests50
-rw-r--r--util-linux/blockdev.c195
65 files changed, 4215 insertions, 3487 deletions
diff --git a/TEST_config_nommu b/TEST_config_nommu
index 911f02f6b..18f448391 100644
--- a/TEST_config_nommu
+++ b/TEST_config_nommu
@@ -904,7 +904,6 @@ CONFIG_HUSH_FUNCTIONS=y
904CONFIG_HUSH_LOCAL=y 904CONFIG_HUSH_LOCAL=y
905CONFIG_HUSH_EXPORT_N=y 905CONFIG_HUSH_EXPORT_N=y
906CONFIG_HUSH_RANDOM_SUPPORT=y 906CONFIG_HUSH_RANDOM_SUPPORT=y
907CONFIG_LASH=y
908CONFIG_MSH=y 907CONFIG_MSH=y
909CONFIG_SH_MATH_SUPPORT=y 908CONFIG_SH_MATH_SUPPORT=y
910CONFIG_SH_MATH_SUPPORT_64=y 909CONFIG_SH_MATH_SUPPORT_64=y
diff --git a/TEST_config_noprintf b/TEST_config_noprintf
index ba003a1fb..22525ecb6 100644
--- a/TEST_config_noprintf
+++ b/TEST_config_noprintf
@@ -904,7 +904,6 @@ CONFIG_FEATURE_SH_IS_NONE=y
904# CONFIG_FEATURE_BASH_IS_ASH is not set 904# CONFIG_FEATURE_BASH_IS_ASH is not set
905# CONFIG_FEATURE_BASH_IS_HUSH is not set 905# CONFIG_FEATURE_BASH_IS_HUSH is not set
906CONFIG_FEATURE_BASH_IS_NONE=y 906CONFIG_FEATURE_BASH_IS_NONE=y
907# CONFIG_LASH is not set
908# CONFIG_MSH is not set 907# CONFIG_MSH is not set
909# CONFIG_SH_MATH_SUPPORT is not set 908# CONFIG_SH_MATH_SUPPORT is not set
910# CONFIG_SH_MATH_SUPPORT_64 is not set 909# CONFIG_SH_MATH_SUPPORT_64 is not set
diff --git a/TEST_config_rh9 b/TEST_config_rh9
index 3ffb1c6f3..e45608351 100644
--- a/TEST_config_rh9
+++ b/TEST_config_rh9
@@ -918,7 +918,6 @@ CONFIG_HUSH_FUNCTIONS=y
918CONFIG_HUSH_LOCAL=y 918CONFIG_HUSH_LOCAL=y
919CONFIG_HUSH_EXPORT_N=y 919CONFIG_HUSH_EXPORT_N=y
920CONFIG_HUSH_RANDOM_SUPPORT=y 920CONFIG_HUSH_RANDOM_SUPPORT=y
921# CONFIG_LASH is not set
922CONFIG_MSH=y 921CONFIG_MSH=y
923CONFIG_SH_MATH_SUPPORT=y 922CONFIG_SH_MATH_SUPPORT=y
924CONFIG_SH_MATH_SUPPORT_64=y 923CONFIG_SH_MATH_SUPPORT_64=y
diff --git a/TODO b/TODO
index 6f8cd8a32..8b9f87f79 100644
--- a/TODO
+++ b/TODO
@@ -82,7 +82,7 @@ Rob Landley suggested this:
82 82
83 initramfs 83 initramfs
84 Busybox should have a sample initramfs build script. This depends on 84 Busybox should have a sample initramfs build script. This depends on
85 bbsh, mdev, and switch_root. 85 shell, mdev, and switch_root.
86 86
87 mkdep 87 mkdep
88 Write a mkdep that doesn't segfault if there's a directory it doesn't 88 Write a mkdep that doesn't segfault if there's a directory it doesn't
diff --git a/archival/dpkg.c b/archival/dpkg.c
index 219512b9a..b36c26198 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -12,7 +12,7 @@
12 * 12 *
13 * started life as a busybox implementation of udpkg 13 * started life as a busybox implementation of udpkg
14 * 14 *
15 * licensed under gplv2 or later, see file license in this tarball for details. 15 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
16 */ 16 */
17 17
18/* 18/*
diff --git a/archival/lzop.c b/archival/lzop.c
index ab4d34c88..c6e718ad7 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -697,10 +697,16 @@ static NOINLINE smallint lzo_compress(const header_t *h)
697 return ok; 697 return ok;
698} 698}
699 699
700static void lzo_check(uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned), 700static FAST_FUNC void lzo_check(
701 uint32_t ref, uint32_t init, 701 uint32_t init,
702 uint8_t* buf, unsigned len) 702 uint8_t* buf, unsigned len,
703 uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned),
704 uint32_t ref)
703{ 705{
706 /* This function, by having the same order of parameters
707 * as fn, and by being marked FAST_FUNC (same as fn),
708 * saves a dozen bytes of code.
709 */
704 uint32_t c = fn(init, buf, len); 710 uint32_t c = fn(init, buf, len);
705 if (c != ref) 711 if (c != ref)
706 bb_error_msg_and_die("checksum error"); 712 bb_error_msg_and_die("checksum error");
@@ -747,9 +753,8 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
747 753
748 if (dst_len > block_size) { 754 if (dst_len > block_size) {
749 if (b2) { 755 if (b2) {
750//FIXME!
751 b2 = NULL;
752 free(b2); 756 free(b2);
757 b2 = NULL;
753 } 758 }
754 block_size = dst_len; 759 block_size = dst_len;
755 mcs_block_size = MAX_COMPRESSED_SIZE(block_size); 760 mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
@@ -781,13 +786,13 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
781 if (!(option_mask32 & OPT_F)) { 786 if (!(option_mask32 & OPT_F)) {
782 /* verify checksum of compressed block */ 787 /* verify checksum of compressed block */
783 if (h->flags & F_ADLER32_C) 788 if (h->flags & F_ADLER32_C)
784 lzo_check(lzo_adler32, c_adler32, 789 lzo_check(ADLER32_INIT_VALUE,
785 ADLER32_INIT_VALUE, 790 b1, src_len,
786 b1, src_len); 791 lzo_adler32, c_adler32);
787 if (h->flags & F_CRC32_C) 792 if (h->flags & F_CRC32_C)
788 lzo_check(lzo_crc32, c_crc32, 793 lzo_check(CRC32_INIT_VALUE,
789 CRC32_INIT_VALUE, 794 b1, src_len,
790 b1, src_len); 795 lzo_crc32, c_crc32);
791 } 796 }
792 797
793 /* decompress */ 798 /* decompress */
@@ -808,11 +813,13 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
808 if (!(option_mask32 & OPT_F)) { 813 if (!(option_mask32 & OPT_F)) {
809 /* verify checksum of uncompressed block */ 814 /* verify checksum of uncompressed block */
810 if (h->flags & F_ADLER32_D) 815 if (h->flags & F_ADLER32_D)
811 lzo_check(lzo_adler32, d_adler32, ADLER32_INIT_VALUE, 816 lzo_check(ADLER32_INIT_VALUE,
812 dst, dst_len); 817 dst, dst_len,
818 lzo_adler32, d_adler32);
813 if (h->flags & F_CRC32_D) 819 if (h->flags & F_CRC32_D)
814 lzo_check(lzo_crc32, d_crc32, CRC32_INIT_VALUE, 820 lzo_check(CRC32_INIT_VALUE,
815 dst, dst_len); 821 dst, dst_len,
822 lzo_crc32, d_crc32);
816 } 823 }
817 824
818 /* write uncompressed block data */ 825 /* write uncompressed block data */
diff --git a/coreutils/ls.c b/coreutils/ls.c
index ab66b42c1..1a670201c 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -573,7 +573,8 @@ static unsigned print_name(const char *name)
573 putchar('\\'); 573 putchar('\\');
574 len++; 574 len++;
575 } 575 }
576 putchar(*name++); 576 putchar(*name);
577 name++;
577 } 578 }
578 putchar('"'); 579 putchar('"');
579 return len; 580 return len;
diff --git a/coreutils/tee.c b/coreutils/tee.c
index 8db9042aa..2e1e367f2 100644
--- a/coreutils/tee.c
+++ b/coreutils/tee.c
@@ -70,8 +70,8 @@ int tee_main(int argc, char **argv)
70 while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) { 70 while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
71 fp = files; 71 fp = files;
72 do 72 do
73 fwrite(buf, 1, c, *fp++); 73 fwrite(buf, 1, c, *fp);
74 while (*fp); 74 while (*++fp);
75 } 75 }
76 if (c < 0) { /* Make sure read errors are signaled. */ 76 if (c < 0) { /* Make sure read errors are signaled. */
77 retval = EXIT_FAILURE; 77 retval = EXIT_FAILURE;
@@ -81,8 +81,8 @@ int tee_main(int argc, char **argv)
81 while ((c = getchar()) != EOF) { 81 while ((c = getchar()) != EOF) {
82 fp = files; 82 fp = files;
83 do 83 do
84 putc(c, *fp++); 84 putc(c, *fp);
85 while (*fp); 85 while (*++fp);
86 } 86 }
87#endif 87#endif
88 88
diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c
index 207fb0b8d..0da9b0988 100644
--- a/coreutils/uudecode.c
+++ b/coreutils/uudecode.c
@@ -77,18 +77,21 @@ static void read_stduu(FILE *src_stream, FILE *dst_stream)
77 77
78static void read_base64(FILE *src_stream, FILE *dst_stream) 78static void read_base64(FILE *src_stream, FILE *dst_stream)
79{ 79{
80 int term_count = 1; 80 int term_count = 0;
81 81
82 while (1) { 82 while (1) {
83 unsigned char translated[4]; 83 unsigned char translated[4];
84 int count = 0; 84 int count = 0;
85 85
86 /* Process one group of 4 chars */
86 while (count < 4) { 87 while (count < 4) {
87 char *table_ptr; 88 char *table_ptr;
88 int ch; 89 int ch;
89 90
90 /* Get next _valid_ character. 91 /* Get next _valid_ character.
91 * global vector bb_uuenc_tbl_base64[] contains this string: 92 * bb_uuenc_tbl_base64[] contains this string:
93 * 0 1 2 3 4 5 6
94 * 012345678901234567890123456789012345678901234567890123456789012345
92 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n" 95 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
93 */ 96 */
94 do { 97 do {
@@ -103,42 +106,38 @@ static void read_base64(FILE *src_stream, FILE *dst_stream)
103 bb_error_msg_and_die("short file"); 106 bb_error_msg_and_die("short file");
104 } 107 }
105 table_ptr = strchr(bb_uuenc_tbl_base64, ch); 108 table_ptr = strchr(bb_uuenc_tbl_base64, ch);
106 } while (table_ptr == NULL); 109 } while (!table_ptr);
107 110
108 /* Convert encoded character to decimal */ 111 /* Convert encoded character to decimal */
109 ch = table_ptr - bb_uuenc_tbl_base64; 112 ch = table_ptr - bb_uuenc_tbl_base64;
110 113
111 if (*table_ptr == '=') { 114 if (ch == 65 /* '\n' */) {
112 if (term_count == 0) { 115 /* Terminating "====" line? */
113 translated[count] = '\0'; 116 if (term_count == 4)
114 break; 117 return; /* yes */
115 }
116 term_count++;
117 } else if (*table_ptr == '\n') {
118 /* Check for terminating line */
119 if (term_count == 5) {
120 return;
121 }
122 term_count = 1;
123 continue;
124 } else {
125 translated[count] = ch;
126 count++;
127 term_count = 0; 118 term_count = 0;
119 continue;
120 }
121 /* ch is 64 is char was '=', otherwise 0..63 */
122 translated[count] = ch & 63; /* 64 -> 0 */
123 if (ch == 64) {
124 term_count++;
125 break;
128 } 126 }
127 count++;
129 } 128 }
130 129
131 /* Merge 6 bit chars to 8 bit */ 130 /* Merge 6 bit chars to 8 bit.
132 if (count > 1) { 131 * count can be < 4 when we decode the tail:
132 * "eQ==" -> "y", not "y NUL NUL"
133 */
134 if (count > 1)
133 fputc(translated[0] << 2 | translated[1] >> 4, dst_stream); 135 fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
134 } 136 if (count > 2)
135 if (count > 2) {
136 fputc(translated[1] << 4 | translated[2] >> 2, dst_stream); 137 fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
137 } 138 if (count > 3)
138 if (count > 3) {
139 fputc(translated[2] << 6 | translated[3], dst_stream); 139 fputc(translated[2] << 6 | translated[3], dst_stream);
140 } 140 } /* while (1) */
141 }
142} 141}
143 142
144#if ENABLE_UUDECODE 143#if ENABLE_UUDECODE
diff --git a/editors/ed.c b/editors/ed.c
index 516b8d78d..859668406 100644
--- a/editors/ed.c
+++ b/editors/ed.c
@@ -671,7 +671,7 @@ static int readLines(const char *file, int num)
671 671
672 fd = open(file, 0); 672 fd = open(file, 0);
673 if (fd < 0) { 673 if (fd < 0) {
674 perror(file); 674 bb_simple_perror_msg(file);
675 return FALSE; 675 return FALSE;
676 } 676 }
677 677
@@ -721,7 +721,7 @@ static int readLines(const char *file, int num)
721 } while (cc > 0); 721 } while (cc > 0);
722 722
723 if (cc < 0) { 723 if (cc < 0) {
724 perror(file); 724 bb_simple_perror_msg(file);
725 close(fd); 725 close(fd);
726 return FALSE; 726 return FALSE;
727 } 727 }
@@ -761,7 +761,7 @@ static int writeLines(const char *file, int num1, int num2)
761 761
762 fd = creat(file, 0666); 762 fd = creat(file, 0666);
763 if (fd < 0) { 763 if (fd < 0) {
764 perror(file); 764 bb_simple_perror_msg(file);
765 return FALSE; 765 return FALSE;
766 } 766 }
767 767
@@ -776,7 +776,7 @@ static int writeLines(const char *file, int num1, int num2)
776 776
777 while (num1++ <= num2) { 777 while (num1++ <= num2) {
778 if (full_write(fd, lp->data, lp->len) != lp->len) { 778 if (full_write(fd, lp->data, lp->len) != lp->len) {
779 perror(file); 779 bb_simple_perror_msg(file);
780 close(fd); 780 close(fd);
781 return FALSE; 781 return FALSE;
782 } 782 }
@@ -786,7 +786,7 @@ static int writeLines(const char *file, int num1, int num2)
786 } 786 }
787 787
788 if (close(fd) < 0) { 788 if (close(fd) < 0) {
789 perror(file); 789 bb_simple_perror_msg(file);
790 return FALSE; 790 return FALSE;
791 } 791 }
792 792
diff --git a/include/applets.src.h b/include/applets.src.h
index 195598fee..0e4f966de 100644
--- a/include/applets.src.h
+++ b/include/applets.src.h
@@ -75,7 +75,6 @@ IF_ARPING(APPLET(arping, _BB_DIR_USR_BIN, _BB_SUID_DROP))
75IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk)) 75IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk))
76IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename)) 76IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename))
77IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP)) 77IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP))
78//IF_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_DROP))
79IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP)) 78IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP))
80IF_BLKID(APPLET(blkid, _BB_DIR_SBIN, _BB_SUID_DROP)) 79IF_BLKID(APPLET(blkid, _BB_DIR_SBIN, _BB_SUID_DROP))
81IF_BOOTCHARTD(APPLET(bootchartd, _BB_DIR_SBIN, _BB_SUID_DROP)) 80IF_BOOTCHARTD(APPLET(bootchartd, _BB_DIR_SBIN, _BB_SUID_DROP))
diff --git a/include/libbb.h b/include/libbb.h
index 75f12467e..31e733d77 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -897,9 +897,9 @@ int spawn_and_wait(char **argv) FAST_FUNC;
897struct nofork_save_area { 897struct nofork_save_area {
898 jmp_buf die_jmp; 898 jmp_buf die_jmp;
899 const char *applet_name; 899 const char *applet_name;
900 int xfunc_error_retval;
901 uint32_t option_mask32; 900 uint32_t option_mask32;
902 int die_sleep; 901 int die_sleep;
902 uint8_t xfunc_error_retval;
903 smallint saved; 903 smallint saved;
904}; 904};
905void save_nofork_data(struct nofork_save_area *save) FAST_FUNC; 905void save_nofork_data(struct nofork_save_area *save) FAST_FUNC;
@@ -1010,7 +1010,7 @@ enum {
1010extern const char *msg_eol; 1010extern const char *msg_eol;
1011extern smallint logmode; 1011extern smallint logmode;
1012extern int die_sleep; 1012extern int die_sleep;
1013extern int xfunc_error_retval; 1013extern uint8_t xfunc_error_retval;
1014extern jmp_buf die_jmp; 1014extern jmp_buf die_jmp;
1015extern void xfunc_die(void) NORETURN FAST_FUNC; 1015extern void xfunc_die(void) NORETURN FAST_FUNC;
1016extern void bb_show_usage(void) NORETURN FAST_FUNC; 1016extern void bb_show_usage(void) NORETURN FAST_FUNC;
diff --git a/include/usage.src.h b/include/usage.src.h
index 0eff1b129..2866bdbba 100644
--- a/include/usage.src.h
+++ b/include/usage.src.h
@@ -4544,7 +4544,7 @@ INSERT
4544 ) \ 4544 ) \
4545 4545
4546#define udhcpd_trivial_usage \ 4546#define udhcpd_trivial_usage \
4547 "[-fS]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [configfile]" \ 4547 "[-fS]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]" \
4548 4548
4549#define udhcpd_full_usage "\n\n" \ 4549#define udhcpd_full_usage "\n\n" \
4550 "DHCP server\n" \ 4550 "DHCP server\n" \
diff --git a/libbb/Config.src b/libbb/Config.src
index 9b01757c6..74dc9c549 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -78,9 +78,9 @@ config FEATURE_EDITING_HISTORY
78config FEATURE_EDITING_SAVEHISTORY 78config FEATURE_EDITING_SAVEHISTORY
79 bool "History saving" 79 bool "History saving"
80 default y 80 default y
81 depends on ASH && FEATURE_EDITING 81 depends on FEATURE_EDITING
82 help 82 help
83 Enable history saving in ash shell. 83 Enable history saving in shells.
84 84
85config FEATURE_TAB_COMPLETION 85config FEATURE_TAB_COMPLETION
86 bool "Tab completion" 86 bool "Tab completion"
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 751000e5d..6a304ded3 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -655,6 +655,7 @@ static int busybox_main(char **argv)
655 "See source distribution for full notice.\n" 655 "See source distribution for full notice.\n"
656 "\n" 656 "\n"
657 "Usage: busybox [function] [arguments]...\n" 657 "Usage: busybox [function] [arguments]...\n"
658 " or: busybox --list[-full]\n"
658 " or: function [arguments]...\n" 659 " or: function [arguments]...\n"
659 "\n" 660 "\n"
660 "\tBusyBox is a multi-call binary that combines many common Unix\n" 661 "\tBusyBox is a multi-call binary that combines many common Unix\n"
diff --git a/libbb/default_error_retval.c b/libbb/default_error_retval.c
index 4da5b3efb..4f6395fa2 100644
--- a/libbb/default_error_retval.c
+++ b/libbb/default_error_retval.c
@@ -15,4 +15,4 @@
15 15
16#include "libbb.h" 16#include "libbb.h"
17 17
18int xfunc_error_retval = EXIT_FAILURE; 18uint8_t xfunc_error_retval = EXIT_FAILURE;
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 866f9230d..3af9f4e64 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -1,6 +1,6 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Termios command line History and Editing. 3 * Command line editing.
4 * 4 *
5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. 5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6 * Written by: Vladimir Oleynik <dzo@simtreas.ru> 6 * Written by: Vladimir Oleynik <dzo@simtreas.ru>
@@ -99,7 +99,6 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
99 99
100 100
101enum { 101enum {
102 /* We use int16_t for positions, need to limit line len */
103 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 102 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
104 ? CONFIG_FEATURE_EDITING_MAX_LEN 103 ? CONFIG_FEATURE_EDITING_MAX_LEN
105 : 0x7ff0 104 : 0x7ff0
@@ -151,14 +150,6 @@ struct lineedit_statics {
151#if ENABLE_FEATURE_EDITING_ASK_TERMINAL 150#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
152 smallint sent_ESC_br6n; 151 smallint sent_ESC_br6n;
153#endif 152#endif
154
155 /* Formerly these were big buffers on stack: */
156#if ENABLE_FEATURE_TAB_COMPLETION
157 char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN];
158 char input_tab__matchBuf[MAX_LINELEN];
159 int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */
160 int16_t find_match__pos_buf[MAX_LINELEN + 1];
161#endif
162}; 153};
163 154
164/* See lineedit_ptr_hack.c */ 155/* See lineedit_ptr_hack.c */
@@ -195,7 +186,7 @@ static void deinit_S(void)
195{ 186{
196#if ENABLE_FEATURE_EDITING_FANCY_PROMPT 187#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
197 /* This one is allocated only if FANCY_PROMPT is on 188 /* This one is allocated only if FANCY_PROMPT is on
198 * (otherwise it points to verbatim prompt (NOT malloced) */ 189 * (otherwise it points to verbatim prompt (NOT malloced)) */
199 free((char*)cmdedit_prompt); 190 free((char*)cmdedit_prompt);
200#endif 191#endif
201#if ENABLE_USERNAME_OR_HOMEDIR 192#if ENABLE_USERNAME_OR_HOMEDIR
@@ -214,7 +205,7 @@ static size_t load_string(const char *src, int maxsize)
214 ssize_t len = mbstowcs(command_ps, src, maxsize - 1); 205 ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
215 if (len < 0) 206 if (len < 0)
216 len = 0; 207 len = 0;
217 command_ps[len] = 0; 208 command_ps[len] = BB_NUL;
218 return len; 209 return len;
219} 210}
220static unsigned save_string(char *dst, unsigned maxsize) 211static unsigned save_string(char *dst, unsigned maxsize)
@@ -233,18 +224,21 @@ static unsigned save_string(char *dst, unsigned maxsize)
233 while (dstpos < maxsize) { 224 while (dstpos < maxsize) {
234 wchar_t wc; 225 wchar_t wc;
235 int n = srcpos; 226 int n = srcpos;
236 while ((wc = command_ps[srcpos]) != 0 227
228 /* Convert up to 1st invalid byte (or up to end) */
229 while ((wc = command_ps[srcpos]) != BB_NUL
237 && !unicode_is_raw_byte(wc) 230 && !unicode_is_raw_byte(wc)
238 ) { 231 ) {
239 srcpos++; 232 srcpos++;
240 } 233 }
241 command_ps[srcpos] = 0; 234 command_ps[srcpos] = BB_NUL;
242 n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos); 235 n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
243 if (n < 0) /* should not happen */ 236 if (n < 0) /* should not happen */
244 break; 237 break;
245 dstpos += n; 238 dstpos += n;
246 if (wc == 0) /* usually is */ 239 if (wc == BB_NUL) /* usually is */
247 break; 240 break;
241
248 /* We do have invalid byte here! */ 242 /* We do have invalid byte here! */
249 command_ps[srcpos] = wc; /* restore it */ 243 command_ps[srcpos] = wc; /* restore it */
250 srcpos++; 244 srcpos++;
@@ -606,55 +600,62 @@ static void add_match(char *matched)
606} 600}
607 601
608#if ENABLE_FEATURE_USERNAME_COMPLETION 602#if ENABLE_FEATURE_USERNAME_COMPLETION
609static void username_tab_completion(char *ud, char *with_shash_flg) 603/* Replace "~user/..." with "/homedir/...".
604 * The parameter is malloced, free it or return it
605 * unchanged if no user is matched.
606 */
607static char *username_path_completion(char *ud)
610{ 608{
611 struct passwd *entry; 609 struct passwd *entry;
612 int userlen; 610 char *tilde_name = ud;
611 char *home = NULL;
613 612
614 ud++; /* ~user/... to user/... */ 613 ud++; /* skip ~ */
615 userlen = strlen(ud); 614 if (*ud == '/') { /* "~/..." */
615 home = home_pwd_buf;
616 } else {
617 /* "~user/..." */
618 ud = strchr(ud, '/');
619 *ud = '\0'; /* "~user" */
620 entry = getpwnam(tilde_name + 1);
621 *ud = '/'; /* restore "~user/..." */
622 if (entry)
623 home = entry->pw_dir;
624 }
625 if (home) {
626 ud = concat_path_file(home, ud);
627 free(tilde_name);
628 tilde_name = ud;
629 }
630 return tilde_name;
631}
616 632
617 if (with_shash_flg) { /* "~/..." or "~user/..." */ 633/* ~use<tab> - find all users with this prefix.
618 char *sav_ud = ud - 1; 634 * Return the length of the prefix used for matching.
619 char *home = NULL; 635 */
636static NOINLINE unsigned complete_username(const char *ud)
637{
638 /* Using _r function to avoid pulling in static buffers */
639 char line_buff[256];
640 struct passwd pwd;
641 struct passwd *result;
642 unsigned userlen;
620 643
621 if (*ud == '/') { /* "~/..." */ 644 ud++; /* skip ~ */
622 home = home_pwd_buf; 645 userlen = strlen(ud);
623 } else { 646
624 /* "~user/..." */ 647 setpwent();
625 char *temp; 648 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
626 temp = strchr(ud, '/'); 649 /* Null usernames should result in all users as possible completions. */
627 *temp = '\0'; /* ~user\0 */ 650 if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
628 entry = getpwnam(ud); 651 add_match(xasprintf("~%s/", pwd.pw_name));
629 *temp = '/'; /* restore ~user/... */
630 ud = temp;
631 if (entry)
632 home = entry->pw_dir;
633 }
634 if (home) {
635 if ((userlen + strlen(home) + 1) < MAX_LINELEN) {
636 /* /home/user/... */
637 sprintf(sav_ud, "%s%s", home, ud);
638 }
639 }
640 } else {
641 /* "~[^/]*" */
642 /* Using _r function to avoid pulling in static buffers */
643 char line_buff[256];
644 struct passwd pwd;
645 struct passwd *result;
646
647 setpwent();
648 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
649 /* Null usernames should result in all users as possible completions. */
650 if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
651 add_match(xasprintf("~%s/", pwd.pw_name));
652 }
653 } 652 }
654 endpwent();
655 } 653 }
654 endpwent();
655
656 return 1 + userlen;
656} 657}
657#endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ 658#endif /* FEATURE_USERNAME_COMPLETION */
658 659
659enum { 660enum {
660 FIND_EXE_ONLY = 0, 661 FIND_EXE_ONLY = 0,
@@ -662,22 +663,19 @@ enum {
662 FIND_FILE_ONLY = 2, 663 FIND_FILE_ONLY = 2,
663}; 664};
664 665
665static int path_parse(char ***p, int flags) 666static int path_parse(char ***p)
666{ 667{
667 int npth; 668 int npth;
668 const char *pth; 669 const char *pth;
669 char *tmp; 670 char *tmp;
670 char **res; 671 char **res;
671 672
672 /* if not setenv PATH variable, to search cur dir "." */
673 if (flags != FIND_EXE_ONLY)
674 return 1;
675
676 if (state->flags & WITH_PATH_LOOKUP) 673 if (state->flags & WITH_PATH_LOOKUP)
677 pth = state->path_lookup; 674 pth = state->path_lookup;
678 else 675 else
679 pth = getenv("PATH"); 676 pth = getenv("PATH");
680 /* PATH=<empty> or PATH=:<empty> */ 677
678 /* PATH="" or PATH=":"? */
681 if (!pth || !pth[0] || LONE_CHAR(pth, ':')) 679 if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
682 return 1; 680 return 1;
683 681
@@ -691,12 +689,13 @@ static int path_parse(char ***p, int flags)
691#endif 689#endif
692 if (!tmp) 690 if (!tmp)
693 break; 691 break;
694 if (*++tmp == '\0') 692 tmp++;
693 if (*tmp == '\0')
695 break; /* :<empty> */ 694 break; /* :<empty> */
696 npth++; 695 npth++;
697 } 696 }
698 697
699 res = xmalloc(npth * sizeof(char*)); 698 *p = res = xmalloc(npth * sizeof(res[0]));
700 res[0] = tmp = xstrdup(pth); 699 res[0] = tmp = xstrdup(pth);
701 npth = 1; 700 npth = 1;
702 while (1) { 701 while (1) {
@@ -712,235 +711,241 @@ static int path_parse(char ***p, int flags)
712 break; /* :<empty> */ 711 break; /* :<empty> */
713 res[npth++] = tmp; 712 res[npth++] = tmp;
714 } 713 }
715 *p = res;
716 return npth; 714 return npth;
717} 715}
718 716
719static void exe_n_cwd_tab_completion(char *command, int type) 717/* Complete command, directory or file name.
718 * Return the length of the prefix used for matching.
719 */
720static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
720{ 721{
721 DIR *dir;
722 struct dirent *next;
723 struct stat st;
724 char *path1[1]; 722 char *path1[1];
725 char **paths = path1; 723 char **paths = path1;
726 int npaths; 724 int npaths;
727 int i; 725 int i;
728 char *found; 726 unsigned pf_len;
729 char *pfind = strrchr(command, '/'); 727 const char *pfind;
730/* char dirbuf[MAX_LINELEN]; */ 728 char *dirbuf = NULL;
731#define dirbuf (S.exe_n_cwd_tab_completion__dirbuf)
732 729
733 npaths = 1; 730 npaths = 1;
734 path1[0] = (char*)"."; 731 path1[0] = (char*)".";
735 732
736 if (pfind == NULL) { 733 pfind = strrchr(command, '/');
737 /* no dir, if flags==EXE_ONLY - get paths, else "." */ 734 if (!pfind) {
738 npaths = path_parse(&paths, type); 735 if (type == FIND_EXE_ONLY)
736 npaths = path_parse(&paths);
739 pfind = command; 737 pfind = command;
740 } else { 738 } else {
739 /* point to 'l' in "..../last_component" */
740 pfind++;
741 /* dirbuf = ".../.../.../" */ 741 /* dirbuf = ".../.../.../" */
742 safe_strncpy(dirbuf, command, (pfind - command) + 2); 742 dirbuf = xstrndup(command, pfind - command);
743#if ENABLE_FEATURE_USERNAME_COMPLETION 743#if ENABLE_FEATURE_USERNAME_COMPLETION
744 if (dirbuf[0] == '~') /* ~/... or ~user/... */ 744 if (dirbuf[0] == '~') /* ~/... or ~user/... */
745 username_tab_completion(dirbuf, dirbuf); 745 dirbuf = username_path_completion(dirbuf);
746#endif 746#endif
747 paths[0] = dirbuf; 747 path1[0] = dirbuf;
748 /* point to 'l' in "..../last_component" */
749 pfind++;
750 } 748 }
749 pf_len = strlen(pfind);
751 750
752 for (i = 0; i < npaths; i++) { 751 for (i = 0; i < npaths; i++) {
752 DIR *dir;
753 struct dirent *next;
754 struct stat st;
755 char *found;
756
753 dir = opendir(paths[i]); 757 dir = opendir(paths[i]);
754 if (!dir) 758 if (!dir)
755 continue; /* don't print an error */ 759 continue; /* don't print an error */
756 760
757 while ((next = readdir(dir)) != NULL) { 761 while ((next = readdir(dir)) != NULL) {
758 int len1; 762 unsigned len;
759 const char *str_found = next->d_name; 763 const char *name_found = next->d_name;
760 764
761 /* matched? */ 765 /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
762 if (strncmp(str_found, pfind, strlen(pfind))) 766 if (!pfind[0] && DOT_OR_DOTDOT(name_found))
763 continue; 767 continue;
764 /* not see .name without .match */ 768 /* match? */
765 if (*str_found == '.' && *pfind == '\0') { 769 if (strncmp(name_found, pfind, pf_len) != 0)
766 if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) 770 continue; /* no */
767 continue; 771
768 str_found = ""; /* only "/" */ 772 found = concat_path_file(paths[i], name_found);
769 }
770 found = concat_path_file(paths[i], str_found);
771 /* hmm, remove in progress? */
772 /* NB: stat() first so that we see is it a directory; 773 /* NB: stat() first so that we see is it a directory;
773 * but if that fails, use lstat() so that 774 * but if that fails, use lstat() so that
774 * we still match dangling links */ 775 * we still match dangling links */
775 if (stat(found, &st) && lstat(found, &st)) 776 if (stat(found, &st) && lstat(found, &st))
776 goto cont; 777 goto cont; /* hmm, remove in progress? */
777 /* find with dirs? */
778 if (paths[i] != dirbuf)
779 strcpy(found, next->d_name); /* only name */
780 778
781 len1 = strlen(found); 779 /* Save only name */
782 found = xrealloc(found, len1 + 2); 780 len = strlen(name_found);
783 found[len1] = '\0'; 781 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
784 found[len1+1] = '\0'; 782 strcpy(found, name_found);
785 783
786 if (S_ISDIR(st.st_mode)) { 784 if (S_ISDIR(st.st_mode)) {
787 /* name is a directory */ 785 /* name is a directory, add slash */
788 if (found[len1-1] != '/') { 786 found[len] = '/';
789 found[len1] = '/'; 787 found[len + 1] = '\0';
790 }
791 } else { 788 } else {
792 /* not put found file if search only dirs for cd */ 789 /* skip files if looking for dirs only (example: cd) */
793 if (type == FIND_DIR_ONLY) 790 if (type == FIND_DIR_ONLY)
794 goto cont; 791 goto cont;
795 } 792 }
796 /* Add it to the list */ 793 /* add it to the list */
797 add_match(found); 794 add_match(found);
798 continue; 795 continue;
799 cont: 796 cont:
800 free(found); 797 free(found);
801 } 798 }
802 closedir(dir); 799 closedir(dir);
803 } 800 } /* for every path */
801
804 if (paths != path1) { 802 if (paths != path1) {
805 free(paths[0]); /* allocated memory is only in first member */ 803 free(paths[0]); /* allocated memory is only in first member */
806 free(paths); 804 free(paths);
807 } 805 }
808#undef dirbuf 806 free(dirbuf);
807
808 return pf_len;
809} 809}
810 810
811/* build_match_prefix:
812 * On entry, match_buf contains everything up to cursor at the moment <tab>
813 * was pressed. This function looks at it, figures out what part of it
814 * constitutes the command/file/directory prefix to use for completion,
815 * and rewrites match_buf to contain only that part.
816 */
817#define dbg_bmp 0
818/* Helpers: */
811/* QUOT is used on elements of int_buf[], which are bytes, 819/* QUOT is used on elements of int_buf[], which are bytes,
812 * not Unicode chars. Therefore it works correctly even in Unicode mode. 820 * not Unicode chars. Therefore it works correctly even in Unicode mode.
813 */ 821 */
814#define QUOT (UCHAR_MAX+1) 822#define QUOT (UCHAR_MAX+1)
815 823static void remove_chunk(int16_t *int_buf, int beg, int end)
816#define int_buf (S.find_match__int_buf)
817#define pos_buf (S.find_match__pos_buf)
818/* is must be <= in */
819static void collapse_pos(int is, int in)
820{ 824{
821 memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0])); 825 /* beg must be <= end */
822 memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0])); 826 if (beg == end)
827 return;
828
829 while ((int_buf[beg] = int_buf[end]) != 0)
830 beg++, end++;
831
832 if (dbg_bmp) {
833 int i;
834 for (i = 0; int_buf[i]; i++)
835 bb_putchar((unsigned char)int_buf[i]);
836 bb_putchar('\n');
837 }
823} 838}
824static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) 839/* Caller ensures that match_buf points to a malloced buffer
840 * big enough to hold strlen(match_buf)*2 + 2
841 */
842static NOINLINE int build_match_prefix(char *match_buf)
825{ 843{
826 int i, j; 844 int i, j;
827 int command_mode; 845 int command_mode;
828 int c, c2; 846 int16_t *int_buf = (int16_t*)match_buf;
829/* Were local, but it uses too much stack */
830/* int16_t int_buf[MAX_LINELEN + 1]; */
831/* int16_t pos_buf[MAX_LINELEN + 1]; */
832
833 /* set to integer dimension characters and own positions */
834 for (i = 0;; i++) {
835 int_buf[i] = (unsigned char)matchBuf[i];
836 if (int_buf[i] == 0) {
837 pos_buf[i] = -1; /* end-fo-line indicator */
838 break;
839 }
840 pos_buf[i] = i;
841 }
842 847
843 /* mask \+symbol and convert '\t' to ' ' */ 848 if (dbg_bmp) printf("\n%s\n", match_buf);
844 for (i = j = 0; matchBuf[i]; i++, j++) { 849
845 if (matchBuf[i] == '\\') { 850 /* Copy in reverse order, since they overlap */
846 collapse_pos(j, j + 1); 851 i = strlen(match_buf);
847 int_buf[j] |= QUOT; 852 do {
848 i++; 853 int_buf[i] = (unsigned char)match_buf[i];
854 i--;
855 } while (i >= 0);
856
857 /* Mark every \c as "quoted c" */
858 for (i = 0; int_buf[i]; i++) {
859 if (int_buf[i] == '\\') {
860 remove_chunk(int_buf, i, i + 1);
861 int_buf[i] |= QUOT;
849 } 862 }
850 } 863 }
851 /* mask "symbols" or 'symbols' */ 864 /* Quote-mark "chars" and 'chars', drop delimiters */
852 c2 = 0; 865 {
853 for (i = 0; int_buf[i]; i++) { 866 int in_quote = 0;
854 c = int_buf[i]; 867 i = 0;
855 if (c == '\'' || c == '"') { 868 while (int_buf[i]) {
856 if (c2 == 0) 869 int cur = int_buf[i];
857 c2 = c; 870 if (!cur)
858 else { 871 break;
859 if (c == c2) 872 if (cur == '\'' || cur == '"') {
860 c2 = 0; 873 if (!in_quote || (cur == in_quote)) {
861 else 874 in_quote ^= cur;
862 int_buf[i] |= QUOT; 875 remove_chunk(int_buf, i, i + 1);
876 continue;
877 }
863 } 878 }
864 } else if (c2 != 0 && c != '$') 879 if (in_quote)
865 int_buf[i] |= QUOT; 880 int_buf[i] = cur | QUOT;
881 i++;
882 }
866 } 883 }
867 884
868 /* skip commands with arguments if line has commands delimiters */ 885 /* Remove everything up to command delimiters:
869 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ 886 * ';' ';;' '&' '|' '&&' '||',
887 * but careful with '>&' '<&' '>|'
888 */
870 for (i = 0; int_buf[i]; i++) { 889 for (i = 0; int_buf[i]; i++) {
871 c = int_buf[i]; 890 int cur = int_buf[i];
872 c2 = int_buf[i + 1]; 891 if (cur == ';' || cur == '&' || cur == '|') {
873 j = i ? int_buf[i - 1] : -1; 892 int prev = i ? int_buf[i - 1] : 0;
874 command_mode = 0; 893 if (cur == '&' && (prev == '>' || prev == '<')) {
875 if (c == ';' || c == '&' || c == '|') { 894 continue;
876 command_mode = 1 + (c == c2); 895 } else if (cur == '|' && prev == '>') {
877 if (c == '&') { 896 continue;
878 if (j == '>' || j == '<') 897 }
879 command_mode = 0; 898 remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
880 } else if (c == '|' && j == '>') 899 i = -1; /* back to square 1 */
881 command_mode = 0;
882 }
883 if (command_mode) {
884 collapse_pos(0, i + command_mode);
885 i = -1; /* hack incremet */
886 } 900 }
887 } 901 }
888 /* collapse `command...` */ 902 /* Remove all `cmd` */
889 for (i = 0; int_buf[i]; i++) { 903 for (i = 0; int_buf[i]; i++) {
890 if (int_buf[i] == '`') { 904 if (int_buf[i] == '`') {
891 for (j = i + 1; int_buf[j]; j++) 905 for (j = i + 1; int_buf[j]; j++) {
892 if (int_buf[j] == '`') { 906 if (int_buf[j] == '`') {
893 collapse_pos(i, j + 1); 907 /* `cmd` should count as a word:
894 j = 0; 908 * `cmd` c<tab> should search for files c*,
895 break; 909 * not commands c*. Therefore we don't drop
910 * `cmd` entirely, we replace it with single `.
911 */
912 remove_chunk(int_buf, i, j);
913 goto next;
896 } 914 }
897 if (j) { 915 }
898 /* not found closing ` - command mode, collapse all previous */ 916 /* No closing ` - command mode, remove all up to ` */
899 collapse_pos(0, i + 1); 917 remove_chunk(int_buf, 0, i + 1);
900 break; 918 break;
901 } else 919 next: ;
902 i--; /* hack incremet */
903 } 920 }
904 } 921 }
905 922
906 /* collapse (command...(command...)...) or {command...{command...}...} */ 923 /* Remove "cmd (" and "cmd {"
907 c = 0; /* "recursive" level */ 924 * Example: "if { c<tab>"
908 c2 = 0; 925 * In this example, c should be matched as command pfx.
926 */
909 for (i = 0; int_buf[i]; i++) { 927 for (i = 0; int_buf[i]; i++) {
910 if (int_buf[i] == '(' || int_buf[i] == '{') { 928 if (int_buf[i] == '(' || int_buf[i] == '{') {
911 if (int_buf[i] == '(') 929 remove_chunk(int_buf, 0, i + 1);
912 c++; 930 i = -1; /* back to square 1 */
913 else
914 c2++;
915 collapse_pos(0, i + 1);
916 i = -1; /* hack incremet */
917 }
918 }
919 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) {
920 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
921 if (int_buf[i] == ')')
922 c--;
923 else
924 c2--;
925 collapse_pos(0, i + 1);
926 i = -1; /* hack incremet */
927 } 931 }
928 } 932 }
929 933
930 /* skip first not quote space */ 934 /* Remove leading unquoted spaces */
931 for (i = 0; int_buf[i]; i++) 935 for (i = 0; int_buf[i]; i++)
932 if (int_buf[i] != ' ') 936 if (int_buf[i] != ' ')
933 break; 937 break;
934 if (i) 938 remove_chunk(int_buf, 0, i);
935 collapse_pos(0, i);
936 939
937 /* set find mode for completion */ 940 /* Determine completion mode */
938 command_mode = FIND_EXE_ONLY; 941 command_mode = FIND_EXE_ONLY;
939 for (i = 0; int_buf[i]; i++) { 942 for (i = 0; int_buf[i]; i++) {
940 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { 943 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
941 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY 944 if (int_buf[i] == ' '
942 && matchBuf[pos_buf[0]] == 'c' 945 && command_mode == FIND_EXE_ONLY
943 && matchBuf[pos_buf[1]] == 'd' 946 && (char)int_buf[0] == 'c'
947 && (char)int_buf[1] == 'd'
948 && i == 2 /* -> int_buf[2] == ' ' */
944 ) { 949 ) {
945 command_mode = FIND_DIR_ONLY; 950 command_mode = FIND_DIR_ONLY;
946 } else { 951 } else {
@@ -949,44 +954,32 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
949 } 954 }
950 } 955 }
951 } 956 }
952 for (i = 0; int_buf[i]; i++) 957 if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
953 /* "strlen" */; 958
954 /* find last word */ 959 /* Remove everything except last word */
960 for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */
961 continue;
955 for (--i; i >= 0; i--) { 962 for (--i; i >= 0; i--) {
956 c = int_buf[i]; 963 int cur = int_buf[i];
957 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { 964 if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
958 collapse_pos(0, i + 1); 965 remove_chunk(int_buf, 0, i + 1);
959 break; 966 break;
960 } 967 }
961 } 968 }
962 /* skip first not quoted '\'' or '"' */ 969
963 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) 970 /* Convert back to string of _chars_ */
964 /*skip*/; 971 i = 0;
965 /* collapse quote or unquote // or /~ */ 972 while ((match_buf[i] = int_buf[i]) != '\0')
966 while ((int_buf[i] & ~QUOT) == '/'
967 && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~')
968 ) {
969 i++; 973 i++;
970 }
971 974
972 /* set only match and destroy quotes */ 975 if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
973 j = 0;
974 for (c = 0; pos_buf[i] >= 0; i++) {
975 matchBuf[c++] = matchBuf[pos_buf[i]];
976 j = pos_buf[i] + 1;
977 }
978 matchBuf[c] = '\0';
979 /* old length matchBuf with quotes symbols */
980 *len_with_quotes = j ? j - pos_buf[0] : 0;
981 976
982 return command_mode; 977 return command_mode;
983} 978}
984#undef int_buf
985#undef pos_buf
986 979
987/* 980/*
988 * display by column (original idea from ls applet, 981 * Display by column (original idea from ls applet,
989 * very optimized by me :) 982 * very optimized by me [Vladimir] :)
990 */ 983 */
991static void showfiles(void) 984static void showfiles(void)
992{ 985{
@@ -1043,156 +1036,173 @@ static char *add_quote_for_spec_chars(char *found)
1043} 1036}
1044 1037
1045/* Do TAB completion */ 1038/* Do TAB completion */
1046static void input_tab(smallint *lastWasTab) 1039static NOINLINE void input_tab(smallint *lastWasTab)
1047{ 1040{
1041 char *chosen_match;
1042 char *match_buf;
1043 size_t len_found;
1044 /* Length of string used for matching */
1045 unsigned match_pfx_len = match_pfx_len;
1046 int find_type;
1047#if ENABLE_UNICODE_SUPPORT
1048 /* cursor pos in command converted to multibyte form */
1049 int cursor_mb;
1050#endif
1048 if (!(state->flags & TAB_COMPLETION)) 1051 if (!(state->flags & TAB_COMPLETION))
1049 return; 1052 return;
1050 1053
1051 if (!*lastWasTab) { 1054 if (*lastWasTab) {
1052 char *tmp, *tmp1; 1055 /* The last char was a TAB too.
1053 size_t len_found; 1056 * Print a list of all the available choices.
1054/* char matchBuf[MAX_LINELEN]; */ 1057 */
1055#define matchBuf (S.input_tab__matchBuf) 1058 if (num_matches > 0) {
1056 int find_type; 1059 /* cursor will be changed by goto_new_line() */
1057 int recalc_pos; 1060 int sav_cursor = cursor;
1058#if ENABLE_UNICODE_SUPPORT 1061 goto_new_line();
1059 /* cursor pos in command converted to multibyte form */ 1062 showfiles();
1060 int cursor_mb; 1063 redraw(0, command_len - sav_cursor);
1061#endif 1064 }
1065 return;
1066 }
1062 1067
1063 *lastWasTab = TRUE; /* flop trigger */ 1068 *lastWasTab = 1;
1069 chosen_match = NULL;
1064 1070
1065 /* Make a local copy of the string -- 1071 /* Make a local copy of the string up to the position of the cursor.
1066 * up to the position of the cursor */ 1072 * build_match_prefix will expand it into int16_t's, need to allocate
1067 save_string(matchBuf, cursor + 1); 1073 * twice as much as the string_len+1.
1068#if ENABLE_UNICODE_SUPPORT 1074 * (we then also (ab)use this extra space later - see (**))
1069 cursor_mb = strlen(matchBuf); 1075 */
1076 match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
1077#if !ENABLE_UNICODE_SUPPORT
1078 save_string(match_buf, cursor + 1); /* +1 for NUL */
1079#else
1080 {
1081 CHAR_T wc = command_ps[cursor];
1082 command_ps[cursor] = BB_NUL;
1083 save_string(match_buf, MAX_LINELEN);
1084 command_ps[cursor] = wc;
1085 cursor_mb = strlen(match_buf);
1086 }
1070#endif 1087#endif
1071 tmp = matchBuf; 1088 find_type = build_match_prefix(match_buf);
1072 1089
1073 find_type = find_match(matchBuf, &recalc_pos); 1090 /* Free up any memory already allocated */
1074 1091 free_tab_completion_data();
1075 /* Free up any memory already allocated */
1076 free_tab_completion_data();
1077 1092
1078#if ENABLE_FEATURE_USERNAME_COMPLETION 1093#if ENABLE_FEATURE_USERNAME_COMPLETION
1079 /* If the word starts with `~' and there is no slash in the word, 1094 /* If the word starts with `~' and there is no slash in the word,
1080 * then try completing this word as a username. */ 1095 * then try completing this word as a username. */
1081 if (state->flags & USERNAME_COMPLETION) 1096 if (state->flags & USERNAME_COMPLETION)
1082 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) 1097 if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
1083 username_tab_completion(matchBuf, NULL); 1098 match_pfx_len = complete_username(match_buf);
1084#endif 1099#endif
1085 /* Try to match any executable in our path and everything 1100 /* Try to match a command in $PATH, or a directory, or a file */
1086 * in the current working directory */ 1101 if (!matches)
1087 if (!matches) 1102 match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
1088 exe_n_cwd_tab_completion(matchBuf, find_type); 1103 /* Remove duplicates */
1089 /* Sort, then remove any duplicates found */ 1104 if (matches) {
1090 if (matches) { 1105 unsigned i;
1091 unsigned i; 1106 unsigned n = 0;
1092 int n = 0; 1107 qsort_string_vector(matches, num_matches);
1093 qsort_string_vector(matches, num_matches); 1108 for (i = 0; i < num_matches - 1; ++i) {
1094 for (i = 0; i < num_matches - 1; ++i) { 1109 //if (matches[i] && matches[i+1]) { /* paranoia */
1095 if (matches[i] && matches[i+1]) { /* paranoia */ 1110 if (strcmp(matches[i], matches[i+1]) == 0) {
1096 if (strcmp(matches[i], matches[i+1]) == 0) { 1111 free(matches[i]);
1097 free(matches[i]); 1112 //matches[i] = NULL; /* paranoia */
1098 matches[i] = NULL; /* paranoia */ 1113 } else {
1099 } else { 1114 matches[n++] = matches[i];
1100 matches[n++] = matches[i];
1101 }
1102 } 1115 }
1103 } 1116 //}
1104 matches[n] = matches[i];
1105 num_matches = n + 1;
1106 } 1117 }
1107 /* Did we find exactly one match? */ 1118 matches[n++] = matches[i];
1108 if (!matches || num_matches > 1) { /* no */ 1119 num_matches = n;
1109 beep(); 1120 }
1110 if (!matches) 1121 /* Did we find exactly one match? */
1111 return; /* not found */ 1122 if (num_matches != 1) { /* no */
1112 /* find minimal match */ 1123 char *cp;
1113 tmp1 = xstrdup(matches[0]); 1124 beep();
1114 for (tmp = tmp1; *tmp; tmp++) { 1125 if (!matches)
1115 for (len_found = 1; len_found < num_matches; len_found++) { 1126 goto ret; /* no matches at all */
1116 if (matches[len_found][tmp - tmp1] != *tmp) { 1127 /* Find common prefix */
1117 *tmp = '\0'; 1128 chosen_match = xstrdup(matches[0]);
1118 break; 1129 for (cp = chosen_match; *cp; cp++) {
1119 } 1130 unsigned n;
1131 for (n = 1; n < num_matches; n++) {
1132 if (matches[n][cp - chosen_match] != *cp) {
1133 goto stop;
1120 } 1134 }
1121 } 1135 }
1122 if (*tmp1 == '\0') { /* have unique */
1123 free(tmp1);
1124 return;
1125 }
1126 tmp = add_quote_for_spec_chars(tmp1);
1127 free(tmp1);
1128 } else { /* one match */
1129 tmp = add_quote_for_spec_chars(matches[0]);
1130 /* for next completion current found */
1131 *lastWasTab = FALSE;
1132
1133 len_found = strlen(tmp);
1134 if (tmp[len_found-1] != '/') {
1135 tmp[len_found] = ' ';
1136 tmp[len_found+1] = '\0';
1137 }
1138 } 1136 }
1137 stop:
1138 if (cp == chosen_match) { /* have unique prefix? */
1139 goto ret; /* no */
1140 }
1141 *cp = '\0';
1142 cp = add_quote_for_spec_chars(chosen_match);
1143 free(chosen_match);
1144 chosen_match = cp;
1145 len_found = strlen(chosen_match);
1146 } else { /* exactly one match */
1147 /* Next <tab> is not a double-tab */
1148 *lastWasTab = 0;
1149
1150 chosen_match = add_quote_for_spec_chars(matches[0]);
1151 len_found = strlen(chosen_match);
1152 if (chosen_match[len_found-1] != '/') {
1153 chosen_match[len_found] = ' ';
1154 chosen_match[++len_found] = '\0';
1155 }
1156 }
1139 1157
1140 len_found = strlen(tmp);
1141#if !ENABLE_UNICODE_SUPPORT 1158#if !ENABLE_UNICODE_SUPPORT
1142 /* have space to place the match? */ 1159 /* Have space to place the match? */
1143 /* The result consists of three parts with these lengths: */ 1160 /* The result consists of three parts with these lengths: */
1144 /* (cursor - recalc_pos) + len_found + (command_len - cursor) */ 1161 /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
1145 /* it simplifies into: */ 1162 /* it simplifies into: */
1146 if ((int)(len_found + command_len - recalc_pos) < S.maxsize) { 1163 if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
1164 int pos;
1165 /* save tail */
1166 strcpy(match_buf, &command_ps[cursor]);
1167 /* add match and tail */
1168 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1169 command_len = strlen(command_ps);
1170 /* new pos */
1171 pos = cursor + len_found - match_pfx_len;
1172 /* write out the matched command */
1173 redraw(cmdedit_y, command_len - pos);
1174 }
1175#else
1176 {
1177 /* Use 2nd half of match_buf as scratch space - see (**) */
1178 char *command = match_buf + MAX_LINELEN;
1179 int len = save_string(command, MAX_LINELEN);
1180 /* Have space to place the match? */
1181 /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
1182 if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
1183 int pos;
1147 /* save tail */ 1184 /* save tail */
1148 strcpy(matchBuf, command_ps + cursor); 1185 strcpy(match_buf, &command[cursor_mb]);
1186 /* where do we want to have cursor after all? */
1187 strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
1188 len = load_string(command, S.maxsize);
1149 /* add match and tail */ 1189 /* add match and tail */
1150 sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf); 1190 sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
1151 command_len = strlen(command_ps); 1191 command_len = load_string(command, S.maxsize);
1152 /* new pos */
1153 recalc_pos = cursor - recalc_pos + len_found;
1154 /* write out the matched command */ 1192 /* write out the matched command */
1155 redraw(cmdedit_y, command_len - recalc_pos); 1193 /* paranoia: load_string can return 0 on conv error,
1156 } 1194 * prevent passing pos = (0 - 12) to redraw */
1157#else 1195 pos = command_len - len;
1158 { 1196 redraw(cmdedit_y, pos >= 0 ? pos : 0);
1159 char command[MAX_LINELEN];
1160 int len = save_string(command, sizeof(command));
1161 /* have space to place the match? */
1162 /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */
1163 if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) {
1164 /* save tail */
1165 strcpy(matchBuf, command + cursor_mb);
1166 /* where do we want to have cursor after all? */
1167 strcpy(&command[cursor_mb - recalc_pos], tmp);
1168 len = load_string(command, S.maxsize);
1169 /* add match and tail */
1170 sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf);
1171 command_len = load_string(command, S.maxsize);
1172 /* write out the matched command */
1173 redraw(cmdedit_y, command_len - len);
1174 }
1175 }
1176#endif
1177 free(tmp);
1178#undef matchBuf
1179 } else {
1180 /* Ok -- the last char was a TAB. Since they
1181 * just hit TAB again, print a list of all the
1182 * available choices... */
1183 if (matches && num_matches > 0) {
1184 /* changed by goto_new_line() */
1185 int sav_cursor = cursor;
1186
1187 /* Go to the next line */
1188 goto_new_line();
1189 showfiles();
1190 redraw(0, command_len - sav_cursor);
1191 } 1197 }
1192 } 1198 }
1199#endif
1200 ret:
1201 free(chosen_match);
1202 free(match_buf);
1193} 1203}
1194 1204
1195#endif /* FEATURE_COMMAND_TAB_COMPLETION */ 1205#endif /* FEATURE_TAB_COMPLETION */
1196 1206
1197 1207
1198line_input_t* FAST_FUNC new_line_input_t(int flags) 1208line_input_t* FAST_FUNC new_line_input_t(int flags)
@@ -1892,7 +1902,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1892{ 1902{
1893 int len; 1903 int len;
1894#if ENABLE_FEATURE_TAB_COMPLETION 1904#if ENABLE_FEATURE_TAB_COMPLETION
1895 smallint lastWasTab = FALSE; 1905 smallint lastWasTab = 0;
1896#endif 1906#endif
1897 smallint break_out = 0; 1907 smallint break_out = 0;
1898#if ENABLE_FEATURE_EDITING_VI 1908#if ENABLE_FEATURE_EDITING_VI
@@ -2169,7 +2179,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2169 2179
2170 ic = lineedit_read_key(read_key_buffer); 2180 ic = lineedit_read_key(read_key_buffer);
2171 if (errno) /* error */ 2181 if (errno) /* error */
2172 goto prepare_to_die; 2182 goto return_error_indicator;
2173 if (ic == ic_raw) { /* "cc", "dd" */ 2183 if (ic == ic_raw) { /* "cc", "dd" */
2174 input_backward(cursor); 2184 input_backward(cursor);
2175 goto clear_to_eol; 2185 goto clear_to_eol;
@@ -2233,7 +2243,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2233//FIXME: unicode case? 2243//FIXME: unicode case?
2234 ic = lineedit_read_key(read_key_buffer); 2244 ic = lineedit_read_key(read_key_buffer);
2235 if (errno) /* error */ 2245 if (errno) /* error */
2236 goto prepare_to_die; 2246 goto return_error_indicator;
2237 if (ic < ' ' || ic > 255) { 2247 if (ic < ' ' || ic > 255) {
2238 beep(); 2248 beep();
2239 } else { 2249 } else {
@@ -2305,9 +2315,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2305 * or exit if len=0 and no chars to delete */ 2315 * or exit if len=0 and no chars to delete */
2306 if (command_len == 0) { 2316 if (command_len == 0) {
2307 errno = 0; 2317 errno = 0;
2308#if ENABLE_FEATURE_EDITING_VI 2318
2309 prepare_to_die: 2319 case -1: /* error (e.g. EIO when tty is destroyed) */
2310#endif 2320 IF_FEATURE_EDITING_VI(return_error_indicator:)
2311 break_out = command_len = -1; 2321 break_out = command_len = -1;
2312 break; 2322 break;
2313 } 2323 }
@@ -2317,7 +2327,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2317// /* Control-V -- force insert of next char */ 2327// /* Control-V -- force insert of next char */
2318// if (c == CTRL('V')) { 2328// if (c == CTRL('V')) {
2319// if (safe_read(STDIN_FILENO, &c, 1) < 1) 2329// if (safe_read(STDIN_FILENO, &c, 1) < 1)
2320// goto prepare_to_die; 2330// goto return_error_indicator;
2321// if (c == 0) { 2331// if (c == 0) {
2322// beep(); 2332// beep();
2323// break; 2333// break;
@@ -2370,7 +2380,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2370 2380
2371#if ENABLE_FEATURE_TAB_COMPLETION 2381#if ENABLE_FEATURE_TAB_COMPLETION
2372 if (ic_raw != '\t') 2382 if (ic_raw != '\t')
2373 lastWasTab = FALSE; 2383 lastWasTab = 0;
2374#endif 2384#endif
2375 } /* while (1) */ 2385 } /* while (1) */
2376 2386
@@ -2464,7 +2474,7 @@ int main(int argc, char **argv)
2464 l = read_line_input(prompt, buff); 2474 l = read_line_input(prompt, buff);
2465 if (l <= 0 || buff[l-1] != '\n') 2475 if (l <= 0 || buff[l-1] != '\n')
2466 break; 2476 break;
2467 buff[l-1] = 0; 2477 buff[l-1] = '\0';
2468 printf("*** read_line_input() returned line =%s=\n", buff); 2478 printf("*** read_line_input() returned line =%s=\n", buff);
2469 } 2479 }
2470 printf("*** read_line_input() detect ^D\n"); 2480 printf("*** read_line_input() detect ^D\n");
diff --git a/libbb/unicode.c b/libbb/unicode.c
index c4b5f86ee..70c6abe00 100644
--- a/libbb/unicode.c
+++ b/libbb/unicode.c
@@ -131,7 +131,7 @@ size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n)
131 size_t len = wcrtomb_internal(tbuf, wc); 131 size_t len = wcrtomb_internal(tbuf, wc);
132 132
133 if (len > n) 133 if (len > n)
134 len = n; 134 break;
135 memcpy(dest, tbuf, len); 135 memcpy(dest, tbuf, len);
136 if (wc == L'\0') 136 if (wc == L'\0')
137 return org_n - n; 137 return org_n - n;
diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c
index 7228954d6..2b34954c0 100644
--- a/modutils/modutils-24.c
+++ b/modutils/modutils-24.c
@@ -3561,7 +3561,7 @@ static void check_tainted_module(struct obj_file *f, const char *m_name)
3561 else if (errno == EACCES) 3561 else if (errno == EACCES)
3562 kernel_has_tainted = 1; 3562 kernel_has_tainted = 1;
3563 else { 3563 else {
3564 perror(TAINT_FILENAME); 3564 bb_simple_perror_msg(TAINT_FILENAME);
3565 kernel_has_tainted = 0; 3565 kernel_has_tainted = 0;
3566 } 3566 }
3567 } 3567 }
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index 5d6994f6e..864fc2acd 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -483,7 +483,7 @@ struct dhcp_client_t {
483 483
484static const struct dhcp_client_t ext_dhcp_clients[] = { 484static const struct dhcp_client_t ext_dhcp_clients[] = {
485 { "dhcpcd", 485 { "dhcpcd",
486 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%", 486 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
487 "dhcpcd -k %iface%", 487 "dhcpcd -k %iface%",
488 }, 488 },
489 { "dhclient", 489 { "dhclient",
@@ -495,7 +495,7 @@ static const struct dhcp_client_t ext_dhcp_clients[] = {
495 "pump -i %iface% -k", 495 "pump -i %iface% -k",
496 }, 496 },
497 { "udhcpc", 497 { "udhcpc",
498 "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]]" 498 "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
499 "[[ -s %script%]][[ %udhcpc_opts%]]", 499 "[[ -s %script%]][[ %udhcpc_opts%]]",
500 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", 500 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
501 }, 501 },
@@ -535,7 +535,7 @@ static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
535 return 0; 535 return 0;
536#endif 536#endif
537 return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid " 537 return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
538 "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]][[ %udhcpc_opts%]]", 538 "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
539 ifd, exec); 539 ifd, exec);
540} 540}
541#else 541#else
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index dbcce8bf4..6b5b176c9 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -858,7 +858,7 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
858 } else { 858 } else {
859 /* Outbound connects. Now we're more picky about args... */ 859 /* Outbound connects. Now we're more picky about args... */
860 if (!themaddr) 860 if (!themaddr)
861 bb_error_msg_and_die("no destination"); 861 bb_show_usage();
862 862
863 remend = *themaddr; 863 remend = *themaddr;
864 if (o_verbose) 864 if (o_verbose)
diff --git a/networking/slattach.c b/networking/slattach.c
index 12a3067de..921ec552d 100644
--- a/networking/slattach.c
+++ b/networking/slattach.c
@@ -4,7 +4,7 @@
4 * 4 *
5 * Author: Ignacio Garcia Perez (iggarpe at gmail dot com) 5 * Author: Ignacio Garcia Perez (iggarpe at gmail dot com)
6 * 6 *
7 * License: GPLv2 or later, see LICENSE file in this tarball. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 * 8 *
9 * There are some differences from the standard net-tools slattach: 9 * There are some differences from the standard net-tools slattach:
10 * 10 *
diff --git a/procps/ps.c b/procps/ps.c
index 305381eb9..48b55a785 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -338,24 +338,16 @@ static ps_out_t* new_out_t(void)
338static const ps_out_t* find_out_spec(const char *name) 338static const ps_out_t* find_out_spec(const char *name)
339{ 339{
340 unsigned i; 340 unsigned i;
341#if ENABLE_DESKTOP
342 char buf[ARRAY_SIZE(out_spec)*7 + 1]; 341 char buf[ARRAY_SIZE(out_spec)*7 + 1];
343 char *p = buf; 342 char *p = buf;
344#endif
345 343
346 for (i = 0; i < ARRAY_SIZE(out_spec); i++) { 344 for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
347 if (strncmp(name, out_spec[i].name6, 6) == 0) 345 if (strncmp(name, out_spec[i].name6, 6) == 0)
348 return &out_spec[i]; 346 return &out_spec[i];
349#if ENABLE_DESKTOP
350 p += sprintf(p, "%.6s,", out_spec[i].name6); 347 p += sprintf(p, "%.6s,", out_spec[i].name6);
351#endif
352 } 348 }
353#if ENABLE_DESKTOP
354 p[-1] = '\0'; 349 p[-1] = '\0';
355 bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf); 350 bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
356#else
357 bb_error_msg_and_die("bad -o argument '%s'");
358#endif
359} 351}
360 352
361static void parse_o(char* opt) 353static void parse_o(char* opt)
diff --git a/procps/smemcap.c b/procps/smemcap.c
index 06cf93c85..f951a5fb6 100644
--- a/procps/smemcap.c
+++ b/procps/smemcap.c
@@ -41,7 +41,7 @@ static void writeheader(const char *path, struct stat *sb, int type)
41 sprintf(header.size, "%o", (unsigned)sb->st_size); 41 sprintf(header.size, "%o", (unsigned)sb->st_size);
42 sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL); 42 sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
43 header.typeflag = type; 43 header.typeflag = type;
44 //strcpy(header.magic, "ustar "); - do we want to be standard-compliant? 44 strcpy(header.magic, "ustar "); /* like GNU tar */
45 45
46 /* Calculate and store the checksum (the sum of all of the bytes of 46 /* Calculate and store the checksum (the sum of all of the bytes of
47 * the header). The checksum field must be filled with blanks for the 47 * the header). The checksum field must be filled with blanks for the
diff --git a/runit/chpst.c b/runit/chpst.c
index ad0811294..b08d42300 100644
--- a/runit/chpst.c
+++ b/runit/chpst.c
@@ -29,7 +29,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29/* Dependencies on runit_lib.c removed */ 29/* Dependencies on runit_lib.c removed */
30 30
31#include "libbb.h" 31#include "libbb.h"
32#include <dirent.h>
33 32
34/* 33/*
35Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit. 34Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
diff --git a/runit/runit_lib.c b/runit/runit_lib.c
index ec18b5edd..8182a909a 100644
--- a/runit/runit_lib.c
+++ b/runit/runit_lib.c
@@ -198,27 +198,29 @@ void iopause(iopause_fd *x, unsigned len, struct taia *deadline, struct taia *st
198 198
199int lock_ex(int fd) 199int lock_ex(int fd)
200{ 200{
201 return flock(fd,LOCK_EX); 201 return flock(fd, LOCK_EX);
202} 202}
203 203
204int lock_exnb(int fd) 204int lock_exnb(int fd)
205{ 205{
206 return flock(fd,LOCK_EX | LOCK_NB); 206 return flock(fd, LOCK_EX | LOCK_NB);
207} 207}
208 208
209#ifdef UNUSED
209int open_append(const char *fn) 210int open_append(const char *fn)
210{ 211{
211 return open(fn, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); 212 return open(fn, O_WRONLY | O_NDELAY | O_APPEND | O_CREAT, 0600);
212} 213}
213 214
214int open_read(const char *fn) 215int open_trunc(const char *fn)
215{ 216{
216 return open(fn, O_RDONLY|O_NDELAY); 217 return open(fn, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
217} 218}
219#endif
218 220
219int open_trunc(const char *fn) 221int open_read(const char *fn)
220{ 222{
221 return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); 223 return open(fn, O_RDONLY|O_NDELAY);
222} 224}
223 225
224int open_write(const char *fn) 226int open_write(const char *fn)
@@ -226,7 +228,7 @@ int open_write(const char *fn)
226 return open(fn, O_WRONLY|O_NDELAY); 228 return open(fn, O_WRONLY|O_NDELAY);
227} 229}
228 230
229unsigned pmatch(const char *p, const char *s, unsigned len) 231unsigned FAST_FUNC pmatch(const char *p, const char *s, unsigned len)
230{ 232{
231 for (;;) { 233 for (;;) {
232 char c = *p++; 234 char c = *p++;
diff --git a/runit/runit_lib.h b/runit/runit_lib.h
index 88d1c9f22..d8304aca6 100644
--- a/runit/runit_lib.h
+++ b/runit/runit_lib.h
@@ -66,18 +66,18 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
66//extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *); 66//extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *);
67 67
68extern int lock_ex(int); 68extern int lock_ex(int);
69extern int lock_un(int); 69//extern int lock_un(int);
70extern int lock_exnb(int); 70extern int lock_exnb(int);
71 71
72extern int open_read(const char *); 72extern int open_read(const char *);
73extern int open_excl(const char *);
74extern int open_append(const char *);
75extern int open_trunc(const char *);
76extern int open_write(const char *); 73extern int open_write(const char *);
74//extern int open_excl(const char *);
75//extern int open_append(const char *);
76//extern int open_trunc(const char *);
77 77
78extern unsigned pmatch(const char *, const char *, unsigned); 78extern unsigned FAST_FUNC pmatch(const char *, const char *, unsigned);
79 79
80#define str_diff(s,t) strcmp((s), (t)) 80//#define str_diff(s,t) strcmp((s), (t))
81#define str_equal(s,t) (!strcmp((s), (t))) 81#define str_equal(s,t) (!strcmp((s), (t)))
82 82
83/* 83/*
diff --git a/runit/runsv.c b/runit/runsv.c
index 5b221e90a..6bb6ec886 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -151,7 +151,8 @@ static char *bb_stpcpy(char *p, const char *to_add)
151 151
152static int open_trunc_or_warn(const char *name) 152static int open_trunc_or_warn(const char *name)
153{ 153{
154 int fd = open_trunc(name); 154 /* Why O_NDELAY? */
155 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
155 if (fd < 0) 156 if (fd < 0)
156 bb_perror_msg("%s: warning: cannot open %s", 157 bb_perror_msg("%s: warning: cannot open %s",
157 dir, name); 158 dir, name);
diff --git a/shell/ash.c b/shell/ash.c
index 6d67a1f8a..0ff5adb04 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -88,7 +88,7 @@
88//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh)) 88//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
89//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash)) 89//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
90 90
91//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o 91//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
92//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o 92//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
93 93
94//config:config ASH 94//config:config ASH
@@ -1002,7 +1002,8 @@ sharg(union node *arg, FILE *fp)
1002 for (p = arg->narg.text; *p; p++) { 1002 for (p = arg->narg.text; *p; p++) {
1003 switch ((unsigned char)*p) { 1003 switch ((unsigned char)*p) {
1004 case CTLESC: 1004 case CTLESC:
1005 putc(*++p, fp); 1005 p++;
1006 putc(*p, fp);
1006 break; 1007 break;
1007 case CTLVAR: 1008 case CTLVAR:
1008 putc('$', fp); 1009 putc('$', fp);
@@ -1011,8 +1012,10 @@ sharg(union node *arg, FILE *fp)
1011 if (subtype == VSLENGTH) 1012 if (subtype == VSLENGTH)
1012 putc('#', fp); 1013 putc('#', fp);
1013 1014
1014 while (*p != '=') 1015 while (*p != '=') {
1015 putc(*p++, fp); 1016 putc(*p, fp);
1017 p++;
1018 }
1016 1019
1017 if (subtype & VSNUL) 1020 if (subtype & VSNUL)
1018 putc(':', fp); 1021 putc(':', fp);
@@ -2031,10 +2034,6 @@ extern struct globals_var *const ash_ptr_to_globals_var;
2031# define optindval() (voptind.var_text + 7) 2034# define optindval() (voptind.var_text + 7)
2032#endif 2035#endif
2033 2036
2034
2035#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2036#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
2037
2038#if ENABLE_ASH_GETOPTS 2037#if ENABLE_ASH_GETOPTS
2039static void FAST_FUNC 2038static void FAST_FUNC
2040getoptsreset(const char *value) 2039getoptsreset(const char *value)
@@ -2044,24 +2043,26 @@ getoptsreset(const char *value)
2044} 2043}
2045#endif 2044#endif
2046 2045
2046/* math.h has these, otherwise define our private copies */
2047#if !ENABLE_SH_MATH_SUPPORT
2048#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2049#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
2047/* 2050/*
2048 * Return of a legal variable name (a letter or underscore followed by zero or 2051 * Return the pointer to the first char which is not part of a legal variable name
2049 * more letters, underscores, and digits). 2052 * (a letter or underscore followed by letters, underscores, and digits).
2050 */ 2053 */
2051static char* FAST_FUNC 2054static const char*
2052endofname(const char *name) 2055endofname(const char *name)
2053{ 2056{
2054 char *p; 2057 if (!is_name(*name))
2055 2058 return name;
2056 p = (char *) name; 2059 while (*++name) {
2057 if (!is_name(*p)) 2060 if (!is_in_name(*name))
2058 return p;
2059 while (*++p) {
2060 if (!is_in_name(*p))
2061 break; 2061 break;
2062 } 2062 }
2063 return p; 2063 return name;
2064} 2064}
2065#endif
2065 2066
2066/* 2067/*
2067 * Compares two strings up to the first = or '\0'. The first 2068 * Compares two strings up to the first = or '\0'. The first
@@ -2244,9 +2245,10 @@ setvareq(char *s, int flags)
2244static void 2245static void
2245setvar(const char *name, const char *val, int flags) 2246setvar(const char *name, const char *val, int flags)
2246{ 2247{
2247 char *p, *q; 2248 const char *q;
2248 size_t namelen; 2249 char *p;
2249 char *nameeq; 2250 char *nameeq;
2251 size_t namelen;
2250 size_t vallen; 2252 size_t vallen;
2251 2253
2252 q = endofname(name); 2254 q = endofname(name);
@@ -2260,12 +2262,13 @@ setvar(const char *name, const char *val, int flags)
2260 } else { 2262 } else {
2261 vallen = strlen(val); 2263 vallen = strlen(val);
2262 } 2264 }
2265
2263 INT_OFF; 2266 INT_OFF;
2264 nameeq = ckmalloc(namelen + vallen + 2); 2267 nameeq = ckmalloc(namelen + vallen + 2);
2265 p = (char *)memcpy(nameeq, name, namelen) + namelen; 2268 p = memcpy(nameeq, name, namelen) + namelen;
2266 if (val) { 2269 if (val) {
2267 *p++ = '='; 2270 *p++ = '=';
2268 p = (char *)memcpy(p, val, vallen) + vallen; 2271 p = memcpy(p, val, vallen) + vallen;
2269 } 2272 }
2270 *p = '\0'; 2273 *p = '\0';
2271 setvareq(nameeq, flags | VNOSAVE); 2274 setvareq(nameeq, flags | VNOSAVE);
@@ -2491,12 +2494,13 @@ static const char *expandstr(const char *ps);
2491#endif 2494#endif
2492 2495
2493static void 2496static void
2494setprompt(int whichprompt) 2497setprompt_if(smallint do_set, int whichprompt)
2495{ 2498{
2496 const char *prompt; 2499 const char *prompt;
2497#if ENABLE_ASH_EXPAND_PRMT 2500 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2498 struct stackmark smark; 2501
2499#endif 2502 if (!do_set)
2503 return;
2500 2504
2501 needprompt = 0; 2505 needprompt = 0;
2502 2506
@@ -5734,7 +5738,7 @@ ash_arith(const char *s)
5734 5738
5735 math_hooks.lookupvar = lookupvar; 5739 math_hooks.lookupvar = lookupvar;
5736 math_hooks.setvar = setvar2; 5740 math_hooks.setvar = setvar2;
5737 math_hooks.endofname = endofname; 5741 //math_hooks.endofname = endofname;
5738 5742
5739 INT_OFF; 5743 INT_OFF;
5740 result = arith(s, &errcode, &math_hooks); 5744 result = arith(s, &errcode, &math_hooks);
@@ -6326,7 +6330,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6326 flags &= ~EXP_TILDE; 6330 flags &= ~EXP_TILDE;
6327 tilde: 6331 tilde:
6328 q = p; 6332 q = p;
6329 if (*q == CTLESC && (flags & EXP_QWORD)) 6333 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
6330 q++; 6334 q++;
6331 if (*q == '~') 6335 if (*q == '~')
6332 p = exptilde(p, q, flags); 6336 p = exptilde(p, q, flags);
@@ -6340,9 +6344,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6340 c = p[length]; 6344 c = p[length];
6341 if (c) { 6345 if (c) {
6342 if (!(c & 0x80) 6346 if (!(c & 0x80)
6343#if ENABLE_SH_MATH_SUPPORT 6347 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
6344 || c == CTLENDARI
6345#endif
6346 ) { 6348 ) {
6347 /* c == '=' || c == ':' || c == CTLENDARI */ 6349 /* c == '=' || c == ':' || c == CTLENDARI */
6348 length++; 6350 length++;
@@ -6389,8 +6391,8 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6389 /* "$@" syntax adherence hack */ 6391 /* "$@" syntax adherence hack */
6390 if (!inquotes 6392 if (!inquotes
6391 && memcmp(p, dolatstr, 4) == 0 6393 && memcmp(p, dolatstr, 4) == 0
6392 && ( p[4] == CTLQUOTEMARK 6394 && ( p[4] == (char)CTLQUOTEMARK
6393 || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK) 6395 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
6394 ) 6396 )
6395 ) { 6397 ) {
6396 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; 6398 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
@@ -6425,8 +6427,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6425#endif 6427#endif
6426 } 6428 }
6427 } 6429 }
6428 breakloop: 6430 breakloop: ;
6429 ;
6430} 6431}
6431 6432
6432static char * 6433static char *
@@ -6611,8 +6612,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6611 int zero; 6612 int zero;
6612 char *(*scan)(char*, char*, char*, char*, int, int); 6613 char *(*scan)(char*, char*, char*, char*, int, int);
6613 6614
6614 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d", 6615 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6615 // p, varname, strloc, subtype, startloc, varflags, quotes); 6616 // p, varname, strloc, subtype, startloc, varflags, quotes);
6616 6617
6617 herefd = -1; 6618 herefd = -1;
6618 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, 6619 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
@@ -7045,8 +7046,8 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7045 vsplus: 7046 vsplus:
7046 if (varlen < 0) { 7047 if (varlen < 0) {
7047 argstr( 7048 argstr(
7048 p, flags | EXP_TILDE | 7049 p,
7049 (quoted ? EXP_QWORD : EXP_WORD), 7050 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
7050 var_str_list 7051 var_str_list
7051 ); 7052 );
7052 goto end; 7053 goto end;
@@ -9870,7 +9871,7 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9870static int 9871static int
9871goodname(const char *p) 9872goodname(const char *p)
9872{ 9873{
9873 return !*endofname(p); 9874 return endofname(p)[0] == '\0';
9874} 9875}
9875 9876
9876 9877
@@ -11501,7 +11502,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11501 startlinno = g_parsefile->linno; 11502 startlinno = g_parsefile->linno;
11502 bqlist = NULL; 11503 bqlist = NULL;
11503 quotef = 0; 11504 quotef = 0;
11504 oldstyle = 0;
11505 prevsyntax = 0; 11505 prevsyntax = 0;
11506#if ENABLE_ASH_EXPAND_PRMT 11506#if ENABLE_ASH_EXPAND_PRMT
11507 pssyntax = (syntax == PSSYNTAX); 11507 pssyntax = (syntax == PSSYNTAX);
@@ -11517,159 +11517,156 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11517 STARTSTACKSTR(out); 11517 STARTSTACKSTR(out);
11518 loop: 11518 loop:
11519 /* For each line, until end of word */ 11519 /* For each line, until end of word */
11520 { 11520 CHECKEND(); /* set c to PEOF if at end of here document */
11521 CHECKEND(); /* set c to PEOF if at end of here document */ 11521 for (;;) { /* until end of line or end of word */
11522 for (;;) { /* until end of line or end of word */ 11522 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11523 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ 11523 switch (SIT(c, syntax)) {
11524 switch (SIT(c, syntax)) { 11524 case CNL: /* '\n' */
11525 case CNL: /* '\n' */ 11525 if (syntax == BASESYNTAX)
11526 if (syntax == BASESYNTAX) 11526 goto endword; /* exit outer loop */
11527 goto endword; /* exit outer loop */ 11527 USTPUTC(c, out);
11528 USTPUTC(c, out); 11528 g_parsefile->linno++;
11529 g_parsefile->linno++; 11529 setprompt_if(doprompt, 2);
11530 if (doprompt) 11530 c = pgetc();
11531 setprompt(2); 11531 goto loop; /* continue outer loop */
11532 c = pgetc(); 11532 case CWORD:
11533 goto loop; /* continue outer loop */ 11533 USTPUTC(c, out);
11534 case CWORD: 11534 break;
11535 USTPUTC(c, out); 11535 case CCTL:
11536 break; 11536 if (eofmark == NULL || dblquote)
11537 case CCTL: 11537 USTPUTC(CTLESC, out);
11538 if (eofmark == NULL || dblquote)
11539 USTPUTC(CTLESC, out);
11540#if ENABLE_ASH_BASH_COMPAT 11538#if ENABLE_ASH_BASH_COMPAT
11541 if (c == '\\' && bash_dollar_squote) { 11539 if (c == '\\' && bash_dollar_squote) {
11542 c = decode_dollar_squote(); 11540 c = decode_dollar_squote();
11543 if (c & 0x100) { 11541 if (c & 0x100) {
11544 USTPUTC('\\', out); 11542 USTPUTC('\\', out);
11545 c = (unsigned char)c; 11543 c = (unsigned char)c;
11546 }
11547 } 11544 }
11545 }
11548#endif 11546#endif
11549 USTPUTC(c, out); 11547 USTPUTC(c, out);
11550 break; 11548 break;
11551 case CBACK: /* backslash */ 11549 case CBACK: /* backslash */
11552 c = pgetc_without_PEOA(); 11550 c = pgetc_without_PEOA();
11553 if (c == PEOF) { 11551 if (c == PEOF) {
11552 USTPUTC(CTLESC, out);
11553 USTPUTC('\\', out);
11554 pungetc();
11555 } else if (c == '\n') {
11556 setprompt_if(doprompt, 2);
11557 } else {
11558#if ENABLE_ASH_EXPAND_PRMT
11559 if (c == '$' && pssyntax) {
11554 USTPUTC(CTLESC, out); 11560 USTPUTC(CTLESC, out);
11555 USTPUTC('\\', out); 11561 USTPUTC('\\', out);
11556 pungetc(); 11562 }
11557 } else if (c == '\n') {
11558 if (doprompt)
11559 setprompt(2);
11560 } else {
11561#if ENABLE_ASH_EXPAND_PRMT
11562 if (c == '$' && pssyntax) {
11563 USTPUTC(CTLESC, out);
11564 USTPUTC('\\', out);
11565 }
11566#endif 11563#endif
11567 /* Backslash is retained if we are in "str" and next char isn't special */ 11564 /* Backslash is retained if we are in "str" and next char isn't special */
11568 if (dblquote 11565 if (dblquote
11569 && c != '\\' 11566 && c != '\\'
11570 && c != '`' 11567 && c != '`'
11571 && c != '$' 11568 && c != '$'
11572 && (c != '"' || eofmark != NULL) 11569 && (c != '"' || eofmark != NULL)
11573 ) { 11570 ) {
11574 USTPUTC(CTLESC, out); 11571 USTPUTC(CTLESC, out);
11575 USTPUTC('\\', out); 11572 USTPUTC('\\', out);
11576 }
11577 if (SIT(c, SQSYNTAX) == CCTL)
11578 USTPUTC(CTLESC, out);
11579 USTPUTC(c, out);
11580 quotef = 1;
11581 } 11573 }
11582 break; 11574 if (SIT(c, SQSYNTAX) == CCTL)
11583 case CSQUOTE: 11575 USTPUTC(CTLESC, out);
11584 syntax = SQSYNTAX; 11576 USTPUTC(c, out);
11577 quotef = 1;
11578 }
11579 break;
11580 case CSQUOTE:
11581 syntax = SQSYNTAX;
11585 quotemark: 11582 quotemark:
11586 if (eofmark == NULL) { 11583 if (eofmark == NULL) {
11587 USTPUTC(CTLQUOTEMARK, out); 11584 USTPUTC(CTLQUOTEMARK, out);
11585 }
11586 break;
11587 case CDQUOTE:
11588 syntax = DQSYNTAX;
11589 dblquote = 1;
11590 goto quotemark;
11591 case CENDQUOTE:
11592 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11593 if (eofmark != NULL && arinest == 0
11594 && varnest == 0
11595 ) {
11596 USTPUTC(c, out);
11597 } else {
11598 if (dqvarnest == 0) {
11599 syntax = BASESYNTAX;
11600 dblquote = 0;
11588 } 11601 }
11589 break; 11602 quotef = 1;
11590 case CDQUOTE:
11591 syntax = DQSYNTAX;
11592 dblquote = 1;
11593 goto quotemark; 11603 goto quotemark;
11594 case CENDQUOTE: 11604 }
11595 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) 11605 break;
11596 if (eofmark != NULL && arinest == 0 11606 case CVAR: /* '$' */
11597 && varnest == 0 11607 PARSESUB(); /* parse substitution */
11598 ) { 11608 break;
11599 USTPUTC(c, out); 11609 case CENDVAR: /* '}' */
11600 } else { 11610 if (varnest > 0) {
11601 if (dqvarnest == 0) { 11611 varnest--;
11602 syntax = BASESYNTAX; 11612 if (dqvarnest > 0) {
11603 dblquote = 0; 11613 dqvarnest--;
11604 }
11605 quotef = 1;
11606 goto quotemark;
11607 }
11608 break;
11609 case CVAR: /* '$' */
11610 PARSESUB(); /* parse substitution */
11611 break;
11612 case CENDVAR: /* '}' */
11613 if (varnest > 0) {
11614 varnest--;
11615 if (dqvarnest > 0) {
11616 dqvarnest--;
11617 }
11618 c = CTLENDVAR;
11619 } 11614 }
11620 USTPUTC(c, out); 11615 c = CTLENDVAR;
11621 break; 11616 }
11617 USTPUTC(c, out);
11618 break;
11622#if ENABLE_SH_MATH_SUPPORT 11619#if ENABLE_SH_MATH_SUPPORT
11623 case CLP: /* '(' in arithmetic */ 11620 case CLP: /* '(' in arithmetic */
11624 parenlevel++; 11621 parenlevel++;
11625 USTPUTC(c, out); 11622 USTPUTC(c, out);
11626 break; 11623 break;
11627 case CRP: /* ')' in arithmetic */ 11624 case CRP: /* ')' in arithmetic */
11628 if (parenlevel > 0) { 11625 if (parenlevel > 0) {
11629 parenlevel--; 11626 parenlevel--;
11630 } else { 11627 } else {
11631 if (pgetc() == ')') { 11628 if (pgetc() == ')') {
11632 if (--arinest == 0) { 11629 if (--arinest == 0) {
11633 syntax = prevsyntax; 11630 syntax = prevsyntax;
11634 dblquote = (syntax == DQSYNTAX); 11631 dblquote = (syntax == DQSYNTAX);
11635 c = CTLENDARI; 11632 c = CTLENDARI;
11636 }
11637 } else {
11638 /*
11639 * unbalanced parens
11640 * (don't 2nd guess - no error)
11641 */
11642 pungetc();
11643 } 11633 }
11634 } else {
11635 /*
11636 * unbalanced parens
11637 * (don't 2nd guess - no error)
11638 */
11639 pungetc();
11644 } 11640 }
11645 USTPUTC(c, out); 11641 }
11646 break; 11642 USTPUTC(c, out);
11643 break;
11647#endif 11644#endif
11648 case CBQUOTE: /* '`' */ 11645 case CBQUOTE: /* '`' */
11649 PARSEBACKQOLD(); 11646 PARSEBACKQOLD();
11650 break; 11647 break;
11651 case CENDFILE: 11648 case CENDFILE:
11652 goto endword; /* exit outer loop */ 11649 goto endword; /* exit outer loop */
11653 case CIGN: 11650 case CIGN:
11654 break; 11651 break;
11655 default: 11652 default:
11656 if (varnest == 0) { 11653 if (varnest == 0) {
11657#if ENABLE_ASH_BASH_COMPAT 11654#if ENABLE_ASH_BASH_COMPAT
11658 if (c == '&') { 11655 if (c == '&') {
11659 if (pgetc() == '>') 11656 if (pgetc() == '>')
11660 c = 0x100 + '>'; /* flag &> */ 11657 c = 0x100 + '>'; /* flag &> */
11661 pungetc(); 11658 pungetc();
11662 }
11663#endif
11664 goto endword; /* exit outer loop */
11665 } 11659 }
11666 IF_ASH_ALIAS(if (c != PEOA)) 11660#endif
11667 USTPUTC(c, out); 11661 goto endword; /* exit outer loop */
11668 } 11662 }
11669 c = pgetc_fast(); 11663 IF_ASH_ALIAS(if (c != PEOA))
11670 } /* for (;;) */ 11664 USTPUTC(c, out);
11671 } 11665 }
11666 c = pgetc_fast();
11667 } /* for (;;) */
11672 endword: 11668 endword:
11669
11673#if ENABLE_SH_MATH_SUPPORT 11670#if ENABLE_SH_MATH_SUPPORT
11674 if (syntax == ARISYNTAX) 11671 if (syntax == ARISYNTAX)
11675 raise_error_syntax("missing '))'"); 11672 raise_error_syntax("missing '))'");
@@ -11907,6 +11904,8 @@ parsesub: {
11907 c = pgetc(); 11904 c = pgetc();
11908#if ENABLE_ASH_BASH_COMPAT 11905#if ENABLE_ASH_BASH_COMPAT
11909 if (c == ':' || c == '$' || isdigit(c)) { 11906 if (c == ':' || c == '$' || isdigit(c)) {
11907//TODO: support more general format ${v:EXPR:EXPR},
11908// where EXPR follows $(()) rules
11910 subtype = VSSUBSTR; 11909 subtype = VSSUBSTR;
11911 pungetc(); 11910 pungetc();
11912 break; /* "goto do_pungetc" is bigger (!) */ 11911 break; /* "goto do_pungetc" is bigger (!) */
@@ -11934,6 +11933,9 @@ parsesub: {
11934 } 11933 }
11935#if ENABLE_ASH_BASH_COMPAT 11934#if ENABLE_ASH_BASH_COMPAT
11936 case '/': 11935 case '/':
11936 /* ${v/[/]pattern/repl} */
11937//TODO: encode pattern and repl separately.
11938// Currently ${v/$var_with_slash/repl} is horribly broken
11937 subtype = VSREPLACE; 11939 subtype = VSREPLACE;
11938 c = pgetc(); 11940 c = pgetc();
11939 if (c != '/') 11941 if (c != '/')
@@ -12000,16 +12002,14 @@ parsebackq: {
12000 treatment to some slashes, and then push the string and 12002 treatment to some slashes, and then push the string and
12001 reread it as input, interpreting it normally. */ 12003 reread it as input, interpreting it normally. */
12002 char *pout; 12004 char *pout;
12003 int pc;
12004 size_t psavelen; 12005 size_t psavelen;
12005 char *pstr; 12006 char *pstr;
12006 12007
12007
12008 STARTSTACKSTR(pout); 12008 STARTSTACKSTR(pout);
12009 for (;;) { 12009 for (;;) {
12010 if (needprompt) { 12010 int pc;
12011 setprompt(2); 12011
12012 } 12012 setprompt_if(needprompt, 2);
12013 pc = pgetc(); 12013 pc = pgetc();
12014 switch (pc) { 12014 switch (pc) {
12015 case '`': 12015 case '`':
@@ -12019,8 +12019,7 @@ parsebackq: {
12019 pc = pgetc(); 12019 pc = pgetc();
12020 if (pc == '\n') { 12020 if (pc == '\n') {
12021 g_parsefile->linno++; 12021 g_parsefile->linno++;
12022 if (doprompt) 12022 setprompt_if(doprompt, 2);
12023 setprompt(2);
12024 /* 12023 /*
12025 * If eating a newline, avoid putting 12024 * If eating a newline, avoid putting
12026 * the newline into the new character 12025 * the newline into the new character
@@ -12183,9 +12182,7 @@ xxreadtoken(void)
12183 tokpushback = 0; 12182 tokpushback = 0;
12184 return lasttoken; 12183 return lasttoken;
12185 } 12184 }
12186 if (needprompt) { 12185 setprompt_if(needprompt, 2);
12187 setprompt(2);
12188 }
12189 startlinno = g_parsefile->linno; 12186 startlinno = g_parsefile->linno;
12190 for (;;) { /* until token or start of word found */ 12187 for (;;) { /* until token or start of word found */
12191 c = pgetc_fast(); 12188 c = pgetc_fast();
@@ -12202,8 +12199,7 @@ xxreadtoken(void)
12202 break; /* return readtoken1(...) */ 12199 break; /* return readtoken1(...) */
12203 } 12200 }
12204 startlinno = ++g_parsefile->linno; 12201 startlinno = ++g_parsefile->linno;
12205 if (doprompt) 12202 setprompt_if(doprompt, 2);
12206 setprompt(2);
12207 } else { 12203 } else {
12208 const char *p; 12204 const char *p;
12209 12205
@@ -12249,9 +12245,7 @@ xxreadtoken(void)
12249 tokpushback = 0; 12245 tokpushback = 0;
12250 return lasttoken; 12246 return lasttoken;
12251 } 12247 }
12252 if (needprompt) { 12248 setprompt_if(needprompt, 2);
12253 setprompt(2);
12254 }
12255 startlinno = g_parsefile->linno; 12249 startlinno = g_parsefile->linno;
12256 for (;;) { /* until token or start of word found */ 12250 for (;;) { /* until token or start of word found */
12257 c = pgetc_fast(); 12251 c = pgetc_fast();
@@ -12267,8 +12261,7 @@ xxreadtoken(void)
12267 case '\\': 12261 case '\\':
12268 if (pgetc() == '\n') { 12262 if (pgetc() == '\n') {
12269 startlinno = ++g_parsefile->linno; 12263 startlinno = ++g_parsefile->linno;
12270 if (doprompt) 12264 setprompt_if(doprompt, 2);
12271 setprompt(2);
12272 continue; 12265 continue;
12273 } 12266 }
12274 pungetc(); 12267 pungetc();
@@ -12394,8 +12387,7 @@ parsecmd(int interact)
12394 12387
12395 tokpushback = 0; 12388 tokpushback = 0;
12396 doprompt = interact; 12389 doprompt = interact;
12397 if (doprompt) 12390 setprompt_if(doprompt, doprompt);
12398 setprompt(doprompt);
12399 needprompt = 0; 12391 needprompt = 0;
12400 t = readtoken(); 12392 t = readtoken();
12401 if (t == TEOF) 12393 if (t == TEOF)
@@ -12419,10 +12411,8 @@ parseheredoc(void)
12419 heredoclist = NULL; 12411 heredoclist = NULL;
12420 12412
12421 while (here) { 12413 while (here) {
12422 if (needprompt) { 12414 setprompt_if(needprompt, 2);
12423 setprompt(2); 12415 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
12424 }
12425 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
12426 here->eofmark, here->striptabs); 12416 here->eofmark, here->striptabs);
12427 n = stzalloc(sizeof(struct narg)); 12417 n = stzalloc(sizeof(struct narg));
12428 n->narg.type = NARG; 12418 n->narg.type = NARG;
diff --git a/shell/ash_test/ash-vars/var_bash3.tests b/shell/ash_test/ash-vars/var_bash3.tests
index eca3318e2..146dbb6a5 100755
--- a/shell/ash_test/ash-vars/var_bash3.tests
+++ b/shell/ash_test/ash-vars/var_bash3.tests
@@ -3,13 +3,6 @@ r=${a//b/\041#}
3echo 1 $r 3echo 1 $r
4echo 2 ${a//b/\041#} 4echo 2 ${a//b/\041#}
5echo 3 "${a//b/\041#}" 5echo 3 "${a//b/\041#}"
6# --- var_bash3.xx
7# +++ var_bash3.right
8# -1 a\041#c
9# +1 a041#c
10# 2 a041#c
11# -3 a041#c
12# +3 a\041#c
13 6
14a='abc' 7a='abc'
15r=${a//b/\\041#} 8r=${a//b/\\041#}
diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests
index 3f49321e1..7f482a554 100755
--- a/shell/ash_test/ash-vars/var_bash5.tests
+++ b/shell/ash_test/ash-vars/var_bash5.tests
@@ -1,5 +1,5 @@
1# This testcase checks whether slashes in ${v/a/b} are parsed before or after expansions 1# This testcase checks whether slashes in ${v/a/b} are parsed before
2# in a part 2# or after expansions
3 3
4v='a/b/c' 4v='a/b/c'
5s='b/c' 5s='b/c'
diff --git a/shell/bbsh.c b/shell/bbsh.c
deleted file mode 100644
index 8e25db458..000000000
--- a/shell/bbsh.c
+++ /dev/null
@@ -1,223 +0,0 @@
1/* vi: set ts=4 :
2 *
3 * bbsh - busybox shell
4 *
5 * Copyright 2006 Rob Landley <rob@landley.net>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10// A section of code that gets repeatedly or conditionally executed is stored
11// as a string and parsed each time it's run.
12
13
14
15// Wheee, debugging.
16
17// Terminal control
18#define ENABLE_BBSH_TTY 0
19
20// &, fg, bg, jobs. (ctrl-z with tty.)
21#define ENABLE_BBSH_JOBCTL 0
22
23// Flow control (if, while, for, functions { })
24#define ENABLE_BBSH_FLOWCTL 0
25
26#define ENABLE_BBSH_ENVVARS 0 // Environment variable support
27
28// Local and synthetic variables, fancy prompts, set, $?, etc.
29#define ENABLE_BBSH_LOCALVARS 0
30
31// Pipes and redirects: | > < >> << && || & () ;
32#define ENABLE_BBSH_PIPES 0
33
34/* Fun:
35
36 echo `echo hello#comment " woot` and more
37*/
38
39#include "libbb.h"
40
41// A single executable, its arguments, and other information we know about it.
42#define BBSH_FLAG_EXIT 1
43#define BBSH_FLAG_SUSPEND 2
44#define BBSH_FLAG_PIPE 4
45#define BBSH_FLAG_AND 8
46#define BBSH_FLAG_OR 16
47#define BBSH_FLAG_AMP 32
48#define BBSH_FLAG_SEMI 64
49#define BBSH_FLAG_PAREN 128
50
51// What we know about a single process.
52struct command {
53 struct command *next;
54 int flags; // exit, suspend, && ||
55 int pid; // pid (or exit code)
56 int argc;
57 char *argv[];
58};
59
60// A collection of processes piped into/waiting on each other.
61struct pipeline {
62 struct pipeline *next;
63 int job_id;
64 struct command *cmd;
65 char *cmdline;
66 int cmdlinelen;
67};
68
69static void free_list(void *list, void (*freeit)(void *data))
70{
71 while (list) {
72 void **next = (void **)list;
73 void *list_next = *next;
74 freeit(list);
75 free(list);
76 list = list_next;
77 }
78}
79
80// Parse one word from the command line, appending one or more argv[] entries
81// to struct command. Handles environment variable substitution and
82// substrings. Returns pointer to next used byte, or NULL if it
83// hit an ending token.
84static char *parse_word(char *start, struct command **cmd)
85{
86 char *end;
87
88 // Detect end of line (and truncate line at comment)
89 if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0;
90
91 // Grab next word. (Add dequote and envvar logic here)
92 end = start;
93 end = skip_non_whitespace(end);
94 (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
95
96 // Allocate more space if there's no room for NULL terminator.
97
98 if (!((*cmd)->argc & 7))
99 *cmd = xrealloc(*cmd,
100 sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
101 (*cmd)->argv[(*cmd)->argc] = 0;
102 return end;
103}
104
105// Parse a line of text into a pipeline.
106// Returns a pointer to the next line.
107
108static char *parse_pipeline(char *cmdline, struct pipeline *line)
109{
110 struct command **cmd = &(line->cmd);
111 char *start = line->cmdline = cmdline;
112
113 if (!cmdline) return 0;
114
115 if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline;
116
117 // Parse command into argv[]
118 for (;;) {
119 char *end;
120
121 // Skip leading whitespace and detect end of line.
122 start = skip_whitespace(start);
123 if (!*start || *start=='#') {
124 if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
125 return 0;
126 }
127
128 // Allocate next command structure if necessary
129 if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
130
131 // Parse next argument and add the results to argv[]
132 end = parse_word(start, cmd);
133
134 // If we hit the end of this command, how did it end?
135 if (!end) {
136 if (ENABLE_BBSH_PIPES && *start) {
137 if (*start==';') {
138 start++;
139 break;
140 }
141 // handle | & < > >> << || &&
142 }
143 break;
144 }
145 start = end;
146 }
147
148 if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
149
150 return start;
151}
152
153// Execute the commands in a pipeline
154static int run_pipeline(struct pipeline *line)
155{
156 struct command *cmd = line->cmd;
157 if (!cmd || !cmd->argc) return 0;
158
159 // Handle local commands. This is totally fake and plastic.
160 if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd"))
161 chdir(cmd->argv[1]);
162 else if (!strcmp(cmd->argv[0],"exit"))
163 exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0);
164 else {
165 int status;
166 pid_t pid=fork();
167 if (!pid) {
168 run_applet_and_exit(cmd->argv[0],cmd->argc,cmd->argv);
169 execvp(cmd->argv[0],cmd->argv);
170 printf("No %s", cmd->argv[0]);
171 exit(EXIT_FAILURE);
172 } else waitpid(pid, &status, 0);
173 }
174
175 return 0;
176}
177
178static void free_cmd(void *data)
179{
180 struct command *cmd=(struct command *)data;
181
182 while (cmd->argc) free(cmd->argv[--cmd->argc]);
183}
184
185
186static void handle(char *command)
187{
188 struct pipeline line;
189 char *start = command;
190
191 for (;;) {
192 memset(&line,0,sizeof(struct pipeline));
193 start = parse_pipeline(start, &line);
194 if (!line.cmd) break;
195
196 run_pipeline(&line);
197 free_list(line.cmd, free_cmd);
198 }
199}
200
201int bbsh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
202int bbsh_main(int argc UNUSED_PARAM, char **argv)
203{
204 char *command=NULL;
205 FILE *f;
206
207 getopt32(argv, "c:", &command);
208
209 f = argv[optind] ? xfopen_for_read(argv[optind]) : NULL;
210 if (command) handle(command);
211 else {
212 unsigned cmdlen=0;
213 for (;;) {
214 if (!f) putchar('$');
215 if (1 > getline(&command, &cmdlen, f ? f : stdin)) break;
216
217 handle(command);
218 }
219 if (ENABLE_FEATURE_CLEAN_UP) free(command);
220 }
221
222 return 1;
223}
diff --git a/shell/hush.c b/shell/hush.c
index e8dfb2499..752efd0c8 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -50,7 +50,6 @@
50 * 50 *
51 * Bash compat TODO: 51 * Bash compat TODO:
52 * redirection of stdout+stderr: &> and >& 52 * redirection of stdout+stderr: &> and >&
53 * subst operator: ${var/[/]expr/expr}
54 * brace expansion: one/{two,three,four} 53 * brace expansion: one/{two,three,four}
55 * reserved words: function select 54 * reserved words: function select
56 * advanced test: [[ ]] 55 * advanced test: [[ ]]
@@ -103,7 +102,6 @@
103 102
104//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP)) 103//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP))
105//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP)) 104//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP))
106//applet:IF_LASH(APPLET(lash, _BB_DIR_BIN, _BB_SUID_DROP))
107//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh)) 105//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh))
108//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash)) 106//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
109 107
@@ -148,6 +146,13 @@
148//config: from stdin just like a shell script from a file. 146//config: from stdin just like a shell script from a file.
149//config: No prompt, no PS1/PS2 magic shell variables. 147//config: No prompt, no PS1/PS2 magic shell variables.
150//config: 148//config:
149//config:config HUSH_SAVEHISTORY
150//config: bool "Save command history to .hush_history"
151//config: default y
152//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
153//config: help
154//config: Enable history saving in hush.
155//config:
151//config:config HUSH_JOB 156//config:config HUSH_JOB
152//config: bool "Job control" 157//config: bool "Job control"
153//config: default y 158//config: default y
@@ -224,13 +229,6 @@
224//config: This instructs hush to print commands before execution. 229//config: This instructs hush to print commands before execution.
225//config: Adds ~300 bytes. 230//config: Adds ~300 bytes.
226//config: 231//config:
227//config:config LASH
228//config: bool "lash (deprecated: aliased to hush)"
229//config: default n
230//config: select HUSH
231//config: help
232//config: lash is deprecated and will be removed, please migrate to hush.
233//config:
234//config:config MSH 232//config:config MSH
235//config: bool "msh (deprecated: aliased to hush)" 233//config: bool "msh (deprecated: aliased to hush)"
236//config: default n 234//config: default n
@@ -241,10 +239,12 @@
241 239
242//usage:#define hush_trivial_usage NOUSAGE_STR 240//usage:#define hush_trivial_usage NOUSAGE_STR
243//usage:#define hush_full_usage "" 241//usage:#define hush_full_usage ""
244//usage:#define lash_trivial_usage NOUSAGE_STR
245//usage:#define lash_full_usage ""
246//usage:#define msh_trivial_usage NOUSAGE_STR 242//usage:#define msh_trivial_usage NOUSAGE_STR
247//usage:#define msh_full_usage "" 243//usage:#define msh_full_usage ""
244//usage:#define sh_trivial_usage NOUSAGE_STR
245//usage:#define sh_full_usage ""
246//usage:#define bash_trivial_usage NOUSAGE_STR
247//usage:#define bash_full_usage ""
248 248
249 249
250/* Build knobs */ 250/* Build knobs */
@@ -330,6 +330,17 @@
330#define _SPECIAL_VARS_STR "_*@$!?#" 330#define _SPECIAL_VARS_STR "_*@$!?#"
331#define SPECIAL_VARS_STR ("_*@$!?#" + 1) 331#define SPECIAL_VARS_STR ("_*@$!?#" + 1)
332#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) 332#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
333#if ENABLE_HUSH_BASH_COMPAT
334/* Support / and // replace ops */
335/* Note that // is stored as \ in "encoded" string representation */
336# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
337# define VAR_SUBST_OPS ("\\/%#:-=+?" + 1)
338# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
339#else
340# define VAR_ENCODED_SUBST_OPS "%#:-=+?"
341# define VAR_SUBST_OPS "%#:-=+?"
342# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
343#endif
333 344
334#define SPECIAL_VAR_SYMBOL 3 345#define SPECIAL_VAR_SYMBOL 3
335 346
@@ -349,7 +360,7 @@ typedef struct nommu_save_t {
349} nommu_save_t; 360} nommu_save_t;
350#endif 361#endif
351 362
352typedef enum reserved_style { 363enum {
353 RES_NONE = 0, 364 RES_NONE = 0,
354#if ENABLE_HUSH_IF 365#if ENABLE_HUSH_IF
355 RES_IF , 366 RES_IF ,
@@ -378,7 +389,13 @@ typedef enum reserved_style {
378#endif 389#endif
379 RES_XXXX , 390 RES_XXXX ,
380 RES_SNTX 391 RES_SNTX
381} reserved_style; 392};
393
394enum {
395 EXP_FLAG_GLOB = 0x200,
396 EXP_FLAG_ESC_GLOB_CHARS = 0x100,
397 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
398};
382 399
383typedef struct o_string { 400typedef struct o_string {
384 char *data; 401 char *data;
@@ -386,11 +403,10 @@ typedef struct o_string {
386 int maxlen; 403 int maxlen;
387 /* Protect newly added chars against globbing 404 /* Protect newly added chars against globbing
388 * (by prepending \ to *, ?, [, \) */ 405 * (by prepending \ to *, ?, [, \) */
389 smallint o_escape; 406 int o_expflags;
390 smallint o_glob;
391 /* At least some part of the string was inside '' or "", 407 /* At least some part of the string was inside '' or "",
392 * possibly empty one: word"", wo''rd etc. */ 408 * possibly empty one: word"", wo''rd etc. */
393 smallint o_quoted; 409 smallint has_quoted_part;
394 smallint has_empty_slot; 410 smallint has_empty_slot;
395 smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ 411 smallint o_assignment; /* 0:maybe, 1:yes, 2:no */
396} o_string; 412} o_string;
@@ -475,20 +491,10 @@ struct command {
475 smallint cmd_type; /* CMD_xxx */ 491 smallint cmd_type; /* CMD_xxx */
476#define CMD_NORMAL 0 492#define CMD_NORMAL 0
477#define CMD_SUBSHELL 1 493#define CMD_SUBSHELL 1
478
479/* used for "[[ EXPR ]]" */
480#if ENABLE_HUSH_BASH_COMPAT 494#if ENABLE_HUSH_BASH_COMPAT
495/* used for "[[ EXPR ]]" */
481# define CMD_SINGLEWORD_NOGLOB 2 496# define CMD_SINGLEWORD_NOGLOB 2
482#endif 497#endif
483
484/* used for "export noglob=* glob* a=`echo a b`" */
485//#define CMD_SINGLEWORD_NOGLOB_COND 3
486// It is hard to implement correctly, it adds significant amounts of tricky code,
487// and all this is only useful for really obscure export statements
488// almost nobody would use anyway. #ifdef CMD_SINGLEWORD_NOGLOB_COND
489// guards the code which implements it, but I have doubts it works
490// in all cases (especially with mixed globbed/non-globbed arguments)
491
492#if ENABLE_HUSH_FUNCTIONS 498#if ENABLE_HUSH_FUNCTIONS
493# define CMD_FUNCDEF 3 499# define CMD_FUNCDEF 3
494#endif 500#endif
@@ -1354,11 +1360,15 @@ static void hush_exit(int exitcode)
1354 /* Prevent recursion: 1360 /* Prevent recursion:
1355 * trap "echo Hi; exit" EXIT; exit 1361 * trap "echo Hi; exit" EXIT; exit
1356 */ 1362 */
1357 char *argv[] = { NULL, G.traps[0], NULL }; 1363 char *argv[3];
1358 G.traps[0] = NULL; 1364 /* argv[0] is unused */
1359 G.exiting = 1; 1365 argv[1] = G.traps[0];
1366 argv[2] = NULL;
1367 G.exiting = 1; /* prevent EXIT trap recursion */
1368 /* Note: G.traps[0] is not cleared!
1369 * "trap" will still show it, if executed
1370 * in the handler */
1360 builtin_eval(argv); 1371 builtin_eval(argv);
1361 free(argv[1]);
1362 } 1372 }
1363 1373
1364#if ENABLE_HUSH_JOB 1374#if ENABLE_HUSH_JOB
@@ -1369,9 +1379,15 @@ static void hush_exit(int exitcode)
1369#endif 1379#endif
1370} 1380}
1371 1381
1382
1372static int check_and_run_traps(int sig) 1383static int check_and_run_traps(int sig)
1373{ 1384{
1374 static const struct timespec zero_timespec; 1385 /* I want it in rodata, not in bss.
1386 * gcc 4.2.1 puts it in rodata only if it has { 0, 0 }
1387 * initializer. But other compilers may still use bss.
1388 * TODO: find more portable solution.
1389 */
1390 static const struct timespec zero_timespec = { 0, 0 };
1375 smalluint save_rcode; 1391 smalluint save_rcode;
1376 int last_sig = 0; 1392 int last_sig = 0;
1377 1393
@@ -1386,10 +1402,12 @@ static int check_and_run_traps(int sig)
1386 if (G.traps && G.traps[sig]) { 1402 if (G.traps && G.traps[sig]) {
1387 if (G.traps[sig][0]) { 1403 if (G.traps[sig][0]) {
1388 /* We have user-defined handler */ 1404 /* We have user-defined handler */
1389 char *argv[] = { NULL, xstrdup(G.traps[sig]), NULL }; 1405 char *argv[3];
1406 /* argv[0] is unused */
1407 argv[1] = G.traps[sig];
1408 argv[2] = NULL;
1390 save_rcode = G.last_exitcode; 1409 save_rcode = G.last_exitcode;
1391 builtin_eval(argv); 1410 builtin_eval(argv);
1392 free(argv[1]);
1393 G.last_exitcode = save_rcode; 1411 G.last_exitcode = save_rcode;
1394 } /* else: "" trap, ignoring signal */ 1412 } /* else: "" trap, ignoring signal */
1395 continue; 1413 continue;
@@ -1449,13 +1467,11 @@ static const char *get_cwd(int force)
1449/* 1467/*
1450 * Shell and environment variable support 1468 * Shell and environment variable support
1451 */ 1469 */
1452static struct variable **get_ptr_to_local_var(const char *name) 1470static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
1453{ 1471{
1454 struct variable **pp; 1472 struct variable **pp;
1455 struct variable *cur; 1473 struct variable *cur;
1456 int len;
1457 1474
1458 len = strlen(name);
1459 pp = &G.top_var; 1475 pp = &G.top_var;
1460 while ((cur = *pp) != NULL) { 1476 while ((cur = *pp) != NULL) {
1461 if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=') 1477 if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
@@ -1465,21 +1481,13 @@ static struct variable **get_ptr_to_local_var(const char *name)
1465 return NULL; 1481 return NULL;
1466} 1482}
1467 1483
1468static struct variable *get_local_var(const char *name)
1469{
1470 struct variable **pp = get_ptr_to_local_var(name);
1471 if (pp)
1472 return *pp;
1473 return NULL;
1474}
1475
1476static const char* FAST_FUNC get_local_var_value(const char *name) 1484static const char* FAST_FUNC get_local_var_value(const char *name)
1477{ 1485{
1478 struct variable **vpp; 1486 struct variable **vpp;
1487 unsigned len = strlen(name);
1479 1488
1480 if (G.expanded_assignments) { 1489 if (G.expanded_assignments) {
1481 char **cpp = G.expanded_assignments; 1490 char **cpp = G.expanded_assignments;
1482 int len = strlen(name);
1483 while (*cpp) { 1491 while (*cpp) {
1484 char *cp = *cpp; 1492 char *cp = *cpp;
1485 if (strncmp(cp, name, len) == 0 && cp[len] == '=') 1493 if (strncmp(cp, name, len) == 0 && cp[len] == '=')
@@ -1488,17 +1496,16 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
1488 } 1496 }
1489 } 1497 }
1490 1498
1491 vpp = get_ptr_to_local_var(name); 1499 vpp = get_ptr_to_local_var(name, len);
1492 if (vpp) 1500 if (vpp)
1493 return strchr((*vpp)->varstr, '=') + 1; 1501 return (*vpp)->varstr + len + 1;
1494 1502
1495 if (strcmp(name, "PPID") == 0) 1503 if (strcmp(name, "PPID") == 0)
1496 return utoa(G.root_ppid); 1504 return utoa(G.root_ppid);
1497 // bash compat: UID? EUID? 1505 // bash compat: UID? EUID?
1498#if ENABLE_HUSH_RANDOM_SUPPORT 1506#if ENABLE_HUSH_RANDOM_SUPPORT
1499 if (strcmp(name, "RANDOM") == 0) { 1507 if (strcmp(name, "RANDOM") == 0)
1500 return utoa(next_random(&G.random_gen)); 1508 return utoa(next_random(&G.random_gen));
1501 }
1502#endif 1509#endif
1503 return NULL; 1510 return NULL;
1504} 1511}
@@ -1687,24 +1694,6 @@ static void unset_vars(char **strings)
1687 free(strings); 1694 free(strings);
1688} 1695}
1689 1696
1690#if ENABLE_SH_MATH_SUPPORT
1691# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1692# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1693static char* FAST_FUNC endofname(const char *name)
1694{
1695 char *p;
1696
1697 p = (char *) name;
1698 if (!is_name(*p))
1699 return p;
1700 while (*++p) {
1701 if (!is_in_name(*p))
1702 break;
1703 }
1704 return p;
1705}
1706#endif
1707
1708static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 1697static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
1709{ 1698{
1710 char *var = xasprintf("%s=%s", name, val); 1699 char *var = xasprintf("%s=%s", name, val);
@@ -1748,9 +1737,7 @@ static struct variable *set_vars_and_save_old(char **strings)
1748 1737
1749 eq = strchr(*s, '='); 1738 eq = strchr(*s, '=');
1750 if (eq) { 1739 if (eq) {
1751 *eq = '\0'; 1740 var_pp = get_ptr_to_local_var(*s, eq - *s);
1752 var_pp = get_ptr_to_local_var(*s);
1753 *eq = '=';
1754 if (var_pp) { 1741 if (var_pp) {
1755 /* Remove variable from global linked list */ 1742 /* Remove variable from global linked list */
1756 var_p = *var_pp; 1743 var_p = *var_pp;
@@ -1802,7 +1789,7 @@ static void cmdedit_update_prompt(void)
1802 G.PS2 = "> "; 1789 G.PS2 = "> ";
1803} 1790}
1804 1791
1805static const char* setup_prompt_string(int promptmode) 1792static const char *setup_prompt_string(int promptmode)
1806{ 1793{
1807 const char *prompt_str; 1794 const char *prompt_str;
1808 debug_printf("setup_prompt_string %d ", promptmode); 1795 debug_printf("setup_prompt_string %d ", promptmode);
@@ -1953,7 +1940,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
1953static void o_reset_to_empty_unquoted(o_string *o) 1940static void o_reset_to_empty_unquoted(o_string *o)
1954{ 1941{
1955 o->length = 0; 1942 o->length = 0;
1956 o->o_quoted = 0; 1943 o->has_quoted_part = 0;
1957 if (o->data) 1944 if (o->data)
1958 o->data[0] = '\0'; 1945 o->data[0] = '\0';
1959} 1946}
@@ -2017,13 +2004,17 @@ static void o_addstr_with_NUL(o_string *o, const char *str)
2017static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) 2004static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
2018{ 2005{
2019 while (len) { 2006 while (len) {
2007 len--;
2020 o_addchr(o, *str); 2008 o_addchr(o, *str);
2021 if (*str++ == '\\' 2009 if (*str++ == '\\') {
2022 && (*str != '*' && *str != '?' && *str != '[') 2010 /* \z -> \\\z; \<eol> -> \\<eol> */
2023 ) {
2024 o_addchr(o, '\\'); 2011 o_addchr(o, '\\');
2012 if (len) {
2013 len--;
2014 o_addchr(o, '\\');
2015 o_addchr(o, *str++);
2016 }
2025 } 2017 }
2026 len--;
2027 } 2018 }
2028} 2019}
2029 2020
@@ -2067,7 +2058,9 @@ static void o_addqchr(o_string *o, int ch)
2067static void o_addQchr(o_string *o, int ch) 2058static void o_addQchr(o_string *o, int ch)
2068{ 2059{
2069 int sz = 1; 2060 int sz = 1;
2070 if (o->o_escape && strchr("*?[\\" MAYBE_BRACES, ch)) { 2061 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
2062 && strchr("*?[\\" MAYBE_BRACES, ch)
2063 ) {
2071 sz++; 2064 sz++;
2072 o->data[o->length] = '\\'; 2065 o->data[o->length] = '\\';
2073 o->length++; 2066 o->length++;
@@ -2078,12 +2071,8 @@ static void o_addQchr(o_string *o, int ch)
2078 o->data[o->length] = '\0'; 2071 o->data[o->length] = '\0';
2079} 2072}
2080 2073
2081static void o_addQstr(o_string *o, const char *str, int len) 2074static void o_addqblock(o_string *o, const char *str, int len)
2082{ 2075{
2083 if (!o->o_escape) {
2084 o_addblock(o, str, len);
2085 return;
2086 }
2087 while (len) { 2076 while (len) {
2088 char ch; 2077 char ch;
2089 int sz; 2078 int sz;
@@ -2110,6 +2099,20 @@ static void o_addQstr(o_string *o, const char *str, int len)
2110 } 2099 }
2111} 2100}
2112 2101
2102static void o_addQblock(o_string *o, const char *str, int len)
2103{
2104 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
2105 o_addblock(o, str, len);
2106 return;
2107 }
2108 o_addqblock(o, str, len);
2109}
2110
2111static void o_addQstr(o_string *o, const char *str)
2112{
2113 o_addQblock(o, str, strlen(str));
2114}
2115
2113/* A special kind of o_string for $VAR and `cmd` expansion. 2116/* A special kind of o_string for $VAR and `cmd` expansion.
2114 * It contains char* list[] at the beginning, which is grown in 16 element 2117 * It contains char* list[] at the beginning, which is grown in 16 element
2115 * increments. Actual string data starts at the next multiple of 16 * (char*). 2118 * increments. Actual string data starts at the next multiple of 16 * (char*).
@@ -2128,8 +2131,11 @@ static void debug_print_list(const char *prefix, o_string *o, int n)
2128 int i = 0; 2131 int i = 0;
2129 2132
2130 indent(); 2133 indent();
2131 fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", 2134 fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
2132 prefix, list, n, string_start, o->length, o->maxlen); 2135 prefix, list, n, string_start, o->length, o->maxlen,
2136 !!(o->o_expflags & EXP_FLAG_GLOB),
2137 o->has_quoted_part,
2138 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2133 while (i < n) { 2139 while (i < n) {
2134 indent(); 2140 indent();
2135 fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i], 2141 fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i],
@@ -2179,7 +2185,7 @@ static int o_save_ptr_helper(o_string *o, int n)
2179 n, string_len, string_start); 2185 n, string_len, string_start);
2180 o->has_empty_slot = 0; 2186 o->has_empty_slot = 0;
2181 } 2187 }
2182 list[n] = (char*)(ptrdiff_t)string_len; 2188 list[n] = (char*)(uintptr_t)string_len;
2183 return n + 1; 2189 return n + 1;
2184} 2190}
2185 2191
@@ -2189,7 +2195,7 @@ static int o_get_last_ptr(o_string *o, int n)
2189 char **list = (char**)o->data; 2195 char **list = (char**)o->data;
2190 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); 2196 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
2191 2197
2192 return ((int)(ptrdiff_t)list[n-1]) + string_start; 2198 return ((int)(uintptr_t)list[n-1]) + string_start;
2193} 2199}
2194 2200
2195#ifdef HUSH_BRACE_EXP 2201#ifdef HUSH_BRACE_EXP
@@ -2227,9 +2233,9 @@ static const char *next_brace_sub(const char *cp)
2227 cp++; 2233 cp++;
2228 continue; 2234 continue;
2229 } 2235 }
2230 /*{*/ if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) 2236 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
2231 break; 2237 break;
2232 if (*cp++ == '{') /*}*/ 2238 if (*cp++ == '{')
2233 depth++; 2239 depth++;
2234 } 2240 }
2235 2241
@@ -2251,7 +2257,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2251 while (1) { 2257 while (1) {
2252 if (*begin == '\0') 2258 if (*begin == '\0')
2253 goto simple_glob; 2259 goto simple_glob;
2254 if (*begin == '{') /*}*/ { 2260 if (*begin == '{') {
2255 /* Find the first sub-pattern and at the same time 2261 /* Find the first sub-pattern and at the same time
2256 * find the rest after the closing brace */ 2262 * find the rest after the closing brace */
2257 next = next_brace_sub(begin); 2263 next = next_brace_sub(begin);
@@ -2259,7 +2265,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2259 /* An illegal expression */ 2265 /* An illegal expression */
2260 goto simple_glob; 2266 goto simple_glob;
2261 } 2267 }
2262 /*{*/ if (*next == '}') { 2268 if (*next == '}') {
2263 /* "{abc}" with no commas - illegal 2269 /* "{abc}" with no commas - illegal
2264 * brace expr, disregard and skip it */ 2270 * brace expr, disregard and skip it */
2265 begin = next + 1; 2271 begin = next + 1;
@@ -2276,7 +2282,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2276 2282
2277 /* Now find the end of the whole brace expression */ 2283 /* Now find the end of the whole brace expression */
2278 rest = next; 2284 rest = next;
2279 /*{*/ while (*rest != '}') { 2285 while (*rest != '}') {
2280 rest = next_brace_sub(rest); 2286 rest = next_brace_sub(rest);
2281 if (rest == NULL) { 2287 if (rest == NULL) {
2282 /* An illegal expression */ 2288 /* An illegal expression */
@@ -2312,7 +2318,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2312 * That's why we re-copy prefix every time (1st memcpy above). 2318 * That's why we re-copy prefix every time (1st memcpy above).
2313 */ 2319 */
2314 n = glob_brace(new_pattern_buf, o, n); 2320 n = glob_brace(new_pattern_buf, o, n);
2315 /*{*/ if (*next == '}') { 2321 if (*next == '}') {
2316 /* We saw the last entry */ 2322 /* We saw the last entry */
2317 break; 2323 break;
2318 } 2324 }
@@ -2362,11 +2368,11 @@ static int glob_brace(char *pattern, o_string *o, int n)
2362/* Performs globbing on last list[], 2368/* Performs globbing on last list[],
2363 * saving each result as a new list[]. 2369 * saving each result as a new list[].
2364 */ 2370 */
2365static int o_glob(o_string *o, int n) 2371static int perform_glob(o_string *o, int n)
2366{ 2372{
2367 char *pattern, *copy; 2373 char *pattern, *copy;
2368 2374
2369 debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); 2375 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
2370 if (!o->data) 2376 if (!o->data)
2371 return o_save_ptr_helper(o, n); 2377 return o_save_ptr_helper(o, n);
2372 pattern = o->data + o_get_last_ptr(o, n); 2378 pattern = o->data + o_get_last_ptr(o, n);
@@ -2384,7 +2390,7 @@ static int o_glob(o_string *o, int n)
2384 n = glob_brace(copy, o, n); 2390 n = glob_brace(copy, o, n);
2385 free(copy); 2391 free(copy);
2386 if (DEBUG_GLOB) 2392 if (DEBUG_GLOB)
2387 debug_print_list("o_glob returning", o, n); 2393 debug_print_list("perform_glob returning", o, n);
2388 return n; 2394 return n;
2389} 2395}
2390 2396
@@ -2409,13 +2415,13 @@ static int glob_needed(const char *s)
2409/* Performs globbing on last list[], 2415/* Performs globbing on last list[],
2410 * saving each result as a new list[]. 2416 * saving each result as a new list[].
2411 */ 2417 */
2412static int o_glob(o_string *o, int n) 2418static int perform_glob(o_string *o, int n)
2413{ 2419{
2414 glob_t globdata; 2420 glob_t globdata;
2415 int gr; 2421 int gr;
2416 char *pattern; 2422 char *pattern;
2417 2423
2418 debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); 2424 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
2419 if (!o->data) 2425 if (!o->data)
2420 return o_save_ptr_helper(o, n); 2426 return o_save_ptr_helper(o, n);
2421 pattern = o->data + o_get_last_ptr(o, n); 2427 pattern = o->data + o_get_last_ptr(o, n);
@@ -2461,22 +2467,22 @@ static int o_glob(o_string *o, int n)
2461 } 2467 }
2462 globfree(&globdata); 2468 globfree(&globdata);
2463 if (DEBUG_GLOB) 2469 if (DEBUG_GLOB)
2464 debug_print_list("o_glob returning", o, n); 2470 debug_print_list("perform_glob returning", o, n);
2465 return n; 2471 return n;
2466} 2472}
2467 2473
2468#endif /* !HUSH_BRACE_EXP */ 2474#endif /* !HUSH_BRACE_EXP */
2469 2475
2470/* If o->o_glob == 1, glob the string so far remembered. 2476/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
2471 * Otherwise, just finish current list[] and start new */ 2477 * Otherwise, just finish current list[] and start new */
2472static int o_save_ptr(o_string *o, int n) 2478static int o_save_ptr(o_string *o, int n)
2473{ 2479{
2474 if (o->o_glob) { /* if globbing is requested */ 2480 if (o->o_expflags & EXP_FLAG_GLOB) {
2475 /* If o->has_empty_slot, list[n] was already globbed 2481 /* If o->has_empty_slot, list[n] was already globbed
2476 * (if it was requested back then when it was filled) 2482 * (if it was requested back then when it was filled)
2477 * so don't do that again! */ 2483 * so don't do that again! */
2478 if (!o->has_empty_slot) 2484 if (!o->has_empty_slot)
2479 return o_glob(o, n); /* o_save_ptr_helper is inside */ 2485 return perform_glob(o, n); /* o_save_ptr_helper is inside */
2480 } 2486 }
2481 return o_save_ptr_helper(o, n); 2487 return o_save_ptr_helper(o, n);
2482} 2488}
@@ -2496,25 +2502,1882 @@ static char **o_finalize_list(o_string *o, int n)
2496 list[--n] = NULL; 2502 list[--n] = NULL;
2497 while (n) { 2503 while (n) {
2498 n--; 2504 n--;
2499 list[n] = o->data + (int)(ptrdiff_t)list[n] + string_start; 2505 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
2500 } 2506 }
2501 return list; 2507 return list;
2502} 2508}
2503 2509
2510static void free_pipe_list(struct pipe *pi);
2504 2511
2505/* Expansion can recurse */ 2512/* Returns pi->next - next pipe in the list */
2506#if ENABLE_HUSH_TICK 2513static struct pipe *free_pipe(struct pipe *pi)
2507static int process_command_subs(o_string *dest, const char *s); 2514{
2515 struct pipe *next;
2516 int i;
2517
2518 debug_printf_clean("free_pipe (pid %d)\n", getpid());
2519 for (i = 0; i < pi->num_cmds; i++) {
2520 struct command *command;
2521 struct redir_struct *r, *rnext;
2522
2523 command = &pi->cmds[i];
2524 debug_printf_clean(" command %d:\n", i);
2525 if (command->argv) {
2526 if (DEBUG_CLEAN) {
2527 int a;
2528 char **p;
2529 for (a = 0, p = command->argv; *p; a++, p++) {
2530 debug_printf_clean(" argv[%d] = %s\n", a, *p);
2531 }
2532 }
2533 free_strings(command->argv);
2534 //command->argv = NULL;
2535 }
2536 /* not "else if": on syntax error, we may have both! */
2537 if (command->group) {
2538 debug_printf_clean(" begin group (cmd_type:%d)\n",
2539 command->cmd_type);
2540 free_pipe_list(command->group);
2541 debug_printf_clean(" end group\n");
2542 //command->group = NULL;
2543 }
2544 /* else is crucial here.
2545 * If group != NULL, child_func is meaningless */
2546#if ENABLE_HUSH_FUNCTIONS
2547 else if (command->child_func) {
2548 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
2549 command->child_func->parent_cmd = NULL;
2550 }
2551#endif
2552#if !BB_MMU
2553 free(command->group_as_string);
2554 //command->group_as_string = NULL;
2508#endif 2555#endif
2509static char *expand_string_to_string(const char *str); 2556 for (r = command->redirects; r; r = rnext) {
2557 debug_printf_clean(" redirect %d%s",
2558 r->rd_fd, redir_table[r->rd_type].descrip);
2559 /* guard against the case >$FOO, where foo is unset or blank */
2560 if (r->rd_filename) {
2561 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
2562 free(r->rd_filename);
2563 //r->rd_filename = NULL;
2564 }
2565 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
2566 rnext = r->next;
2567 free(r);
2568 }
2569 //command->redirects = NULL;
2570 }
2571 free(pi->cmds); /* children are an array, they get freed all at once */
2572 //pi->cmds = NULL;
2573#if ENABLE_HUSH_JOB
2574 free(pi->cmdtext);
2575 //pi->cmdtext = NULL;
2576#endif
2577
2578 next = pi->next;
2579 free(pi);
2580 return next;
2581}
2582
2583static void free_pipe_list(struct pipe *pi)
2584{
2585 while (pi) {
2586#if HAS_KEYWORDS
2587 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
2588#endif
2589 debug_printf_clean("pipe followup code %d\n", pi->followup);
2590 pi = free_pipe(pi);
2591 }
2592}
2593
2594
2595/*** Parsing routines ***/
2596
2597static struct pipe *new_pipe(void)
2598{
2599 struct pipe *pi;
2600 pi = xzalloc(sizeof(struct pipe));
2601 /*pi->followup = 0; - deliberately invalid value */
2602 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
2603 return pi;
2604}
2605
2606/* Command (member of a pipe) is complete, or we start a new pipe
2607 * if ctx->command is NULL.
2608 * No errors possible here.
2609 */
2610static int done_command(struct parse_context *ctx)
2611{
2612 /* The command is really already in the pipe structure, so
2613 * advance the pipe counter and make a new, null command. */
2614 struct pipe *pi = ctx->pipe;
2615 struct command *command = ctx->command;
2616
2617 if (command) {
2618 if (IS_NULL_CMD(command)) {
2619 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
2620 goto clear_and_ret;
2621 }
2622 pi->num_cmds++;
2623 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
2624 //debug_print_tree(ctx->list_head, 20);
2625 } else {
2626 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
2627 }
2628
2629 /* Only real trickiness here is that the uncommitted
2630 * command structure is not counted in pi->num_cmds. */
2631 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
2632 ctx->command = command = &pi->cmds[pi->num_cmds];
2633 clear_and_ret:
2634 memset(command, 0, sizeof(*command));
2635 return pi->num_cmds; /* used only for 0/nonzero check */
2636}
2637
2638static void done_pipe(struct parse_context *ctx, pipe_style type)
2639{
2640 int not_null;
2641
2642 debug_printf_parse("done_pipe entered, followup %d\n", type);
2643 /* Close previous command */
2644 not_null = done_command(ctx);
2645 ctx->pipe->followup = type;
2646#if HAS_KEYWORDS
2647 ctx->pipe->pi_inverted = ctx->ctx_inverted;
2648 ctx->ctx_inverted = 0;
2649 ctx->pipe->res_word = ctx->ctx_res_w;
2650#endif
2651
2652 /* Without this check, even just <enter> on command line generates
2653 * tree of three NOPs (!). Which is harmless but annoying.
2654 * IOW: it is safe to do it unconditionally. */
2655 if (not_null
2656#if ENABLE_HUSH_IF
2657 || ctx->ctx_res_w == RES_FI
2658#endif
2659#if ENABLE_HUSH_LOOPS
2660 || ctx->ctx_res_w == RES_DONE
2661 || ctx->ctx_res_w == RES_FOR
2662 || ctx->ctx_res_w == RES_IN
2663#endif
2664#if ENABLE_HUSH_CASE
2665 || ctx->ctx_res_w == RES_ESAC
2666#endif
2667 ) {
2668 struct pipe *new_p;
2669 debug_printf_parse("done_pipe: adding new pipe: "
2670 "not_null:%d ctx->ctx_res_w:%d\n",
2671 not_null, ctx->ctx_res_w);
2672 new_p = new_pipe();
2673 ctx->pipe->next = new_p;
2674 ctx->pipe = new_p;
2675 /* RES_THEN, RES_DO etc are "sticky" -
2676 * they remain set for pipes inside if/while.
2677 * This is used to control execution.
2678 * RES_FOR and RES_IN are NOT sticky (needed to support
2679 * cases where variable or value happens to match a keyword):
2680 */
2681#if ENABLE_HUSH_LOOPS
2682 if (ctx->ctx_res_w == RES_FOR
2683 || ctx->ctx_res_w == RES_IN)
2684 ctx->ctx_res_w = RES_NONE;
2685#endif
2686#if ENABLE_HUSH_CASE
2687 if (ctx->ctx_res_w == RES_MATCH)
2688 ctx->ctx_res_w = RES_CASE_BODY;
2689 if (ctx->ctx_res_w == RES_CASE)
2690 ctx->ctx_res_w = RES_CASE_IN;
2691#endif
2692 ctx->command = NULL; /* trick done_command below */
2693 /* Create the memory for command, roughly:
2694 * ctx->pipe->cmds = new struct command;
2695 * ctx->command = &ctx->pipe->cmds[0];
2696 */
2697 done_command(ctx);
2698 //debug_print_tree(ctx->list_head, 10);
2699 }
2700 debug_printf_parse("done_pipe return\n");
2701}
2702
2703static void initialize_context(struct parse_context *ctx)
2704{
2705 memset(ctx, 0, sizeof(*ctx));
2706 ctx->pipe = ctx->list_head = new_pipe();
2707 /* Create the memory for command, roughly:
2708 * ctx->pipe->cmds = new struct command;
2709 * ctx->command = &ctx->pipe->cmds[0];
2710 */
2711 done_command(ctx);
2712}
2713
2714/* If a reserved word is found and processed, parse context is modified
2715 * and 1 is returned.
2716 */
2717#if HAS_KEYWORDS
2718struct reserved_combo {
2719 char literal[6];
2720 unsigned char res;
2721 unsigned char assignment_flag;
2722 int flag;
2723};
2724enum {
2725 FLAG_END = (1 << RES_NONE ),
2726# if ENABLE_HUSH_IF
2727 FLAG_IF = (1 << RES_IF ),
2728 FLAG_THEN = (1 << RES_THEN ),
2729 FLAG_ELIF = (1 << RES_ELIF ),
2730 FLAG_ELSE = (1 << RES_ELSE ),
2731 FLAG_FI = (1 << RES_FI ),
2732# endif
2733# if ENABLE_HUSH_LOOPS
2734 FLAG_FOR = (1 << RES_FOR ),
2735 FLAG_WHILE = (1 << RES_WHILE),
2736 FLAG_UNTIL = (1 << RES_UNTIL),
2737 FLAG_DO = (1 << RES_DO ),
2738 FLAG_DONE = (1 << RES_DONE ),
2739 FLAG_IN = (1 << RES_IN ),
2740# endif
2741# if ENABLE_HUSH_CASE
2742 FLAG_MATCH = (1 << RES_MATCH),
2743 FLAG_ESAC = (1 << RES_ESAC ),
2744# endif
2745 FLAG_START = (1 << RES_XXXX ),
2746};
2747
2748static const struct reserved_combo* match_reserved_word(o_string *word)
2749{
2750 /* Mostly a list of accepted follow-up reserved words.
2751 * FLAG_END means we are done with the sequence, and are ready
2752 * to turn the compound list into a command.
2753 * FLAG_START means the word must start a new compound list.
2754 */
2755 static const struct reserved_combo reserved_list[] = {
2756# if ENABLE_HUSH_IF
2757 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
2758 { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
2759 { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
2760 { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN },
2761 { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI },
2762 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
2763# endif
2764# if ENABLE_HUSH_LOOPS
2765 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
2766 { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
2767 { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
2768 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
2769 { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE },
2770 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
2771# endif
2772# if ENABLE_HUSH_CASE
2773 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
2774 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
2775# endif
2776 };
2777 const struct reserved_combo *r;
2778
2779 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
2780 if (strcmp(word->data, r->literal) == 0)
2781 return r;
2782 }
2783 return NULL;
2784}
2785/* Return 0: not a keyword, 1: keyword
2786 */
2787static int reserved_word(o_string *word, struct parse_context *ctx)
2788{
2789# if ENABLE_HUSH_CASE
2790 static const struct reserved_combo reserved_match = {
2791 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
2792 };
2793# endif
2794 const struct reserved_combo *r;
2795
2796 if (word->has_quoted_part)
2797 return 0;
2798 r = match_reserved_word(word);
2799 if (!r)
2800 return 0;
2801
2802 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
2803# if ENABLE_HUSH_CASE
2804 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
2805 /* "case word IN ..." - IN part starts first MATCH part */
2806 r = &reserved_match;
2807 } else
2808# endif
2809 if (r->flag == 0) { /* '!' */
2810 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
2811 syntax_error("! ! command");
2812 ctx->ctx_res_w = RES_SNTX;
2813 }
2814 ctx->ctx_inverted = 1;
2815 return 1;
2816 }
2817 if (r->flag & FLAG_START) {
2818 struct parse_context *old;
2819
2820 old = xmalloc(sizeof(*old));
2821 debug_printf_parse("push stack %p\n", old);
2822 *old = *ctx; /* physical copy */
2823 initialize_context(ctx);
2824 ctx->stack = old;
2825 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
2826 syntax_error_at(word->data);
2827 ctx->ctx_res_w = RES_SNTX;
2828 return 1;
2829 } else {
2830 /* "{...} fi" is ok. "{...} if" is not
2831 * Example:
2832 * if { echo foo; } then { echo bar; } fi */
2833 if (ctx->command->group)
2834 done_pipe(ctx, PIPE_SEQ);
2835 }
2836
2837 ctx->ctx_res_w = r->res;
2838 ctx->old_flag = r->flag;
2839 word->o_assignment = r->assignment_flag;
2840
2841 if (ctx->old_flag & FLAG_END) {
2842 struct parse_context *old;
2843
2844 done_pipe(ctx, PIPE_SEQ);
2845 debug_printf_parse("pop stack %p\n", ctx->stack);
2846 old = ctx->stack;
2847 old->command->group = ctx->list_head;
2848 old->command->cmd_type = CMD_NORMAL;
2849# if !BB_MMU
2850 o_addstr(&old->as_string, ctx->as_string.data);
2851 o_free_unsafe(&ctx->as_string);
2852 old->command->group_as_string = xstrdup(old->as_string.data);
2853 debug_printf_parse("pop, remembering as:'%s'\n",
2854 old->command->group_as_string);
2855# endif
2856 *ctx = *old; /* physical copy */
2857 free(old);
2858 }
2859 return 1;
2860}
2861#endif /* HAS_KEYWORDS */
2862
2863/* Word is complete, look at it and update parsing context.
2864 * Normal return is 0. Syntax errors return 1.
2865 * Note: on return, word is reset, but not o_free'd!
2866 */
2867static int done_word(o_string *word, struct parse_context *ctx)
2868{
2869 struct command *command = ctx->command;
2870
2871 debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
2872 if (word->length == 0 && !word->has_quoted_part) {
2873 debug_printf_parse("done_word return 0: true null, ignored\n");
2874 return 0;
2875 }
2876
2877 if (ctx->pending_redirect) {
2878 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
2879 * only if run as "bash", not "sh" */
2880 /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
2881 * "2.7 Redirection
2882 * ...the word that follows the redirection operator
2883 * shall be subjected to tilde expansion, parameter expansion,
2884 * command substitution, arithmetic expansion, and quote
2885 * removal. Pathname expansion shall not be performed
2886 * on the word by a non-interactive shell; an interactive
2887 * shell may perform it, but shall do so only when
2888 * the expansion would result in one word."
2889 */
2890 ctx->pending_redirect->rd_filename = xstrdup(word->data);
2891 /* Cater for >\file case:
2892 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
2893 * Same with heredocs:
2894 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
2895 */
2896 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
2897 unbackslash(ctx->pending_redirect->rd_filename);
2898 /* Is it <<"HEREDOC"? */
2899 if (word->has_quoted_part) {
2900 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
2901 }
2902 }
2903 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
2904 ctx->pending_redirect = NULL;
2905 } else {
2906 /* If this word wasn't an assignment, next ones definitely
2907 * can't be assignments. Even if they look like ones. */
2908 if (word->o_assignment != DEFINITELY_ASSIGNMENT
2909 && word->o_assignment != WORD_IS_KEYWORD
2910 ) {
2911 word->o_assignment = NOT_ASSIGNMENT;
2912 } else {
2913 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
2914 command->assignment_cnt++;
2915 word->o_assignment = MAYBE_ASSIGNMENT;
2916 }
2917
2918#if HAS_KEYWORDS
2919# if ENABLE_HUSH_CASE
2920 if (ctx->ctx_dsemicolon
2921 && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
2922 ) {
2923 /* already done when ctx_dsemicolon was set to 1: */
2924 /* ctx->ctx_res_w = RES_MATCH; */
2925 ctx->ctx_dsemicolon = 0;
2926 } else
2927# endif
2928 if (!command->argv /* if it's the first word... */
2929# if ENABLE_HUSH_LOOPS
2930 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
2931 && ctx->ctx_res_w != RES_IN
2932# endif
2933# if ENABLE_HUSH_CASE
2934 && ctx->ctx_res_w != RES_CASE
2935# endif
2936 ) {
2937 debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
2938 if (reserved_word(word, ctx)) {
2939 o_reset_to_empty_unquoted(word);
2940 debug_printf_parse("done_word return %d\n",
2941 (ctx->ctx_res_w == RES_SNTX));
2942 return (ctx->ctx_res_w == RES_SNTX);
2943 }
2944# if ENABLE_HUSH_BASH_COMPAT
2945 if (strcmp(word->data, "[[") == 0) {
2946 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
2947 }
2948 /* fall through */
2949# endif
2950 }
2951#endif
2952 if (command->group) {
2953 /* "{ echo foo; } echo bar" - bad */
2954 syntax_error_at(word->data);
2955 debug_printf_parse("done_word return 1: syntax error, "
2956 "groups and arglists don't mix\n");
2957 return 1;
2958 }
2959 if (word->has_quoted_part
2960 /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
2961 && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
2962 /* (otherwise it's known to be not empty and is already safe) */
2963 ) {
2964 /* exclude "$@" - it can expand to no word despite "" */
2965 char *p = word->data;
2966 while (p[0] == SPECIAL_VAR_SYMBOL
2967 && (p[1] & 0x7f) == '@'
2968 && p[2] == SPECIAL_VAR_SYMBOL
2969 ) {
2970 p += 3;
2971 }
2972 if (p == word->data || p[0] != '\0') {
2973 /* saw no "$@", or not only "$@" but some
2974 * real text is there too */
2975 /* insert "empty variable" reference, this makes
2976 * e.g. "", $empty"" etc to not disappear */
2977 o_addchr(word, SPECIAL_VAR_SYMBOL);
2978 o_addchr(word, SPECIAL_VAR_SYMBOL);
2979 }
2980 }
2981 command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
2982 debug_print_strings("word appended to argv", command->argv);
2983 }
2984
2985#if ENABLE_HUSH_LOOPS
2986 if (ctx->ctx_res_w == RES_FOR) {
2987 if (word->has_quoted_part
2988 || !is_well_formed_var_name(command->argv[0], '\0')
2989 ) {
2990 /* bash says just "not a valid identifier" */
2991 syntax_error("not a valid identifier in for");
2992 return 1;
2993 }
2994 /* Force FOR to have just one word (variable name) */
2995 /* NB: basically, this makes hush see "for v in ..."
2996 * syntax as if it is "for v; in ...". FOR and IN become
2997 * two pipe structs in parse tree. */
2998 done_pipe(ctx, PIPE_SEQ);
2999 }
3000#endif
3001#if ENABLE_HUSH_CASE
3002 /* Force CASE to have just one word */
3003 if (ctx->ctx_res_w == RES_CASE) {
3004 done_pipe(ctx, PIPE_SEQ);
3005 }
3006#endif
3007
3008 o_reset_to_empty_unquoted(word);
3009
3010 debug_printf_parse("done_word return 0\n");
3011 return 0;
3012}
3013
3014
3015/* Peek ahead in the input to find out if we have a "&n" construct,
3016 * as in "2>&1", that represents duplicating a file descriptor.
3017 * Return:
3018 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
3019 * REDIRFD_SYNTAX_ERR if syntax error,
3020 * REDIRFD_TO_FILE if no & was seen,
3021 * or the number found.
3022 */
3023#if BB_MMU
3024#define parse_redir_right_fd(as_string, input) \
3025 parse_redir_right_fd(input)
3026#endif
3027static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
3028{
3029 int ch, d, ok;
3030
3031 ch = i_peek(input);
3032 if (ch != '&')
3033 return REDIRFD_TO_FILE;
3034
3035 ch = i_getch(input); /* get the & */
3036 nommu_addchr(as_string, ch);
3037 ch = i_peek(input);
3038 if (ch == '-') {
3039 ch = i_getch(input);
3040 nommu_addchr(as_string, ch);
3041 return REDIRFD_CLOSE;
3042 }
3043 d = 0;
3044 ok = 0;
3045 while (ch != EOF && isdigit(ch)) {
3046 d = d*10 + (ch-'0');
3047 ok = 1;
3048 ch = i_getch(input);
3049 nommu_addchr(as_string, ch);
3050 ch = i_peek(input);
3051 }
3052 if (ok) return d;
3053
3054//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
3055
3056 bb_error_msg("ambiguous redirect");
3057 return REDIRFD_SYNTAX_ERR;
3058}
3059
3060/* Return code is 0 normal, 1 if a syntax error is detected
3061 */
3062static int parse_redirect(struct parse_context *ctx,
3063 int fd,
3064 redir_type style,
3065 struct in_str *input)
3066{
3067 struct command *command = ctx->command;
3068 struct redir_struct *redir;
3069 struct redir_struct **redirp;
3070 int dup_num;
3071
3072 dup_num = REDIRFD_TO_FILE;
3073 if (style != REDIRECT_HEREDOC) {
3074 /* Check for a '>&1' type redirect */
3075 dup_num = parse_redir_right_fd(&ctx->as_string, input);
3076 if (dup_num == REDIRFD_SYNTAX_ERR)
3077 return 1;
3078 } else {
3079 int ch = i_peek(input);
3080 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
3081 if (dup_num) { /* <<-... */
3082 ch = i_getch(input);
3083 nommu_addchr(&ctx->as_string, ch);
3084 ch = i_peek(input);
3085 }
3086 }
3087
3088 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
3089 int ch = i_peek(input);
3090 if (ch == '|') {
3091 /* >|FILE redirect ("clobbering" >).
3092 * Since we do not support "set -o noclobber" yet,
3093 * >| and > are the same for now. Just eat |.
3094 */
3095 ch = i_getch(input);
3096 nommu_addchr(&ctx->as_string, ch);
3097 }
3098 }
3099
3100 /* Create a new redir_struct and append it to the linked list */
3101 redirp = &command->redirects;
3102 while ((redir = *redirp) != NULL) {
3103 redirp = &(redir->next);
3104 }
3105 *redirp = redir = xzalloc(sizeof(*redir));
3106 /* redir->next = NULL; */
3107 /* redir->rd_filename = NULL; */
3108 redir->rd_type = style;
3109 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
3110
3111 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
3112 redir_table[style].descrip);
3113
3114 redir->rd_dup = dup_num;
3115 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
3116 /* Erik had a check here that the file descriptor in question
3117 * is legit; I postpone that to "run time"
3118 * A "-" representation of "close me" shows up as a -3 here */
3119 debug_printf_parse("duplicating redirect '%d>&%d'\n",
3120 redir->rd_fd, redir->rd_dup);
3121 } else {
3122 /* Set ctx->pending_redirect, so we know what to do at the
3123 * end of the next parsed word. */
3124 ctx->pending_redirect = redir;
3125 }
3126 return 0;
3127}
3128
3129/* If a redirect is immediately preceded by a number, that number is
3130 * supposed to tell which file descriptor to redirect. This routine
3131 * looks for such preceding numbers. In an ideal world this routine
3132 * needs to handle all the following classes of redirects...
3133 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
3134 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
3135 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
3136 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
3137 *
3138 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
3139 * "2.7 Redirection
3140 * ... If n is quoted, the number shall not be recognized as part of
3141 * the redirection expression. For example:
3142 * echo \2>a
3143 * writes the character 2 into file a"
3144 * We are getting it right by setting ->has_quoted_part on any \<char>
3145 *
3146 * A -1 return means no valid number was found,
3147 * the caller should use the appropriate default for this redirection.
3148 */
3149static int redirect_opt_num(o_string *o)
3150{
3151 int num;
3152
3153 if (o->data == NULL)
3154 return -1;
3155 num = bb_strtou(o->data, NULL, 10);
3156 if (errno || num < 0)
3157 return -1;
3158 o_reset_to_empty_unquoted(o);
3159 return num;
3160}
3161
3162#if BB_MMU
3163#define fetch_till_str(as_string, input, word, skip_tabs) \
3164 fetch_till_str(input, word, skip_tabs)
3165#endif
3166static char *fetch_till_str(o_string *as_string,
3167 struct in_str *input,
3168 const char *word,
3169 int heredoc_flags)
3170{
3171 o_string heredoc = NULL_O_STRING;
3172 unsigned past_EOL;
3173 int prev = 0; /* not \ */
3174 int ch;
3175
3176 goto jump_in;
3177 while (1) {
3178 ch = i_getch(input);
3179 if (ch != EOF)
3180 nommu_addchr(as_string, ch);
3181 if ((ch == '\n' || ch == EOF)
3182 && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\')
3183 ) {
3184 if (strcmp(heredoc.data + past_EOL, word) == 0) {
3185 heredoc.data[past_EOL] = '\0';
3186 debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
3187 return heredoc.data;
3188 }
3189 while (ch == '\n') {
3190 o_addchr(&heredoc, ch);
3191 prev = ch;
3192 jump_in:
3193 past_EOL = heredoc.length;
3194 do {
3195 ch = i_getch(input);
3196 if (ch != EOF)
3197 nommu_addchr(as_string, ch);
3198 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
3199 }
3200 }
3201 if (ch == EOF) {
3202 o_free_unsafe(&heredoc);
3203 return NULL;
3204 }
3205 o_addchr(&heredoc, ch);
3206 nommu_addchr(as_string, ch);
3207 if (prev == '\\' && ch == '\\')
3208 /* Correctly handle foo\\<eol> (not a line cont.) */
3209 prev = 0; /* not \ */
3210 else
3211 prev = ch;
3212 }
3213}
3214
3215/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
3216 * and load them all. There should be exactly heredoc_cnt of them.
3217 */
3218static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
3219{
3220 struct pipe *pi = ctx->list_head;
3221
3222 while (pi && heredoc_cnt) {
3223 int i;
3224 struct command *cmd = pi->cmds;
3225
3226 debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
3227 pi->num_cmds,
3228 cmd->argv ? cmd->argv[0] : "NONE");
3229 for (i = 0; i < pi->num_cmds; i++) {
3230 struct redir_struct *redir = cmd->redirects;
3231
3232 debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
3233 i, cmd->argv ? cmd->argv[0] : "NONE");
3234 while (redir) {
3235 if (redir->rd_type == REDIRECT_HEREDOC) {
3236 char *p;
3237
3238 redir->rd_type = REDIRECT_HEREDOC2;
3239 /* redir->rd_dup is (ab)used to indicate <<- */
3240 p = fetch_till_str(&ctx->as_string, input,
3241 redir->rd_filename, redir->rd_dup);
3242 if (!p) {
3243 syntax_error("unexpected EOF in here document");
3244 return 1;
3245 }
3246 free(redir->rd_filename);
3247 redir->rd_filename = p;
3248 heredoc_cnt--;
3249 }
3250 redir = redir->next;
3251 }
3252 cmd++;
3253 }
3254 pi = pi->next;
3255 }
3256#if 0
3257 /* Should be 0. If it isn't, it's a parse error */
3258 if (heredoc_cnt)
3259 bb_error_msg_and_die("heredoc BUG 2");
3260#endif
3261 return 0;
3262}
3263
3264
3265static int run_list(struct pipe *pi);
3266#if BB_MMU
3267#define parse_stream(pstring, input, end_trigger) \
3268 parse_stream(input, end_trigger)
3269#endif
3270static struct pipe *parse_stream(char **pstring,
3271 struct in_str *input,
3272 int end_trigger);
3273
3274
3275#if !ENABLE_HUSH_FUNCTIONS
3276#define parse_group(dest, ctx, input, ch) \
3277 parse_group(ctx, input, ch)
3278#endif
3279static int parse_group(o_string *dest, struct parse_context *ctx,
3280 struct in_str *input, int ch)
3281{
3282 /* dest contains characters seen prior to ( or {.
3283 * Typically it's empty, but for function defs,
3284 * it contains function name (without '()'). */
3285 struct pipe *pipe_list;
3286 int endch;
3287 struct command *command = ctx->command;
3288
3289 debug_printf_parse("parse_group entered\n");
3290#if ENABLE_HUSH_FUNCTIONS
3291 if (ch == '(' && !dest->has_quoted_part) {
3292 if (dest->length)
3293 if (done_word(dest, ctx))
3294 return 1;
3295 if (!command->argv)
3296 goto skip; /* (... */
3297 if (command->argv[1]) { /* word word ... (... */
3298 syntax_error_unexpected_ch('(');
3299 return 1;
3300 }
3301 /* it is "word(..." or "word (..." */
3302 do
3303 ch = i_getch(input);
3304 while (ch == ' ' || ch == '\t');
3305 if (ch != ')') {
3306 syntax_error_unexpected_ch(ch);
3307 return 1;
3308 }
3309 nommu_addchr(&ctx->as_string, ch);
3310 do
3311 ch = i_getch(input);
3312 while (ch == ' ' || ch == '\t' || ch == '\n');
3313 if (ch != '{') {
3314 syntax_error_unexpected_ch(ch);
3315 return 1;
3316 }
3317 nommu_addchr(&ctx->as_string, ch);
3318 command->cmd_type = CMD_FUNCDEF;
3319 goto skip;
3320 }
3321#endif
3322
3323#if 0 /* Prevented by caller */
3324 if (command->argv /* word [word]{... */
3325 || dest->length /* word{... */
3326 || dest->has_quoted_part /* ""{... */
3327 ) {
3328 syntax_error(NULL);
3329 debug_printf_parse("parse_group return 1: "
3330 "syntax error, groups and arglists don't mix\n");
3331 return 1;
3332 }
3333#endif
3334
3335#if ENABLE_HUSH_FUNCTIONS
3336 skip:
3337#endif
3338 endch = '}';
3339 if (ch == '(') {
3340 endch = ')';
3341 command->cmd_type = CMD_SUBSHELL;
3342 } else {
3343 /* bash does not allow "{echo...", requires whitespace */
3344 ch = i_getch(input);
3345 if (ch != ' ' && ch != '\t' && ch != '\n') {
3346 syntax_error_unexpected_ch(ch);
3347 return 1;
3348 }
3349 nommu_addchr(&ctx->as_string, ch);
3350 }
3351
3352 {
2510#if BB_MMU 3353#if BB_MMU
2511#define parse_stream_dquoted(as_string, dest, input, dquote_end) \ 3354# define as_string NULL
2512 parse_stream_dquoted(dest, input, dquote_end) 3355#else
3356 char *as_string = NULL;
3357#endif
3358 pipe_list = parse_stream(&as_string, input, endch);
3359#if !BB_MMU
3360 if (as_string)
3361 o_addstr(&ctx->as_string, as_string);
2513#endif 3362#endif
2514static int parse_stream_dquoted(o_string *as_string, 3363 /* empty ()/{} or parse error? */
3364 if (!pipe_list || pipe_list == ERR_PTR) {
3365 /* parse_stream already emitted error msg */
3366 if (!BB_MMU)
3367 free(as_string);
3368 debug_printf_parse("parse_group return 1: "
3369 "parse_stream returned %p\n", pipe_list);
3370 return 1;
3371 }
3372 command->group = pipe_list;
3373#if !BB_MMU
3374 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
3375 command->group_as_string = as_string;
3376 debug_printf_parse("end of group, remembering as:'%s'\n",
3377 command->group_as_string);
3378#endif
3379#undef as_string
3380 }
3381 debug_printf_parse("parse_group return 0\n");
3382 return 0;
3383 /* command remains "open", available for possible redirects */
3384}
3385
3386#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
3387/* Subroutines for copying $(...) and `...` things */
3388static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
3389/* '...' */
3390static void add_till_single_quote(o_string *dest, struct in_str *input)
3391{
3392 while (1) {
3393 int ch = i_getch(input);
3394 if (ch == EOF) {
3395 syntax_error_unterm_ch('\'');
3396 /*xfunc_die(); - redundant */
3397 }
3398 if (ch == '\'')
3399 return;
3400 o_addchr(dest, ch);
3401 }
3402}
3403/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
3404static void add_till_double_quote(o_string *dest, struct in_str *input)
3405{
3406 while (1) {
3407 int ch = i_getch(input);
3408 if (ch == EOF) {
3409 syntax_error_unterm_ch('"');
3410 /*xfunc_die(); - redundant */
3411 }
3412 if (ch == '"')
3413 return;
3414 if (ch == '\\') { /* \x. Copy both chars. */
3415 o_addchr(dest, ch);
3416 ch = i_getch(input);
3417 }
3418 o_addchr(dest, ch);
3419 if (ch == '`') {
3420 add_till_backquote(dest, input, /*in_dquote:*/ 1);
3421 o_addchr(dest, ch);
3422 continue;
3423 }
3424 //if (ch == '$') ...
3425 }
3426}
3427/* Process `cmd` - copy contents until "`" is seen. Complicated by
3428 * \` quoting.
3429 * "Within the backquoted style of command substitution, backslash
3430 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
3431 * The search for the matching backquote shall be satisfied by the first
3432 * backquote found without a preceding backslash; during this search,
3433 * if a non-escaped backquote is encountered within a shell comment,
3434 * a here-document, an embedded command substitution of the $(command)
3435 * form, or a quoted string, undefined results occur. A single-quoted
3436 * or double-quoted string that begins, but does not end, within the
3437 * "`...`" sequence produces undefined results."
3438 * Example Output
3439 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
3440 */
3441static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
3442{
3443 while (1) {
3444 int ch = i_getch(input);
3445 if (ch == '`')
3446 return;
3447 if (ch == '\\') {
3448 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
3449 ch = i_getch(input);
3450 if (ch != '`'
3451 && ch != '$'
3452 && ch != '\\'
3453 && (!in_dquote || ch != '"')
3454 ) {
3455 o_addchr(dest, '\\');
3456 }
3457 }
3458 if (ch == EOF) {
3459 syntax_error_unterm_ch('`');
3460 /*xfunc_die(); - redundant */
3461 }
3462 o_addchr(dest, ch);
3463 }
3464}
3465/* Process $(cmd) - copy contents until ")" is seen. Complicated by
3466 * quoting and nested ()s.
3467 * "With the $(command) style of command substitution, all characters
3468 * following the open parenthesis to the matching closing parenthesis
3469 * constitute the command. Any valid shell script can be used for command,
3470 * except a script consisting solely of redirections which produces
3471 * unspecified results."
3472 * Example Output
3473 * echo $(echo '(TEST)' BEST) (TEST) BEST
3474 * echo $(echo 'TEST)' BEST) TEST) BEST
3475 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
3476 *
3477 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
3478 * can contain arbitrary constructs, just like $(cmd).
3479 * In bash compat mode, it needs to also be able to stop on ':' or '/'
3480 * for ${var:N[:M]} and ${var/P[/R]} parsing.
3481 */
3482#define DOUBLE_CLOSE_CHAR_FLAG 0x80
3483static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
3484{
3485 int ch;
3486 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
3487# if ENABLE_HUSH_BASH_COMPAT
3488 char end_char2 = end_ch >> 8;
3489# endif
3490 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
3491
3492 while (1) {
3493 ch = i_getch(input);
3494 if (ch == EOF) {
3495 syntax_error_unterm_ch(end_ch);
3496 /*xfunc_die(); - redundant */
3497 }
3498 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
3499 if (!dbl)
3500 break;
3501 /* we look for closing )) of $((EXPR)) */
3502 if (i_peek(input) == end_ch) {
3503 i_getch(input); /* eat second ')' */
3504 break;
3505 }
3506 }
3507 o_addchr(dest, ch);
3508 if (ch == '(' || ch == '{') {
3509 ch = (ch == '(' ? ')' : '}');
3510 add_till_closing_bracket(dest, input, ch);
3511 o_addchr(dest, ch);
3512 continue;
3513 }
3514 if (ch == '\'') {
3515 add_till_single_quote(dest, input);
3516 o_addchr(dest, ch);
3517 continue;
3518 }
3519 if (ch == '"') {
3520 add_till_double_quote(dest, input);
3521 o_addchr(dest, ch);
3522 continue;
3523 }
3524 if (ch == '`') {
3525 add_till_backquote(dest, input, /*in_dquote:*/ 0);
3526 o_addchr(dest, ch);
3527 continue;
3528 }
3529 if (ch == '\\') {
3530 /* \x. Copy verbatim. Important for \(, \) */
3531 ch = i_getch(input);
3532 if (ch == EOF) {
3533 syntax_error_unterm_ch(')');
3534 /*xfunc_die(); - redundant */
3535 }
3536 o_addchr(dest, ch);
3537 continue;
3538 }
3539 }
3540 return ch;
3541}
3542#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
3543
3544/* Return code: 0 for OK, 1 for syntax error */
3545#if BB_MMU
3546#define parse_dollar(as_string, dest, input, quote_mask) \
3547 parse_dollar(dest, input, quote_mask)
3548#define as_string NULL
3549#endif
3550static int parse_dollar(o_string *as_string,
3551 o_string *dest,
3552 struct in_str *input, unsigned char quote_mask)
3553{
3554 int ch = i_peek(input); /* first character after the $ */
3555
3556 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
3557 if (isalpha(ch)) {
3558 ch = i_getch(input);
3559 nommu_addchr(as_string, ch);
3560 make_var:
3561 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3562 while (1) {
3563 debug_printf_parse(": '%c'\n", ch);
3564 o_addchr(dest, ch | quote_mask);
3565 quote_mask = 0;
3566 ch = i_peek(input);
3567 if (!isalnum(ch) && ch != '_')
3568 break;
3569 ch = i_getch(input);
3570 nommu_addchr(as_string, ch);
3571 }
3572 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3573 } else if (isdigit(ch)) {
3574 make_one_char_var:
3575 ch = i_getch(input);
3576 nommu_addchr(as_string, ch);
3577 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3578 debug_printf_parse(": '%c'\n", ch);
3579 o_addchr(dest, ch | quote_mask);
3580 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3581 } else switch (ch) {
3582 case '$': /* pid */
3583 case '!': /* last bg pid */
3584 case '?': /* last exit code */
3585 case '#': /* number of args */
3586 case '*': /* args */
3587 case '@': /* args */
3588 goto make_one_char_var;
3589 case '{': {
3590 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3591
3592 ch = i_getch(input); /* eat '{' */
3593 nommu_addchr(as_string, ch);
3594
3595 ch = i_getch(input); /* first char after '{' */
3596 /* It should be ${?}, or ${#var},
3597 * or even ${?+subst} - operator acting on a special variable,
3598 * or the beginning of variable name.
3599 */
3600 if (ch == EOF
3601 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
3602 ) {
3603 bad_dollar_syntax:
3604 syntax_error_unterm_str("${name}");
3605 debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
3606 return 1;
3607 }
3608 nommu_addchr(as_string, ch);
3609 ch |= quote_mask;
3610
3611 /* It's possible to just call add_till_closing_bracket() at this point.
3612 * However, this regresses some of our testsuite cases
3613 * which check invalid constructs like ${%}.
3614 * Oh well... let's check that the var name part is fine... */
3615
3616 while (1) {
3617 unsigned pos;
3618
3619 o_addchr(dest, ch);
3620 debug_printf_parse(": '%c'\n", ch);
3621
3622 ch = i_getch(input);
3623 nommu_addchr(as_string, ch);
3624 if (ch == '}')
3625 break;
3626
3627 if (!isalnum(ch) && ch != '_') {
3628 unsigned end_ch;
3629 unsigned char last_ch;
3630 /* handle parameter expansions
3631 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
3632 */
3633 if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */
3634 goto bad_dollar_syntax;
3635
3636 /* Eat everything until closing '}' (or ':') */
3637 end_ch = '}';
3638 if (ENABLE_HUSH_BASH_COMPAT
3639 && ch == ':'
3640 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
3641 ) {
3642 /* It's ${var:N[:M]} thing */
3643 end_ch = '}' * 0x100 + ':';
3644 }
3645 if (ENABLE_HUSH_BASH_COMPAT
3646 && ch == '/'
3647 ) {
3648 /* It's ${var/[/]pattern[/repl]} thing */
3649 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
3650 i_getch(input);
3651 nommu_addchr(as_string, '/');
3652 ch = '\\';
3653 }
3654 end_ch = '}' * 0x100 + '/';
3655 }
3656 o_addchr(dest, ch);
3657 again:
3658 if (!BB_MMU)
3659 pos = dest->length;
3660#if ENABLE_HUSH_DOLLAR_OPS
3661 last_ch = add_till_closing_bracket(dest, input, end_ch);
3662#else
3663#error Simple code to only allow ${var} is not implemented
3664#endif
3665 if (as_string) {
3666 o_addstr(as_string, dest->data + pos);
3667 o_addchr(as_string, last_ch);
3668 }
3669
3670 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
3671 /* close the first block: */
3672 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3673 /* while parsing N from ${var:N[:M]}
3674 * or pattern from ${var/[/]pattern[/repl]} */
3675 if ((end_ch & 0xff) == last_ch) {
3676 /* got ':' or '/'- parse the rest */
3677 end_ch = '}';
3678 goto again;
3679 }
3680 /* got '}' */
3681 if (end_ch == '}' * 0x100 + ':') {
3682 /* it's ${var:N} - emulate :999999999 */
3683 o_addstr(dest, "999999999");
3684 } /* else: it's ${var/[/]pattern} */
3685 }
3686 break;
3687 }
3688 }
3689 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3690 break;
3691 }
3692#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
3693 case '(': {
3694 unsigned pos;
3695
3696 ch = i_getch(input);
3697 nommu_addchr(as_string, ch);
3698# if ENABLE_SH_MATH_SUPPORT
3699 if (i_peek(input) == '(') {
3700 ch = i_getch(input);
3701 nommu_addchr(as_string, ch);
3702 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3703 o_addchr(dest, /*quote_mask |*/ '+');
3704 if (!BB_MMU)
3705 pos = dest->length;
3706 add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
3707 if (as_string) {
3708 o_addstr(as_string, dest->data + pos);
3709 o_addchr(as_string, ')');
3710 o_addchr(as_string, ')');
3711 }
3712 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3713 break;
3714 }
3715# endif
3716# if ENABLE_HUSH_TICK
3717 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3718 o_addchr(dest, quote_mask | '`');
3719 if (!BB_MMU)
3720 pos = dest->length;
3721 add_till_closing_bracket(dest, input, ')');
3722 if (as_string) {
3723 o_addstr(as_string, dest->data + pos);
3724 o_addchr(as_string, ')');
3725 }
3726 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3727# endif
3728 break;
3729 }
3730#endif
3731 case '_':
3732 ch = i_getch(input);
3733 nommu_addchr(as_string, ch);
3734 ch = i_peek(input);
3735 if (isalnum(ch)) { /* it's $_name or $_123 */
3736 ch = '_';
3737 goto make_var;
3738 }
3739 /* else: it's $_ */
3740 /* TODO: $_ and $-: */
3741 /* $_ Shell or shell script name; or last argument of last command
3742 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
3743 * but in command's env, set to full pathname used to invoke it */
3744 /* $- Option flags set by set builtin or shell options (-i etc) */
3745 default:
3746 o_addQchr(dest, '$');
3747 }
3748 debug_printf_parse("parse_dollar return 0\n");
3749 return 0;
3750#undef as_string
3751}
3752
3753#if BB_MMU
3754# if ENABLE_HUSH_BASH_COMPAT
3755#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
3756 encode_string(dest, input, dquote_end, process_bkslash)
3757# else
3758/* only ${var/pattern/repl} (its pattern part) needs additional mode */
3759#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
3760 encode_string(dest, input, dquote_end)
3761# endif
3762#define as_string NULL
3763
3764#else /* !MMU */
3765
3766# if ENABLE_HUSH_BASH_COMPAT
3767/* all parameters are needed, no macro tricks */
3768# else
3769#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
3770 encode_string(as_string, dest, input, dquote_end)
3771# endif
3772#endif
3773static int encode_string(o_string *as_string,
2515 o_string *dest, 3774 o_string *dest,
2516 struct in_str *input, 3775 struct in_str *input,
2517 int dquote_end); 3776 int dquote_end,
3777 int process_bkslash)
3778{
3779#if !ENABLE_HUSH_BASH_COMPAT
3780 const int process_bkslash = 1;
3781#endif
3782 int ch;
3783 int next;
3784
3785 again:
3786 ch = i_getch(input);
3787 if (ch != EOF)
3788 nommu_addchr(as_string, ch);
3789 if (ch == dquote_end) { /* may be only '"' or EOF */
3790 debug_printf_parse("encode_string return 0\n");
3791 return 0;
3792 }
3793 /* note: can't move it above ch == dquote_end check! */
3794 if (ch == EOF) {
3795 syntax_error_unterm_ch('"');
3796 /*xfunc_die(); - redundant */
3797 }
3798 next = '\0';
3799 if (ch != '\n') {
3800 next = i_peek(input);
3801 }
3802 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
3803 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3804 if (process_bkslash && ch == '\\') {
3805 if (next == EOF) {
3806 syntax_error("\\<eof>");
3807 xfunc_die();
3808 }
3809 /* bash:
3810 * "The backslash retains its special meaning [in "..."]
3811 * only when followed by one of the following characters:
3812 * $, `, ", \, or <newline>. A double quote may be quoted
3813 * within double quotes by preceding it with a backslash."
3814 * NB: in (unquoted) heredoc, above does not apply to ",
3815 * therefore we check for it by "next == dquote_end" cond.
3816 */
3817 if (next == dquote_end || strchr("$`\\\n", next)) {
3818 ch = i_getch(input); /* eat next */
3819 if (ch == '\n')
3820 goto again; /* skip \<newline> */
3821 } /* else: ch remains == '\\', and we double it below: */
3822 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
3823 nommu_addchr(as_string, ch);
3824 goto again;
3825 }
3826 if (ch == '$') {
3827 if (parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80) != 0) {
3828 debug_printf_parse("encode_string return 1: "
3829 "parse_dollar returned non-0\n");
3830 return 1;
3831 }
3832 goto again;
3833 }
3834#if ENABLE_HUSH_TICK
3835 if (ch == '`') {
3836 //unsigned pos = dest->length;
3837 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3838 o_addchr(dest, 0x80 | '`');
3839 add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"');
3840 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3841 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
3842 goto again;
3843 }
3844#endif
3845 o_addQchr(dest, ch);
3846 goto again;
3847#undef as_string
3848}
3849
3850/*
3851 * Scan input until EOF or end_trigger char.
3852 * Return a list of pipes to execute, or NULL on EOF
3853 * or if end_trigger character is met.
3854 * On syntax error, exit is shell is not interactive,
3855 * reset parsing machinery and start parsing anew,
3856 * or return ERR_PTR.
3857 */
3858static struct pipe *parse_stream(char **pstring,
3859 struct in_str *input,
3860 int end_trigger)
3861{
3862 struct parse_context ctx;
3863 o_string dest = NULL_O_STRING;
3864 int heredoc_cnt;
3865
3866 /* Single-quote triggers a bypass of the main loop until its mate is
3867 * found. When recursing, quote state is passed in via dest->o_expflags.
3868 */
3869 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
3870 end_trigger ? end_trigger : 'X');
3871 debug_enter();
3872
3873 /* If very first arg is "" or '', dest.data may end up NULL.
3874 * Preventing this: */
3875 o_addchr(&dest, '\0');
3876 dest.length = 0;
3877
3878 /* We used to separate words on $IFS here. This was wrong.
3879 * $IFS is used only for word splitting when $var is expanded,
3880 * here we should use blank chars as separators, not $IFS
3881 */
3882
3883 reset: /* we come back here only on syntax errors in interactive shell */
3884
3885#if ENABLE_HUSH_INTERACTIVE
3886 input->promptmode = 0; /* PS1 */
3887#endif
3888 if (MAYBE_ASSIGNMENT != 0)
3889 dest.o_assignment = MAYBE_ASSIGNMENT;
3890 initialize_context(&ctx);
3891 heredoc_cnt = 0;
3892 while (1) {
3893 const char *is_blank;
3894 const char *is_special;
3895 int ch;
3896 int next;
3897 int redir_fd;
3898 redir_type redir_style;
3899
3900 ch = i_getch(input);
3901 debug_printf_parse(": ch=%c (%d) escape=%d\n",
3902 ch, ch, !!(dest.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3903 if (ch == EOF) {
3904 struct pipe *pi;
3905
3906 if (heredoc_cnt) {
3907 syntax_error_unterm_str("here document");
3908 goto parse_error;
3909 }
3910 /* end_trigger == '}' case errors out earlier,
3911 * checking only ')' */
3912 if (end_trigger == ')') {
3913 syntax_error_unterm_ch('('); /* exits */
3914 /* goto parse_error; */
3915 }
3916
3917 if (done_word(&dest, &ctx)) {
3918 goto parse_error;
3919 }
3920 o_free(&dest);
3921 done_pipe(&ctx, PIPE_SEQ);
3922 pi = ctx.list_head;
3923 /* If we got nothing... */
3924 /* (this makes bare "&" cmd a no-op.
3925 * bash says: "syntax error near unexpected token '&'") */
3926 if (pi->num_cmds == 0
3927 IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
3928 ) {
3929 free_pipe_list(pi);
3930 pi = NULL;
3931 }
3932#if !BB_MMU
3933 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
3934 if (pstring)
3935 *pstring = ctx.as_string.data;
3936 else
3937 o_free_unsafe(&ctx.as_string);
3938#endif
3939 debug_leave();
3940 debug_printf_parse("parse_stream return %p\n", pi);
3941 return pi;
3942 }
3943 nommu_addchr(&ctx.as_string, ch);
3944
3945 next = '\0';
3946 if (ch != '\n')
3947 next = i_peek(input);
3948
3949 is_special = "{}<>;&|()#'" /* special outside of "str" */
3950 "\\$\"" IF_HUSH_TICK("`"); /* always special */
3951 /* Are { and } special here? */
3952 if (ctx.command->argv /* word [word]{... - non-special */
3953 || dest.length /* word{... - non-special */
3954 || dest.has_quoted_part /* ""{... - non-special */
3955 || (next != ';' /* }; - special */
3956 && next != ')' /* }) - special */
3957 && next != '&' /* }& and }&& ... - special */
3958 && next != '|' /* }|| ... - special */
3959 && !strchr(defifs, next) /* {word - non-special */
3960 )
3961 ) {
3962 /* They are not special, skip "{}" */
3963 is_special += 2;
3964 }
3965 is_special = strchr(is_special, ch);
3966 is_blank = strchr(defifs, ch);
3967
3968 if (!is_special && !is_blank) { /* ordinary char */
3969 ordinary_char:
3970 o_addQchr(&dest, ch);
3971 if ((dest.o_assignment == MAYBE_ASSIGNMENT
3972 || dest.o_assignment == WORD_IS_KEYWORD)
3973 && ch == '='
3974 && is_well_formed_var_name(dest.data, '=')
3975 ) {
3976 dest.o_assignment = DEFINITELY_ASSIGNMENT;
3977 }
3978 continue;
3979 }
3980
3981 if (is_blank) {
3982 if (done_word(&dest, &ctx)) {
3983 goto parse_error;
3984 }
3985 if (ch == '\n') {
3986#if ENABLE_HUSH_CASE
3987 /* "case ... in <newline> word) ..." -
3988 * newlines are ignored (but ';' wouldn't be) */
3989 if (ctx.command->argv == NULL
3990 && ctx.ctx_res_w == RES_MATCH
3991 ) {
3992 continue;
3993 }
3994#endif
3995 /* Treat newline as a command separator. */
3996 done_pipe(&ctx, PIPE_SEQ);
3997 debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
3998 if (heredoc_cnt) {
3999 if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
4000 goto parse_error;
4001 }
4002 heredoc_cnt = 0;
4003 }
4004 dest.o_assignment = MAYBE_ASSIGNMENT;
4005 ch = ';';
4006 /* note: if (is_blank) continue;
4007 * will still trigger for us */
4008 }
4009 }
4010
4011 /* "cmd}" or "cmd }..." without semicolon or &:
4012 * } is an ordinary char in this case, even inside { cmd; }
4013 * Pathological example: { ""}; } should exec "}" cmd
4014 */
4015 if (ch == '}') {
4016 if (!IS_NULL_CMD(ctx.command) /* cmd } */
4017 || dest.length != 0 /* word} */
4018 || dest.has_quoted_part /* ""} */
4019 ) {
4020 goto ordinary_char;
4021 }
4022 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
4023 goto skip_end_trigger;
4024 /* else: } does terminate a group */
4025 }
4026
4027 if (end_trigger && end_trigger == ch
4028 && (ch != ';' || heredoc_cnt == 0)
4029#if ENABLE_HUSH_CASE
4030 && (ch != ')'
4031 || ctx.ctx_res_w != RES_MATCH
4032 || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0)
4033 )
4034#endif
4035 ) {
4036 if (heredoc_cnt) {
4037 /* This is technically valid:
4038 * { cat <<HERE; }; echo Ok
4039 * heredoc
4040 * heredoc
4041 * HERE
4042 * but we don't support this.
4043 * We require heredoc to be in enclosing {}/(),
4044 * if any.
4045 */
4046 syntax_error_unterm_str("here document");
4047 goto parse_error;
4048 }
4049 if (done_word(&dest, &ctx)) {
4050 goto parse_error;
4051 }
4052 done_pipe(&ctx, PIPE_SEQ);
4053 dest.o_assignment = MAYBE_ASSIGNMENT;
4054 /* Do we sit outside of any if's, loops or case's? */
4055 if (!HAS_KEYWORDS
4056 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
4057 ) {
4058 o_free(&dest);
4059#if !BB_MMU
4060 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
4061 if (pstring)
4062 *pstring = ctx.as_string.data;
4063 else
4064 o_free_unsafe(&ctx.as_string);
4065#endif
4066 debug_leave();
4067 debug_printf_parse("parse_stream return %p: "
4068 "end_trigger char found\n",
4069 ctx.list_head);
4070 return ctx.list_head;
4071 }
4072 }
4073 skip_end_trigger:
4074 if (is_blank)
4075 continue;
4076
4077 /* Catch <, > before deciding whether this word is
4078 * an assignment. a=1 2>z b=2: b=2 is still assignment */
4079 switch (ch) {
4080 case '>':
4081 redir_fd = redirect_opt_num(&dest);
4082 if (done_word(&dest, &ctx)) {
4083 goto parse_error;
4084 }
4085 redir_style = REDIRECT_OVERWRITE;
4086 if (next == '>') {
4087 redir_style = REDIRECT_APPEND;
4088 ch = i_getch(input);
4089 nommu_addchr(&ctx.as_string, ch);
4090 }
4091#if 0
4092 else if (next == '(') {
4093 syntax_error(">(process) not supported");
4094 goto parse_error;
4095 }
4096#endif
4097 if (parse_redirect(&ctx, redir_fd, redir_style, input))
4098 goto parse_error;
4099 continue; /* back to top of while (1) */
4100 case '<':
4101 redir_fd = redirect_opt_num(&dest);
4102 if (done_word(&dest, &ctx)) {
4103 goto parse_error;
4104 }
4105 redir_style = REDIRECT_INPUT;
4106 if (next == '<') {
4107 redir_style = REDIRECT_HEREDOC;
4108 heredoc_cnt++;
4109 debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
4110 ch = i_getch(input);
4111 nommu_addchr(&ctx.as_string, ch);
4112 } else if (next == '>') {
4113 redir_style = REDIRECT_IO;
4114 ch = i_getch(input);
4115 nommu_addchr(&ctx.as_string, ch);
4116 }
4117#if 0
4118 else if (next == '(') {
4119 syntax_error("<(process) not supported");
4120 goto parse_error;
4121 }
4122#endif
4123 if (parse_redirect(&ctx, redir_fd, redir_style, input))
4124 goto parse_error;
4125 continue; /* back to top of while (1) */
4126 }
4127
4128 if (dest.o_assignment == MAYBE_ASSIGNMENT
4129 /* check that we are not in word in "a=1 2>word b=1": */
4130 && !ctx.pending_redirect
4131 ) {
4132 /* ch is a special char and thus this word
4133 * cannot be an assignment */
4134 dest.o_assignment = NOT_ASSIGNMENT;
4135 }
4136
4137 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
4138
4139 switch (ch) {
4140 case '#':
4141 if (dest.length == 0) {
4142 while (1) {
4143 ch = i_peek(input);
4144 if (ch == EOF || ch == '\n')
4145 break;
4146 i_getch(input);
4147 /* note: we do not add it to &ctx.as_string */
4148 }
4149 nommu_addchr(&ctx.as_string, '\n');
4150 } else {
4151 o_addQchr(&dest, ch);
4152 }
4153 break;
4154 case '\\':
4155 if (next == EOF) {
4156 syntax_error("\\<eof>");
4157 xfunc_die();
4158 }
4159 ch = i_getch(input);
4160 if (ch != '\n') {
4161 o_addchr(&dest, '\\');
4162 /*nommu_addchr(&ctx.as_string, '\\'); - already done */
4163 o_addchr(&dest, ch);
4164 nommu_addchr(&ctx.as_string, ch);
4165 /* Example: echo Hello \2>file
4166 * we need to know that word 2 is quoted */
4167 dest.has_quoted_part = 1;
4168 }
4169#if !BB_MMU
4170 else {
4171 /* It's "\<newline>". Remove trailing '\' from ctx.as_string */
4172 ctx.as_string.data[--ctx.as_string.length] = '\0';
4173 }
4174#endif
4175 break;
4176 case '$':
4177 if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) {
4178 debug_printf_parse("parse_stream parse error: "
4179 "parse_dollar returned non-0\n");
4180 goto parse_error;
4181 }
4182 break;
4183 case '\'':
4184 dest.has_quoted_part = 1;
4185 while (1) {
4186 ch = i_getch(input);
4187 if (ch == EOF) {
4188 syntax_error_unterm_ch('\'');
4189 /*xfunc_die(); - redundant */
4190 }
4191 nommu_addchr(&ctx.as_string, ch);
4192 if (ch == '\'')
4193 break;
4194 o_addqchr(&dest, ch);
4195 }
4196 break;
4197 case '"':
4198 dest.has_quoted_part = 1;
4199 if (dest.o_assignment == NOT_ASSIGNMENT)
4200 dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
4201 if (encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
4202 goto parse_error;
4203 dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
4204 break;
4205#if ENABLE_HUSH_TICK
4206 case '`': {
4207 unsigned pos;
4208
4209 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
4210 o_addchr(&dest, '`');
4211 pos = dest.length;
4212 add_till_backquote(&dest, input, /*in_dquote:*/ 0);
4213# if !BB_MMU
4214 o_addstr(&ctx.as_string, dest.data + pos);
4215 o_addchr(&ctx.as_string, '`');
4216# endif
4217 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
4218 //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
4219 break;
4220 }
4221#endif
4222 case ';':
4223#if ENABLE_HUSH_CASE
4224 case_semi:
4225#endif
4226 if (done_word(&dest, &ctx)) {
4227 goto parse_error;
4228 }
4229 done_pipe(&ctx, PIPE_SEQ);
4230#if ENABLE_HUSH_CASE
4231 /* Eat multiple semicolons, detect
4232 * whether it means something special */
4233 while (1) {
4234 ch = i_peek(input);
4235 if (ch != ';')
4236 break;
4237 ch = i_getch(input);
4238 nommu_addchr(&ctx.as_string, ch);
4239 if (ctx.ctx_res_w == RES_CASE_BODY) {
4240 ctx.ctx_dsemicolon = 1;
4241 ctx.ctx_res_w = RES_MATCH;
4242 break;
4243 }
4244 }
4245#endif
4246 new_cmd:
4247 /* We just finished a cmd. New one may start
4248 * with an assignment */
4249 dest.o_assignment = MAYBE_ASSIGNMENT;
4250 break;
4251 case '&':
4252 if (done_word(&dest, &ctx)) {
4253 goto parse_error;
4254 }
4255 if (next == '&') {
4256 ch = i_getch(input);
4257 nommu_addchr(&ctx.as_string, ch);
4258 done_pipe(&ctx, PIPE_AND);
4259 } else {
4260 done_pipe(&ctx, PIPE_BG);
4261 }
4262 goto new_cmd;
4263 case '|':
4264 if (done_word(&dest, &ctx)) {
4265 goto parse_error;
4266 }
4267#if ENABLE_HUSH_CASE
4268 if (ctx.ctx_res_w == RES_MATCH)
4269 break; /* we are in case's "word | word)" */
4270#endif
4271 if (next == '|') { /* || */
4272 ch = i_getch(input);
4273 nommu_addchr(&ctx.as_string, ch);
4274 done_pipe(&ctx, PIPE_OR);
4275 } else {
4276 /* we could pick up a file descriptor choice here
4277 * with redirect_opt_num(), but bash doesn't do it.
4278 * "echo foo 2| cat" yields "foo 2". */
4279 done_command(&ctx);
4280#if !BB_MMU
4281 o_reset_to_empty_unquoted(&ctx.as_string);
4282#endif
4283 }
4284 goto new_cmd;
4285 case '(':
4286#if ENABLE_HUSH_CASE
4287 /* "case... in [(]word)..." - skip '(' */
4288 if (ctx.ctx_res_w == RES_MATCH
4289 && ctx.command->argv == NULL /* not (word|(... */
4290 && dest.length == 0 /* not word(... */
4291 && dest.has_quoted_part == 0 /* not ""(... */
4292 ) {
4293 continue;
4294 }
4295#endif
4296 case '{':
4297 if (parse_group(&dest, &ctx, input, ch) != 0) {
4298 goto parse_error;
4299 }
4300 goto new_cmd;
4301 case ')':
4302#if ENABLE_HUSH_CASE
4303 if (ctx.ctx_res_w == RES_MATCH)
4304 goto case_semi;
4305#endif
4306 case '}':
4307 /* proper use of this character is caught by end_trigger:
4308 * if we see {, we call parse_group(..., end_trigger='}')
4309 * and it will match } earlier (not here). */
4310 syntax_error_unexpected_ch(ch);
4311 goto parse_error;
4312 default:
4313 if (HUSH_DEBUG)
4314 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
4315 }
4316 } /* while (1) */
4317
4318 parse_error:
4319 {
4320 struct parse_context *pctx;
4321 IF_HAS_KEYWORDS(struct parse_context *p2;)
4322
4323 /* Clean up allocated tree.
4324 * Sample for finding leaks on syntax error recovery path.
4325 * Run it from interactive shell, watch pmap `pidof hush`.
4326 * while if false; then false; fi; do break; fi
4327 * Samples to catch leaks at execution:
4328 * while if (true | {true;}); then echo ok; fi; do break; done
4329 * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
4330 */
4331 pctx = &ctx;
4332 do {
4333 /* Update pipe/command counts,
4334 * otherwise freeing may miss some */
4335 done_pipe(pctx, PIPE_SEQ);
4336 debug_printf_clean("freeing list %p from ctx %p\n",
4337 pctx->list_head, pctx);
4338 debug_print_tree(pctx->list_head, 0);
4339 free_pipe_list(pctx->list_head);
4340 debug_printf_clean("freed list %p\n", pctx->list_head);
4341#if !BB_MMU
4342 o_free_unsafe(&pctx->as_string);
4343#endif
4344 IF_HAS_KEYWORDS(p2 = pctx->stack;)
4345 if (pctx != &ctx) {
4346 free(pctx);
4347 }
4348 IF_HAS_KEYWORDS(pctx = p2;)
4349 } while (HAS_KEYWORDS && pctx);
4350 /* Free text, clear all dest fields */
4351 o_free(&dest);
4352 /* If we are not in top-level parse, we return,
4353 * our caller will propagate error.
4354 */
4355 if (end_trigger != ';') {
4356#if !BB_MMU
4357 if (pstring)
4358 *pstring = NULL;
4359#endif
4360 debug_leave();
4361 return ERR_PTR;
4362 }
4363 /* Discard cached input, force prompt */
4364 input->p = NULL;
4365 IF_HUSH_INTERACTIVE(input->promptme = 1;)
4366 goto reset;
4367 }
4368}
4369
4370
4371/*** Execution routines ***/
4372
4373/* Expansion can recurse, need forward decls: */
4374#if !ENABLE_HUSH_BASH_COMPAT
4375/* only ${var/pattern/repl} (its pattern part) needs additional mode */
4376#define expand_string_to_string(str, do_unbackslash) \
4377 expand_string_to_string(str)
4378#endif
4379static char *expand_string_to_string(const char *str, int do_unbackslash);
4380static int process_command_subs(o_string *dest, const char *s);
2518 4381
2519/* expand_strvec_to_strvec() takes a list of strings, expands 4382/* expand_strvec_to_strvec() takes a list of strings, expands
2520 * all variable references within and returns a pointer to 4383 * all variable references within and returns a pointer to
@@ -2522,7 +4385,7 @@ static int parse_stream_dquoted(o_string *as_string,
2522 * of strings. (Think VAR="a b"; echo $VAR). 4385 * of strings. (Think VAR="a b"; echo $VAR).
2523 * This new list is allocated as a single malloc block. 4386 * This new list is allocated as a single malloc block.
2524 * NULL-terminated list of char* pointers is at the beginning of it, 4387 * NULL-terminated list of char* pointers is at the beginning of it,
2525 * followed by strings themself. 4388 * followed by strings themselves.
2526 * Caller can deallocate entire list by single free(list). */ 4389 * Caller can deallocate entire list by single free(list). */
2527 4390
2528/* Store given string, finalizing the word and starting new one whenever 4391/* Store given string, finalizing the word and starting new one whenever
@@ -2533,10 +4396,18 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
2533 while (1) { 4396 while (1) {
2534 int word_len = strcspn(str, G.ifs); 4397 int word_len = strcspn(str, G.ifs);
2535 if (word_len) { 4398 if (word_len) {
2536 if (output->o_escape || !output->o_glob) 4399 if (!(output->o_expflags & EXP_FLAG_GLOB))
2537 o_addQstr(output, str, word_len); 4400 o_addblock(output, str, word_len);
2538 else /* protect backslashes against globbing up :) */ 4401 else {
4402 /* Protect backslashes against globbing up :)
4403 * Example: "v='\*'; echo b$v" prints "b\*"
4404 * (and does not try to glob on "*")
4405 */
2539 o_addblock_duplicate_backslash(output, str, word_len); 4406 o_addblock_duplicate_backslash(output, str, word_len);
4407 /*/ Why can't we do it easier? */
4408 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
4409 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
4410 }
2540 str += word_len; 4411 str += word_len;
2541 } 4412 }
2542 if (!*str) /* EOL - do not finalize word */ 4413 if (!*str) /* EOL - do not finalize word */
@@ -2557,15 +4428,21 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
2557 * Returns malloced string. 4428 * Returns malloced string.
2558 * As an optimization, we return NULL if expansion is not needed. 4429 * As an optimization, we return NULL if expansion is not needed.
2559 */ 4430 */
2560static char *expand_pseudo_dquoted(const char *str) 4431#if !ENABLE_HUSH_BASH_COMPAT
4432/* only ${var/pattern/repl} (its pattern part) needs additional mode */
4433#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
4434 encode_then_expand_string(str)
4435#endif
4436static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash)
2561{ 4437{
2562 char *exp_str; 4438 char *exp_str;
2563 struct in_str input; 4439 struct in_str input;
2564 o_string dest = NULL_O_STRING; 4440 o_string dest = NULL_O_STRING;
2565 4441
2566 if (strchr(str, '$') == NULL 4442 if (!strchr(str, '$')
4443 && !strchr(str, '\\')
2567#if ENABLE_HUSH_TICK 4444#if ENABLE_HUSH_TICK
2568 && strchr(str, '`') == NULL 4445 && !strchr(str, '`')
2569#endif 4446#endif
2570 ) { 4447 ) {
2571 return NULL; 4448 return NULL;
@@ -2575,9 +4452,9 @@ static char *expand_pseudo_dquoted(const char *str)
2575 * echo $(($a + `echo 1`)) $((1 + $((2)) )) 4452 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
2576 */ 4453 */
2577 setup_string_in_str(&input, str); 4454 setup_string_in_str(&input, str);
2578 parse_stream_dquoted(NULL, &dest, &input, EOF); 4455 encode_string(NULL, &dest, &input, EOF, process_bkslash);
2579 //bb_error_msg("'%s' -> '%s'", str, dest.data); 4456 //bb_error_msg("'%s' -> '%s'", str, dest.data);
2580 exp_str = expand_string_to_string(dest.data); 4457 exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash);
2581 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); 4458 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
2582 o_free_unsafe(&dest); 4459 o_free_unsafe(&dest);
2583 return exp_str; 4460 return exp_str;
@@ -2592,38 +4469,369 @@ static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p)
2592 4469
2593 hooks.lookupvar = get_local_var_value; 4470 hooks.lookupvar = get_local_var_value;
2594 hooks.setvar = set_local_var_from_halves; 4471 hooks.setvar = set_local_var_from_halves;
2595 hooks.endofname = endofname; 4472 //hooks.endofname = endofname;
2596 exp_str = expand_pseudo_dquoted(arg); 4473 exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
2597 res = arith(exp_str ? exp_str : arg, errcode_p, &hooks); 4474 res = arith(exp_str ? exp_str : arg, errcode_p, &hooks);
2598 free(exp_str); 4475 free(exp_str);
2599 return res; 4476 return res;
2600} 4477}
2601#endif 4478#endif
2602 4479
4480#if ENABLE_HUSH_BASH_COMPAT
4481/* ${var/[/]pattern[/repl]} helpers */
4482static char *strstr_pattern(char *val, const char *pattern, int *size)
4483{
4484 while (1) {
4485 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
4486 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
4487 if (end) {
4488 *size = end - val;
4489 return val;
4490 }
4491 if (*val == '\0')
4492 return NULL;
4493 /* Optimization: if "*pat" did not match the start of "string",
4494 * we know that "tring", "ring" etc will not match too:
4495 */
4496 if (pattern[0] == '*')
4497 return NULL;
4498 val++;
4499 }
4500}
4501static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
4502{
4503 char *result = NULL;
4504 unsigned res_len = 0;
4505 unsigned repl_len = strlen(repl);
4506
4507 while (1) {
4508 int size;
4509 char *s = strstr_pattern(val, pattern, &size);
4510 if (!s)
4511 break;
4512
4513 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
4514 memcpy(result + res_len, val, s - val);
4515 res_len += s - val;
4516 strcpy(result + res_len, repl);
4517 res_len += repl_len;
4518 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
4519
4520 val = s + size;
4521 if (exp_op == '/')
4522 break;
4523 }
4524 if (val[0] && result) {
4525 result = xrealloc(result, res_len + strlen(val) + 1);
4526 strcpy(result + res_len, val);
4527 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
4528 }
4529 debug_printf_varexp("result:'%s'\n", result);
4530 return result;
4531}
4532#endif
4533
4534/* Helper:
4535 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
4536 */
4537static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp)
4538{
4539 const char *val = NULL;
4540 char *to_be_freed = NULL;
4541 char *p = *pp;
4542 char *var;
4543 char first_char;
4544 char exp_op;
4545 char exp_save = exp_save; /* for compiler */
4546 char *exp_saveptr; /* points to expansion operator */
4547 char *exp_word = exp_word; /* for compiler */
4548 char arg0;
4549
4550 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
4551 var = arg;
4552 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
4553 arg0 = arg[0];
4554 first_char = arg[0] = arg0 & 0x7f;
4555 exp_op = 0;
4556
4557 if (first_char == '#' /* ${#... */
4558 && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */
4559 ) {
4560 /* It must be length operator: ${#var} */
4561 var++;
4562 exp_op = 'L';
4563 } else {
4564 /* Maybe handle parameter expansion */
4565 if (exp_saveptr /* if 2nd char is one of expansion operators */
4566 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
4567 ) {
4568 /* ${?:0}, ${#[:]%0} etc */
4569 exp_saveptr = var + 1;
4570 } else {
4571 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
4572 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
4573 }
4574 exp_op = exp_save = *exp_saveptr;
4575 if (exp_op) {
4576 exp_word = exp_saveptr + 1;
4577 if (exp_op == ':') {
4578 exp_op = *exp_word++;
4579//TODO: try ${var:} and ${var:bogus} in non-bash config
4580 if (ENABLE_HUSH_BASH_COMPAT
4581 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
4582 ) {
4583 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
4584 exp_op = ':';
4585 exp_word--;
4586 }
4587 }
4588 *exp_saveptr = '\0';
4589 } /* else: it's not an expansion op, but bare ${var} */
4590 }
4591
4592 /* Look up the variable in question */
4593 if (isdigit(var[0])) {
4594 /* parse_dollar should have vetted var for us */
4595 int n = xatoi_positive(var);
4596 if (n < G.global_argc)
4597 val = G.global_argv[n];
4598 /* else val remains NULL: $N with too big N */
4599 } else {
4600 switch (var[0]) {
4601 case '$': /* pid */
4602 val = utoa(G.root_pid);
4603 break;
4604 case '!': /* bg pid */
4605 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
4606 break;
4607 case '?': /* exitcode */
4608 val = utoa(G.last_exitcode);
4609 break;
4610 case '#': /* argc */
4611 val = utoa(G.global_argc ? G.global_argc-1 : 0);
4612 break;
4613 default:
4614 val = get_local_var_value(var);
4615 }
4616 }
4617
4618 /* Handle any expansions */
4619 if (exp_op == 'L') {
4620 debug_printf_expand("expand: length(%s)=", val);
4621 val = utoa(val ? strlen(val) : 0);
4622 debug_printf_expand("%s\n", val);
4623 } else if (exp_op) {
4624 if (exp_op == '%' || exp_op == '#') {
4625 /* Standard-mandated substring removal ops:
4626 * ${parameter%word} - remove smallest suffix pattern
4627 * ${parameter%%word} - remove largest suffix pattern
4628 * ${parameter#word} - remove smallest prefix pattern
4629 * ${parameter##word} - remove largest prefix pattern
4630 *
4631 * Word is expanded to produce a glob pattern.
4632 * Then var's value is matched to it and matching part removed.
4633 */
4634 if (val && val[0]) {
4635 char *t;
4636 char *exp_exp_word;
4637 char *loc;
4638 unsigned scan_flags = pick_scan(exp_op, *exp_word);
4639 if (exp_op == *exp_word) /* ## or %% */
4640 exp_word++;
4641 exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
4642 if (exp_exp_word)
4643 exp_word = exp_exp_word;
4644 /* HACK ALERT. We depend here on the fact that
4645 * G.global_argv and results of utoa and get_local_var_value
4646 * are actually in writable memory:
4647 * scan_and_match momentarily stores NULs there. */
4648 t = (char*)val;
4649 loc = scan_and_match(t, exp_word, scan_flags);
4650 //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
4651 // exp_op, t, exp_word, loc);
4652 free(exp_exp_word);
4653 if (loc) { /* match was found */
4654 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
4655 val = loc; /* take right part */
4656 else /* %[%] */
4657 val = to_be_freed = xstrndup(val, loc - val); /* left */
4658 }
4659 }
4660 }
4661#if ENABLE_HUSH_BASH_COMPAT
4662 else if (exp_op == '/' || exp_op == '\\') {
4663 /* It's ${var/[/]pattern[/repl]} thing.
4664 * Note that in encoded form it has TWO parts:
4665 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
4666 * and if // is used, it is encoded as \:
4667 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
4668 */
4669 /* Empty variable always gives nothing: */
4670 // "v=''; echo ${v/*/w}" prints "", not "w"
4671 if (val && val[0]) {
4672 /* pattern uses non-standard expansion.
4673 * repl should be unbackslashed and globbed
4674 * by the usual expansion rules:
4675 * >az; >bz;
4676 * v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
4677 * v='a bz'; echo "${v/a*z/\z}" prints "\z"
4678 * v='a bz'; echo ${v/a*z/a*z} prints "az"
4679 * v='a bz'; echo ${v/a*z/\z} prints "z"
4680 * (note that a*z _pattern_ is never globbed!)
4681 */
4682 char *pattern, *repl, *t;
4683 pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0);
4684 if (!pattern)
4685 pattern = xstrdup(exp_word);
4686 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
4687 *p++ = SPECIAL_VAR_SYMBOL;
4688 exp_word = p;
4689 p = strchr(p, SPECIAL_VAR_SYMBOL);
4690 *p = '\0';
4691 repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ arg0 & 0x80, /*unbackslash:*/ 1);
4692 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
4693 /* HACK ALERT. We depend here on the fact that
4694 * G.global_argv and results of utoa and get_local_var_value
4695 * are actually in writable memory:
4696 * replace_pattern momentarily stores NULs there. */
4697 t = (char*)val;
4698 to_be_freed = replace_pattern(t,
4699 pattern,
4700 (repl ? repl : exp_word),
4701 exp_op);
4702 if (to_be_freed) /* at least one replace happened */
4703 val = to_be_freed;
4704 free(pattern);
4705 free(repl);
4706 }
4707 }
4708#endif
4709 else if (exp_op == ':') {
4710#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
4711 /* It's ${var:N[:M]} bashism.
4712 * Note that in encoded form it has TWO parts:
4713 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
4714 */
4715 arith_t beg, len;
4716 int errcode = 0;
4717
4718 beg = expand_and_evaluate_arith(exp_word, &errcode);
4719 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
4720 *p++ = SPECIAL_VAR_SYMBOL;
4721 exp_word = p;
4722 p = strchr(p, SPECIAL_VAR_SYMBOL);
4723 *p = '\0';
4724 len = expand_and_evaluate_arith(exp_word, &errcode);
4725 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
4726
4727 if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
4728 if (beg < 0) /* bash compat */
4729 beg = 0;
4730 debug_printf_varexp("from val:'%s'\n", val);
4731 if (len == 0 || !val || beg >= strlen(val))
4732 val = "";
4733 else {
4734 /* Paranoia. What if user entered 9999999999999
4735 * which fits in arith_t but not int? */
4736 if (len >= INT_MAX)
4737 len = INT_MAX;
4738 val = to_be_freed = xstrndup(val + beg, len);
4739 }
4740 debug_printf_varexp("val:'%s'\n", val);
4741 } else
4742#endif
4743 {
4744 die_if_script("malformed ${%s:...}", var);
4745 val = "";
4746 }
4747 } else { /* one of "-=+?" */
4748 /* Standard-mandated substitution ops:
4749 * ${var?word} - indicate error if unset
4750 * If var is unset, word (or a message indicating it is unset
4751 * if word is null) is written to standard error
4752 * and the shell exits with a non-zero exit status.
4753 * Otherwise, the value of var is substituted.
4754 * ${var-word} - use default value
4755 * If var is unset, word is substituted.
4756 * ${var=word} - assign and use default value
4757 * If var is unset, word is assigned to var.
4758 * In all cases, final value of var is substituted.
4759 * ${var+word} - use alternative value
4760 * If var is unset, null is substituted.
4761 * Otherwise, word is substituted.
4762 *
4763 * Word is subjected to tilde expansion, parameter expansion,
4764 * command substitution, and arithmetic expansion.
4765 * If word is not needed, it is not expanded.
4766 *
4767 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
4768 * but also treat null var as if it is unset.
4769 */
4770 int use_word = (!val || ((exp_save == ':') && !val[0]));
4771 if (exp_op == '+')
4772 use_word = !use_word;
4773 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
4774 (exp_save == ':') ? "true" : "false", use_word);
4775 if (use_word) {
4776 to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
4777 if (to_be_freed)
4778 exp_word = to_be_freed;
4779 if (exp_op == '?') {
4780 /* mimic bash message */
4781 die_if_script("%s: %s",
4782 var,
4783 exp_word[0] ? exp_word : "parameter null or not set"
4784 );
4785//TODO: how interactive bash aborts expansion mid-command?
4786 } else {
4787 val = exp_word;
4788 }
4789
4790 if (exp_op == '=') {
4791 /* ${var=[word]} or ${var:=[word]} */
4792 if (isdigit(var[0]) || var[0] == '#') {
4793 /* mimic bash message */
4794 die_if_script("$%s: cannot assign in this way", var);
4795 val = NULL;
4796 } else {
4797 char *new_var = xasprintf("%s=%s", var, val);
4798 set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4799 }
4800 }
4801 }
4802 } /* one of "-=+?" */
4803
4804 *exp_saveptr = exp_save;
4805 } /* if (exp_op) */
4806
4807 arg[0] = arg0;
4808
4809 *pp = p;
4810 *to_be_freed_pp = to_be_freed;
4811 return val;
4812}
4813
2603/* Expand all variable references in given string, adding words to list[] 4814/* Expand all variable references in given string, adding words to list[]
2604 * at n, n+1,... positions. Return updated n (so that list[n] is next one 4815 * at n, n+1,... positions. Return updated n (so that list[n] is next one
2605 * to be filled). This routine is extremely tricky: has to deal with 4816 * to be filled). This routine is extremely tricky: has to deal with
2606 * variables/parameters with whitespace, $* and $@, and constructs like 4817 * variables/parameters with whitespace, $* and $@, and constructs like
2607 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ 4818 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
2608static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) 4819static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
2609{ 4820{
2610 /* or_mask is either 0 (normal case) or 0x80 - 4821 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
2611 * expansion of right-hand side of assignment == 1-element expand. 4822 * expansion of right-hand side of assignment == 1-element expand.
2612 * It will also do no globbing, and thus we must not backslash-quote!
2613 */ 4823 */
2614 char ored_ch; 4824 char cant_be_null = 0; /* only bit 0x80 matters */
2615 char *p; 4825 char *p;
2616 4826
2617 ored_ch = 0; 4827 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
2618 4828 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
2619 debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask);
2620 debug_print_list("expand_vars_to_list", output, n); 4829 debug_print_list("expand_vars_to_list", output, n);
2621 n = o_save_ptr(output, n); 4830 n = o_save_ptr(output, n);
2622 debug_print_list("expand_vars_to_list[0]", output, n); 4831 debug_print_list("expand_vars_to_list[0]", output, n);
2623 4832
2624 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { 4833 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
2625 char first_ch; 4834 char first_ch;
2626 int i;
2627 char *to_be_freed = NULL; 4835 char *to_be_freed = NULL;
2628 const char *val = NULL; 4836 const char *val = NULL;
2629#if ENABLE_HUSH_TICK 4837#if ENABLE_HUSH_TICK
@@ -2637,24 +4845,28 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2637 arg = ++p; 4845 arg = ++p;
2638 p = strchr(p, SPECIAL_VAR_SYMBOL); 4846 p = strchr(p, SPECIAL_VAR_SYMBOL);
2639 4847
2640 first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */ 4848 /* Fetch special var name (if it is indeed one of them)
2641 /* "$@" is special. Even if quoted, it can still 4849 * and quote bit, force the bit on if singleword expansion -
2642 * expand to nothing (not even an empty string) */ 4850 * important for not getting v=$@ expand to many words. */
4851 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
4852
4853 /* Is this variable quoted and thus expansion can't be null?
4854 * "$@" is special. Even if quoted, it can still
4855 * expand to nothing (not even an empty string),
4856 * thus it is excluded. */
2643 if ((first_ch & 0x7f) != '@') 4857 if ((first_ch & 0x7f) != '@')
2644 ored_ch |= first_ch; 4858 cant_be_null |= first_ch;
2645 4859
2646 switch (first_ch & 0x7f) { 4860 switch (first_ch & 0x7f) {
2647 /* Highest bit in first_ch indicates that var is double-quoted */ 4861 /* Highest bit in first_ch indicates that var is double-quoted */
2648 case '*': 4862 case '*':
2649 case '@': 4863 case '@': {
2650 i = 1; 4864 int i;
2651 if (!G.global_argv[i]) 4865 if (!G.global_argv[1])
2652 break; 4866 break;
2653 ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ 4867 i = 1;
4868 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
2654 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ 4869 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
2655 smallint sv = output->o_escape;
2656 /* unquoted var's contents should be globbed, so don't escape */
2657 output->o_escape = 0;
2658 while (G.global_argv[i]) { 4870 while (G.global_argv[i]) {
2659 n = expand_on_ifs(output, n, G.global_argv[i]); 4871 n = expand_on_ifs(output, n, G.global_argv[i]);
2660 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); 4872 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
@@ -2667,22 +4879,23 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2667 debug_print_list("expand_vars_to_list[3]", output, n); 4879 debug_print_list("expand_vars_to_list[3]", output, n);
2668 } 4880 }
2669 } 4881 }
2670 output->o_escape = sv;
2671 } else 4882 } else
2672 /* If or_mask is nonzero, we handle assignment 'a=....$@.....' 4883 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
2673 * and in this case should treat it like '$*' - see 'else...' below */ 4884 * and in this case should treat it like '$*' - see 'else...' below */
2674 if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ 4885 if (first_ch == ('@'|0x80) /* quoted $@ */
4886 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
4887 ) {
2675 while (1) { 4888 while (1) {
2676 o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); 4889 o_addQstr(output, G.global_argv[i]);
2677 if (++i >= G.global_argc) 4890 if (++i >= G.global_argc)
2678 break; 4891 break;
2679 o_addchr(output, '\0'); 4892 o_addchr(output, '\0');
2680 debug_print_list("expand_vars_to_list[4]", output, n); 4893 debug_print_list("expand_vars_to_list[4]", output, n);
2681 n = o_save_ptr(output, n); 4894 n = o_save_ptr(output, n);
2682 } 4895 }
2683 } else { /* quoted $*: add as one word */ 4896 } else { /* quoted $* (or v="$@" case): add as one word */
2684 while (1) { 4897 while (1) {
2685 o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); 4898 o_addQstr(output, G.global_argv[i]);
2686 if (!G.global_argv[++i]) 4899 if (!G.global_argv[++i])
2687 break; 4900 break;
2688 if (G.ifs[0]) 4901 if (G.ifs[0])
@@ -2690,14 +4903,15 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2690 } 4903 }
2691 } 4904 }
2692 break; 4905 break;
4906 }
2693 case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ 4907 case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
2694 /* "Empty variable", used to make "" etc to not disappear */ 4908 /* "Empty variable", used to make "" etc to not disappear */
2695 arg++; 4909 arg++;
2696 ored_ch = 0x80; 4910 cant_be_null = 0x80;
2697 break; 4911 break;
2698#if ENABLE_HUSH_TICK 4912#if ENABLE_HUSH_TICK
2699 case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ 4913 case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
2700 *p = '\0'; 4914 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
2701 arg++; 4915 arg++;
2702 /* Can't just stuff it into output o_string, 4916 /* Can't just stuff it into output o_string,
2703 * expanded result may need to be globbed 4917 * expanded result may need to be globbed
@@ -2739,240 +4953,31 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2739 break; 4953 break;
2740 } 4954 }
2741#endif 4955#endif
2742 default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ 4956 default:
2743 char *var; 4957 val = expand_one_var(&to_be_freed, arg, &p);
2744 char first_char; 4958 IF_HUSH_TICK(store_val:)
2745 char exp_op;
2746 char exp_save = exp_save; /* for compiler */
2747 char *exp_saveptr; /* points to expansion operator */
2748 char *exp_word = exp_word; /* for compiler */
2749
2750 var = arg;
2751 *p = '\0';
2752 exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL;
2753 first_char = arg[0] = first_ch & 0x7f;
2754 exp_op = 0;
2755
2756 if (first_char == '#' && arg[1] && !exp_saveptr) {
2757 /* handle length expansion ${#var} */
2758 var++;
2759 exp_op = 'L';
2760 } else {
2761 /* maybe handle parameter expansion */
2762 if (exp_saveptr /* if 2nd char is one of expansion operators */
2763 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
2764 ) {
2765 /* ${?:0}, ${#[:]%0} etc */
2766 exp_saveptr = var + 1;
2767 } else {
2768 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
2769 exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?");
2770 }
2771 exp_op = exp_save = *exp_saveptr;
2772 if (exp_op) {
2773 exp_word = exp_saveptr + 1;
2774 if (exp_op == ':') {
2775 exp_op = *exp_word++;
2776 if (ENABLE_HUSH_BASH_COMPAT
2777 && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op))
2778 ) {
2779 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
2780 exp_op = ':';
2781 exp_word--;
2782 }
2783 }
2784 *exp_saveptr = '\0';
2785 } /* else: it's not an expansion op, but bare ${var} */
2786 }
2787
2788 /* lookup the variable in question */
2789 if (isdigit(var[0])) {
2790 /* parse_dollar() should have vetted var for us */
2791 i = xatoi_positive(var);
2792 if (i < G.global_argc)
2793 val = G.global_argv[i];
2794 /* else val remains NULL: $N with too big N */
2795 } else {
2796 switch (var[0]) {
2797 case '$': /* pid */
2798 val = utoa(G.root_pid);
2799 break;
2800 case '!': /* bg pid */
2801 val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
2802 break;
2803 case '?': /* exitcode */
2804 val = utoa(G.last_exitcode);
2805 break;
2806 case '#': /* argc */
2807 val = utoa(G.global_argc ? G.global_argc-1 : 0);
2808 break;
2809 default:
2810 val = get_local_var_value(var);
2811 }
2812 }
2813
2814 /* handle any expansions */
2815 if (exp_op == 'L') {
2816 debug_printf_expand("expand: length(%s)=", val);
2817 val = utoa(val ? strlen(val) : 0);
2818 debug_printf_expand("%s\n", val);
2819 } else if (exp_op) {
2820 if (exp_op == '%' || exp_op == '#') {
2821 /* Standard-mandated substring removal ops:
2822 * ${parameter%word} - remove smallest suffix pattern
2823 * ${parameter%%word} - remove largest suffix pattern
2824 * ${parameter#word} - remove smallest prefix pattern
2825 * ${parameter##word} - remove largest prefix pattern
2826 *
2827 * Word is expanded to produce a glob pattern.
2828 * Then var's value is matched to it and matching part removed.
2829 */
2830 if (val) {
2831 bool match_at_left;
2832 char *loc;
2833 scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left);
2834 if (exp_op == *exp_word) /* ## or %% */
2835 exp_word++;
2836 val = to_be_freed = xstrdup(val);
2837 {
2838 char *exp_exp_word = expand_pseudo_dquoted(exp_word);
2839 if (exp_exp_word)
2840 exp_word = exp_exp_word;
2841 loc = scan(to_be_freed, exp_word, match_at_left);
2842 //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
2843 // exp_op, to_be_freed, exp_word, loc);
2844 free(exp_exp_word);
2845 }
2846 if (loc) { /* match was found */
2847 if (match_at_left) /* # or ## */
2848 val = loc;
2849 else /* % or %% */
2850 *loc = '\0';
2851 }
2852 }
2853 } else if (exp_op == ':') {
2854#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
2855 /* It's ${var:N[:M]} bashism.
2856 * Note that in encoded form it has TWO parts:
2857 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
2858 */
2859 arith_t beg, len;
2860 int errcode = 0;
2861
2862 beg = expand_and_evaluate_arith(exp_word, &errcode);
2863 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
2864 *p++ = SPECIAL_VAR_SYMBOL;
2865 exp_word = p;
2866 p = strchr(p, SPECIAL_VAR_SYMBOL);
2867 *p = '\0';
2868 len = expand_and_evaluate_arith(exp_word, &errcode);
2869 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
2870
2871 if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
2872 if (beg < 0) /* bash compat */
2873 beg = 0;
2874 debug_printf_varexp("from val:'%s'\n", val);
2875 if (len == 0 || !val || beg >= strlen(val))
2876 val = "";
2877 else {
2878 /* Paranoia. What if user entered 9999999999999
2879 * which fits in arith_t but not int? */
2880 if (len >= INT_MAX)
2881 len = INT_MAX;
2882 val = to_be_freed = xstrndup(val + beg, len);
2883 }
2884 debug_printf_varexp("val:'%s'\n", val);
2885 } else
2886#endif
2887 {
2888 die_if_script("malformed ${%s:...}", var);
2889 val = "";
2890 }
2891 } else { /* one of "-=+?" */
2892 /* Standard-mandated substitution ops:
2893 * ${var?word} - indicate error if unset
2894 * If var is unset, word (or a message indicating it is unset
2895 * if word is null) is written to standard error
2896 * and the shell exits with a non-zero exit status.
2897 * Otherwise, the value of var is substituted.
2898 * ${var-word} - use default value
2899 * If var is unset, word is substituted.
2900 * ${var=word} - assign and use default value
2901 * If var is unset, word is assigned to var.
2902 * In all cases, final value of var is substituted.
2903 * ${var+word} - use alternative value
2904 * If var is unset, null is substituted.
2905 * Otherwise, word is substituted.
2906 *
2907 * Word is subjected to tilde expansion, parameter expansion,
2908 * command substitution, and arithmetic expansion.
2909 * If word is not needed, it is not expanded.
2910 *
2911 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
2912 * but also treat null var as if it is unset.
2913 */
2914 int use_word = (!val || ((exp_save == ':') && !val[0]));
2915 if (exp_op == '+')
2916 use_word = !use_word;
2917 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
2918 (exp_save == ':') ? "true" : "false", use_word);
2919 if (use_word) {
2920 to_be_freed = expand_pseudo_dquoted(exp_word);
2921 if (to_be_freed)
2922 exp_word = to_be_freed;
2923 if (exp_op == '?') {
2924 /* mimic bash message */
2925 die_if_script("%s: %s",
2926 var,
2927 exp_word[0] ? exp_word : "parameter null or not set"
2928 );
2929//TODO: how interactive bash aborts expansion mid-command?
2930 } else {
2931 val = exp_word;
2932 }
2933
2934 if (exp_op == '=') {
2935 /* ${var=[word]} or ${var:=[word]} */
2936 if (isdigit(var[0]) || var[0] == '#') {
2937 /* mimic bash message */
2938 die_if_script("$%s: cannot assign in this way", var);
2939 val = NULL;
2940 } else {
2941 char *new_var = xasprintf("%s=%s", var, val);
2942 set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
2943 }
2944 }
2945 }
2946 } /* one of "-=+?" */
2947
2948 *exp_saveptr = exp_save;
2949 } /* if (exp_op) */
2950
2951 arg[0] = first_ch;
2952#if ENABLE_HUSH_TICK
2953 store_val:
2954#endif
2955 if (!(first_ch & 0x80)) { /* unquoted $VAR */ 4959 if (!(first_ch & 0x80)) { /* unquoted $VAR */
2956 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape); 4960 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
2957 if (val) { 4961 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2958 /* unquoted var's contents should be globbed, so don't escape */ 4962 if (val && val[0]) {
2959 smallint sv = output->o_escape;
2960 output->o_escape = 0;
2961 n = expand_on_ifs(output, n, val); 4963 n = expand_on_ifs(output, n, val);
2962 val = NULL; 4964 val = NULL;
2963 output->o_escape = sv;
2964 } 4965 }
2965 } else { /* quoted $VAR, val will be appended below */ 4966 } else { /* quoted $VAR, val will be appended below */
2966 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape); 4967 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
4968 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2967 } 4969 }
2968 } /* default: */ 4970 break;
4971
2969 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ 4972 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
2970 4973
2971 if (val) { 4974 if (val && val[0]) {
2972 o_addQstr(output, val, strlen(val)); 4975 o_addQstr(output, val);
2973 } 4976 }
2974 free(to_be_freed); 4977 free(to_be_freed);
2975 /* Do the check to avoid writing to a const string */ 4978
4979 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
4980 * Do the check to avoid writing to a const string. */
2976 if (*p != SPECIAL_VAR_SYMBOL) 4981 if (*p != SPECIAL_VAR_SYMBOL)
2977 *p = SPECIAL_VAR_SYMBOL; 4982 *p = SPECIAL_VAR_SYMBOL;
2978 4983
@@ -2989,7 +4994,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2989 o_addstr_with_NUL(output, arg); 4994 o_addstr_with_NUL(output, arg);
2990 debug_print_list("expand_vars_to_list[b]", output, n); 4995 debug_print_list("expand_vars_to_list[b]", output, n);
2991 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ 4996 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
2992 && !(ored_ch & 0x80) /* and all vars were not quoted. */ 4997 && !(cant_be_null & 0x80) /* and all vars were not quoted. */
2993 ) { 4998 ) {
2994 n--; 4999 n--;
2995 /* allow to reuse list[n] later without re-growth */ 5000 /* allow to reuse list[n] later without re-growth */
@@ -2997,27 +5002,22 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2997 } else { 5002 } else {
2998 o_addchr(output, '\0'); 5003 o_addchr(output, '\0');
2999 } 5004 }
5005
3000 return n; 5006 return n;
3001} 5007}
3002 5008
3003static char **expand_variables(char **argv, int or_mask) 5009static char **expand_variables(char **argv, unsigned expflags)
3004{ 5010{
3005 int n; 5011 int n;
3006 char **list; 5012 char **list;
3007 char **v;
3008 o_string output = NULL_O_STRING; 5013 o_string output = NULL_O_STRING;
3009 5014
3010 if (or_mask & 0x100) { 5015 output.o_expflags = expflags;
3011 output.o_escape = 1; /* protect against globbing for "$var" */
3012 /* (unquoted $var will temporarily switch it off) */
3013 output.o_glob = 1;
3014 }
3015 5016
3016 n = 0; 5017 n = 0;
3017 v = argv; 5018 while (*argv) {
3018 while (*v) { 5019 n = expand_vars_to_list(&output, n, *argv);
3019 n = expand_vars_to_list(&output, n, *v, (unsigned char)or_mask); 5020 argv++;
3020 v++;
3021 } 5021 }
3022 debug_print_list("expand_variables", &output, n); 5022 debug_print_list("expand_variables", &output, n);
3023 5023
@@ -3029,68 +5029,54 @@ static char **expand_variables(char **argv, int or_mask)
3029 5029
3030static char **expand_strvec_to_strvec(char **argv) 5030static char **expand_strvec_to_strvec(char **argv)
3031{ 5031{
3032 return expand_variables(argv, 0x100); 5032 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
3033} 5033}
3034 5034
3035#if ENABLE_HUSH_BASH_COMPAT 5035#if ENABLE_HUSH_BASH_COMPAT
3036static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 5036static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
3037{ 5037{
3038 return expand_variables(argv, 0x80); 5038 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
3039} 5039}
3040#endif 5040#endif
3041 5041
3042#ifdef CMD_SINGLEWORD_NOGLOB_COND 5042/* Used for expansion of right hand of assignments,
3043static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv) 5043 * $((...)), heredocs, variable espansion parts.
5044 *
5045 * NB: should NOT do globbing!
5046 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
5047 */
5048static char *expand_string_to_string(const char *str, int do_unbackslash)
3044{ 5049{
3045 int n; 5050#if !ENABLE_HUSH_BASH_COMPAT
3046 char **list; 5051 const int do_unbackslash = 1;
3047 char **v;
3048 o_string output = NULL_O_STRING;
3049
3050 n = 0;
3051 v = argv;
3052 while (*v) {
3053 int is_var = is_well_formed_var_name(*v, '=');
3054 /* is_var * 0x80: singleword expansion for vars */
3055 n = expand_vars_to_list(&output, n, *v, is_var * 0x80);
3056
3057 /* Subtle! expand_vars_to_list did not glob last word yet.
3058 * It does this only when fed with further data.
3059 * Therefore we set globbing flags AFTER it, not before:
3060 */
3061
3062 /* if it is not recognizably abc=...; then: */
3063 output.o_escape = !is_var; /* protect against globbing for "$var" */
3064 /* (unquoted $var will temporarily switch it off) */
3065 output.o_glob = !is_var; /* and indeed do globbing */
3066 v++;
3067 }
3068 debug_print_list("expand_cond", &output, n);
3069
3070 /* output.data (malloced in one block) gets returned in "list" */
3071 list = o_finalize_list(&output, n);
3072 debug_print_strings("expand_cond[1]", list);
3073 return list;
3074}
3075#endif 5052#endif
3076
3077/* Used for expansion of right hand of assignments */
3078/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
3079 * "v=/bin/c*" */
3080static char *expand_string_to_string(const char *str)
3081{
3082 char *argv[2], **list; 5053 char *argv[2], **list;
3083 5054
5055 debug_printf_expand("string_to_string<='%s'\n", str);
5056 /* This is generally an optimization, but it also
5057 * handles "", which otherwise trips over !list[0] check below.
5058 * (is this ever happens that we actually get str="" here?)
5059 */
5060 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
5061 //TODO: Can use on strings with \ too, just unbackslash() them?
5062 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
5063 return xstrdup(str);
5064 }
5065
3084 argv[0] = (char*)str; 5066 argv[0] = (char*)str;
3085 argv[1] = NULL; 5067 argv[1] = NULL;
3086 list = expand_variables(argv, 0x80); /* 0x80: singleword expansion */ 5068 list = expand_variables(argv, do_unbackslash
5069 ? EXP_FLAG_ESC_GLOB_CHARS | EXP_FLAG_SINGLEWORD
5070 : EXP_FLAG_SINGLEWORD
5071 );
3087 if (HUSH_DEBUG) 5072 if (HUSH_DEBUG)
3088 if (!list[0] || list[1]) 5073 if (!list[0] || list[1])
3089 bb_error_msg_and_die("BUG in varexp2"); 5074 bb_error_msg_and_die("BUG in varexp2");
3090 /* actually, just move string 2*sizeof(char*) bytes back */ 5075 /* actually, just move string 2*sizeof(char*) bytes back */
3091 overlapping_strcpy((char*)list, list[0]); 5076 overlapping_strcpy((char*)list, list[0]);
3092 unbackslash((char*)list); 5077 if (do_unbackslash)
3093 debug_printf_expand("string_to_string='%s'\n", (char*)list); 5078 unbackslash((char*)list);
5079 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
3094 return (char*)list; 5080 return (char*)list;
3095} 5081}
3096 5082
@@ -3099,7 +5085,7 @@ static char* expand_strvec_to_string(char **argv)
3099{ 5085{
3100 char **list; 5086 char **list;
3101 5087
3102 list = expand_variables(argv, 0x80); 5088 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
3103 /* Convert all NULs to spaces */ 5089 /* Convert all NULs to spaces */
3104 if (list[0]) { 5090 if (list[0]) {
3105 int n = 1; 5091 int n = 1;
@@ -3125,7 +5111,7 @@ static char **expand_assignments(char **argv, int count)
3125 G.expanded_assignments = p = NULL; 5111 G.expanded_assignments = p = NULL;
3126 /* Expand assignments into one string each */ 5112 /* Expand assignments into one string each */
3127 for (i = 0; i < count; i++) { 5113 for (i = 0; i < count; i++) {
3128 G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i])); 5114 G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i], /*unbackslash:*/ 1));
3129 } 5115 }
3130 G.expanded_assignments = NULL; 5116 G.expanded_assignments = NULL;
3131 return p; 5117 return p;
@@ -3268,7 +5254,7 @@ static void re_execute_shell(char ***to_free, const char *s,
3268 *pp++ = (char *) G.argv0_for_re_execing; 5254 *pp++ = (char *) G.argv0_for_re_execing;
3269 *pp++ = param_buf; 5255 *pp++ = param_buf;
3270 for (cur = G.top_var; cur; cur = cur->next) { 5256 for (cur = G.top_var; cur; cur = cur->next) {
3271 if (cur->varstr == hush_version_str) 5257 if (strcmp(cur->varstr, hush_version_str) == 0)
3272 continue; 5258 continue;
3273 if (cur->flg_read_only) { 5259 if (cur->flg_read_only) {
3274 *pp++ = (char *) "-R"; 5260 *pp++ = (char *) "-R";
@@ -3332,6 +5318,194 @@ static void re_execute_shell(char ***to_free, const char *s,
3332#endif /* !BB_MMU */ 5318#endif /* !BB_MMU */
3333 5319
3334 5320
5321static int run_and_free_list(struct pipe *pi);
5322
5323/* Executing from string: eval, sh -c '...'
5324 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
5325 * end_trigger controls how often we stop parsing
5326 * NUL: parse all, execute, return
5327 * ';': parse till ';' or newline, execute, repeat till EOF
5328 */
5329static void parse_and_run_stream(struct in_str *inp, int end_trigger)
5330{
5331 /* Why we need empty flag?
5332 * An obscure corner case "false; ``; echo $?":
5333 * empty command in `` should still set $? to 0.
5334 * But we can't just set $? to 0 at the start,
5335 * this breaks "false; echo `echo $?`" case.
5336 */
5337 bool empty = 1;
5338 while (1) {
5339 struct pipe *pipe_list;
5340
5341 pipe_list = parse_stream(NULL, inp, end_trigger);
5342 if (!pipe_list) { /* EOF */
5343 if (empty)
5344 G.last_exitcode = 0;
5345 break;
5346 }
5347 debug_print_tree(pipe_list, 0);
5348 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
5349 run_and_free_list(pipe_list);
5350 empty = 0;
5351 }
5352}
5353
5354static void parse_and_run_string(const char *s)
5355{
5356 struct in_str input;
5357 setup_string_in_str(&input, s);
5358 parse_and_run_stream(&input, '\0');
5359}
5360
5361static void parse_and_run_file(FILE *f)
5362{
5363 struct in_str input;
5364 setup_file_in_str(&input, f);
5365 parse_and_run_stream(&input, ';');
5366}
5367
5368#if ENABLE_HUSH_TICK
5369static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5370{
5371 pid_t pid;
5372 int channel[2];
5373# if !BB_MMU
5374 char **to_free = NULL;
5375# endif
5376
5377 xpipe(channel);
5378 pid = BB_MMU ? xfork() : xvfork();
5379 if (pid == 0) { /* child */
5380 disable_restore_tty_pgrp_on_exit();
5381 /* Process substitution is not considered to be usual
5382 * 'command execution'.
5383 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
5384 */
5385 bb_signals(0
5386 + (1 << SIGTSTP)
5387 + (1 << SIGTTIN)
5388 + (1 << SIGTTOU)
5389 , SIG_IGN);
5390 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
5391 close(channel[0]); /* NB: close _first_, then move fd! */
5392 xmove_fd(channel[1], 1);
5393 /* Prevent it from trying to handle ctrl-z etc */
5394 IF_HUSH_JOB(G.run_list_level = 1;)
5395 /* Awful hack for `trap` or $(trap).
5396 *
5397 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5398 * contains an example where "trap" is executed in a subshell:
5399 *
5400 * save_traps=$(trap)
5401 * ...
5402 * eval "$save_traps"
5403 *
5404 * Standard does not say that "trap" in subshell shall print
5405 * parent shell's traps. It only says that its output
5406 * must have suitable form, but then, in the above example
5407 * (which is not supposed to be normative), it implies that.
5408 *
5409 * bash (and probably other shell) does implement it
5410 * (traps are reset to defaults, but "trap" still shows them),
5411 * but as a result, "trap" logic is hopelessly messed up:
5412 *
5413 * # trap
5414 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5415 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5416 * # true | trap <--- trap is in subshell - no output (ditto)
5417 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5418 * trap -- 'echo Ho' SIGWINCH
5419 * # echo `(trap)` <--- in subshell in subshell - output
5420 * trap -- 'echo Ho' SIGWINCH
5421 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5422 * trap -- 'echo Ho' SIGWINCH
5423 *
5424 * The rules when to forget and when to not forget traps
5425 * get really complex and nonsensical.
5426 *
5427 * Our solution: ONLY bare $(trap) or `trap` is special.
5428 */
5429 s = skip_whitespace(s);
5430 if (strncmp(s, "trap", 4) == 0
5431 && skip_whitespace(s + 4)[0] == '\0'
5432 ) {
5433 static const char *const argv[] = { NULL, NULL };
5434 builtin_trap((char**)argv);
5435 exit(0); /* not _exit() - we need to fflush */
5436 }
5437# if BB_MMU
5438 reset_traps_to_defaults();
5439 parse_and_run_string(s);
5440 _exit(G.last_exitcode);
5441# else
5442 /* We re-execute after vfork on NOMMU. This makes this script safe:
5443 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
5444 * huge=`cat BIG` # was blocking here forever
5445 * echo OK
5446 */
5447 re_execute_shell(&to_free,
5448 s,
5449 G.global_argv[0],
5450 G.global_argv + 1,
5451 NULL);
5452# endif
5453 }
5454
5455 /* parent */
5456 *pid_p = pid;
5457# if ENABLE_HUSH_FAST
5458 G.count_SIGCHLD++;
5459//bb_error_msg("[%d] fork in generate_stream_from_string:"
5460// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
5461// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
5462# endif
5463 enable_restore_tty_pgrp_on_exit();
5464# if !BB_MMU
5465 free(to_free);
5466# endif
5467 close(channel[1]);
5468 close_on_exec_on(channel[0]);
5469 return xfdopen_for_read(channel[0]);
5470}
5471
5472/* Return code is exit status of the process that is run. */
5473static int process_command_subs(o_string *dest, const char *s)
5474{
5475 FILE *fp;
5476 struct in_str pipe_str;
5477 pid_t pid;
5478 int status, ch, eol_cnt;
5479
5480 fp = generate_stream_from_string(s, &pid);
5481
5482 /* Now send results of command back into original context */
5483 setup_file_in_str(&pipe_str, fp);
5484 eol_cnt = 0;
5485 while ((ch = i_getch(&pipe_str)) != EOF) {
5486 if (ch == '\n') {
5487 eol_cnt++;
5488 continue;
5489 }
5490 while (eol_cnt) {
5491 o_addchr(dest, '\n');
5492 eol_cnt--;
5493 }
5494 o_addQchr(dest, ch);
5495 }
5496
5497 debug_printf("done reading from `cmd` pipe, closing it\n");
5498 fclose(fp);
5499 /* We need to extract exitcode. Test case
5500 * "true; echo `sleep 1; false` $?"
5501 * should print 1 */
5502 safe_waitpid(pid, &status, 0);
5503 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
5504 return WEXITSTATUS(status);
5505}
5506#endif /* ENABLE_HUSH_TICK */
5507
5508
3335static void setup_heredoc(struct redir_struct *redir) 5509static void setup_heredoc(struct redir_struct *redir)
3336{ 5510{
3337 struct fd_pair pair; 5511 struct fd_pair pair;
@@ -3346,7 +5520,7 @@ static void setup_heredoc(struct redir_struct *redir)
3346 5520
3347 expanded = NULL; 5521 expanded = NULL;
3348 if (!(redir->rd_dup & HEREDOC_QUOTED)) { 5522 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
3349 expanded = expand_pseudo_dquoted(heredoc); 5523 expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
3350 if (expanded) 5524 if (expanded)
3351 heredoc = expanded; 5525 heredoc = expanded;
3352 } 5526 }
@@ -3446,7 +5620,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
3446 continue; 5620 continue;
3447 } 5621 }
3448 mode = redir_table[redir->rd_type].mode; 5622 mode = redir_table[redir->rd_type].mode;
3449 p = expand_string_to_string(redir->rd_filename); 5623 p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1);
3450 openfd = open_or_warn(p, mode); 5624 openfd = open_or_warn(p, mode);
3451 free(p); 5625 free(p);
3452 if (openfd < 0) { 5626 if (openfd < 0) {
@@ -3491,101 +5665,6 @@ static void restore_redirects(int squirrel[])
3491 } 5665 }
3492} 5666}
3493 5667
3494
3495static void free_pipe_list(struct pipe *head);
3496
3497/* Return code is the exit status of the pipe */
3498static void free_pipe(struct pipe *pi)
3499{
3500 char **p;
3501 struct command *command;
3502 struct redir_struct *r, *rnext;
3503 int a, i;
3504
3505 if (pi->stopped_cmds > 0) /* why? */
3506 return;
3507 debug_printf_clean("run pipe: (pid %d)\n", getpid());
3508 for (i = 0; i < pi->num_cmds; i++) {
3509 command = &pi->cmds[i];
3510 debug_printf_clean(" command %d:\n", i);
3511 if (command->argv) {
3512 for (a = 0, p = command->argv; *p; a++, p++) {
3513 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3514 }
3515 free_strings(command->argv);
3516 command->argv = NULL;
3517 }
3518 /* not "else if": on syntax error, we may have both! */
3519 if (command->group) {
3520 debug_printf_clean(" begin group (cmd_type:%d)\n",
3521 command->cmd_type);
3522 free_pipe_list(command->group);
3523 debug_printf_clean(" end group\n");
3524 command->group = NULL;
3525 }
3526 /* else is crucial here.
3527 * If group != NULL, child_func is meaningless */
3528#if ENABLE_HUSH_FUNCTIONS
3529 else if (command->child_func) {
3530 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3531 command->child_func->parent_cmd = NULL;
3532 }
3533#endif
3534#if !BB_MMU
3535 free(command->group_as_string);
3536 command->group_as_string = NULL;
3537#endif
3538 for (r = command->redirects; r; r = rnext) {
3539 debug_printf_clean(" redirect %d%s",
3540 r->rd_fd, redir_table[r->rd_type].descrip);
3541 /* guard against the case >$FOO, where foo is unset or blank */
3542 if (r->rd_filename) {
3543 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3544 free(r->rd_filename);
3545 r->rd_filename = NULL;
3546 }
3547 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3548 rnext = r->next;
3549 free(r);
3550 }
3551 command->redirects = NULL;
3552 }
3553 free(pi->cmds); /* children are an array, they get freed all at once */
3554 pi->cmds = NULL;
3555#if ENABLE_HUSH_JOB
3556 free(pi->cmdtext);
3557 pi->cmdtext = NULL;
3558#endif
3559}
3560
3561static void free_pipe_list(struct pipe *head)
3562{
3563 struct pipe *pi, *next;
3564
3565 for (pi = head; pi; pi = next) {
3566#if HAS_KEYWORDS
3567 debug_printf_clean(" pipe reserved word %d\n", pi->res_word);
3568#endif
3569 free_pipe(pi);
3570 debug_printf_clean("pipe followup code %d\n", pi->followup);
3571 next = pi->next;
3572 /*pi->next = NULL;*/
3573 free(pi);
3574 }
3575}
3576
3577
3578static int run_list(struct pipe *pi);
3579#if BB_MMU
3580#define parse_stream(pstring, input, end_trigger) \
3581 parse_stream(input, end_trigger)
3582#endif
3583static struct pipe *parse_stream(char **pstring,
3584 struct in_str *input,
3585 int end_trigger);
3586static void parse_and_run_string(const char *s);
3587
3588
3589static char *find_in_path(const char *arg) 5668static char *find_in_path(const char *arg)
3590{ 5669{
3591 char *ret = NULL; 5670 char *ret = NULL;
@@ -3619,7 +5698,7 @@ static char *find_in_path(const char *arg)
3619 return ret; 5698 return ret;
3620} 5699}
3621 5700
3622static const struct built_in_command* find_builtin_helper(const char *name, 5701static const struct built_in_command *find_builtin_helper(const char *name,
3623 const struct built_in_command *x, 5702 const struct built_in_command *x,
3624 const struct built_in_command *end) 5703 const struct built_in_command *end)
3625{ 5704{
@@ -3633,11 +5712,11 @@ static const struct built_in_command* find_builtin_helper(const char *name,
3633 } 5712 }
3634 return NULL; 5713 return NULL;
3635} 5714}
3636static const struct built_in_command* find_builtin1(const char *name) 5715static const struct built_in_command *find_builtin1(const char *name)
3637{ 5716{
3638 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]); 5717 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
3639} 5718}
3640static const struct built_in_command* find_builtin(const char *name) 5719static const struct built_in_command *find_builtin(const char *name)
3641{ 5720{
3642 const struct built_in_command *x = find_builtin1(name); 5721 const struct built_in_command *x = find_builtin1(name);
3643 if (x) 5722 if (x)
@@ -4133,15 +6212,13 @@ static void remove_bg_job(struct pipe *pi)
4133static void delete_finished_bg_job(struct pipe *pi) 6212static void delete_finished_bg_job(struct pipe *pi)
4134{ 6213{
4135 remove_bg_job(pi); 6214 remove_bg_job(pi);
4136 pi->stopped_cmds = 0;
4137 free_pipe(pi); 6215 free_pipe(pi);
4138 free(pi);
4139} 6216}
4140#endif /* JOB */ 6217#endif /* JOB */
4141 6218
4142/* Check to see if any processes have exited -- if they 6219/* Check to see if any processes have exited -- if they
4143 * have, figure out why and see if a job has completed */ 6220 * have, figure out why and see if a job has completed */
4144static int checkjobs(struct pipe* fg_pipe) 6221static int checkjobs(struct pipe *fg_pipe)
4145{ 6222{
4146 int attributes; 6223 int attributes;
4147 int status; 6224 int status;
@@ -4307,7 +6384,7 @@ static int checkjobs(struct pipe* fg_pipe)
4307} 6384}
4308 6385
4309#if ENABLE_HUSH_JOB 6386#if ENABLE_HUSH_JOB
4310static int checkjobs_and_fg_shell(struct pipe* fg_pipe) 6387static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
4311{ 6388{
4312 pid_t p; 6389 pid_t p;
4313 int rcode = checkjobs(fg_pipe); 6390 int rcode = checkjobs(fg_pipe);
@@ -4351,7 +6428,11 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
4351#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \ 6428#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \
4352 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) 6429 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
4353#endif 6430#endif
4354static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded) 6431static int redirect_and_varexp_helper(char ***new_env_p,
6432 struct variable **old_vars_p,
6433 struct command *command,
6434 int squirrel[3],
6435 char **argv_expanded)
4355{ 6436{
4356 /* setup_redirects acts on file descriptors, not FILEs. 6437 /* setup_redirects acts on file descriptors, not FILEs.
4357 * This is perfect for work that comes after exec(). 6438 * This is perfect for work that comes after exec().
@@ -4384,6 +6465,13 @@ static NOINLINE int run_pipe(struct pipe *pi)
4384 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); 6465 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
4385 debug_enter(); 6466 debug_enter();
4386 6467
6468 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
6469 * Result should be 3 lines: q w e, qwe, q w e
6470 */
6471 G.ifs = get_local_var_value("IFS");
6472 if (!G.ifs)
6473 G.ifs = defifs;
6474
4387 IF_HUSH_JOB(pi->pgrp = -1;) 6475 IF_HUSH_JOB(pi->pgrp = -1;)
4388 pi->stopped_cmds = 0; 6476 pi->stopped_cmds = 0;
4389 command = &pi->cmds[0]; 6477 command = &pi->cmds[0];
@@ -4483,7 +6571,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4483 if (G_x_mode) 6571 if (G_x_mode)
4484 bb_putchar_stderr('+'); 6572 bb_putchar_stderr('+');
4485 while (*argv) { 6573 while (*argv) {
4486 char *p = expand_string_to_string(*argv); 6574 char *p = expand_string_to_string(*argv, /*unbackslash:*/ 1);
4487 if (G_x_mode) 6575 if (G_x_mode)
4488 fprintf(stderr, " %s", p); 6576 fprintf(stderr, " %s", p);
4489 debug_printf_exec("set shell var:'%s'->'%s'\n", 6577 debug_printf_exec("set shell var:'%s'->'%s'\n",
@@ -4515,11 +6603,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
4515 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 6603 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
4516 } 6604 }
4517#endif 6605#endif
4518#ifdef CMD_SINGLEWORD_NOGLOB_COND
4519 else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB_COND) {
4520 argv_expanded = expand_strvec_to_strvec_singleword_noglob_cond(argv + command->assignment_cnt);
4521 }
4522#endif
4523 else { 6606 else {
4524 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 6607 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
4525 } 6608 }
@@ -4835,7 +6918,7 @@ static int run_list(struct pipe *pi)
4835 enum { cond_code = 0 }; 6918 enum { cond_code = 0 };
4836#endif 6919#endif
4837#if HAS_KEYWORDS 6920#if HAS_KEYWORDS
4838 smallint rword; /* enum reserved_style */ 6921 smallint rword; /* RES_foo */
4839 smallint last_rword; /* ditto */ 6922 smallint last_rword; /* ditto */
4840#endif 6923#endif
4841 6924
@@ -4992,7 +7075,7 @@ static int run_list(struct pipe *pi)
4992 /* all prev words didn't match, does this one match? */ 7075 /* all prev words didn't match, does this one match? */
4993 argv = pi->cmds->argv; 7076 argv = pi->cmds->argv;
4994 while (*argv) { 7077 while (*argv) {
4995 char *pattern = expand_string_to_string(*argv); 7078 char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1);
4996 /* TODO: which FNM_xxx flags to use? */ 7079 /* TODO: which FNM_xxx flags to use? */
4997 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); 7080 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
4998 free(pattern); 7081 free(pattern);
@@ -5160,1930 +7243,6 @@ static int run_and_free_list(struct pipe *pi)
5160} 7243}
5161 7244
5162 7245
5163static struct pipe *new_pipe(void)
5164{
5165 struct pipe *pi;
5166 pi = xzalloc(sizeof(struct pipe));
5167 /*pi->followup = 0; - deliberately invalid value */
5168 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
5169 return pi;
5170}
5171
5172/* Command (member of a pipe) is complete, or we start a new pipe
5173 * if ctx->command is NULL.
5174 * No errors possible here.
5175 */
5176static int done_command(struct parse_context *ctx)
5177{
5178 /* The command is really already in the pipe structure, so
5179 * advance the pipe counter and make a new, null command. */
5180 struct pipe *pi = ctx->pipe;
5181 struct command *command = ctx->command;
5182
5183 if (command) {
5184 if (IS_NULL_CMD(command)) {
5185 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
5186 goto clear_and_ret;
5187 }
5188 pi->num_cmds++;
5189 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
5190 //debug_print_tree(ctx->list_head, 20);
5191 } else {
5192 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
5193 }
5194
5195 /* Only real trickiness here is that the uncommitted
5196 * command structure is not counted in pi->num_cmds. */
5197 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
5198 ctx->command = command = &pi->cmds[pi->num_cmds];
5199 clear_and_ret:
5200 memset(command, 0, sizeof(*command));
5201 return pi->num_cmds; /* used only for 0/nonzero check */
5202}
5203
5204static void done_pipe(struct parse_context *ctx, pipe_style type)
5205{
5206 int not_null;
5207
5208 debug_printf_parse("done_pipe entered, followup %d\n", type);
5209 /* Close previous command */
5210 not_null = done_command(ctx);
5211 ctx->pipe->followup = type;
5212#if HAS_KEYWORDS
5213 ctx->pipe->pi_inverted = ctx->ctx_inverted;
5214 ctx->ctx_inverted = 0;
5215 ctx->pipe->res_word = ctx->ctx_res_w;
5216#endif
5217
5218 /* Without this check, even just <enter> on command line generates
5219 * tree of three NOPs (!). Which is harmless but annoying.
5220 * IOW: it is safe to do it unconditionally. */
5221 if (not_null
5222#if ENABLE_HUSH_IF
5223 || ctx->ctx_res_w == RES_FI
5224#endif
5225#if ENABLE_HUSH_LOOPS
5226 || ctx->ctx_res_w == RES_DONE
5227 || ctx->ctx_res_w == RES_FOR
5228 || ctx->ctx_res_w == RES_IN
5229#endif
5230#if ENABLE_HUSH_CASE
5231 || ctx->ctx_res_w == RES_ESAC
5232#endif
5233 ) {
5234 struct pipe *new_p;
5235 debug_printf_parse("done_pipe: adding new pipe: "
5236 "not_null:%d ctx->ctx_res_w:%d\n",
5237 not_null, ctx->ctx_res_w);
5238 new_p = new_pipe();
5239 ctx->pipe->next = new_p;
5240 ctx->pipe = new_p;
5241 /* RES_THEN, RES_DO etc are "sticky" -
5242 * they remain set for pipes inside if/while.
5243 * This is used to control execution.
5244 * RES_FOR and RES_IN are NOT sticky (needed to support
5245 * cases where variable or value happens to match a keyword):
5246 */
5247#if ENABLE_HUSH_LOOPS
5248 if (ctx->ctx_res_w == RES_FOR
5249 || ctx->ctx_res_w == RES_IN)
5250 ctx->ctx_res_w = RES_NONE;
5251#endif
5252#if ENABLE_HUSH_CASE
5253 if (ctx->ctx_res_w == RES_MATCH)
5254 ctx->ctx_res_w = RES_CASE_BODY;
5255 if (ctx->ctx_res_w == RES_CASE)
5256 ctx->ctx_res_w = RES_CASE_IN;
5257#endif
5258 ctx->command = NULL; /* trick done_command below */
5259 /* Create the memory for command, roughly:
5260 * ctx->pipe->cmds = new struct command;
5261 * ctx->command = &ctx->pipe->cmds[0];
5262 */
5263 done_command(ctx);
5264 //debug_print_tree(ctx->list_head, 10);
5265 }
5266 debug_printf_parse("done_pipe return\n");
5267}
5268
5269static void initialize_context(struct parse_context *ctx)
5270{
5271 memset(ctx, 0, sizeof(*ctx));
5272 ctx->pipe = ctx->list_head = new_pipe();
5273 /* Create the memory for command, roughly:
5274 * ctx->pipe->cmds = new struct command;
5275 * ctx->command = &ctx->pipe->cmds[0];
5276 */
5277 done_command(ctx);
5278}
5279
5280/* If a reserved word is found and processed, parse context is modified
5281 * and 1 is returned.
5282 */
5283#if HAS_KEYWORDS
5284struct reserved_combo {
5285 char literal[6];
5286 unsigned char res;
5287 unsigned char assignment_flag;
5288 int flag;
5289};
5290enum {
5291 FLAG_END = (1 << RES_NONE ),
5292# if ENABLE_HUSH_IF
5293 FLAG_IF = (1 << RES_IF ),
5294 FLAG_THEN = (1 << RES_THEN ),
5295 FLAG_ELIF = (1 << RES_ELIF ),
5296 FLAG_ELSE = (1 << RES_ELSE ),
5297 FLAG_FI = (1 << RES_FI ),
5298# endif
5299# if ENABLE_HUSH_LOOPS
5300 FLAG_FOR = (1 << RES_FOR ),
5301 FLAG_WHILE = (1 << RES_WHILE),
5302 FLAG_UNTIL = (1 << RES_UNTIL),
5303 FLAG_DO = (1 << RES_DO ),
5304 FLAG_DONE = (1 << RES_DONE ),
5305 FLAG_IN = (1 << RES_IN ),
5306# endif
5307# if ENABLE_HUSH_CASE
5308 FLAG_MATCH = (1 << RES_MATCH),
5309 FLAG_ESAC = (1 << RES_ESAC ),
5310# endif
5311 FLAG_START = (1 << RES_XXXX ),
5312};
5313
5314static const struct reserved_combo* match_reserved_word(o_string *word)
5315{
5316 /* Mostly a list of accepted follow-up reserved words.
5317 * FLAG_END means we are done with the sequence, and are ready
5318 * to turn the compound list into a command.
5319 * FLAG_START means the word must start a new compound list.
5320 */
5321 static const struct reserved_combo reserved_list[] = {
5322# if ENABLE_HUSH_IF
5323 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
5324 { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
5325 { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
5326 { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN },
5327 { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI },
5328 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
5329# endif
5330# if ENABLE_HUSH_LOOPS
5331 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
5332 { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
5333 { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
5334 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
5335 { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE },
5336 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
5337# endif
5338# if ENABLE_HUSH_CASE
5339 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
5340 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
5341# endif
5342 };
5343 const struct reserved_combo *r;
5344
5345 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
5346 if (strcmp(word->data, r->literal) == 0)
5347 return r;
5348 }
5349 return NULL;
5350}
5351/* Return 0: not a keyword, 1: keyword
5352 */
5353static int reserved_word(o_string *word, struct parse_context *ctx)
5354{
5355# if ENABLE_HUSH_CASE
5356 static const struct reserved_combo reserved_match = {
5357 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
5358 };
5359# endif
5360 const struct reserved_combo *r;
5361
5362 if (word->o_quoted)
5363 return 0;
5364 r = match_reserved_word(word);
5365 if (!r)
5366 return 0;
5367
5368 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
5369# if ENABLE_HUSH_CASE
5370 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
5371 /* "case word IN ..." - IN part starts first MATCH part */
5372 r = &reserved_match;
5373 } else
5374# endif
5375 if (r->flag == 0) { /* '!' */
5376 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
5377 syntax_error("! ! command");
5378 ctx->ctx_res_w = RES_SNTX;
5379 }
5380 ctx->ctx_inverted = 1;
5381 return 1;
5382 }
5383 if (r->flag & FLAG_START) {
5384 struct parse_context *old;
5385
5386 old = xmalloc(sizeof(*old));
5387 debug_printf_parse("push stack %p\n", old);
5388 *old = *ctx; /* physical copy */
5389 initialize_context(ctx);
5390 ctx->stack = old;
5391 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
5392 syntax_error_at(word->data);
5393 ctx->ctx_res_w = RES_SNTX;
5394 return 1;
5395 } else {
5396 /* "{...} fi" is ok. "{...} if" is not
5397 * Example:
5398 * if { echo foo; } then { echo bar; } fi */
5399 if (ctx->command->group)
5400 done_pipe(ctx, PIPE_SEQ);
5401 }
5402
5403 ctx->ctx_res_w = r->res;
5404 ctx->old_flag = r->flag;
5405 word->o_assignment = r->assignment_flag;
5406
5407 if (ctx->old_flag & FLAG_END) {
5408 struct parse_context *old;
5409
5410 done_pipe(ctx, PIPE_SEQ);
5411 debug_printf_parse("pop stack %p\n", ctx->stack);
5412 old = ctx->stack;
5413 old->command->group = ctx->list_head;
5414 old->command->cmd_type = CMD_NORMAL;
5415# if !BB_MMU
5416 o_addstr(&old->as_string, ctx->as_string.data);
5417 o_free_unsafe(&ctx->as_string);
5418 old->command->group_as_string = xstrdup(old->as_string.data);
5419 debug_printf_parse("pop, remembering as:'%s'\n",
5420 old->command->group_as_string);
5421# endif
5422 *ctx = *old; /* physical copy */
5423 free(old);
5424 }
5425 return 1;
5426}
5427#endif /* HAS_KEYWORDS */
5428
5429/* Word is complete, look at it and update parsing context.
5430 * Normal return is 0. Syntax errors return 1.
5431 * Note: on return, word is reset, but not o_free'd!
5432 */
5433static int done_word(o_string *word, struct parse_context *ctx)
5434{
5435 struct command *command = ctx->command;
5436
5437 debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
5438 if (word->length == 0 && word->o_quoted == 0) {
5439 debug_printf_parse("done_word return 0: true null, ignored\n");
5440 return 0;
5441 }
5442
5443 if (ctx->pending_redirect) {
5444 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
5445 * only if run as "bash", not "sh" */
5446 /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
5447 * "2.7 Redirection
5448 * ...the word that follows the redirection operator
5449 * shall be subjected to tilde expansion, parameter expansion,
5450 * command substitution, arithmetic expansion, and quote
5451 * removal. Pathname expansion shall not be performed
5452 * on the word by a non-interactive shell; an interactive
5453 * shell may perform it, but shall do so only when
5454 * the expansion would result in one word."
5455 */
5456 ctx->pending_redirect->rd_filename = xstrdup(word->data);
5457 /* Cater for >\file case:
5458 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
5459 * Same with heredocs:
5460 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
5461 */
5462 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
5463 unbackslash(ctx->pending_redirect->rd_filename);
5464 /* Is it <<"HEREDOC"? */
5465 if (word->o_quoted) {
5466 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
5467 }
5468 }
5469 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
5470 ctx->pending_redirect = NULL;
5471 } else {
5472 /* If this word wasn't an assignment, next ones definitely
5473 * can't be assignments. Even if they look like ones. */
5474 if (word->o_assignment != DEFINITELY_ASSIGNMENT
5475 && word->o_assignment != WORD_IS_KEYWORD
5476 ) {
5477 word->o_assignment = NOT_ASSIGNMENT;
5478 } else {
5479 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
5480 command->assignment_cnt++;
5481 word->o_assignment = MAYBE_ASSIGNMENT;
5482 }
5483
5484#if HAS_KEYWORDS
5485# if ENABLE_HUSH_CASE
5486 if (ctx->ctx_dsemicolon
5487 && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
5488 ) {
5489 /* already done when ctx_dsemicolon was set to 1: */
5490 /* ctx->ctx_res_w = RES_MATCH; */
5491 ctx->ctx_dsemicolon = 0;
5492 } else
5493# endif
5494 if (!command->argv /* if it's the first word... */
5495# if ENABLE_HUSH_LOOPS
5496 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
5497 && ctx->ctx_res_w != RES_IN
5498# endif
5499# if ENABLE_HUSH_CASE
5500 && ctx->ctx_res_w != RES_CASE
5501# endif
5502 ) {
5503 debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
5504 if (reserved_word(word, ctx)) {
5505 o_reset_to_empty_unquoted(word);
5506 debug_printf_parse("done_word return %d\n",
5507 (ctx->ctx_res_w == RES_SNTX));
5508 return (ctx->ctx_res_w == RES_SNTX);
5509 }
5510# ifdef CMD_SINGLEWORD_NOGLOB_COND
5511 if (strcmp(word->data, "export") == 0
5512# if ENABLE_HUSH_LOCAL
5513 || strcmp(word->data, "local") == 0
5514# endif
5515 ) {
5516 command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND;
5517 } else
5518# endif
5519# if ENABLE_HUSH_BASH_COMPAT
5520 if (strcmp(word->data, "[[") == 0) {
5521 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
5522 }
5523 /* fall through */
5524# endif
5525 }
5526#endif
5527 if (command->group) {
5528 /* "{ echo foo; } echo bar" - bad */
5529 syntax_error_at(word->data);
5530 debug_printf_parse("done_word return 1: syntax error, "
5531 "groups and arglists don't mix\n");
5532 return 1;
5533 }
5534 if (word->o_quoted /* word had "xx" or 'xx' at least as part of it. */
5535 /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
5536 && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
5537 /* (otherwise it's known to be not empty and is already safe) */
5538 ) {
5539 /* exclude "$@" - it can expand to no word despite "" */
5540 char *p = word->data;
5541 while (p[0] == SPECIAL_VAR_SYMBOL
5542 && (p[1] & 0x7f) == '@'
5543 && p[2] == SPECIAL_VAR_SYMBOL
5544 ) {
5545 p += 3;
5546 }
5547 if (p == word->data || p[0] != '\0') {
5548 /* saw no "$@", or not only "$@" but some
5549 * real text is there too */
5550 /* insert "empty variable" reference, this makes
5551 * e.g. "", $empty"" etc to not disappear */
5552 o_addchr(word, SPECIAL_VAR_SYMBOL);
5553 o_addchr(word, SPECIAL_VAR_SYMBOL);
5554 }
5555 }
5556 command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
5557 debug_print_strings("word appended to argv", command->argv);
5558 }
5559
5560#if ENABLE_HUSH_LOOPS
5561 if (ctx->ctx_res_w == RES_FOR) {
5562 if (word->o_quoted
5563 || !is_well_formed_var_name(command->argv[0], '\0')
5564 ) {
5565 /* bash says just "not a valid identifier" */
5566 syntax_error("not a valid identifier in for");
5567 return 1;
5568 }
5569 /* Force FOR to have just one word (variable name) */
5570 /* NB: basically, this makes hush see "for v in ..."
5571 * syntax as if it is "for v; in ...". FOR and IN become
5572 * two pipe structs in parse tree. */
5573 done_pipe(ctx, PIPE_SEQ);
5574 }
5575#endif
5576#if ENABLE_HUSH_CASE
5577 /* Force CASE to have just one word */
5578 if (ctx->ctx_res_w == RES_CASE) {
5579 done_pipe(ctx, PIPE_SEQ);
5580 }
5581#endif
5582
5583 o_reset_to_empty_unquoted(word);
5584
5585 debug_printf_parse("done_word return 0\n");
5586 return 0;
5587}
5588
5589
5590/* Peek ahead in the input to find out if we have a "&n" construct,
5591 * as in "2>&1", that represents duplicating a file descriptor.
5592 * Return:
5593 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
5594 * REDIRFD_SYNTAX_ERR if syntax error,
5595 * REDIRFD_TO_FILE if no & was seen,
5596 * or the number found.
5597 */
5598#if BB_MMU
5599#define parse_redir_right_fd(as_string, input) \
5600 parse_redir_right_fd(input)
5601#endif
5602static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
5603{
5604 int ch, d, ok;
5605
5606 ch = i_peek(input);
5607 if (ch != '&')
5608 return REDIRFD_TO_FILE;
5609
5610 ch = i_getch(input); /* get the & */
5611 nommu_addchr(as_string, ch);
5612 ch = i_peek(input);
5613 if (ch == '-') {
5614 ch = i_getch(input);
5615 nommu_addchr(as_string, ch);
5616 return REDIRFD_CLOSE;
5617 }
5618 d = 0;
5619 ok = 0;
5620 while (ch != EOF && isdigit(ch)) {
5621 d = d*10 + (ch-'0');
5622 ok = 1;
5623 ch = i_getch(input);
5624 nommu_addchr(as_string, ch);
5625 ch = i_peek(input);
5626 }
5627 if (ok) return d;
5628
5629//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
5630
5631 bb_error_msg("ambiguous redirect");
5632 return REDIRFD_SYNTAX_ERR;
5633}
5634
5635/* Return code is 0 normal, 1 if a syntax error is detected
5636 */
5637static int parse_redirect(struct parse_context *ctx,
5638 int fd,
5639 redir_type style,
5640 struct in_str *input)
5641{
5642 struct command *command = ctx->command;
5643 struct redir_struct *redir;
5644 struct redir_struct **redirp;
5645 int dup_num;
5646
5647 dup_num = REDIRFD_TO_FILE;
5648 if (style != REDIRECT_HEREDOC) {
5649 /* Check for a '>&1' type redirect */
5650 dup_num = parse_redir_right_fd(&ctx->as_string, input);
5651 if (dup_num == REDIRFD_SYNTAX_ERR)
5652 return 1;
5653 } else {
5654 int ch = i_peek(input);
5655 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
5656 if (dup_num) { /* <<-... */
5657 ch = i_getch(input);
5658 nommu_addchr(&ctx->as_string, ch);
5659 ch = i_peek(input);
5660 }
5661 }
5662
5663 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
5664 int ch = i_peek(input);
5665 if (ch == '|') {
5666 /* >|FILE redirect ("clobbering" >).
5667 * Since we do not support "set -o noclobber" yet,
5668 * >| and > are the same for now. Just eat |.
5669 */
5670 ch = i_getch(input);
5671 nommu_addchr(&ctx->as_string, ch);
5672 }
5673 }
5674
5675 /* Create a new redir_struct and append it to the linked list */
5676 redirp = &command->redirects;
5677 while ((redir = *redirp) != NULL) {
5678 redirp = &(redir->next);
5679 }
5680 *redirp = redir = xzalloc(sizeof(*redir));
5681 /* redir->next = NULL; */
5682 /* redir->rd_filename = NULL; */
5683 redir->rd_type = style;
5684 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
5685
5686 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
5687 redir_table[style].descrip);
5688
5689 redir->rd_dup = dup_num;
5690 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
5691 /* Erik had a check here that the file descriptor in question
5692 * is legit; I postpone that to "run time"
5693 * A "-" representation of "close me" shows up as a -3 here */
5694 debug_printf_parse("duplicating redirect '%d>&%d'\n",
5695 redir->rd_fd, redir->rd_dup);
5696 } else {
5697 /* Set ctx->pending_redirect, so we know what to do at the
5698 * end of the next parsed word. */
5699 ctx->pending_redirect = redir;
5700 }
5701 return 0;
5702}
5703
5704/* If a redirect is immediately preceded by a number, that number is
5705 * supposed to tell which file descriptor to redirect. This routine
5706 * looks for such preceding numbers. In an ideal world this routine
5707 * needs to handle all the following classes of redirects...
5708 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
5709 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
5710 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
5711 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
5712 *
5713 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
5714 * "2.7 Redirection
5715 * ... If n is quoted, the number shall not be recognized as part of
5716 * the redirection expression. For example:
5717 * echo \2>a
5718 * writes the character 2 into file a"
5719 * We are getting it right by setting ->o_quoted on any \<char>
5720 *
5721 * A -1 return means no valid number was found,
5722 * the caller should use the appropriate default for this redirection.
5723 */
5724static int redirect_opt_num(o_string *o)
5725{
5726 int num;
5727
5728 if (o->data == NULL)
5729 return -1;
5730 num = bb_strtou(o->data, NULL, 10);
5731 if (errno || num < 0)
5732 return -1;
5733 o_reset_to_empty_unquoted(o);
5734 return num;
5735}
5736
5737#if BB_MMU
5738#define fetch_till_str(as_string, input, word, skip_tabs) \
5739 fetch_till_str(input, word, skip_tabs)
5740#endif
5741static char *fetch_till_str(o_string *as_string,
5742 struct in_str *input,
5743 const char *word,
5744 int skip_tabs)
5745{
5746 o_string heredoc = NULL_O_STRING;
5747 int past_EOL = 0;
5748 int ch;
5749
5750 goto jump_in;
5751 while (1) {
5752 ch = i_getch(input);
5753 nommu_addchr(as_string, ch);
5754 if (ch == '\n') {
5755 if (strcmp(heredoc.data + past_EOL, word) == 0) {
5756 heredoc.data[past_EOL] = '\0';
5757 debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
5758 return heredoc.data;
5759 }
5760 do {
5761 o_addchr(&heredoc, ch);
5762 past_EOL = heredoc.length;
5763 jump_in:
5764 do {
5765 ch = i_getch(input);
5766 nommu_addchr(as_string, ch);
5767 } while (skip_tabs && ch == '\t');
5768 } while (ch == '\n');
5769 }
5770 if (ch == EOF) {
5771 o_free_unsafe(&heredoc);
5772 return NULL;
5773 }
5774 o_addchr(&heredoc, ch);
5775 nommu_addchr(as_string, ch);
5776 }
5777}
5778
5779/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
5780 * and load them all. There should be exactly heredoc_cnt of them.
5781 */
5782static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
5783{
5784 struct pipe *pi = ctx->list_head;
5785
5786 while (pi && heredoc_cnt) {
5787 int i;
5788 struct command *cmd = pi->cmds;
5789
5790 debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
5791 pi->num_cmds,
5792 cmd->argv ? cmd->argv[0] : "NONE");
5793 for (i = 0; i < pi->num_cmds; i++) {
5794 struct redir_struct *redir = cmd->redirects;
5795
5796 debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
5797 i, cmd->argv ? cmd->argv[0] : "NONE");
5798 while (redir) {
5799 if (redir->rd_type == REDIRECT_HEREDOC) {
5800 char *p;
5801
5802 redir->rd_type = REDIRECT_HEREDOC2;
5803 /* redir->rd_dup is (ab)used to indicate <<- */
5804 p = fetch_till_str(&ctx->as_string, input,
5805 redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
5806 if (!p) {
5807 syntax_error("unexpected EOF in here document");
5808 return 1;
5809 }
5810 free(redir->rd_filename);
5811 redir->rd_filename = p;
5812 heredoc_cnt--;
5813 }
5814 redir = redir->next;
5815 }
5816 cmd++;
5817 }
5818 pi = pi->next;
5819 }
5820#if 0
5821 /* Should be 0. If it isn't, it's a parse error */
5822 if (heredoc_cnt)
5823 bb_error_msg_and_die("heredoc BUG 2");
5824#endif
5825 return 0;
5826}
5827
5828
5829#if ENABLE_HUSH_TICK
5830static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5831{
5832 pid_t pid;
5833 int channel[2];
5834# if !BB_MMU
5835 char **to_free = NULL;
5836# endif
5837
5838 xpipe(channel);
5839 pid = BB_MMU ? xfork() : xvfork();
5840 if (pid == 0) { /* child */
5841 disable_restore_tty_pgrp_on_exit();
5842 /* Process substitution is not considered to be usual
5843 * 'command execution'.
5844 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
5845 */
5846 bb_signals(0
5847 + (1 << SIGTSTP)
5848 + (1 << SIGTTIN)
5849 + (1 << SIGTTOU)
5850 , SIG_IGN);
5851 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
5852 close(channel[0]); /* NB: close _first_, then move fd! */
5853 xmove_fd(channel[1], 1);
5854 /* Prevent it from trying to handle ctrl-z etc */
5855 IF_HUSH_JOB(G.run_list_level = 1;)
5856 /* Awful hack for `trap` or $(trap).
5857 *
5858 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5859 * contains an example where "trap" is executed in a subshell:
5860 *
5861 * save_traps=$(trap)
5862 * ...
5863 * eval "$save_traps"
5864 *
5865 * Standard does not say that "trap" in subshell shall print
5866 * parent shell's traps. It only says that its output
5867 * must have suitable form, but then, in the above example
5868 * (which is not supposed to be normative), it implies that.
5869 *
5870 * bash (and probably other shell) does implement it
5871 * (traps are reset to defaults, but "trap" still shows them),
5872 * but as a result, "trap" logic is hopelessly messed up:
5873 *
5874 * # trap
5875 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5876 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5877 * # true | trap <--- trap is in subshell - no output (ditto)
5878 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5879 * trap -- 'echo Ho' SIGWINCH
5880 * # echo `(trap)` <--- in subshell in subshell - output
5881 * trap -- 'echo Ho' SIGWINCH
5882 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5883 * trap -- 'echo Ho' SIGWINCH
5884 *
5885 * The rules when to forget and when to not forget traps
5886 * get really complex and nonsensical.
5887 *
5888 * Our solution: ONLY bare $(trap) or `trap` is special.
5889 */
5890 s = skip_whitespace(s);
5891 if (strncmp(s, "trap", 4) == 0 && (*skip_whitespace(s + 4) == '\0'))
5892 {
5893 static const char *const argv[] = { NULL, NULL };
5894 builtin_trap((char**)argv);
5895 exit(0); /* not _exit() - we need to fflush */
5896 }
5897# if BB_MMU
5898 reset_traps_to_defaults();
5899 parse_and_run_string(s);
5900 _exit(G.last_exitcode);
5901# else
5902 /* We re-execute after vfork on NOMMU. This makes this script safe:
5903 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
5904 * huge=`cat BIG` # was blocking here forever
5905 * echo OK
5906 */
5907 re_execute_shell(&to_free,
5908 s,
5909 G.global_argv[0],
5910 G.global_argv + 1,
5911 NULL);
5912# endif
5913 }
5914
5915 /* parent */
5916 *pid_p = pid;
5917# if ENABLE_HUSH_FAST
5918 G.count_SIGCHLD++;
5919//bb_error_msg("[%d] fork in generate_stream_from_string: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
5920# endif
5921 enable_restore_tty_pgrp_on_exit();
5922# if !BB_MMU
5923 free(to_free);
5924# endif
5925 close(channel[1]);
5926 close_on_exec_on(channel[0]);
5927 return xfdopen_for_read(channel[0]);
5928}
5929
5930/* Return code is exit status of the process that is run. */
5931static int process_command_subs(o_string *dest, const char *s)
5932{
5933 FILE *fp;
5934 struct in_str pipe_str;
5935 pid_t pid;
5936 int status, ch, eol_cnt;
5937
5938 fp = generate_stream_from_string(s, &pid);
5939
5940 /* Now send results of command back into original context */
5941 setup_file_in_str(&pipe_str, fp);
5942 eol_cnt = 0;
5943 while ((ch = i_getch(&pipe_str)) != EOF) {
5944 if (ch == '\n') {
5945 eol_cnt++;
5946 continue;
5947 }
5948 while (eol_cnt) {
5949 o_addchr(dest, '\n');
5950 eol_cnt--;
5951 }
5952 o_addQchr(dest, ch);
5953 }
5954
5955 debug_printf("done reading from `cmd` pipe, closing it\n");
5956 fclose(fp);
5957 /* We need to extract exitcode. Test case
5958 * "true; echo `sleep 1; false` $?"
5959 * should print 1 */
5960 safe_waitpid(pid, &status, 0);
5961 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
5962 return WEXITSTATUS(status);
5963}
5964#endif /* ENABLE_HUSH_TICK */
5965
5966#if !ENABLE_HUSH_FUNCTIONS
5967#define parse_group(dest, ctx, input, ch) \
5968 parse_group(ctx, input, ch)
5969#endif
5970static int parse_group(o_string *dest, struct parse_context *ctx,
5971 struct in_str *input, int ch)
5972{
5973 /* dest contains characters seen prior to ( or {.
5974 * Typically it's empty, but for function defs,
5975 * it contains function name (without '()'). */
5976 struct pipe *pipe_list;
5977 int endch;
5978 struct command *command = ctx->command;
5979
5980 debug_printf_parse("parse_group entered\n");
5981#if ENABLE_HUSH_FUNCTIONS
5982 if (ch == '(' && !dest->o_quoted) {
5983 if (dest->length)
5984 if (done_word(dest, ctx))
5985 return 1;
5986 if (!command->argv)
5987 goto skip; /* (... */
5988 if (command->argv[1]) { /* word word ... (... */
5989 syntax_error_unexpected_ch('(');
5990 return 1;
5991 }
5992 /* it is "word(..." or "word (..." */
5993 do
5994 ch = i_getch(input);
5995 while (ch == ' ' || ch == '\t');
5996 if (ch != ')') {
5997 syntax_error_unexpected_ch(ch);
5998 return 1;
5999 }
6000 nommu_addchr(&ctx->as_string, ch);
6001 do
6002 ch = i_getch(input);
6003 while (ch == ' ' || ch == '\t' || ch == '\n');
6004 if (ch != '{') {
6005 syntax_error_unexpected_ch(ch);
6006 return 1;
6007 }
6008 nommu_addchr(&ctx->as_string, ch);
6009 command->cmd_type = CMD_FUNCDEF;
6010 goto skip;
6011 }
6012#endif
6013
6014#if 0 /* Prevented by caller */
6015 if (command->argv /* word [word]{... */
6016 || dest->length /* word{... */
6017 || dest->o_quoted /* ""{... */
6018 ) {
6019 syntax_error(NULL);
6020 debug_printf_parse("parse_group return 1: "
6021 "syntax error, groups and arglists don't mix\n");
6022 return 1;
6023 }
6024#endif
6025
6026#if ENABLE_HUSH_FUNCTIONS
6027 skip:
6028#endif
6029 endch = '}';
6030 if (ch == '(') {
6031 endch = ')';
6032 command->cmd_type = CMD_SUBSHELL;
6033 } else {
6034 /* bash does not allow "{echo...", requires whitespace */
6035 ch = i_getch(input);
6036 if (ch != ' ' && ch != '\t' && ch != '\n') {
6037 syntax_error_unexpected_ch(ch);
6038 return 1;
6039 }
6040 nommu_addchr(&ctx->as_string, ch);
6041 }
6042
6043 {
6044#if BB_MMU
6045# define as_string NULL
6046#else
6047 char *as_string = NULL;
6048#endif
6049 pipe_list = parse_stream(&as_string, input, endch);
6050#if !BB_MMU
6051 if (as_string)
6052 o_addstr(&ctx->as_string, as_string);
6053#endif
6054 /* empty ()/{} or parse error? */
6055 if (!pipe_list || pipe_list == ERR_PTR) {
6056 /* parse_stream already emitted error msg */
6057 if (!BB_MMU)
6058 free(as_string);
6059 debug_printf_parse("parse_group return 1: "
6060 "parse_stream returned %p\n", pipe_list);
6061 return 1;
6062 }
6063 command->group = pipe_list;
6064#if !BB_MMU
6065 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
6066 command->group_as_string = as_string;
6067 debug_printf_parse("end of group, remembering as:'%s'\n",
6068 command->group_as_string);
6069#endif
6070#undef as_string
6071 }
6072 debug_printf_parse("parse_group return 0\n");
6073 return 0;
6074 /* command remains "open", available for possible redirects */
6075}
6076
6077#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
6078/* Subroutines for copying $(...) and `...` things */
6079static void add_till_backquote(o_string *dest, struct in_str *input);
6080/* '...' */
6081static void add_till_single_quote(o_string *dest, struct in_str *input)
6082{
6083 while (1) {
6084 int ch = i_getch(input);
6085 if (ch == EOF) {
6086 syntax_error_unterm_ch('\'');
6087 /*xfunc_die(); - redundant */
6088 }
6089 if (ch == '\'')
6090 return;
6091 o_addchr(dest, ch);
6092 }
6093}
6094/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
6095static void add_till_double_quote(o_string *dest, struct in_str *input)
6096{
6097 while (1) {
6098 int ch = i_getch(input);
6099 if (ch == EOF) {
6100 syntax_error_unterm_ch('"');
6101 /*xfunc_die(); - redundant */
6102 }
6103 if (ch == '"')
6104 return;
6105 if (ch == '\\') { /* \x. Copy both chars. */
6106 o_addchr(dest, ch);
6107 ch = i_getch(input);
6108 }
6109 o_addchr(dest, ch);
6110 if (ch == '`') {
6111 add_till_backquote(dest, input);
6112 o_addchr(dest, ch);
6113 continue;
6114 }
6115 //if (ch == '$') ...
6116 }
6117}
6118/* Process `cmd` - copy contents until "`" is seen. Complicated by
6119 * \` quoting.
6120 * "Within the backquoted style of command substitution, backslash
6121 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
6122 * The search for the matching backquote shall be satisfied by the first
6123 * backquote found without a preceding backslash; during this search,
6124 * if a non-escaped backquote is encountered within a shell comment,
6125 * a here-document, an embedded command substitution of the $(command)
6126 * form, or a quoted string, undefined results occur. A single-quoted
6127 * or double-quoted string that begins, but does not end, within the
6128 * "`...`" sequence produces undefined results."
6129 * Example Output
6130 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
6131 */
6132static void add_till_backquote(o_string *dest, struct in_str *input)
6133{
6134 while (1) {
6135 int ch = i_getch(input);
6136 if (ch == EOF) {
6137 syntax_error_unterm_ch('`');
6138 /*xfunc_die(); - redundant */
6139 }
6140 if (ch == '`')
6141 return;
6142 if (ch == '\\') {
6143 /* \x. Copy both chars unless it is \` */
6144 int ch2 = i_getch(input);
6145 if (ch2 == EOF) {
6146 syntax_error_unterm_ch('`');
6147 /*xfunc_die(); - redundant */
6148 }
6149 if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
6150 o_addchr(dest, ch);
6151 ch = ch2;
6152 }
6153 o_addchr(dest, ch);
6154 }
6155}
6156/* Process $(cmd) - copy contents until ")" is seen. Complicated by
6157 * quoting and nested ()s.
6158 * "With the $(command) style of command substitution, all characters
6159 * following the open parenthesis to the matching closing parenthesis
6160 * constitute the command. Any valid shell script can be used for command,
6161 * except a script consisting solely of redirections which produces
6162 * unspecified results."
6163 * Example Output
6164 * echo $(echo '(TEST)' BEST) (TEST) BEST
6165 * echo $(echo 'TEST)' BEST) TEST) BEST
6166 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
6167 *
6168 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
6169 * can contain arbitrary constructs, just like $(cmd).
6170 * In bash compat mode, it needs to also be able to stop on '}' or ':'
6171 * for ${var:N[:M]} parsing.
6172 */
6173#define DOUBLE_CLOSE_CHAR_FLAG 0x80
6174static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
6175{
6176 int ch;
6177 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
6178# if ENABLE_HUSH_BASH_COMPAT
6179 char end_char2 = end_ch >> 8;
6180# endif
6181 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
6182
6183 while (1) {
6184 ch = i_getch(input);
6185 if (ch == EOF) {
6186 syntax_error_unterm_ch(end_ch);
6187 /*xfunc_die(); - redundant */
6188 }
6189 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
6190 if (!dbl)
6191 break;
6192 /* we look for closing )) of $((EXPR)) */
6193 if (i_peek(input) == end_ch) {
6194 i_getch(input); /* eat second ')' */
6195 break;
6196 }
6197 }
6198 o_addchr(dest, ch);
6199 if (ch == '(' || ch == '{') {
6200 ch = (ch == '(' ? ')' : '}');
6201 add_till_closing_bracket(dest, input, ch);
6202 o_addchr(dest, ch);
6203 continue;
6204 }
6205 if (ch == '\'') {
6206 add_till_single_quote(dest, input);
6207 o_addchr(dest, ch);
6208 continue;
6209 }
6210 if (ch == '"') {
6211 add_till_double_quote(dest, input);
6212 o_addchr(dest, ch);
6213 continue;
6214 }
6215 if (ch == '`') {
6216 add_till_backquote(dest, input);
6217 o_addchr(dest, ch);
6218 continue;
6219 }
6220 if (ch == '\\') {
6221 /* \x. Copy verbatim. Important for \(, \) */
6222 ch = i_getch(input);
6223 if (ch == EOF) {
6224 syntax_error_unterm_ch(')');
6225 /*xfunc_die(); - redundant */
6226 }
6227 o_addchr(dest, ch);
6228 continue;
6229 }
6230 }
6231 return ch;
6232}
6233#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
6234
6235/* Return code: 0 for OK, 1 for syntax error */
6236#if BB_MMU
6237#define parse_dollar(as_string, dest, input) \
6238 parse_dollar(dest, input)
6239#define as_string NULL
6240#endif
6241static int parse_dollar(o_string *as_string,
6242 o_string *dest,
6243 struct in_str *input)
6244{
6245 int ch = i_peek(input); /* first character after the $ */
6246 unsigned char quote_mask = dest->o_escape ? 0x80 : 0;
6247
6248 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
6249 if (isalpha(ch)) {
6250 ch = i_getch(input);
6251 nommu_addchr(as_string, ch);
6252 make_var:
6253 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6254 while (1) {
6255 debug_printf_parse(": '%c'\n", ch);
6256 o_addchr(dest, ch | quote_mask);
6257 quote_mask = 0;
6258 ch = i_peek(input);
6259 if (!isalnum(ch) && ch != '_')
6260 break;
6261 ch = i_getch(input);
6262 nommu_addchr(as_string, ch);
6263 }
6264 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6265 } else if (isdigit(ch)) {
6266 make_one_char_var:
6267 ch = i_getch(input);
6268 nommu_addchr(as_string, ch);
6269 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6270 debug_printf_parse(": '%c'\n", ch);
6271 o_addchr(dest, ch | quote_mask);
6272 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6273 } else switch (ch) {
6274 case '$': /* pid */
6275 case '!': /* last bg pid */
6276 case '?': /* last exit code */
6277 case '#': /* number of args */
6278 case '*': /* args */
6279 case '@': /* args */
6280 goto make_one_char_var;
6281 case '{': {
6282 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6283
6284 ch = i_getch(input); /* eat '{' */
6285 nommu_addchr(as_string, ch);
6286
6287 ch = i_getch(input); /* first char after '{' */
6288 nommu_addchr(as_string, ch);
6289 /* It should be ${?}, or ${#var},
6290 * or even ${?+subst} - operator acting on a special variable,
6291 * or the beginning of variable name.
6292 */
6293 if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */
6294 bad_dollar_syntax:
6295 syntax_error_unterm_str("${name}");
6296 debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
6297 return 1;
6298 }
6299 ch |= quote_mask;
6300
6301 /* It's possible to just call add_till_closing_bracket() at this point.
6302 * However, this regresses some of our testsuite cases
6303 * which check invalid constructs like ${%}.
6304 * Oh well... let's check that the var name part is fine... */
6305
6306 while (1) {
6307 unsigned pos;
6308
6309 o_addchr(dest, ch);
6310 debug_printf_parse(": '%c'\n", ch);
6311
6312 ch = i_getch(input);
6313 nommu_addchr(as_string, ch);
6314 if (ch == '}')
6315 break;
6316
6317 if (!isalnum(ch) && ch != '_') {
6318 unsigned end_ch;
6319 unsigned char last_ch;
6320 /* handle parameter expansions
6321 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
6322 */
6323 if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
6324 goto bad_dollar_syntax;
6325 o_addchr(dest, ch);
6326
6327 /* Eat everything until closing '}' (or ':') */
6328 end_ch = '}';
6329 if (ENABLE_HUSH_BASH_COMPAT
6330 && ch == ':'
6331 && !strchr("%#:-=+?"+3, i_peek(input))
6332 ) {
6333 /* It's ${var:N[:M]} thing */
6334 end_ch = '}' * 0x100 + ':';
6335 }
6336 again:
6337 if (!BB_MMU)
6338 pos = dest->length;
6339#if ENABLE_HUSH_DOLLAR_OPS
6340 last_ch = add_till_closing_bracket(dest, input, end_ch);
6341#else
6342#error Simple code to only allow ${var} is not implemented
6343#endif
6344 if (as_string) {
6345 o_addstr(as_string, dest->data + pos);
6346 o_addchr(as_string, last_ch);
6347 }
6348
6349 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
6350 /* close the first block: */
6351 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6352 /* while parsing N from ${var:N[:M]}... */
6353 if ((end_ch & 0xff) == last_ch) {
6354 /* ...got ':' - parse the rest */
6355 end_ch = '}';
6356 goto again;
6357 }
6358 /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */
6359 o_addstr(dest, "999999999");
6360 }
6361 break;
6362 }
6363 }
6364 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6365 break;
6366 }
6367#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
6368 case '(': {
6369 unsigned pos;
6370
6371 ch = i_getch(input);
6372 nommu_addchr(as_string, ch);
6373# if ENABLE_SH_MATH_SUPPORT
6374 if (i_peek(input) == '(') {
6375 ch = i_getch(input);
6376 nommu_addchr(as_string, ch);
6377 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6378 o_addchr(dest, /*quote_mask |*/ '+');
6379 if (!BB_MMU)
6380 pos = dest->length;
6381 add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
6382 if (as_string) {
6383 o_addstr(as_string, dest->data + pos);
6384 o_addchr(as_string, ')');
6385 o_addchr(as_string, ')');
6386 }
6387 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6388 break;
6389 }
6390# endif
6391# if ENABLE_HUSH_TICK
6392 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6393 o_addchr(dest, quote_mask | '`');
6394 if (!BB_MMU)
6395 pos = dest->length;
6396 add_till_closing_bracket(dest, input, ')');
6397 if (as_string) {
6398 o_addstr(as_string, dest->data + pos);
6399 o_addchr(as_string, ')');
6400 }
6401 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6402# endif
6403 break;
6404 }
6405#endif
6406 case '_':
6407 ch = i_getch(input);
6408 nommu_addchr(as_string, ch);
6409 ch = i_peek(input);
6410 if (isalnum(ch)) { /* it's $_name or $_123 */
6411 ch = '_';
6412 goto make_var;
6413 }
6414 /* else: it's $_ */
6415 /* TODO: $_ and $-: */
6416 /* $_ Shell or shell script name; or last argument of last command
6417 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
6418 * but in command's env, set to full pathname used to invoke it */
6419 /* $- Option flags set by set builtin or shell options (-i etc) */
6420 default:
6421 o_addQchr(dest, '$');
6422 }
6423 debug_printf_parse("parse_dollar return 0\n");
6424 return 0;
6425#undef as_string
6426}
6427
6428#if BB_MMU
6429#define parse_stream_dquoted(as_string, dest, input, dquote_end) \
6430 parse_stream_dquoted(dest, input, dquote_end)
6431#define as_string NULL
6432#endif
6433static int parse_stream_dquoted(o_string *as_string,
6434 o_string *dest,
6435 struct in_str *input,
6436 int dquote_end)
6437{
6438 int ch;
6439 int next;
6440
6441 again:
6442 ch = i_getch(input);
6443 if (ch != EOF)
6444 nommu_addchr(as_string, ch);
6445 if (ch == dquote_end) { /* may be only '"' or EOF */
6446 if (dest->o_assignment == NOT_ASSIGNMENT)
6447 dest->o_escape ^= 1;
6448 debug_printf_parse("parse_stream_dquoted return 0\n");
6449 return 0;
6450 }
6451 /* note: can't move it above ch == dquote_end check! */
6452 if (ch == EOF) {
6453 syntax_error_unterm_ch('"');
6454 /*xfunc_die(); - redundant */
6455 }
6456 next = '\0';
6457 if (ch != '\n') {
6458 next = i_peek(input);
6459 }
6460 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
6461 ch, ch, dest->o_escape);
6462 if (ch == '\\') {
6463 if (next == EOF) {
6464 syntax_error("\\<eof>");
6465 xfunc_die();
6466 }
6467 /* bash:
6468 * "The backslash retains its special meaning [in "..."]
6469 * only when followed by one of the following characters:
6470 * $, `, ", \, or <newline>. A double quote may be quoted
6471 * within double quotes by preceding it with a backslash."
6472 */
6473 if (strchr("$`\"\\\n", next) != NULL) {
6474 ch = i_getch(input);
6475 if (ch != '\n') {
6476 o_addqchr(dest, ch);
6477 nommu_addchr(as_string, ch);
6478 }
6479 } else {
6480 o_addqchr(dest, '\\');
6481 nommu_addchr(as_string, '\\');
6482 }
6483 goto again;
6484 }
6485 if (ch == '$') {
6486 if (parse_dollar(as_string, dest, input) != 0) {
6487 debug_printf_parse("parse_stream_dquoted return 1: "
6488 "parse_dollar returned non-0\n");
6489 return 1;
6490 }
6491 goto again;
6492 }
6493#if ENABLE_HUSH_TICK
6494 if (ch == '`') {
6495 //unsigned pos = dest->length;
6496 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6497 o_addchr(dest, 0x80 | '`');
6498 add_till_backquote(dest, input);
6499 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6500 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6501 goto again;
6502 }
6503#endif
6504 o_addQchr(dest, ch);
6505 if (ch == '='
6506 && (dest->o_assignment == MAYBE_ASSIGNMENT
6507 || dest->o_assignment == WORD_IS_KEYWORD)
6508 && is_well_formed_var_name(dest->data, '=')
6509 ) {
6510 dest->o_assignment = DEFINITELY_ASSIGNMENT;
6511 }
6512 goto again;
6513#undef as_string
6514}
6515
6516/*
6517 * Scan input until EOF or end_trigger char.
6518 * Return a list of pipes to execute, or NULL on EOF
6519 * or if end_trigger character is met.
6520 * On syntax error, exit is shell is not interactive,
6521 * reset parsing machinery and start parsing anew,
6522 * or return ERR_PTR.
6523 */
6524static struct pipe *parse_stream(char **pstring,
6525 struct in_str *input,
6526 int end_trigger)
6527{
6528 struct parse_context ctx;
6529 o_string dest = NULL_O_STRING;
6530 int is_in_dquote;
6531 int heredoc_cnt;
6532
6533 /* Double-quote state is handled in the state variable is_in_dquote.
6534 * A single-quote triggers a bypass of the main loop until its mate is
6535 * found. When recursing, quote state is passed in via dest->o_escape.
6536 */
6537 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
6538 end_trigger ? end_trigger : 'X');
6539 debug_enter();
6540
6541 /* If very first arg is "" or '', dest.data may end up NULL.
6542 * Preventing this: */
6543 o_addchr(&dest, '\0');
6544 dest.length = 0;
6545
6546 G.ifs = get_local_var_value("IFS");
6547 if (G.ifs == NULL)
6548 G.ifs = defifs;
6549
6550 reset:
6551#if ENABLE_HUSH_INTERACTIVE
6552 input->promptmode = 0; /* PS1 */
6553#endif
6554 /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */
6555 initialize_context(&ctx);
6556 is_in_dquote = 0;
6557 heredoc_cnt = 0;
6558 while (1) {
6559 const char *is_ifs;
6560 const char *is_special;
6561 int ch;
6562 int next;
6563 int redir_fd;
6564 redir_type redir_style;
6565
6566 if (is_in_dquote) {
6567 /* dest.o_quoted = 1; - already is (see below) */
6568 if (parse_stream_dquoted(&ctx.as_string, &dest, input, '"')) {
6569 goto parse_error;
6570 }
6571 /* We reached closing '"' */
6572 is_in_dquote = 0;
6573 }
6574 ch = i_getch(input);
6575 debug_printf_parse(": ch=%c (%d) escape=%d\n",
6576 ch, ch, dest.o_escape);
6577 if (ch == EOF) {
6578 struct pipe *pi;
6579
6580 if (heredoc_cnt) {
6581 syntax_error_unterm_str("here document");
6582 goto parse_error;
6583 }
6584 /* end_trigger == '}' case errors out earlier,
6585 * checking only ')' */
6586 if (end_trigger == ')') {
6587 syntax_error_unterm_ch('('); /* exits */
6588 /* goto parse_error; */
6589 }
6590
6591 if (done_word(&dest, &ctx)) {
6592 goto parse_error;
6593 }
6594 o_free(&dest);
6595 done_pipe(&ctx, PIPE_SEQ);
6596 pi = ctx.list_head;
6597 /* If we got nothing... */
6598 /* (this makes bare "&" cmd a no-op.
6599 * bash says: "syntax error near unexpected token '&'") */
6600 if (pi->num_cmds == 0
6601 IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
6602 ) {
6603 free_pipe_list(pi);
6604 pi = NULL;
6605 }
6606#if !BB_MMU
6607 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
6608 if (pstring)
6609 *pstring = ctx.as_string.data;
6610 else
6611 o_free_unsafe(&ctx.as_string);
6612#endif
6613 debug_leave();
6614 debug_printf_parse("parse_stream return %p\n", pi);
6615 return pi;
6616 }
6617 nommu_addchr(&ctx.as_string, ch);
6618
6619 next = '\0';
6620 if (ch != '\n')
6621 next = i_peek(input);
6622
6623 is_special = "{}<>;&|()#'" /* special outside of "str" */
6624 "\\$\"" IF_HUSH_TICK("`"); /* always special */
6625 /* Are { and } special here? */
6626 if (ctx.command->argv /* word [word]{... - non-special */
6627 || dest.length /* word{... - non-special */
6628 || dest.o_quoted /* ""{... - non-special */
6629 || (next != ';' /* }; - special */
6630 && next != ')' /* }) - special */
6631 && next != '&' /* }& and }&& ... - special */
6632 && next != '|' /* }|| ... - special */
6633 && !strchr(G.ifs, next) /* {word - non-special */
6634 )
6635 ) {
6636 /* They are not special, skip "{}" */
6637 is_special += 2;
6638 }
6639 is_special = strchr(is_special, ch);
6640 is_ifs = strchr(G.ifs, ch);
6641
6642 if (!is_special && !is_ifs) { /* ordinary char */
6643 ordinary_char:
6644 o_addQchr(&dest, ch);
6645 if ((dest.o_assignment == MAYBE_ASSIGNMENT
6646 || dest.o_assignment == WORD_IS_KEYWORD)
6647 && ch == '='
6648 && is_well_formed_var_name(dest.data, '=')
6649 ) {
6650 dest.o_assignment = DEFINITELY_ASSIGNMENT;
6651 }
6652 continue;
6653 }
6654
6655 if (is_ifs) {
6656 if (done_word(&dest, &ctx)) {
6657 goto parse_error;
6658 }
6659 if (ch == '\n') {
6660#if ENABLE_HUSH_CASE
6661 /* "case ... in <newline> word) ..." -
6662 * newlines are ignored (but ';' wouldn't be) */
6663 if (ctx.command->argv == NULL
6664 && ctx.ctx_res_w == RES_MATCH
6665 ) {
6666 continue;
6667 }
6668#endif
6669 /* Treat newline as a command separator. */
6670 done_pipe(&ctx, PIPE_SEQ);
6671 debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
6672 if (heredoc_cnt) {
6673 if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
6674 goto parse_error;
6675 }
6676 heredoc_cnt = 0;
6677 }
6678 dest.o_assignment = MAYBE_ASSIGNMENT;
6679 ch = ';';
6680 /* note: if (is_ifs) continue;
6681 * will still trigger for us */
6682 }
6683 }
6684
6685 /* "cmd}" or "cmd }..." without semicolon or &:
6686 * } is an ordinary char in this case, even inside { cmd; }
6687 * Pathological example: { ""}; } should exec "}" cmd
6688 */
6689 if (ch == '}') {
6690 if (!IS_NULL_CMD(ctx.command) /* cmd } */
6691 || dest.length != 0 /* word} */
6692 || dest.o_quoted /* ""} */
6693 ) {
6694 goto ordinary_char;
6695 }
6696 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6697 goto skip_end_trigger;
6698 /* else: } does terminate a group */
6699 }
6700
6701 if (end_trigger && end_trigger == ch
6702 && (ch != ';' || heredoc_cnt == 0)
6703#if ENABLE_HUSH_CASE
6704 && (ch != ')'
6705 || ctx.ctx_res_w != RES_MATCH
6706 || (!dest.o_quoted && strcmp(dest.data, "esac") == 0)
6707 )
6708#endif
6709 ) {
6710 if (heredoc_cnt) {
6711 /* This is technically valid:
6712 * { cat <<HERE; }; echo Ok
6713 * heredoc
6714 * heredoc
6715 * HERE
6716 * but we don't support this.
6717 * We require heredoc to be in enclosing {}/(),
6718 * if any.
6719 */
6720 syntax_error_unterm_str("here document");
6721 goto parse_error;
6722 }
6723 if (done_word(&dest, &ctx)) {
6724 goto parse_error;
6725 }
6726 done_pipe(&ctx, PIPE_SEQ);
6727 dest.o_assignment = MAYBE_ASSIGNMENT;
6728 /* Do we sit outside of any if's, loops or case's? */
6729 if (!HAS_KEYWORDS
6730 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6731 ) {
6732 o_free(&dest);
6733#if !BB_MMU
6734 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
6735 if (pstring)
6736 *pstring = ctx.as_string.data;
6737 else
6738 o_free_unsafe(&ctx.as_string);
6739#endif
6740 debug_leave();
6741 debug_printf_parse("parse_stream return %p: "
6742 "end_trigger char found\n",
6743 ctx.list_head);
6744 return ctx.list_head;
6745 }
6746 }
6747 skip_end_trigger:
6748 if (is_ifs)
6749 continue;
6750
6751 /* Catch <, > before deciding whether this word is
6752 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6753 switch (ch) {
6754 case '>':
6755 redir_fd = redirect_opt_num(&dest);
6756 if (done_word(&dest, &ctx)) {
6757 goto parse_error;
6758 }
6759 redir_style = REDIRECT_OVERWRITE;
6760 if (next == '>') {
6761 redir_style = REDIRECT_APPEND;
6762 ch = i_getch(input);
6763 nommu_addchr(&ctx.as_string, ch);
6764 }
6765#if 0
6766 else if (next == '(') {
6767 syntax_error(">(process) not supported");
6768 goto parse_error;
6769 }
6770#endif
6771 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6772 goto parse_error;
6773 continue; /* back to top of while (1) */
6774 case '<':
6775 redir_fd = redirect_opt_num(&dest);
6776 if (done_word(&dest, &ctx)) {
6777 goto parse_error;
6778 }
6779 redir_style = REDIRECT_INPUT;
6780 if (next == '<') {
6781 redir_style = REDIRECT_HEREDOC;
6782 heredoc_cnt++;
6783 debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
6784 ch = i_getch(input);
6785 nommu_addchr(&ctx.as_string, ch);
6786 } else if (next == '>') {
6787 redir_style = REDIRECT_IO;
6788 ch = i_getch(input);
6789 nommu_addchr(&ctx.as_string, ch);
6790 }
6791#if 0
6792 else if (next == '(') {
6793 syntax_error("<(process) not supported");
6794 goto parse_error;
6795 }
6796#endif
6797 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6798 goto parse_error;
6799 continue; /* back to top of while (1) */
6800 }
6801
6802 if (dest.o_assignment == MAYBE_ASSIGNMENT
6803 /* check that we are not in word in "a=1 2>word b=1": */
6804 && !ctx.pending_redirect
6805 ) {
6806 /* ch is a special char and thus this word
6807 * cannot be an assignment */
6808 dest.o_assignment = NOT_ASSIGNMENT;
6809 }
6810
6811 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6812
6813 switch (ch) {
6814 case '#':
6815 if (dest.length == 0) {
6816 while (1) {
6817 ch = i_peek(input);
6818 if (ch == EOF || ch == '\n')
6819 break;
6820 i_getch(input);
6821 /* note: we do not add it to &ctx.as_string */
6822 }
6823 nommu_addchr(&ctx.as_string, '\n');
6824 } else {
6825 o_addQchr(&dest, ch);
6826 }
6827 break;
6828 case '\\':
6829 if (next == EOF) {
6830 syntax_error("\\<eof>");
6831 xfunc_die();
6832 }
6833 ch = i_getch(input);
6834 if (ch != '\n') {
6835 o_addchr(&dest, '\\');
6836 /*nommu_addchr(&ctx.as_string, '\\'); - already done */
6837 o_addchr(&dest, ch);
6838 nommu_addchr(&ctx.as_string, ch);
6839 /* Example: echo Hello \2>file
6840 * we need to know that word 2 is quoted */
6841 dest.o_quoted = 1;
6842 }
6843#if !BB_MMU
6844 else {
6845 /* It's "\<newline>". Remove trailing '\' from ctx.as_string */
6846 ctx.as_string.data[--ctx.as_string.length] = '\0';
6847 }
6848#endif
6849 break;
6850 case '$':
6851 if (parse_dollar(&ctx.as_string, &dest, input) != 0) {
6852 debug_printf_parse("parse_stream parse error: "
6853 "parse_dollar returned non-0\n");
6854 goto parse_error;
6855 }
6856 break;
6857 case '\'':
6858 dest.o_quoted = 1;
6859 while (1) {
6860 ch = i_getch(input);
6861 if (ch == EOF) {
6862 syntax_error_unterm_ch('\'');
6863 /*xfunc_die(); - redundant */
6864 }
6865 nommu_addchr(&ctx.as_string, ch);
6866 if (ch == '\'')
6867 break;
6868 o_addqchr(&dest, ch);
6869 }
6870 break;
6871 case '"':
6872 dest.o_quoted = 1;
6873 is_in_dquote ^= 1; /* invert */
6874 if (dest.o_assignment == NOT_ASSIGNMENT)
6875 dest.o_escape ^= 1;
6876 break;
6877#if ENABLE_HUSH_TICK
6878 case '`': {
6879 unsigned pos;
6880
6881 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6882 o_addchr(&dest, '`');
6883 pos = dest.length;
6884 add_till_backquote(&dest, input);
6885# if !BB_MMU
6886 o_addstr(&ctx.as_string, dest.data + pos);
6887 o_addchr(&ctx.as_string, '`');
6888# endif
6889 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6890 //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
6891 break;
6892 }
6893#endif
6894 case ';':
6895#if ENABLE_HUSH_CASE
6896 case_semi:
6897#endif
6898 if (done_word(&dest, &ctx)) {
6899 goto parse_error;
6900 }
6901 done_pipe(&ctx, PIPE_SEQ);
6902#if ENABLE_HUSH_CASE
6903 /* Eat multiple semicolons, detect
6904 * whether it means something special */
6905 while (1) {
6906 ch = i_peek(input);
6907 if (ch != ';')
6908 break;
6909 ch = i_getch(input);
6910 nommu_addchr(&ctx.as_string, ch);
6911 if (ctx.ctx_res_w == RES_CASE_BODY) {
6912 ctx.ctx_dsemicolon = 1;
6913 ctx.ctx_res_w = RES_MATCH;
6914 break;
6915 }
6916 }
6917#endif
6918 new_cmd:
6919 /* We just finished a cmd. New one may start
6920 * with an assignment */
6921 dest.o_assignment = MAYBE_ASSIGNMENT;
6922 break;
6923 case '&':
6924 if (done_word(&dest, &ctx)) {
6925 goto parse_error;
6926 }
6927 if (next == '&') {
6928 ch = i_getch(input);
6929 nommu_addchr(&ctx.as_string, ch);
6930 done_pipe(&ctx, PIPE_AND);
6931 } else {
6932 done_pipe(&ctx, PIPE_BG);
6933 }
6934 goto new_cmd;
6935 case '|':
6936 if (done_word(&dest, &ctx)) {
6937 goto parse_error;
6938 }
6939#if ENABLE_HUSH_CASE
6940 if (ctx.ctx_res_w == RES_MATCH)
6941 break; /* we are in case's "word | word)" */
6942#endif
6943 if (next == '|') { /* || */
6944 ch = i_getch(input);
6945 nommu_addchr(&ctx.as_string, ch);
6946 done_pipe(&ctx, PIPE_OR);
6947 } else {
6948 /* we could pick up a file descriptor choice here
6949 * with redirect_opt_num(), but bash doesn't do it.
6950 * "echo foo 2| cat" yields "foo 2". */
6951 done_command(&ctx);
6952#if !BB_MMU
6953 o_reset_to_empty_unquoted(&ctx.as_string);
6954#endif
6955 }
6956 goto new_cmd;
6957 case '(':
6958#if ENABLE_HUSH_CASE
6959 /* "case... in [(]word)..." - skip '(' */
6960 if (ctx.ctx_res_w == RES_MATCH
6961 && ctx.command->argv == NULL /* not (word|(... */
6962 && dest.length == 0 /* not word(... */
6963 && dest.o_quoted == 0 /* not ""(... */
6964 ) {
6965 continue;
6966 }
6967#endif
6968 case '{':
6969 if (parse_group(&dest, &ctx, input, ch) != 0) {
6970 goto parse_error;
6971 }
6972 goto new_cmd;
6973 case ')':
6974#if ENABLE_HUSH_CASE
6975 if (ctx.ctx_res_w == RES_MATCH)
6976 goto case_semi;
6977#endif
6978 case '}':
6979 /* proper use of this character is caught by end_trigger:
6980 * if we see {, we call parse_group(..., end_trigger='}')
6981 * and it will match } earlier (not here). */
6982 syntax_error_unexpected_ch(ch);
6983 goto parse_error;
6984 default:
6985 if (HUSH_DEBUG)
6986 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
6987 }
6988 } /* while (1) */
6989
6990 parse_error:
6991 {
6992 struct parse_context *pctx;
6993 IF_HAS_KEYWORDS(struct parse_context *p2;)
6994
6995 /* Clean up allocated tree.
6996 * Sample for finding leaks on syntax error recovery path.
6997 * Run it from interactive shell, watch pmap `pidof hush`.
6998 * while if false; then false; fi; do break; fi
6999 * Samples to catch leaks at execution:
7000 * while if (true | {true;}); then echo ok; fi; do break; done
7001 * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
7002 */
7003 pctx = &ctx;
7004 do {
7005 /* Update pipe/command counts,
7006 * otherwise freeing may miss some */
7007 done_pipe(pctx, PIPE_SEQ);
7008 debug_printf_clean("freeing list %p from ctx %p\n",
7009 pctx->list_head, pctx);
7010 debug_print_tree(pctx->list_head, 0);
7011 free_pipe_list(pctx->list_head);
7012 debug_printf_clean("freed list %p\n", pctx->list_head);
7013#if !BB_MMU
7014 o_free_unsafe(&pctx->as_string);
7015#endif
7016 IF_HAS_KEYWORDS(p2 = pctx->stack;)
7017 if (pctx != &ctx) {
7018 free(pctx);
7019 }
7020 IF_HAS_KEYWORDS(pctx = p2;)
7021 } while (HAS_KEYWORDS && pctx);
7022 /* Free text, clear all dest fields */
7023 o_free(&dest);
7024 /* If we are not in top-level parse, we return,
7025 * our caller will propagate error.
7026 */
7027 if (end_trigger != ';') {
7028#if !BB_MMU
7029 if (pstring)
7030 *pstring = NULL;
7031#endif
7032 debug_leave();
7033 return ERR_PTR;
7034 }
7035 /* Discard cached input, force prompt */
7036 input->p = NULL;
7037 IF_HUSH_INTERACTIVE(input->promptme = 1;)
7038 goto reset;
7039 }
7040}
7041
7042/* Executing from string: eval, sh -c '...'
7043 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
7044 * end_trigger controls how often we stop parsing
7045 * NUL: parse all, execute, return
7046 * ';': parse till ';' or newline, execute, repeat till EOF
7047 */
7048static void parse_and_run_stream(struct in_str *inp, int end_trigger)
7049{
7050 /* Why we need empty flag?
7051 * An obscure corner case "false; ``; echo $?":
7052 * empty command in `` should still set $? to 0.
7053 * But we can't just set $? to 0 at the start,
7054 * this breaks "false; echo `echo $?`" case.
7055 */
7056 bool empty = 1;
7057 while (1) {
7058 struct pipe *pipe_list;
7059
7060 pipe_list = parse_stream(NULL, inp, end_trigger);
7061 if (!pipe_list) { /* EOF */
7062 if (empty)
7063 G.last_exitcode = 0;
7064 break;
7065 }
7066 debug_print_tree(pipe_list, 0);
7067 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
7068 run_and_free_list(pipe_list);
7069 empty = 0;
7070 }
7071}
7072
7073static void parse_and_run_string(const char *s)
7074{
7075 struct in_str input;
7076 setup_string_in_str(&input, s);
7077 parse_and_run_stream(&input, '\0');
7078}
7079
7080static void parse_and_run_file(FILE *f)
7081{
7082 struct in_str input;
7083 setup_file_in_str(&input, f);
7084 parse_and_run_stream(&input, ';');
7085}
7086
7087/* Called a few times only (or even once if "sh -c") */ 7246/* Called a few times only (or even once if "sh -c") */
7088static void init_sigmasks(void) 7247static void init_sigmasks(void)
7089{ 7248{
@@ -7183,13 +7342,6 @@ static int set_mode(const char cstate, const char mode)
7183int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 7342int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7184int hush_main(int argc, char **argv) 7343int hush_main(int argc, char **argv)
7185{ 7344{
7186 static const struct variable const_shell_ver = {
7187 .next = NULL,
7188 .varstr = (char*)hush_version_str,
7189 .max_len = 1, /* 0 can provoke free(name) */
7190 .flg_export = 1,
7191 .flg_read_only = 1,
7192 };
7193 int opt; 7345 int opt;
7194 unsigned builtin_argc; 7346 unsigned builtin_argc;
7195 char **e; 7347 char **e;
@@ -7202,12 +7354,16 @@ int hush_main(int argc, char **argv)
7202 G.argv0_for_re_execing = argv[0]; 7354 G.argv0_for_re_execing = argv[0];
7203#endif 7355#endif
7204 /* Deal with HUSH_VERSION */ 7356 /* Deal with HUSH_VERSION */
7205 G.shell_ver = const_shell_ver; /* copying struct here */ 7357 G.shell_ver.flg_export = 1;
7358 G.shell_ver.flg_read_only = 1;
7359 /* Code which handles ${var<op>...} needs writable values for all variables,
7360 * therefore we xstrdup: */
7361 G.shell_ver.varstr = xstrdup(hush_version_str),
7206 G.top_var = &G.shell_ver; 7362 G.top_var = &G.shell_ver;
7363 /* Create shell local variables from the values
7364 * currently living in the environment */
7207 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 7365 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
7208 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ 7366 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
7209 /* Initialize our shell local variables with the values
7210 * currently living in the environment */
7211 cur_var = G.top_var; 7367 cur_var = G.top_var;
7212 e = environ; 7368 e = environ;
7213 if (e) while (*e) { 7369 if (e) while (*e) {
@@ -7221,9 +7377,9 @@ int hush_main(int argc, char **argv)
7221 } 7377 }
7222 e++; 7378 e++;
7223 } 7379 }
7224 /* reinstate HUSH_VERSION */ 7380 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
7225 debug_printf_env("putenv '%s'\n", hush_version_str); 7381 debug_printf_env("putenv '%s'\n", G.shell_ver.varstr);
7226 putenv((char *)hush_version_str); 7382 putenv(G.shell_ver.varstr);
7227 7383
7228 /* Export PWD */ 7384 /* Export PWD */
7229 set_pwd_var(/*exp:*/ 1); 7385 set_pwd_var(/*exp:*/ 1);
@@ -7266,7 +7422,20 @@ int hush_main(int argc, char **argv)
7266 7422
7267#if ENABLE_FEATURE_EDITING 7423#if ENABLE_FEATURE_EDITING
7268 G.line_input_state = new_line_input_t(FOR_SHELL); 7424 G.line_input_state = new_line_input_t(FOR_SHELL);
7425# if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_HUSH_SAVEHISTORY
7426 {
7427 const char *hp = get_local_var_value("HISTFILE");
7428 if (!hp) {
7429 hp = get_local_var_value("HOME");
7430 if (hp) {
7431 G.line_input_state->hist_file = concat_path_file(hp, ".hush_history");
7432 //set_local_var(xasprintf("HISTFILE=%s", ...));
7433 }
7434 }
7435 }
7436# endif
7269#endif 7437#endif
7438
7270 G.global_argc = argc; 7439 G.global_argc = argc;
7271 G.global_argv = argv; 7440 G.global_argv = argv;
7272 /* Initialize some more globals to non-zero values */ 7441 /* Initialize some more globals to non-zero values */
@@ -7586,15 +7755,6 @@ int hush_main(int argc, char **argv)
7586} 7755}
7587 7756
7588 7757
7589#if ENABLE_LASH
7590int lash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7591int lash_main(int argc, char **argv)
7592{
7593 bb_error_msg("lash is deprecated, please use hush instead");
7594 return hush_main(argc, argv);
7595}
7596#endif
7597
7598#if ENABLE_MSH 7758#if ENABLE_MSH
7599int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 7759int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7600int msh_main(int argc, char **argv) 7760int msh_main(int argc, char **argv)
@@ -7725,7 +7885,7 @@ static int FAST_FUNC builtin_exit(char **argv)
7725 * exit 7885 * exit
7726 # EEE (then bash exits) 7886 # EEE (then bash exits)
7727 * 7887 *
7728 * we can use G.exiting = -1 as indicator "last cmd was exit" 7888 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
7729 */ 7889 */
7730 7890
7731 /* note: EXIT trap is run by hush_exit */ 7891 /* note: EXIT trap is run by hush_exit */
@@ -7765,13 +7925,16 @@ static void helper_export_local(char **argv, int exp, int lvl)
7765{ 7925{
7766 do { 7926 do {
7767 char *name = *argv; 7927 char *name = *argv;
7928 char *name_end = strchrnul(name, '=');
7768 7929
7769 /* So far we do not check that name is valid (TODO?) */ 7930 /* So far we do not check that name is valid (TODO?) */
7770 7931
7771 if (strchr(name, '=') == NULL) { 7932 if (*name_end == '\0') {
7772 struct variable *var; 7933 struct variable *var, **vpp;
7934
7935 vpp = get_ptr_to_local_var(name, name_end - name);
7936 var = vpp ? *vpp : NULL;
7773 7937
7774 var = get_local_var(name);
7775 if (exp == -1) { /* unexporting? */ 7938 if (exp == -1) { /* unexporting? */
7776 /* export -n NAME (without =VALUE) */ 7939 /* export -n NAME (without =VALUE) */
7777 if (var) { 7940 if (var) {
diff --git a/shell/hush_test/hush-bugs/export_exp.tests b/shell/hush_test/hush-bugs/export_exp.tests.disabled
index 91f57aa2c..0913fd3f2 100755..100644
--- a/shell/hush_test/hush-bugs/export_exp.tests
+++ b/shell/hush_test/hush-bugs/export_exp.tests.disabled
@@ -1,3 +1,6 @@
1# This test shows a very special handling of export and local
2# builtins by bash.
3
1v="a=aa0 b=bb0" 4v="a=aa0 b=bb0"
2# only 1st arg should be expanded in multiple words 5# only 1st arg should be expanded in multiple words
3export $v c=$v 6export $v c=$v
diff --git a/shell/hush_test/hush-glob/glob2.right b/shell/hush_test/hush-glob/glob2.right
new file mode 100644
index 000000000..7a70c2263
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob2.right
@@ -0,0 +1,18 @@
1Expected Actual
2Z\* : Z\*
3Z* : Z*
4Z\f : Z\f
5Z\* : Z\*
6
7Z\z : Z\z
8Zz : Zz
9Z\z : Z\z
10Z\z : Z\z
11
12Z\ : Z\
13Z\ : Z\
14
15Z\f Zf : Z\f Zf
16Z\f Zf : Z\f Zf
17
18Done: 0
diff --git a/shell/hush_test/hush-glob/glob2.tests b/shell/hush_test/hush-glob/glob2.tests
new file mode 100755
index 000000000..4dbc92599
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob2.tests
@@ -0,0 +1,27 @@
1# This test demonstrates that in unquoted $v, backslashes expand by this rule:
2# \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not),
3# and subsequently globbing converts \\ to \ and treats \z as literal z
4# even if it is a special char.
5
6>'Zf'
7>'Z\f'
8 echo 'Expected' 'Actual'
9v='\*'; echo 'Z\* :' Z$v
10 echo 'Z* :' Z\*
11 echo 'Z\f :' Z\\*
12 echo 'Z\* :' Z\\\* # NB! only this matches Z$v output
13echo
14v='\z'; echo 'Z\z :' Z$v
15 echo 'Zz :' Z\z
16 echo 'Z\z :' Z\\z
17 echo 'Z\z :' Z\\\z
18echo
19v='\'; echo 'Z\ :' Z$v
20 echo 'Z\ :' Z\\
21echo
22v='*'; echo 'Z\f Zf :' Z$v
23 echo 'Z\f Zf :' Z*
24echo
25
26rm 'Z\f' 'Zf'
27echo Done: $?
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-misc/heredoc_backslash1.right
new file mode 100644
index 000000000..6a6114821
--- /dev/null
+++ b/shell/hush_test/hush-misc/heredoc_backslash1.right
@@ -0,0 +1,43 @@
1Quoted heredoc:
2a\
3 b
4a\\
5 b
6 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
7 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
8 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
9 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
10c\
11
12Unquoted heredoc:
13a b
14a\
15 b
16 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
17 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
18 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
19 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
20cEOF2
21
22Quoted -heredoc:
23a\
24b
25a\\
26b
27 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
28-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
29 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
30 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
31c\
32
33Unquoted -heredoc:
34a b
35a\
36b
37 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
38-qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
39 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
40 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
41cEOF4
42
43Done: 0
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-misc/heredoc_backslash1.tests
new file mode 100755
index 000000000..501af5490
--- /dev/null
+++ b/shell/hush_test/hush-misc/heredoc_backslash1.tests
@@ -0,0 +1,70 @@
1# Test for correct handling of backslashes.
2# Note that some lines in each heredoc start with a tab.
3
4a=qwerty
5
6echo Quoted heredoc:
7cat <<"EOF1"
8a\
9 b
10a\\
11 b
12 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
13 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
14 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
15 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
16c\
17EOF1
18echo
19
20echo Unquoted heredoc:
21cat <<EOF2
22a\
23 b
24a\\
25 b
26 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
27 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
28 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
29 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
30c\
31EOF2
32EOF2
33echo
34
35echo Quoted -heredoc:
36cat <<-"EOF3"
37a\
38 b
39a\\
40 b
41 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
42 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
43 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
44 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
45c\
46 EOF3
47# In -heredoc case the marker is detected even if it is indented.
48echo
49
50echo Unquoted -heredoc:
51cat <<-EOF4
52a\
53 b
54a\\
55 b
56 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
57 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
58 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
59 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
60c\
61EOF4
62 EOF4
63# The marker is not detected if preceding line ends in backslash.
64# TODO: marker should be detected even if it is split by line continuation:
65# EOF\
66# 4
67# but currently hush doesn't do it. (Tab before "4" is not allowed, though.)
68echo
69
70echo "Done: $?"
diff --git a/shell/hush_test/hush-psubst/tick3.right b/shell/hush_test/hush-psubst/tick3.right
index dc84e9263..00f267ae5 100644
--- a/shell/hush_test/hush-psubst/tick3.right
+++ b/shell/hush_test/hush-psubst/tick3.right
@@ -2,5 +2,5 @@
2$TEST 2$TEST
3Q 3Q
4a\bc 4a\bc
5a"c 511-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44
6done:0 6done:0
diff --git a/shell/hush_test/hush-psubst/tick3.tests b/shell/hush_test/hush-psubst/tick3.tests
index 469c43c27..3aeb241c3 100755
--- a/shell/hush_test/hush-psubst/tick3.tests
+++ b/shell/hush_test/hush-psubst/tick3.tests
@@ -7,6 +7,8 @@ echo `echo '\'TEST\`echo ZZ\`BEST`
7echo `echo \\$TEST` 7echo `echo \\$TEST`
8echo `echo \$TEST` 8echo `echo \$TEST`
9echo a`echo \\\\b`c 9echo a`echo \\\\b`c
10# \" etc are NOT special (passed verbatim WITH \)! 10
11echo a`echo \"`c 11# \" is not special if in unquoted `cmd` (passed verbatim WITH \),
12# but is special in quoted one
13echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`"
12echo done:$? 14echo done:$?
diff --git a/shell/hush_test/hush-trap/exit.right b/shell/hush_test/hush-trap/exit.right
index b4932fb7f..3d0072564 100644
--- a/shell/hush_test/hush-trap/exit.right
+++ b/shell/hush_test/hush-trap/exit.right
@@ -1,2 +1,12 @@
1cow 1cow
2moo 2moo
3Traps1:
4trap -- 'exitfunc' EXIT
5Traps2:
6trap -- 'echo Should not run' EXIT
7Check1: 42
8Traps1:
9trap -- 'exitfunc' EXIT
10Traps2:
11trap -- 'echo Should not run' EXIT
12Check2: 42
diff --git a/shell/hush_test/hush-trap/exit.tests b/shell/hush_test/hush-trap/exit.tests
index 092543c25..2061105dd 100755
--- a/shell/hush_test/hush-trap/exit.tests
+++ b/shell/hush_test/hush-trap/exit.tests
@@ -1,3 +1,34 @@
1"$THIS_SH" -c 'trap "echo cow" 0' 1"$THIS_SH" -c 'trap "echo cow" 0'
2"$THIS_SH" -c 'trap "echo moo" EXIT' 2"$THIS_SH" -c 'trap "echo moo" EXIT'
3"$THIS_SH" -c 'trap "echo no" 0; trap 0' 3"$THIS_SH" -c 'trap "echo no" 0; trap 0'
4
5(
6exitfunc() {
7 echo "Traps1:"
8 trap
9 # EXIT trap is disabled after it is triggered,
10 # it can not be "re-armed" like this:
11 trap "echo Should not run" EXIT
12 echo "Traps2:"
13 trap
14}
15trap 'exitfunc' EXIT
16exit 42
17)
18echo Check1: $?
19
20(
21exitfunc() {
22 echo "Traps1:"
23 trap
24 # EXIT trap is disabled after it is triggered,
25 # it can not be "re-armed" like this:
26 trap "echo Should not run" EXIT
27 echo "Traps2:"
28 trap
29 exit 42
30}
31trap 'exitfunc' EXIT
32exit 66
33)
34echo Check2: $?
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-trap/subshell.tests
index 045294bf4..d877f2b82 100755
--- a/shell/hush_test/hush-trap/subshell.tests
+++ b/shell/hush_test/hush-trap/subshell.tests
@@ -11,10 +11,9 @@ trap 'bad: caught WINCH' WINCH
11# With TERM we'll check whether it is reset 11# With TERM we'll check whether it is reset
12trap 'bad: caught TERM' TERM 12trap 'bad: caught TERM' TERM
13 13
14# using bash, because we don't have $PPID (yet) 14(trap; "$THIS_SH" -c 'kill -HUP $PPID'; echo Ok)
15(trap; bash -c 'kill -HUP $PPID'; echo Ok) 15(trap; "$THIS_SH" -c 'kill -QUIT $PPID'; echo Ok)
16(trap; bash -c 'kill -QUIT $PPID'; echo Ok) 16(trap; "$THIS_SH" -c 'kill -SYS $PPID'; echo Ok)
17(trap; bash -c 'kill -SYS $PPID'; echo Ok) 17(trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok)
18(trap; bash -c 'kill -WINCH $PPID'; echo Ok) 18(trap; "$THIS_SH" -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
19(trap; bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
20echo Done 19echo Done
diff --git a/shell/hush_test/hush-vars/var_bash1.right b/shell/hush_test/hush-vars/var_bash1.right
new file mode 100644
index 000000000..c0a07699b
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1.right
@@ -0,0 +1,14 @@
1
2
3f
4bcdef
5abcdef
6abcdef
7bcde
8abcd
9abcd
10abcdef
11bcdef
12abcdef
13abcdef
14abcdef
diff --git a/shell/hush_test/hush-vars/var_bash1.tests b/shell/hush_test/hush-vars/var_bash1.tests
new file mode 100755
index 000000000..24d3c9a00
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1.tests
@@ -0,0 +1,18 @@
1var=abcdef
2
3echo ${var:7}
4echo ${var:6}
5echo ${var:5}
6echo ${var:1}
7echo ${var:0}
8echo ${var:-1}
9
10echo ${var:1:4}
11echo ${var:0:4}
12echo ${var::4}
13echo ${var:-1:4}
14
15echo ${var:1:7}
16echo ${var:0:7}
17echo ${var::7}
18echo ${var:-1:7}
diff --git a/shell/hush_test/hush-vars/var_bash2.right b/shell/hush_test/hush-vars/var_bash2.right
new file mode 100644
index 000000000..acba5c6fb
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash2.right
@@ -0,0 +1,10 @@
1abc123xcba123
2abx123dcba123
3abx123dxba123
4abcx23dcba123
5abcxxxdcbaxxx
6abx
7xba123
8abx23
9abc23dcba123
10abcdcba
diff --git a/shell/hush_test/hush-vars/var_bash2.tests b/shell/hush_test/hush-vars/var_bash2.tests
new file mode 100755
index 000000000..29c526cef
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash2.tests
@@ -0,0 +1,24 @@
1var=abc123dcba123
2
3echo ${var/d/x}
4echo ${var/c/x}
5echo ${var//c/x}
6echo ${var/[123]/x}
7echo ${var//[123]/x}
8echo ${var/c*/x}
9echo ${var/*c/x}
10
11# must match longest match: result is "abx23"
12echo ${var/c*1/x}
13
14# empty replacement - 2nd slash can be omitted
15echo ${var/[123]}
16echo ${var//[123]}
17
18### ash doesn't support
19### # match only at the beginning:
20### echo ${var/#a/x}
21### echo ${var/#b/x} # should not match
22### echo ${var//#b/x} # should not match
23### # match only at the end:
24### echo ${var/%3/x}
diff --git a/shell/hush_test/hush-vars/var_bash3.right b/shell/hush_test/hush-vars/var_bash3.right
new file mode 100644
index 000000000..a97c850ea
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash3.right
@@ -0,0 +1,20 @@
11 a041#c
22 a041#c
33 a\041#c
44 a\041#c
55 a\041#c
66 a\041#c
77 a\041#c
88 a\041#c
99 a\041#c
1010 a\c
1111 a\c
1212 a\c
1313 a\\c
1414 a\\c
1515 a\\c
1616 a\tc
1717 a\tc
1818 a\tc
1919 atc
2020 a\tc
diff --git a/shell/hush_test/hush-vars/var_bash3.tests b/shell/hush_test/hush-vars/var_bash3.tests
new file mode 100755
index 000000000..146dbb6a5
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash3.tests
@@ -0,0 +1,41 @@
1a='abc'
2r=${a//b/\041#}
3echo 1 $r
4echo 2 ${a//b/\041#}
5echo 3 "${a//b/\041#}"
6
7a='abc'
8r=${a//b/\\041#}
9echo 4 $r
10echo 5 ${a//b/\\041#}
11echo 6 "${a//b/\\041#}"
12
13a='abc'
14b='\041#'
15r=${a//b/$b}
16echo 7 $r
17echo 8 ${a//b/$b}
18echo 9 "${a//b/$b}"
19
20a='abc'
21b='\'
22r="${a//b/$b}"
23echo 10 $r
24echo 11 ${a//b/$b}
25echo 12 "${a//b/$b}"
26
27a='abc'
28b='\\'
29r="${a//b/$b}"
30echo 13 $r
31echo 14 ${a//b/$b}
32echo 15 "${a//b/$b}"
33
34a='abc'
35b='\t'
36r="${a//b/$b}"
37echo 16 $r
38echo 17 ${a//b/$b}
39echo 18 "${a//b/$b}"
40echo 19 ${a//b/\t}
41echo 20 "${a//b/\t}"
diff --git a/shell/hush_test/hush-vars/var_bash4.right b/shell/hush_test/hush-vars/var_bash4.right
new file mode 100644
index 000000000..0ef1bf661
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash4.right
@@ -0,0 +1,40 @@
1Source: a*b\*c
2Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star"
4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c
7Quoted =: a_\_\z_b\*c
8Pattern: double backslash and star: "replace backslash and everything after it"
9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_
11Quoted: a*b_\_\z_
12Quoted =: a*b_\_\z_
13
14Source: a\bc
15Replace str: _\\_\z_
16Pattern: single backslash and b: "replace b"
17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c
19Quoted: a\_\_\z_c
20Quoted =: a\_\_\z_c
21Pattern: double backslash and b: "replace backslash and b"
22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c
24Quoted: a_\_\z_c
25Quoted =: a_\_\z_c
26
27Source: a\bc
28Replace str: _\\_\z_ (as variable $s)
29Pattern: single backslash and b: "replace b"
30Unquoted: a\_\\_\z_c
31Unquoted =: a\_\\_\z_c
32Quoted: a\_\\_\z_c
33Quoted =: a\_\\_\z_c
34Pattern: double backslash and b: "replace backslash and b"
35Unquoted: a_\\_\z_c
36Unquoted =: a_\\_\z_c
37Quoted: a_\\_\z_c
38Quoted =: a_\\_\z_c
39
40Done: 0
diff --git a/shell/hush_test/hush-vars/var_bash4.tests b/shell/hush_test/hush-vars/var_bash4.tests
new file mode 100755
index 000000000..32aa2b34c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash4.tests
@@ -0,0 +1,81 @@
1# This testcase demonstrates that backslashes are treated differently
2# in 1st and 2nd parts of ${var/search/repl}:
3# if quoted ("${var/search/repl}"), and repl contains \a (a non-special char),
4# the backslash in repl stays; if unquoted, backslash is removed.
5# But search part does not act like that: \a is always converted to just a,
6# even in quotes.
7#
8# bash4 (and probably bash3 too): "Quoted:" results are different from
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.
13
14v='a*b\*c'
15echo 'Source: ' "$v"
16echo 'Replace str: ' '_\\_\z_'
17
18echo 'Pattern: ' 'single backslash and star: "replace literal star"'
19echo 'Unquoted: ' ${v/\*/_\\_\z_}
20r=${v/\*/_\\_\z_}
21echo 'Unquoted =: ' "$r"
22echo 'Quoted: ' "${v/\*/_\\_\z_}"
23r="${v/\*/_\\_\z_}"
24echo 'Quoted =: ' "$r"
25
26echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"'
27echo 'Unquoted: ' ${v/\\*/_\\_\z_}
28r=${v/\\*/_\\_\z_}
29echo 'Unquoted =: ' "$r"
30echo 'Quoted: ' "${v/\\*/_\\_\z_}"
31r="${v/\\*/_\\_\z_}"
32echo 'Quoted =: ' "$r"
33
34echo
35
36v='a\bc'
37echo 'Source: ' "$v"
38echo 'Replace str: ' '_\\_\z_'
39
40echo 'Pattern: ' 'single backslash and b: "replace b"'
41echo 'Unquoted: ' ${v/\b/_\\_\z_}
42r=${v/\b/_\\_\z_}
43echo 'Unquoted =: ' "$r"
44echo 'Quoted: ' "${v/\b/_\\_\z_}"
45r="${v/\b/_\\_\z_}"
46echo 'Quoted =: ' "$r"
47
48echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
49echo 'Unquoted: ' ${v/\\b/_\\_\z_}
50r=${v/\\b/_\\_\z_}
51echo 'Unquoted =: ' "$r"
52echo 'Quoted: ' "${v/\\b/_\\_\z_}"
53r="${v/\\b/_\\_\z_}"
54echo 'Quoted =: ' "$r"
55
56echo
57
58v='a\bc'
59s='_\\_\z_'
60echo 'Source: ' "$v"
61echo 'Replace str: ' "$s" '(as variable $s)'
62
63echo 'Pattern: ' 'single backslash and b: "replace b"'
64echo 'Unquoted: ' ${v/\b/$s}
65r=${v/\b/$s}
66echo 'Unquoted =: ' "$r"
67echo 'Quoted: ' "${v/\b/$s}"
68r="${v/\b/$s}"
69echo 'Quoted =: ' "$r"
70
71echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
72echo 'Unquoted: ' ${v/\\b/$s}
73r=${v/\\b/$s}
74echo 'Unquoted =: ' "$r"
75echo 'Quoted: ' "${v/\\b/$s}"
76r="${v/\\b/$s}"
77echo 'Quoted =: ' "$r"
78
79echo
80
81echo Done: $?
diff --git a/shell/hush_test/hush-vars/var_bash5.right b/shell/hush_test/hush-vars/var_bash5.right
new file mode 100644
index 000000000..1990902b2
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash5.right
@@ -0,0 +1,11 @@
11 a/
22 a/d
33 a/e/f
44 a\
55 a\d
66 a\e\f
77 a\\
88 a\\d
99 a\\e\\f
10a ab
11Done: 0
diff --git a/shell/hush_test/hush-vars/var_bash5.tests b/shell/hush_test/hush-vars/var_bash5.tests
new file mode 100755
index 000000000..5748b4ac7
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash5.tests
@@ -0,0 +1,29 @@
1# This testcase checks whether slashes in ${v/a/b} are parsed before
2# or after expansions
3
4v='a/b/c'
5s='b/c'
6r='e/f'
7echo "1 ${v/$s}"
8echo "2 ${v/$s/d}"
9echo "3 ${v/$s/$r}"
10
11v='a\b\c'
12s='b\\c'
13r='e\f'
14echo "4 ${v/$s}"
15echo "5 ${v/$s/d}"
16echo "6 ${v/$s/$r}"
17
18v='a\\b\\c'
19s='b\\\\c'
20r='e\\f'
21echo "7 ${v/$s}"
22echo "8 ${v/$s/d}"
23echo "9 ${v/$s/$r}"
24
25v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b'
26s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-'
27echo "a ${v/$s}"
28
29echo Done: $?
diff --git a/shell/hush_test/hush-vars/var_bash6.right b/shell/hush_test/hush-vars/var_bash6.right
new file mode 100644
index 000000000..63fc23df8
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash6.right
@@ -0,0 +1,5 @@
1Expected Actual
2a*z : a*z
3\z : \z
4a1z a2z: a1z a2z
5z : z
diff --git a/shell/hush_test/hush-vars/var_bash6.tests b/shell/hush_test/hush-vars/var_bash6.tests
new file mode 100755
index 000000000..cf2e4f020
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash6.tests
@@ -0,0 +1,9 @@
1# This testcase checks globbing correctness in ${v/a/b}
2
3>a1z; >a2z;
4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z
diff --git a/shell/hush_test/hush-vars/var_unbackslash.right b/shell/hush_test/hush-vars/var_unbackslash.right
new file mode 100644
index 000000000..8bc834711
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash.right
@@ -0,0 +1,11 @@
1b1=-qwerty-t-\-"-`---z-*-?-
2b1=-qwerty-t-\-"-`---z-*-?-
3b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
4b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
5b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
6b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
7c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
8c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
9c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
10c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
11Done: 0
diff --git a/shell/hush_test/hush-vars/var_unbackslash.tests b/shell/hush_test/hush-vars/var_unbackslash.tests
new file mode 100755
index 000000000..bb52af3d0
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash.tests
@@ -0,0 +1,23 @@
1# Test for correct handling of backslashes
2a=qwerty
3
4b=-$a-\t-\\-\"-\`-\--\z-\*-\?-
5echo b1=$b
6echo "b1=$b"
7b="-$a-\t-\\-\"-\`-\--\z-\*-\?-"
8echo b2=$b
9echo "b2=$b"
10b='-$a-\t-\\-\"-\`-\--\z-\*-\?-'
11echo b3=$b
12echo "b3=$b"
13
14c=$b
15echo "c=$c"
16c=${b}
17echo "c=$c"
18c="$b"
19echo "c=$c"
20c="${b}"
21echo "c=$c"
22
23echo "Done: $?"
diff --git a/shell/match.c b/shell/match.c
index 8b1ddacd5..fee3cf2a8 100644
--- a/shell/match.c
+++ b/shell/match.c
@@ -18,65 +18,78 @@
18# include <stdlib.h> 18# include <stdlib.h>
19# include <string.h> 19# include <string.h>
20# include <unistd.h> 20# include <unistd.h>
21# define FAST_FUNC /* nothing */
22# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
23# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
21#else 24#else
22# include "libbb.h" 25# include "libbb.h"
23#endif 26#endif
24#include <fnmatch.h> 27#include <fnmatch.h>
25#include "match.h" 28#include "match.h"
26 29
27#define pmatch(a, b) !fnmatch((a), (b), 0) 30char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
28
29char *scanleft(char *string, char *pattern, bool match_at_left)
30{ 31{
31 char c; 32 char *loc;
32 char *loc = string; 33 char *end;
33 34 unsigned len = strlen(string);
34 do { 35 int early_exit;
35 int match; 36
36 const char *s; 37 /* We can stop the scan early only if the string part
37 38 * we are matching against is shrinking, and the pattern has
38 c = *loc; 39 * an unquoted "star" at the corresponding end. There are two cases.
39 if (match_at_left) { 40 * Case 1:
40 *loc = '\0'; 41 * "qwerty" does not match against pattern "*zy",
41 s = string; 42 * no point in trying to match "werty", "erty" etc:
42 } else 43 */
43 s = loc; 44 early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
44 match = pmatch(pattern, s); 45
45 *loc = c; 46 if (flags & SCAN_MOVE_FROM_LEFT) {
46 47 loc = string;
47 if (match) 48 end = string + len + 1;
48 return loc; 49 } else {
49 50 loc = string + len;
50 loc++; 51 end = string - 1;
51 } while (c); 52 if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
52 53 /* Case 2:
53 return NULL; 54 * "qwerty" does not match against pattern "qz*",
54} 55 * no point in trying to match "qwert", "qwer" etc:
55 56 */
56char *scanright(char *string, char *pattern, bool match_at_left) 57 const char *p = pattern + strlen(pattern);
57{ 58 if (--p >= pattern && *p == '*') {
58 char c; 59 early_exit = 1;
59 char *loc = string + strlen(string); 60 while (--p >= pattern && *p == '\\')
61 early_exit ^= 1;
62 }
63 }
64 }
60 65
61 while (loc >= string) { 66 while (loc != end) {
62 int match; 67 char c;
63 const char *s; 68 int r;
64 69
65 c = *loc; 70 c = *loc;
66 if (match_at_left) { 71 if (flags & SCAN_MATCH_LEFT_HALF) {
67 *loc = '\0'; 72 *loc = '\0';
68 s = string; 73 r = fnmatch(pattern, string, 0);
69 } else 74 *loc = c;
70 s = loc; 75 } else {
71 match = pmatch(pattern, s); 76 r = fnmatch(pattern, loc, 0);
72 *loc = c; 77 }
73 78 if (r == 0) /* match found */
74 if (match)
75 return loc; 79 return loc;
80 if (early_exit) {
81#ifdef STANDALONE
82 printf("(early exit) ");
83#endif
84 break;
85 }
76 86
77 loc--; 87 if (flags & SCAN_MOVE_FROM_LEFT) {
88 loc++;
89 } else {
90 loc--;
91 }
78 } 92 }
79
80 return NULL; 93 return NULL;
81} 94}
82 95
@@ -86,12 +99,11 @@ int main(int argc, char *argv[])
86 char *string; 99 char *string;
87 char *op; 100 char *op;
88 char *pattern; 101 char *pattern;
89 bool match_at_left;
90 char *loc; 102 char *loc;
91 103
92 int i; 104 setvbuf(stdout, NULL, _IONBF, 0);
93 105
94 if (argc == 1) { 106 if (!argv[1]) {
95 puts( 107 puts(
96 "Usage: match <test> [test...]\n\n" 108 "Usage: match <test> [test...]\n\n"
97 "Where a <test> is the form: <string><op><match>\n" 109 "Where a <test> is the form: <string><op><match>\n"
@@ -101,36 +113,34 @@ int main(int argc, char *argv[])
101 return 1; 113 return 1;
102 } 114 }
103 115
104 for (i = 1; i < argc; ++i) { 116 while (*++argv) {
105 size_t off; 117 size_t off;
106 scan_t scan; 118 unsigned scan_flags;
107
108 printf("'%s': ", argv[i]);
109 119
110 string = strdup(argv[i]); 120 string = *argv;
111 off = strcspn(string, "#%"); 121 off = strcspn(string, "#%");
112 if (!off) { 122 if (!off) {
113 printf("invalid format\n"); 123 printf("invalid format\n");
114 free(string);
115 continue; 124 continue;
116 } 125 }
117 op = string + off; 126 op = string + off;
118 scan = pick_scan(op[0], op[1], &match_at_left); 127 scan_flags = pick_scan(op[0], op[1]);
128
129 printf("'%s': flags:%x, ", string, scan_flags);
119 pattern = op + 1; 130 pattern = op + 1;
120 if (op[0] == op[1]) 131 if (op[0] == op[1])
121 op[1] = '\0', ++pattern; 132 pattern++;
122 op[0] = '\0'; 133 op[0] = '\0';
123 134
124 loc = scan(string, pattern, match_at_left); 135 loc = scan_and_match(string, pattern, scan_flags);
125 136
126 if (match_at_left) { 137 if (scan_flags & SCAN_MATCH_LEFT_HALF) {
127 printf("'%s'\n", loc); 138 printf("'%s'\n", loc);
128 } else { 139 } else {
129 *loc = '\0'; 140 if (loc)
141 *loc = '\0';
130 printf("'%s'\n", string); 142 printf("'%s'\n", string);
131 } 143 }
132
133 free(string);
134 } 144 }
135 145
136 return 0; 146 return 0;
diff --git a/shell/match.h b/shell/match.h
index c022ceb25..aa393ed1a 100644
--- a/shell/match.h
+++ b/shell/match.h
@@ -7,25 +7,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
7 7
8//TODO! Why ash.c still uses internal version?! 8//TODO! Why ash.c still uses internal version?!
9 9
10typedef char *(*scan_t)(char *string, char *match, bool match_at_left); 10enum {
11 SCAN_MOVE_FROM_LEFT = (1 << 0),
12 SCAN_MOVE_FROM_RIGHT = (1 << 1),
13 SCAN_MATCH_LEFT_HALF = (1 << 2),
14 SCAN_MATCH_RIGHT_HALF = (1 << 3),
15};
11 16
12char *scanleft(char *string, char *match, bool match_at_left); 17char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags);
13char *scanright(char *string, char *match, bool match_at_left);
14 18
15static inline scan_t pick_scan(char op1, char op2, bool *match_at_left) 19static inline unsigned pick_scan(char op1, char op2)
16{ 20{
17 /* # - scanleft 21 unsigned scan_flags;
18 * ## - scanright
19 * % - scanright
20 * %% - scanleft
21 */
22 if (op1 == '#') { 22 if (op1 == '#') {
23 *match_at_left = true; 23 scan_flags = SCAN_MATCH_LEFT_HALF +
24 return op1 == op2 ? scanright : scanleft; 24 (op1 == op2 ? SCAN_MOVE_FROM_RIGHT : SCAN_MOVE_FROM_LEFT);
25 } else { 25 } else { /* % */
26 *match_at_left = false; 26 scan_flags = SCAN_MATCH_RIGHT_HALF +
27 return op1 == op2 ? scanleft : scanright; 27 (op1 == op2 ? SCAN_MOVE_FROM_LEFT : SCAN_MOVE_FROM_RIGHT);
28 } 28 }
29 return scan_flags;
29} 30}
30 31
31POP_SAVED_FUNCTION_VISIBILITY 32POP_SAVED_FUNCTION_VISIBILITY
diff --git a/shell/math.c b/shell/math.c
index f0cc2e35d..a4c55a4d0 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -122,7 +122,7 @@
122#define a_e_h_t arith_eval_hooks_t 122#define a_e_h_t arith_eval_hooks_t
123#define lookupvar (math_hooks->lookupvar) 123#define lookupvar (math_hooks->lookupvar)
124#define setvar (math_hooks->setvar ) 124#define setvar (math_hooks->setvar )
125#define endofname (math_hooks->endofname) 125//#define endofname (math_hooks->endofname)
126 126
127#define arith_isspace(arithval) \ 127#define arith_isspace(arithval) \
128 (arithval == ' ' || arithval == '\n' || arithval == '\t') 128 (arithval == ' ' || arithval == '\n' || arithval == '\t')
@@ -479,6 +479,18 @@ static const char op_tokens[] ALIGN1 = {
479/* ptr to ")" */ 479/* ptr to ")" */
480#define endexpression (&op_tokens[sizeof(op_tokens)-7]) 480#define endexpression (&op_tokens[sizeof(op_tokens)-7])
481 481
482const char* FAST_FUNC
483endofname(const char *name)
484{
485 if (!is_name(*name))
486 return name;
487 while (*++name) {
488 if (!is_in_name(*name))
489 break;
490 }
491 return name;
492}
493
482arith_t 494arith_t
483arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) 495arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
484{ 496{
diff --git a/shell/math.h b/shell/math.h
index 2b0b2b89d..96088b4d2 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -87,14 +87,19 @@ typedef long arith_t;
87#define strto_arith_t strtoul 87#define strto_arith_t strtoul
88#endif 88#endif
89 89
90/* ash's and hush's endofname is the same, so... */
91# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
92# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
93const char* FAST_FUNC endofname(const char *name);
94
90typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); 95typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
91typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); 96typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
92typedef char* FAST_FUNC (*arith_var_endofname_t)(const char *name); 97//typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
93 98
94typedef struct arith_eval_hooks { 99typedef struct arith_eval_hooks {
95 arith_var_lookup_t lookupvar; 100 arith_var_lookup_t lookupvar;
96 arith_var_set_t setvar; 101 arith_var_set_t setvar;
97 arith_var_endofname_t endofname; 102// arith_var_endofname_t endofname;
98} arith_eval_hooks_t; 103} arith_eval_hooks_t;
99 104
100arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*); 105arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*);
diff --git a/testsuite/makedevs.tests b/testsuite/makedevs.tests
index eb51410b3..fd12460ec 100755
--- a/testsuite/makedevs.tests
+++ b/testsuite/makedevs.tests
@@ -4,6 +4,11 @@
4 4
5. ./testing.sh 5. ./testing.sh
6 6
7test x"`id -u`" = x"0" || {
8 echo "SKIPPED: makedevs (must be root to test this)"
9 exit 0
10}
11
7unset LANG 12unset LANG
8unset LC_COLLATE 13unset LC_COLLATE
9unset LC_ALL 14unset LC_ALL
diff --git a/testsuite/printf.tests b/testsuite/printf.tests
index b48e293f4..9a3c87473 100755
--- a/testsuite/printf.tests
+++ b/testsuite/printf.tests
@@ -89,9 +89,9 @@ testing "printf understands %Ld" \
89 89
90testing "printf handles %d bad_input" \ 90testing "printf handles %d bad_input" \
91 "${bb}printf '%d\n' 1 - 2 bad 3 123bad 4 2>&1; echo \$?" \ 91 "${bb}printf '%d\n' 1 - 2 bad 3 123bad 4 2>&1; echo \$?" \
92"1\n""printf: -: invalid number\n""0\n"\ 92"1\n""printf: invalid number '-'\n""0\n"\
93"2\n""printf: bad: invalid number\n""0\n"\ 93"2\n""printf: invalid number 'bad'\n""0\n"\
94"3\n""printf: 123bad: invalid number\n""0\n"\ 94"3\n""printf: invalid number '123bad'\n""0\n"\
95"4\n""1\n" \ 95"4\n""1\n" \
96 "" "" 96 "" ""
97 97
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index cef16b65e..f2f4e9348 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -55,16 +55,19 @@ rm -rf input_* test.tar 2>/dev/null
55chmod 741 input_hard1 55chmod 741 input_hard1
56ln input_hard1 input_hard2 56ln input_hard1 input_hard2
57mkdir input_dir 57mkdir input_dir
58chmod 550 input_dir
59ln input_hard1 input_dir 58ln input_hard1 input_dir
60ln input_hard2 input_dir 59ln input_hard2 input_dir
61tar cf test.tar input_* 60chmod 550 input_dir
61# On some filesystems, input_dir/input_hard2 is returned by readdir
62# BEFORE input_dir/input_hard1! Thats why we cant just "tar cf ... input_*":
63tar cf test.tar input_dir/input_hard* input_hard*
62tar tvf test.tar | sed "s/.*[0-9] input/input/" 64tar tvf test.tar | sed "s/.*[0-9] input/input/"
65chmod 770 input_dir
66rm -rf input_*
63tar xf test.tar 2>&1 67tar xf test.tar 2>&1
64echo Ok: $? 68echo Ok: $?
65ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" 69ls -l . input_dir/* | grep "input.*hard" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
66' "\ 70' "\
67input_dir/
68input_dir/input_hard1 71input_dir/input_hard1
69input_dir/input_hard2 -> input_dir/input_hard1 72input_dir/input_hard2 -> input_dir/input_hard1
70input_hard1 -> input_dir/input_hard1 73input_hard1 -> input_dir/input_hard1
@@ -72,7 +75,6 @@ input_hard2 -> input_dir/input_hard1
72Ok: 0 75Ok: 0
73-rwxr----x input_dir/input_hard1 76-rwxr----x input_dir/input_hard1
74-rwxr----x input_dir/input_hard2 77-rwxr----x input_dir/input_hard2
75dr-xr-x--- input_dir
76-rwxr----x input_hard1 78-rwxr----x input_hard1
77-rwxr----x input_hard2 79-rwxr----x input_hard2
78" \ 80" \
@@ -86,16 +88,17 @@ rm -rf input_* test.tar 2>/dev/null
86chmod 741 input_file 88chmod 741 input_file
87ln -s input_file input_soft 89ln -s input_file input_soft
88mkdir input_dir 90mkdir input_dir
89chmod 550 input_dir
90ln input_file input_dir 91ln input_file input_dir
91ln input_soft input_dir 92ln input_soft input_dir
92tar cf test.tar input_* 93chmod 550 input_dir
94tar cf test.tar input_dir/* input_[fs]*
93tar tvf test.tar | sed "s/.*[0-9] input/input/" | sort 95tar tvf test.tar | sed "s/.*[0-9] input/input/" | sort
96chmod 770 input_dir
97rm -rf input_*
94tar xf test.tar 2>&1 98tar xf test.tar 2>&1
95echo Ok: $? 99echo Ok: $?
96ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" 100ls -l . input_dir/* | grep "input_[fs]" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
97' "\ 101' "\
98input_dir/
99input_dir/input_file 102input_dir/input_file
100input_dir/input_soft -> input_file 103input_dir/input_soft -> input_file
101input_file -> input_dir/input_file 104input_file -> input_dir/input_file
@@ -103,7 +106,6 @@ input_soft -> input_dir/input_soft
103Ok: 0 106Ok: 0
104-rwxr----x input_dir/input_file 107-rwxr----x input_dir/input_file
105lrwxrwxrwx input_file 108lrwxrwxrwx input_file
106dr-xr-x--- input_dir
107-rwxr----x input_file 109-rwxr----x input_file
108lrwxrwxrwx input_file 110lrwxrwxrwx input_file
109" \ 111" \
@@ -125,6 +127,34 @@ Ok
125"Ok\n" "" 127"Ok\n" ""
126SKIP= 128SKIP=
127 129
130test x"$SKIP_KNOWN_BUGS" = x"" && {
131# Needs to be run under non-root for meaningful test
132optional FEATURE_TAR_CREATE
133testing "tar writing into read-only dir" '\
134rm -rf input_* test.tar 2>/dev/null
135mkdir input_dir
136>input_dir/input_file
137chmod 550 input_dir
138tar cf test.tar input_dir
139tar tvf test.tar | sed "s/.*[0-9] input/input/"
140chmod 770 input_dir
141rm -rf input_*
142tar xf test.tar 2>&1
143echo Ok: $?
144ls -l input_dir/* . | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
145chmod 770 input_dir
146' "\
147input_dir/
148input_dir/input_file
149Ok: 0
150-rw-r--r-- input_dir/input_file
151dr-xr-x--- input_dir
152" \
153"" ""
154SKIP=
155}
156
157
128cd .. && rm -rf tar.tempdir || exit 1 158cd .. && rm -rf tar.tempdir || exit 1
129 159
130exit $FAILCOUNT 160exit $FAILCOUNT
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
new file mode 100644
index 000000000..c61116df9
--- /dev/null
+++ b/util-linux/blockdev.c
@@ -0,0 +1,195 @@
1/*
2 * blockdev implementation for busybox
3 *
4 * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8
9//applet:IF_BLOCKDEV(APPLET(blockdev, _BB_DIR_SBIN, _BB_SUID_DROP))
10
11//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
12
13//config:config BLOCKDEV
14//config: bool "blockdev"
15//config: default y
16//config: help
17//config: Performs some ioctls with block devices.
18
19//usage:#define blockdev_trivial_usage
20//usage: "OPTION BLOCKDEV"
21//usage:#define blockdev_full_usage "\n\n"
22//usage: "Options:"
23//usage: "\n --setro Set ro"
24//usage: "\n --setrw Set rw"
25//usage: "\n --getro Get ro"
26//usage: "\n --getss Get sector size"
27//usage: "\n --getbsz Get block size"
28//usage: "\n --setbsz BYTES Set block size"
29//usage: "\n --getsize Get device size in 512-byte sectors"
30//usage: "\n --getsize64 Get device size in bytes"
31//usage: "\n --flushbufs Flush buffers"
32//usage: "\n --rereadpt Reread partition table"
33
34
35#include "libbb.h"
36#include <linux/fs.h>
37
38enum {
39 ARG_NONE = 0,
40 ARG_INT = 1,
41 ARG_ULONG = 2,
42 /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
43 ARG_U64 = 3,
44 ARG_MASK = 3,
45
46 FL_USRARG = 4, /* argument is provided by user */
47 FL_NORESULT = 8,
48};
49
50struct bdc {
51 uint32_t ioc; /* ioctl code */
52 const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */
53 uint8_t flags;
54 int8_t argval; /* default argument value */
55};
56
57static const struct bdc bdcommands[] = {
58 {
59 .ioc = BLKROSET,
60 .name = "setro",
61 .flags = ARG_INT + FL_NORESULT,
62 .argval = 1,
63 },{
64 .ioc = BLKROSET,
65 .name = "setrw",
66 .flags = ARG_INT + FL_NORESULT,
67 .argval = 0,
68 },{
69 .ioc = BLKROGET,
70 .name = "getro",
71 .flags = ARG_INT,
72 .argval = -1,
73 },{
74 .ioc = BLKSSZGET,
75 .name = "getss",
76 .flags = ARG_INT,
77 .argval = -1,
78 },{
79 .ioc = BLKBSZGET,
80 .name = "getbsz",
81 .flags = ARG_INT,
82 .argval = -1,
83 },{
84 .ioc = BLKBSZSET,
85 .name = "setbsz",
86 .flags = ARG_INT + FL_NORESULT + FL_USRARG,
87 .argval = 0,
88 },{
89 .ioc = BLKGETSIZE,
90 .name = "getsize",
91 .flags = ARG_ULONG,
92 .argval = -1,
93 },{
94 .ioc = BLKGETSIZE64,
95 .name = "getsize64",
96 .flags = ARG_U64,
97 .argval = -1,
98 },{
99 .ioc = BLKFLSBUF,
100 .name = "flushbufs",
101 .flags = ARG_NONE + FL_NORESULT,
102 .argval = 0,
103 },{
104 .ioc = BLKRRPART,
105 .name = "rereadpt",
106 .flags = ARG_NONE + FL_NORESULT,
107 .argval = 0,
108 }
109};
110
111static const struct bdc *find_cmd(const char *s)
112{
113 const struct bdc *bdcmd = bdcommands;
114 if (s[0] == '-' && s[1] == '-') {
115 s += 2;
116 do {
117 if (strcmp(s, bdcmd->name) == 0)
118 return bdcmd;
119 bdcmd++;
120 } while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands));
121 }
122 bb_show_usage();
123}
124
125int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
126int blockdev_main(int argc, char **argv)
127{
128 const struct bdc *bdcmd;
129 int fd;
130 uint64_t u64;
131 union {
132 int i;
133 unsigned long lu;
134 uint64_t u64;
135 } ioctl_val_on_stack;
136
137 if ((unsigned)(argc - 3) > 1) /* must have 2 or 3 args */
138 bb_show_usage();
139
140 bdcmd = find_cmd(*++argv);
141
142 u64 = (int)bdcmd->argval;
143 if (bdcmd->flags & FL_USRARG)
144 u64 = xatoi_positive(*++argv);
145
146 if (!*++argv || argv[1])
147 bb_show_usage();
148 fd = xopen(*argv, O_RDONLY);
149
150 ioctl_val_on_stack.u64 = u64;
151#if BB_BIG_ENDIAN
152 /* Store data properly wrt data size.
153 * (1) It's no-op for little-endian.
154 * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1,
155 * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
156 * Thus, we don't need to handle ARG_ULONG.
157 */
158 switch (bdcmd->flags & ARG_MASK) {
159 case ARG_INT:
160 ioctl_val_on_stack.i = (int)u64;
161 break;
162# if 0 /* unused */
163 case ARG_ULONG:
164 ioctl_val_on_stack.lu = (unsigned long)u64;
165 break;
166# endif
167 }
168#endif
169
170 if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1)
171 bb_simple_perror_msg_and_die(*argv);
172
173 /* Fetch it into register(s) */
174 u64 = ioctl_val_on_stack.u64;
175
176 /* Zero- or one-extend the value if needed, then print */
177 switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) {
178 case ARG_INT:
179 /* Smaller code when we use long long
180 * (gcc tail-merges printf call)
181 */
182 printf("%lld\n", (long long)(int)u64);
183 break;
184 case ARG_ULONG:
185 u64 = (unsigned long)u64;
186 /* FALLTHROUGH */
187 case ARG_U64:
188 printf("%llu\n", (unsigned long long)u64);
189 break;
190 }
191
192 if (ENABLE_FEATURE_CLEAN_UP)
193 close(fd);
194 return EXIT_SUCCESS;
195}