diff options
Diffstat (limited to 'shell')
64 files changed, 736 insertions, 210 deletions
diff --git a/shell/Config.src b/shell/Config.src index ccb1b15fe..81c4ec874 100644 --- a/shell/Config.src +++ b/shell/Config.src | |||
@@ -10,26 +10,26 @@ choice | |||
10 | prompt "Choose which shell is aliased to 'sh' name" | 10 | prompt "Choose which shell is aliased to 'sh' name" |
11 | default SH_IS_ASH | 11 | default SH_IS_ASH |
12 | help | 12 | help |
13 | Choose which shell you want to be executed by 'sh' alias. | 13 | Choose which shell you want to be executed by 'sh' alias. |
14 | The ash shell is the most bash compatible and full featured one. | 14 | The ash shell is the most bash compatible and full featured one. |
15 | 15 | ||
16 | # note: cannot use "select ASH" here, it breaks "make allnoconfig" | 16 | # note: cannot use "select ASH" here, it breaks "make allnoconfig" |
17 | config SH_IS_ASH | 17 | config SH_IS_ASH |
18 | depends on !NOMMU | 18 | depends on !NOMMU |
19 | bool "ash" | 19 | bool "ash" |
20 | help | 20 | help |
21 | Choose ash to be the shell executed by 'sh' name. | 21 | Choose ash to be the shell executed by 'sh' name. |
22 | The ash code will be built into busybox. If you don't select | 22 | The ash code will be built into busybox. If you don't select |
23 | "ash" choice (CONFIG_ASH), this shell may only be invoked by | 23 | "ash" choice (CONFIG_ASH), this shell may only be invoked by |
24 | the name 'sh' (and not 'ash'). | 24 | the name 'sh' (and not 'ash'). |
25 | 25 | ||
26 | config SH_IS_HUSH | 26 | config SH_IS_HUSH |
27 | bool "hush" | 27 | bool "hush" |
28 | help | 28 | help |
29 | Choose hush to be the shell executed by 'sh' name. | 29 | Choose hush to be the shell executed by 'sh' name. |
30 | The hush code will be built into busybox. If you don't select | 30 | The hush code will be built into busybox. If you don't select |
31 | "hush" choice (CONFIG_HUSH), this shell may only be invoked by | 31 | "hush" choice (CONFIG_HUSH), this shell may only be invoked by |
32 | the name 'sh' (and not 'hush'). | 32 | the name 'sh' (and not 'hush'). |
33 | 33 | ||
34 | config SH_IS_NONE | 34 | config SH_IS_NONE |
35 | bool "none" | 35 | bool "none" |
@@ -40,36 +40,36 @@ choice | |||
40 | prompt "Choose which shell is aliased to 'bash' name" | 40 | prompt "Choose which shell is aliased to 'bash' name" |
41 | default BASH_IS_NONE | 41 | default BASH_IS_NONE |
42 | help | 42 | help |
43 | Choose which shell you want to be executed by 'bash' alias. | 43 | Choose which shell you want to be executed by 'bash' alias. |
44 | The ash shell is the most bash compatible and full featured one, | 44 | The ash shell is the most bash compatible and full featured one, |
45 | although compatibility is far from being complete. | 45 | although compatibility is far from being complete. |
46 | 46 | ||
47 | Note that selecting this option does not switch on any bash | 47 | Note that selecting this option does not switch on any bash |
48 | compatibility code. It merely makes it possible to install | 48 | compatibility code. It merely makes it possible to install |
49 | /bin/bash (sym)link and run scripts which start with | 49 | /bin/bash (sym)link and run scripts which start with |
50 | #!/bin/bash line. | 50 | #!/bin/bash line. |
51 | 51 | ||
52 | Many systems use it in scripts which use bash-specific features, | 52 | Many systems use it in scripts which use bash-specific features, |
53 | even simple ones like $RANDOM. Without this option, busybox | 53 | even simple ones like $RANDOM. Without this option, busybox |
54 | can't be used for running them because it won't recongnize | 54 | can't be used for running them because it won't recongnize |
55 | "bash" as a supported applet name. | 55 | "bash" as a supported applet name. |
56 | 56 | ||
57 | config BASH_IS_ASH | 57 | config BASH_IS_ASH |
58 | depends on !NOMMU | 58 | depends on !NOMMU |
59 | bool "ash" | 59 | bool "ash" |
60 | help | 60 | help |
61 | Choose ash to be the shell executed by 'bash' name. | 61 | Choose ash to be the shell executed by 'bash' name. |
62 | The ash code will be built into busybox. If you don't select | 62 | The ash code will be built into busybox. If you don't select |
63 | "ash" choice (CONFIG_ASH), this shell may only be invoked by | 63 | "ash" choice (CONFIG_ASH), this shell may only be invoked by |
64 | the name 'bash' (and not 'ash'). | 64 | the name 'bash' (and not 'ash'). |
65 | 65 | ||
66 | config BASH_IS_HUSH | 66 | config BASH_IS_HUSH |
67 | bool "hush" | 67 | bool "hush" |
68 | help | 68 | help |
69 | Choose hush to be the shell executed by 'bash' name. | 69 | Choose hush to be the shell executed by 'bash' name. |
70 | The hush code will be built into busybox. If you don't select | 70 | The hush code will be built into busybox. If you don't select |
71 | "hush" choice (CONFIG_HUSH), this shell may only be invoked by | 71 | "hush" choice (CONFIG_HUSH), this shell may only be invoked by |
72 | the name 'bash' (and not 'hush'). | 72 | the name 'bash' (and not 'hush'). |
73 | 73 | ||
74 | config BASH_IS_NONE | 74 | config BASH_IS_NONE |
75 | bool "none" | 75 | bool "none" |
@@ -88,71 +88,78 @@ config FEATURE_SH_MATH | |||
88 | default y | 88 | default y |
89 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH | 89 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH |
90 | help | 90 | help |
91 | Enable math support in the shell via $((...)) syntax. | 91 | Enable math support in the shell via $((...)) syntax. |
92 | 92 | ||
93 | config FEATURE_SH_MATH_64 | 93 | config FEATURE_SH_MATH_64 |
94 | bool "Extend POSIX math support to 64 bit" | 94 | bool "Extend POSIX math support to 64 bit" |
95 | default y | 95 | default y |
96 | depends on FEATURE_SH_MATH | 96 | depends on FEATURE_SH_MATH |
97 | help | 97 | help |
98 | Enable 64-bit math support in the shell. This will make the shell | 98 | Enable 64-bit math support in the shell. This will make the shell |
99 | slightly larger, but will allow computation with very large numbers. | 99 | slightly larger, but will allow computation with very large numbers. |
100 | This is not in POSIX, so do not rely on this in portable code. | 100 | This is not in POSIX, so do not rely on this in portable code. |
101 | 101 | ||
102 | config FEATURE_SH_EXTRA_QUIET | 102 | config FEATURE_SH_EXTRA_QUIET |
103 | bool "Hide message on interactive shell startup" | 103 | bool "Hide message on interactive shell startup" |
104 | default y | 104 | default y |
105 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH | 105 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH |
106 | help | 106 | help |
107 | Remove the busybox introduction when starting a shell. | 107 | Remove the busybox introduction when starting a shell. |
108 | 108 | ||
109 | config FEATURE_SH_STANDALONE | 109 | config FEATURE_SH_STANDALONE |
110 | bool "Standalone shell" | 110 | bool "Standalone shell" |
111 | default n | 111 | default n |
112 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH | 112 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH |
113 | help | 113 | help |
114 | This option causes busybox shells to use busybox applets | 114 | This option causes busybox shells to use busybox applets |
115 | in preference to executables in the PATH whenever possible. For | 115 | in preference to executables in the PATH whenever possible. For |
116 | example, entering the command 'ifconfig' into the shell would cause | 116 | example, entering the command 'ifconfig' into the shell would cause |
117 | busybox to use the ifconfig busybox applet. Specifying the fully | 117 | busybox to use the ifconfig busybox applet. Specifying the fully |
118 | qualified executable name, such as '/sbin/ifconfig' will still | 118 | qualified executable name, such as '/sbin/ifconfig' will still |
119 | execute the /sbin/ifconfig executable on the filesystem. This option | 119 | execute the /sbin/ifconfig executable on the filesystem. This option |
120 | is generally used when creating a statically linked version of busybox | 120 | is generally used when creating a statically linked version of busybox |
121 | for use as a rescue shell, in the event that you screw up your system. | 121 | for use as a rescue shell, in the event that you screw up your system. |
122 | 122 | ||
123 | This is implemented by re-execing /proc/self/exe (typically) | 123 | This is implemented by re-execing /proc/self/exe (typically) |
124 | with right parameters. | 124 | with right parameters. |
125 | 125 | ||
126 | However, there are drawbacks: it is problematic in chroot jails | 126 | However, there are drawbacks: it is problematic in chroot jails |
127 | without mounted /proc, and ps/top may show command name as 'exe' | 127 | without mounted /proc, and ps/top may show command name as 'exe' |
128 | for applets started this way. | 128 | for applets started this way. |
129 | 129 | ||
130 | config FEATURE_SH_NOFORK | 130 | config FEATURE_SH_NOFORK |
131 | bool "Run 'nofork' applets directly" | 131 | bool "Run 'nofork' applets directly" |
132 | default n | 132 | default n |
133 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH | 133 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH |
134 | help | 134 | help |
135 | This option causes busybox shells to not execute typical | 135 | This option causes busybox shells to not execute typical |
136 | fork/exec/wait sequence, but call <applet>_main directly, | 136 | fork/exec/wait sequence, but call <applet>_main directly, |
137 | if possible. (Sometimes it is not possible: for example, | 137 | if possible. (Sometimes it is not possible: for example, |
138 | this is not possible in pipes). | 138 | this is not possible in pipes). |
139 | 139 | ||
140 | This will be done only for some applets (those which are marked | 140 | This will be done only for some applets (those which are marked |
141 | NOFORK in include/applets.h). | 141 | NOFORK in include/applets.h). |
142 | 142 | ||
143 | This may significantly speed up some shell scripts. | 143 | This may significantly speed up some shell scripts. |
144 | 144 | ||
145 | This feature is relatively new. Use with care. Report bugs | 145 | This feature is relatively new. Use with care. Report bugs |
146 | to project mailing list. | 146 | to project mailing list. |
147 | |||
148 | config FEATURE_SH_READ_FRAC | ||
149 | bool "read -t N.NNN support (+110 bytes)" | ||
150 | default y | ||
151 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH | ||
152 | help | ||
153 | Enable support for fractional second timeout in read builtin. | ||
147 | 154 | ||
148 | config FEATURE_SH_HISTFILESIZE | 155 | config FEATURE_SH_HISTFILESIZE |
149 | bool "Use $HISTFILESIZE" | 156 | bool "Use $HISTFILESIZE" |
150 | default y | 157 | default y |
151 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH | 158 | depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH |
152 | help | 159 | help |
153 | This option makes busybox shells to use $HISTFILESIZE variable | 160 | This option makes busybox shells to use $HISTFILESIZE variable |
154 | to set shell history size. Note that its max value is capped | 161 | to set shell history size. Note that its max value is capped |
155 | by "History size" setting in library tuning section. | 162 | by "History size" setting in library tuning section. |
156 | 163 | ||
157 | endif # Options common to all shells | 164 | endif # Options common to all shells |
158 | 165 | ||
diff --git a/shell/ash.c b/shell/ash.c index dc5561765..28b522d7c 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -31,15 +31,14 @@ | |||
31 | */ | 31 | */ |
32 | 32 | ||
33 | //config:config ASH | 33 | //config:config ASH |
34 | //config: bool "ash" | 34 | //config: bool "ash (77 kb)" |
35 | //config: default y | 35 | //config: default y |
36 | //config: depends on !NOMMU | 36 | //config: depends on !NOMMU |
37 | //config: help | 37 | //config: help |
38 | //config: Tha 'ash' shell adds about 60k in the default configuration and is | 38 | //config: The most complete and most pedantically correct shell included with |
39 | //config: the most complete and most pedantically correct shell included with | 39 | //config: busybox. This shell is actually a derivative of the Debian 'dash' |
40 | //config: busybox. This shell is actually a derivative of the Debian 'dash' | 40 | //config: shell (by Herbert Xu), which was created by porting the 'ash' shell |
41 | //config: shell (by Herbert Xu), which was created by porting the 'ash' shell | 41 | //config: (written by Kenneth Almquist) from NetBSD. |
42 | //config: (written by Kenneth Almquist) from NetBSD. | ||
43 | //config: | 42 | //config: |
44 | //config:# ash options | 43 | //config:# ash options |
45 | //config:# note: Don't remove !NOMMU part in the next line; it would break | 44 | //config:# note: Don't remove !NOMMU part in the next line; it would break |
@@ -56,11 +55,11 @@ | |||
56 | //config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now | 55 | //config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now |
57 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 56 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
58 | //config: help | 57 | //config: help |
59 | //config: Do not use glob() function from libc, use internal implementation. | 58 | //config: Do not use glob() function from libc, use internal implementation. |
60 | //config: Use this if you are getting "glob.h: No such file or directory" | 59 | //config: Use this if you are getting "glob.h: No such file or directory" |
61 | //config: or similar build errors. | 60 | //config: or similar build errors. |
62 | //config: Note that as of now (2017-01), uclibc and musl glob() both have bugs | 61 | //config: Note that as of now (2017-01), uclibc and musl glob() both have bugs |
63 | //config: which would break ash if you select N here. | 62 | //config: which would break ash if you select N here. |
64 | //config: | 63 | //config: |
65 | //config:config ASH_BASH_COMPAT | 64 | //config:config ASH_BASH_COMPAT |
66 | //config: bool "bash-compatible extensions" | 65 | //config: bool "bash-compatible extensions" |
@@ -82,37 +81,37 @@ | |||
82 | //config: default y | 81 | //config: default y |
83 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 82 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
84 | //config: help | 83 | //config: help |
85 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". | 84 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". |
86 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. | 85 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. |
87 | //config: You can reset the generator by using a specified start value. | 86 | //config: You can reset the generator by using a specified start value. |
88 | //config: After "unset RANDOM" the generator will switch off and this | 87 | //config: After "unset RANDOM" the generator will switch off and this |
89 | //config: variable will no longer have special treatment. | 88 | //config: variable will no longer have special treatment. |
90 | //config: | 89 | //config: |
91 | //config:config ASH_EXPAND_PRMT | 90 | //config:config ASH_EXPAND_PRMT |
92 | //config: bool "Expand prompt string" | 91 | //config: bool "Expand prompt string" |
93 | //config: default y | 92 | //config: default y |
94 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 93 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
95 | //config: help | 94 | //config: help |
96 | //config: $PS# may contain volatile content, such as backquote commands. | 95 | //config: $PS# may contain volatile content, such as backquote commands. |
97 | //config: This option recreates the prompt string from the environment | 96 | //config: This option recreates the prompt string from the environment |
98 | //config: variable each time it is displayed. | 97 | //config: variable each time it is displayed. |
99 | //config: | 98 | //config: |
100 | //config:config ASH_IDLE_TIMEOUT | 99 | //config:config ASH_IDLE_TIMEOUT |
101 | //config: bool "Idle timeout variable $TMOUT" | 100 | //config: bool "Idle timeout variable $TMOUT" |
102 | //config: default y | 101 | //config: default y |
103 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 102 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
104 | //config: help | 103 | //config: help |
105 | //config: Enable bash-like auto-logout after $TMOUT seconds of idle time. | 104 | //config: Enable bash-like auto-logout after $TMOUT seconds of idle time. |
106 | //config: | 105 | //config: |
107 | //config:config ASH_MAIL | 106 | //config:config ASH_MAIL |
108 | //config: bool "Check for new mail in interactive shell" | 107 | //config: bool "Check for new mail in interactive shell" |
109 | //config: default y | 108 | //config: default y |
110 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 109 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
111 | //config: help | 110 | //config: help |
112 | //config: Enable "check for new mail" function: | 111 | //config: Enable "check for new mail" function: |
113 | //config: if set, $MAIL file and $MAILPATH list of files | 112 | //config: if set, $MAIL file and $MAILPATH list of files |
114 | //config: are checked for mtime changes, and "you have mail" | 113 | //config: are checked for mtime changes, and "you have mail" |
115 | //config: message is printed if change is detected. | 114 | //config: message is printed if change is detected. |
116 | //config: | 115 | //config: |
117 | //config:config ASH_ECHO | 116 | //config:config ASH_ECHO |
118 | //config: bool "echo builtin" | 117 | //config: bool "echo builtin" |
@@ -144,9 +143,9 @@ | |||
144 | //config: default y | 143 | //config: default y |
145 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 144 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
146 | //config: help | 145 | //config: help |
147 | //config: Enable support for the 'command' builtin, which allows | 146 | //config: Enable support for the 'command' builtin, which allows |
148 | //config: you to run the specified command or builtin, | 147 | //config: you to run the specified command or builtin, |
149 | //config: even when there is a function with the same name. | 148 | //config: even when there is a function with the same name. |
150 | //config: | 149 | //config: |
151 | //config: | 150 | //config: |
152 | //config:config ASH_NOCONSOLE | 151 | //config:config ASH_NOCONSOLE |
@@ -1746,7 +1745,7 @@ static char * | |||
1746 | stack_nputstr(const char *s, size_t n, char *p) | 1745 | stack_nputstr(const char *s, size_t n, char *p) |
1747 | { | 1746 | { |
1748 | p = makestrspace(n, p); | 1747 | p = makestrspace(n, p); |
1749 | p = (char *)memcpy(p, s, n) + n; | 1748 | p = (char *)mempcpy(p, s, n); |
1750 | return p; | 1749 | return p; |
1751 | } | 1750 | } |
1752 | 1751 | ||
@@ -1830,7 +1829,7 @@ number(const char *s) | |||
1830 | } | 1829 | } |
1831 | 1830 | ||
1832 | /* | 1831 | /* |
1833 | * Produce a possibly single quoted string suitable as input to the shell. | 1832 | * Produce a single quoted string suitable as input to the shell. |
1834 | * The return string is allocated on the stack. | 1833 | * The return string is allocated on the stack. |
1835 | */ | 1834 | */ |
1836 | static char * | 1835 | static char * |
@@ -1849,7 +1848,7 @@ single_quote(const char *s) | |||
1849 | q = p = makestrspace(len + 3, p); | 1848 | q = p = makestrspace(len + 3, p); |
1850 | 1849 | ||
1851 | *q++ = '\''; | 1850 | *q++ = '\''; |
1852 | q = (char *)memcpy(q, s, len) + len; | 1851 | q = (char *)mempcpy(q, s, len); |
1853 | *q++ = '\''; | 1852 | *q++ = '\''; |
1854 | s += len; | 1853 | s += len; |
1855 | 1854 | ||
@@ -1863,7 +1862,7 @@ single_quote(const char *s) | |||
1863 | q = p = makestrspace(len + 3, p); | 1862 | q = p = makestrspace(len + 3, p); |
1864 | 1863 | ||
1865 | *q++ = '"'; | 1864 | *q++ = '"'; |
1866 | q = (char *)memcpy(q, s - len, len) + len; | 1865 | q = (char *)mempcpy(q, s - len, len); |
1867 | *q++ = '"'; | 1866 | *q++ = '"'; |
1868 | 1867 | ||
1869 | STADJUST(q - p, p); | 1868 | STADJUST(q - p, p); |
@@ -1874,6 +1873,47 @@ single_quote(const char *s) | |||
1874 | return stackblock(); | 1873 | return stackblock(); |
1875 | } | 1874 | } |
1876 | 1875 | ||
1876 | /* | ||
1877 | * Produce a possibly single quoted string suitable as input to the shell. | ||
1878 | * If 'conditional' is nonzero, quoting is only done if the string contains | ||
1879 | * non-shellsafe characters, or is identical to a shell keyword (reserved | ||
1880 | * word); if it is zero, quoting is always done. | ||
1881 | * If quoting was done, the return string is allocated on the stack, | ||
1882 | * otherwise a pointer to the original string is returned. | ||
1883 | */ | ||
1884 | static const char * | ||
1885 | maybe_single_quote(const char *s) | ||
1886 | { | ||
1887 | const char *p = s; | ||
1888 | |||
1889 | while (*p) { | ||
1890 | /* Assuming ACSII */ | ||
1891 | /* quote ctrl_chars space !"#$%&'()* */ | ||
1892 | if (*p < '+') | ||
1893 | goto need_quoting; | ||
1894 | /* quote ;<=>? */ | ||
1895 | if (*p >= ';' && *p <= '?') | ||
1896 | goto need_quoting; | ||
1897 | /* quote `[\ */ | ||
1898 | if (*p == '`') | ||
1899 | goto need_quoting; | ||
1900 | if (*p == '[') | ||
1901 | goto need_quoting; | ||
1902 | if (*p == '\\') | ||
1903 | goto need_quoting; | ||
1904 | /* quote {|}~ DEL and high bytes */ | ||
1905 | if (*p > 'z') | ||
1906 | goto need_quoting; | ||
1907 | /* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */ | ||
1908 | /* TODO: maybe avoid quoting % */ | ||
1909 | p++; | ||
1910 | } | ||
1911 | return s; | ||
1912 | |||
1913 | need_quoting: | ||
1914 | return single_quote(s); | ||
1915 | } | ||
1916 | |||
1877 | 1917 | ||
1878 | /* ============ nextopt */ | 1918 | /* ============ nextopt */ |
1879 | 1919 | ||
@@ -2362,10 +2402,10 @@ setvar(const char *name, const char *val, int flags) | |||
2362 | 2402 | ||
2363 | INT_OFF; | 2403 | INT_OFF; |
2364 | nameeq = ckmalloc(namelen + vallen + 2); | 2404 | nameeq = ckmalloc(namelen + vallen + 2); |
2365 | p = memcpy(nameeq, name, namelen) + namelen; | 2405 | p = mempcpy(nameeq, name, namelen); |
2366 | if (val) { | 2406 | if (val) { |
2367 | *p++ = '='; | 2407 | *p++ = '='; |
2368 | p = memcpy(p, val, vallen) + vallen; | 2408 | p = mempcpy(p, val, vallen); |
2369 | } | 2409 | } |
2370 | *p = '\0'; | 2410 | *p = '\0'; |
2371 | setvareq(nameeq, flags | VNOSAVE); | 2411 | setvareq(nameeq, flags | VNOSAVE); |
@@ -2512,8 +2552,7 @@ path_advance(const char **path, const char *name) | |||
2512 | growstackblock(); | 2552 | growstackblock(); |
2513 | q = stackblock(); | 2553 | q = stackblock(); |
2514 | if (p != start) { | 2554 | if (p != start) { |
2515 | memcpy(q, start, p - start); | 2555 | q = mempcpy(q, start, p - start); |
2516 | q += p - start; | ||
2517 | *q++ = '/'; | 2556 | *q++ = '/'; |
2518 | } | 2557 | } |
2519 | strcpy(q, name); | 2558 | strcpy(q, name); |
@@ -6277,7 +6316,7 @@ rmescapes(char *str, int flag) | |||
6277 | } | 6316 | } |
6278 | q = r; | 6317 | q = r; |
6279 | if (len > 0) { | 6318 | if (len > 0) { |
6280 | q = (char *)memcpy(q, str, len) + len; | 6319 | q = (char *)mempcpy(q, str, len); |
6281 | } | 6320 | } |
6282 | } | 6321 | } |
6283 | 6322 | ||
@@ -10161,18 +10200,36 @@ evalcommand(union node *cmd, int flags) | |||
10161 | 10200 | ||
10162 | /* Print the command if xflag is set. */ | 10201 | /* Print the command if xflag is set. */ |
10163 | if (xflag) { | 10202 | if (xflag) { |
10164 | int n; | 10203 | const char *pfx = ""; |
10165 | const char *p = " %s" + 1; | 10204 | |
10205 | fdprintf(preverrout_fd, "%s", expandstr(ps4val())); | ||
10166 | 10206 | ||
10167 | fdprintf(preverrout_fd, p, expandstr(ps4val())); | ||
10168 | sp = varlist.list; | 10207 | sp = varlist.list; |
10169 | for (n = 0; n < 2; n++) { | 10208 | while (sp) { |
10170 | while (sp) { | 10209 | char *varval = sp->text; |
10171 | fdprintf(preverrout_fd, p, sp->text); | 10210 | char *eq = strchrnul(varval, '='); |
10172 | sp = sp->next; | 10211 | if (*eq) |
10173 | p = " %s"; | 10212 | eq++; |
10174 | } | 10213 | fdprintf(preverrout_fd, "%s%.*s%s", |
10175 | sp = arglist.list; | 10214 | pfx, |
10215 | (int)(eq - varval), varval, | ||
10216 | maybe_single_quote(eq) | ||
10217 | ); | ||
10218 | sp = sp->next; | ||
10219 | pfx = " "; | ||
10220 | } | ||
10221 | |||
10222 | sp = arglist.list; | ||
10223 | while (sp) { | ||
10224 | fdprintf(preverrout_fd, "%s%s", | ||
10225 | pfx, | ||
10226 | /* always quote if matches reserved word: */ | ||
10227 | findkwd(sp->text) | ||
10228 | ? single_quote(sp->text) | ||
10229 | : maybe_single_quote(sp->text) | ||
10230 | ); | ||
10231 | sp = sp->next; | ||
10232 | pfx = " "; | ||
10176 | } | 10233 | } |
10177 | safe_write(preverrout_fd, "\n", 1); | 10234 | safe_write(preverrout_fd, "\n", 1); |
10178 | } | 10235 | } |
diff --git a/shell/ash_test/ash-parsing/argv0.right b/shell/ash_test/ash-parsing/argv0.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/ash_test/ash-parsing/argv0.right | |||
@@ -0,0 +1 @@ | |||
OK | |||
diff --git a/shell/ash_test/ash-parsing/argv0.tests b/shell/ash_test/ash-parsing/argv0.tests new file mode 100755 index 000000000..f5c40f6fe --- /dev/null +++ b/shell/ash_test/ash-parsing/argv0.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" "$0" arg | ||
3 | fi | ||
4 | echo OK | ||
diff --git a/shell/ash_test/ash-parsing/brace1.right b/shell/ash_test/ash-parsing/brace1.right new file mode 100644 index 000000000..10aa7a419 --- /dev/null +++ b/shell/ash_test/ash-parsing/brace1.right | |||
@@ -0,0 +1,7 @@ | |||
1 | {abc} | ||
2 | { | ||
3 | } | ||
4 | ./brace1.tests: line 4: {cmd: not found | ||
5 | ./brace1.tests: line 5: {: not found | ||
6 | ./brace1.tests: line 6: {: not found | ||
7 | Done: 127 | ||
diff --git a/shell/ash_test/ash-parsing/brace1.tests b/shell/ash_test/ash-parsing/brace1.tests new file mode 100755 index 000000000..2b45927c0 --- /dev/null +++ b/shell/ash_test/ash-parsing/brace1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | echo {abc} | ||
2 | echo { | ||
3 | echo } | ||
4 | {cmd | ||
5 | ""{ | ||
6 | {"" | ||
7 | echo Done: $? | ||
diff --git a/shell/ash_test/ash-parsing/brace2.right b/shell/ash_test/ash-parsing/brace2.right new file mode 100644 index 000000000..37a966654 --- /dev/null +++ b/shell/ash_test/ash-parsing/brace2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | {q,w} | ||
2 | {q,w} | ||
3 | Done | ||
diff --git a/shell/ash_test/ash-parsing/brace2.tests b/shell/ash_test/ash-parsing/brace2.tests new file mode 100755 index 000000000..ef75f0b70 --- /dev/null +++ b/shell/ash_test/ash-parsing/brace2.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | v='{q,w}' | ||
2 | # Should not brace-expand v value | ||
3 | echo $v | ||
4 | echo "$v" | ||
5 | echo Done | ||
diff --git a/shell/ash_test/ash-parsing/comment1.right b/shell/ash_test/ash-parsing/comment1.right new file mode 100644 index 000000000..a102b1d4e --- /dev/null +++ b/shell/ash_test/ash-parsing/comment1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Nothing: | ||
2 | String: #should-be-echoed | ||
diff --git a/shell/ash_test/ash-parsing/comment1.tests b/shell/ash_test/ash-parsing/comment1.tests new file mode 100755 index 000000000..d268860ff --- /dev/null +++ b/shell/ash_test/ash-parsing/comment1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | echo Nothing: #should-not-be-echoed | ||
2 | echo String: ""#should-be-echoed | ||
diff --git a/shell/ash_test/ash-parsing/eol1.right b/shell/ash_test/ash-parsing/eol1.right new file mode 100644 index 000000000..31c896f62 --- /dev/null +++ b/shell/ash_test/ash-parsing/eol1.right | |||
@@ -0,0 +1 @@ | |||
Done:0 | |||
diff --git a/shell/ash_test/ash-parsing/eol1.tests b/shell/ash_test/ash-parsing/eol1.tests new file mode 100755 index 000000000..f1b55e8b8 --- /dev/null +++ b/shell/ash_test/ash-parsing/eol1.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | # bug was that we treated <newline> as ';' in this line: | ||
2 | true || echo foo | | ||
3 | echo BAD1 | cat | ||
4 | |||
5 | # variation on the same theme | ||
6 | true || echo foo | | ||
7 | # comment | ||
8 | echo BAD2 | cat | ||
9 | |||
10 | # variation on the same theme | ||
11 | true || echo foo | | ||
12 | |||
13 | echo BAD3 | cat | ||
14 | |||
15 | # this should error out, but currently works in hush: | ||
16 | #true || echo foo |; | ||
17 | |||
18 | echo Done:$? | ||
diff --git a/shell/ash_test/ash-parsing/escape1.right b/shell/ash_test/ash-parsing/escape1.right new file mode 100644 index 000000000..1899b87ef --- /dev/null +++ b/shell/ash_test/ash-parsing/escape1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | \ | ||
2 | a\b | ||
3 | \\ | ||
4 | c\\d | ||
diff --git a/shell/ash_test/ash-parsing/escape1.tests b/shell/ash_test/ash-parsing/escape1.tests new file mode 100755 index 000000000..25ac96b25 --- /dev/null +++ b/shell/ash_test/ash-parsing/escape1.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 | ||
2 | |||
3 | echo "\\" | ||
4 | echo a"\\"b | ||
5 | echo '\\' | ||
6 | echo c'\\'d | ||
diff --git a/shell/ash_test/ash-parsing/escape2.right b/shell/ash_test/ash-parsing/escape2.right new file mode 100644 index 000000000..f55fd4a42 --- /dev/null +++ b/shell/ash_test/ash-parsing/escape2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | *?[a]* | ||
2 | a*?[a]*b | ||
3 | *?[a]* | ||
4 | c*?[a]*d | ||
diff --git a/shell/ash_test/ash-parsing/escape2.tests b/shell/ash_test/ash-parsing/escape2.tests new file mode 100755 index 000000000..ee718018d --- /dev/null +++ b/shell/ash_test/ash-parsing/escape2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | echo "*?[a]*" | ||
2 | echo a"*?[a]*"b | ||
3 | echo '*?[a]*' | ||
4 | echo c'*?[a]*'d | ||
diff --git a/shell/ash_test/ash-parsing/escape3.right b/shell/ash_test/ash-parsing/escape3.right new file mode 100644 index 000000000..da02a976a --- /dev/null +++ b/shell/ash_test/ash-parsing/escape3.right | |||
@@ -0,0 +1,23 @@ | |||
1 | v: a \ b \\ c \\\ d \\\\ e | ||
2 | v: a \ b \\ c \\\ d \\\\ e | ||
3 | Unquoted: | ||
4 | .a. | ||
5 | .\. | ||
6 | .b. | ||
7 | .\\. | ||
8 | .c. | ||
9 | .\\\. | ||
10 | .d. | ||
11 | .\\\\. | ||
12 | .e. | ||
13 | Quoted: | ||
14 | .a. | ||
15 | .\. | ||
16 | .b. | ||
17 | .\\. | ||
18 | .c. | ||
19 | .\\\. | ||
20 | .d. | ||
21 | .\\\\. | ||
22 | .e. | ||
23 | done | ||
diff --git a/shell/ash_test/ash-parsing/escape3.tests b/shell/ash_test/ash-parsing/escape3.tests new file mode 100755 index 000000000..18705bd0c --- /dev/null +++ b/shell/ash_test/ash-parsing/escape3.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 | ||
2 | |||
3 | v='a \ b \\ c \\\ d \\\\ e' | ||
4 | echo v: $v | ||
5 | echo v: "$v" | ||
6 | echo Unquoted: | ||
7 | for a in $v; do echo .$a.; done | ||
8 | echo Quoted: | ||
9 | for a in $v; do echo ".$a."; done | ||
10 | echo done | ||
diff --git a/shell/ash_test/ash-parsing/escape4.right b/shell/ash_test/ash-parsing/escape4.right new file mode 100644 index 000000000..5de3e0c90 --- /dev/null +++ b/shell/ash_test/ash-parsing/escape4.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok | ||
2 | End | ||
diff --git a/shell/ash_test/ash-parsing/escape4.tests b/shell/ash_test/ash-parsing/escape4.tests new file mode 100755 index 000000000..df8bf0af7 --- /dev/null +++ b/shell/ash_test/ash-parsing/escape4.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | i\ | ||
2 | f tr\ | ||
3 | ue; th\ | ||
4 | en echo "O\ | ||
5 | k"; fi; echo "\ | ||
6 | End" \ No newline at end of file | ||
diff --git a/shell/ash_test/ash-parsing/escape5.right b/shell/ash_test/ash-parsing/escape5.right new file mode 100644 index 000000000..3cdd393c7 --- /dev/null +++ b/shell/ash_test/ash-parsing/escape5.right | |||
@@ -0,0 +1,9 @@ | |||
1 | a\nb\nc\n | ||
2 | a | ||
3 | b | ||
4 | c | ||
5 | a\nb\nc\n | ||
6 | a | ||
7 | b | ||
8 | c | ||
9 | Done | ||
diff --git a/shell/ash_test/ash-parsing/escape5.tests b/shell/ash_test/ash-parsing/escape5.tests new file mode 100755 index 000000000..337a98ec7 --- /dev/null +++ b/shell/ash_test/ash-parsing/escape5.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | v="a\nb\nc\n" | ||
2 | echo "$v" | ||
3 | printf "$v" | ||
4 | v='a\nb\nc\n' | ||
5 | echo "$v" | ||
6 | printf "$v" | ||
7 | echo Done | ||
diff --git a/shell/ash_test/ash-parsing/group1.right b/shell/ash_test/ash-parsing/group1.right new file mode 100644 index 000000000..6a7c4be0a --- /dev/null +++ b/shell/ash_test/ash-parsing/group1.right | |||
@@ -0,0 +1 @@ | |||
word} } | |||
diff --git a/shell/ash_test/ash-parsing/group1.tests b/shell/ash_test/ash-parsing/group1.tests new file mode 100755 index 000000000..f063fbcb3 --- /dev/null +++ b/shell/ash_test/ash-parsing/group1.tests | |||
@@ -0,0 +1 @@ | |||
{ echo word} }; } | |||
diff --git a/shell/ash_test/ash-parsing/group2.right b/shell/ash_test/ash-parsing/group2.right new file mode 100644 index 000000000..df4d9306a --- /dev/null +++ b/shell/ash_test/ash-parsing/group2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | got TERM | ||
2 | Done: 0 | ||
diff --git a/shell/ash_test/ash-parsing/group2.tests b/shell/ash_test/ash-parsing/group2.tests new file mode 100755 index 000000000..d99178585 --- /dev/null +++ b/shell/ash_test/ash-parsing/group2.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # Bug was in handling of "}&" without space | ||
2 | { trap "echo got TERM" TERM; sleep 2; }& sleep 1; kill $!; wait | ||
3 | echo Done: $? | ||
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords1.right b/shell/ash_test/ash-parsing/groups_and_keywords1.right new file mode 100644 index 000000000..4c46650dc --- /dev/null +++ b/shell/ash_test/ash-parsing/groups_and_keywords1.right | |||
@@ -0,0 +1,11 @@ | |||
1 | Semicolons after } can be omitted 1: | ||
2 | foo | ||
3 | bar | ||
4 | Semicolons after } can be omitted 2: | ||
5 | foo | ||
6 | bar | ||
7 | Semicolons after fi can be omitted: | ||
8 | foo | ||
9 | bar | ||
10 | baz | ||
11 | Done:0 | ||
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords1.tests b/shell/ash_test/ash-parsing/groups_and_keywords1.tests new file mode 100755 index 000000000..01944d714 --- /dev/null +++ b/shell/ash_test/ash-parsing/groups_and_keywords1.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | echo "Semicolons after } can be omitted 1:" | ||
2 | if { echo foo; } then { echo bar; } fi | ||
3 | |||
4 | echo "Semicolons after } can be omitted 2:" | ||
5 | while { echo foo; } do { echo bar; break; } done | ||
6 | |||
7 | echo "Semicolons after fi can be omitted:" | ||
8 | while if echo foo; then echo bar; fi do echo baz; break; done | ||
9 | |||
10 | echo Done:$? | ||
diff --git a/shell/ash_test/ash-parsing/negate.right b/shell/ash_test/ash-parsing/negate.right new file mode 100644 index 000000000..61d2ecd3a --- /dev/null +++ b/shell/ash_test/ash-parsing/negate.right | |||
@@ -0,0 +1,36 @@ | |||
1 | ! printing ! | ||
2 | 0 | ||
3 | 1 | ||
4 | 1 | ||
5 | 0 | ||
6 | 0 | ||
7 | 0 | ||
8 | ! | ||
9 | a | ||
10 | b | ||
11 | c | ||
12 | ! 1 | ||
13 | a 1 | ||
14 | b 1 | ||
15 | c 1 | ||
16 | ! 1 | ||
17 | a 1 | ||
18 | b 1 | ||
19 | c 1 | ||
20 | 0 | ||
21 | 0 | ||
22 | 0 | ||
23 | 0 | ||
24 | 1 | ||
25 | 1 | ||
26 | 1 | ||
27 | 1 | ||
28 | 0 | ||
29 | 0 | ||
30 | 0 | ||
31 | 0 | ||
32 | 1 | ||
33 | 1 | ||
34 | 1 | ||
35 | 1 | ||
36 | Done | ||
diff --git a/shell/ash_test/ash-parsing/negate.tests b/shell/ash_test/ash-parsing/negate.tests new file mode 100755 index 000000000..51151cbd4 --- /dev/null +++ b/shell/ash_test/ash-parsing/negate.tests | |||
@@ -0,0 +1,19 @@ | |||
1 | echo ! printing ! | ||
2 | ! false | ||
3 | echo $? | ||
4 | ! true | ||
5 | echo $? | ||
6 | if ! false; then false; echo $?; fi | ||
7 | echo $? | ||
8 | if ! false; then ! false; echo $?; fi | ||
9 | echo $? | ||
10 | PRINTF=`which printf` | ||
11 | for a in ! a b c; do echo $a; done | ||
12 | for a in ! a b c; do ! printf "$a "; echo $?; done | ||
13 | test x"$PRINTF" = x"" && exit 1 | ||
14 | for a in ! a b c; do ! "$PRINTF" "$a "; echo $?; done | ||
15 | for a in ! a b c; do ! printf "$a " | false; echo $?; done | ||
16 | for a in ! a b c; do ! printf "$a " | true; echo $?; done | ||
17 | for a in ! a b c; do ! { printf "$a " | false; }; echo $?; done | ||
18 | for a in ! a b c; do ! { printf "$a " | true; }; echo $?; done | ||
19 | echo Done | ||
diff --git a/shell/ash_test/ash-parsing/noeol.right b/shell/ash_test/ash-parsing/noeol.right new file mode 100644 index 000000000..e427984d4 --- /dev/null +++ b/shell/ash_test/ash-parsing/noeol.right | |||
@@ -0,0 +1 @@ | |||
HELLO | |||
diff --git a/shell/ash_test/ash-parsing/noeol.tests b/shell/ash_test/ash-parsing/noeol.tests new file mode 100755 index 000000000..a93113a03 --- /dev/null +++ b/shell/ash_test/ash-parsing/noeol.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | # next line has no EOL! | ||
2 | echo HELLO \ No newline at end of file | ||
diff --git a/shell/ash_test/ash-parsing/noeol2.right b/shell/ash_test/ash-parsing/noeol2.right new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/shell/ash_test/ash-parsing/noeol2.right | |||
@@ -0,0 +1 @@ | |||
1 | |||
diff --git a/shell/ash_test/ash-parsing/noeol2.tests b/shell/ash_test/ash-parsing/noeol2.tests new file mode 100755 index 000000000..1220f056f --- /dev/null +++ b/shell/ash_test/ash-parsing/noeol2.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | # last line has no EOL! | ||
2 | if true | ||
3 | then | ||
4 | echo 1 | ||
5 | else | ||
6 | echo 2 | ||
7 | fi \ No newline at end of file | ||
diff --git a/shell/ash_test/ash-parsing/noeol3.right b/shell/ash_test/ash-parsing/noeol3.right new file mode 100644 index 000000000..c2a0e38f8 --- /dev/null +++ b/shell/ash_test/ash-parsing/noeol3.right | |||
@@ -0,0 +1 @@ | |||
./noeol3.tests: line 2: syntax error: unterminated quoted string | |||
diff --git a/shell/ash_test/ash-parsing/noeol3.tests b/shell/ash_test/ash-parsing/noeol3.tests new file mode 100755 index 000000000..ec958ed7a --- /dev/null +++ b/shell/ash_test/ash-parsing/noeol3.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | # last line has no EOL! | ||
2 | echo "unterminated \ No newline at end of file | ||
diff --git a/shell/ash_test/ash-parsing/process_subst.right b/shell/ash_test/ash-parsing/process_subst.right new file mode 100644 index 000000000..397bc8067 --- /dev/null +++ b/shell/ash_test/ash-parsing/process_subst.right | |||
@@ -0,0 +1,3 @@ | |||
1 | TESTzzBEST | ||
2 | TEST$(echo zz)BEST | ||
3 | TEST'BEST | ||
diff --git a/shell/ash_test/ash-parsing/process_subst.tests b/shell/ash_test/ash-parsing/process_subst.tests new file mode 100755 index 000000000..21996bc0e --- /dev/null +++ b/shell/ash_test/ash-parsing/process_subst.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | echo "TEST`echo zz;echo;echo`BEST" | ||
2 | echo "TEST`echo '$(echo zz)'`BEST" | ||
3 | echo "TEST`echo "'"`BEST" | ||
diff --git a/shell/ash_test/ash-parsing/quote1.right b/shell/ash_test/ash-parsing/quote1.right new file mode 100644 index 000000000..cb382054c --- /dev/null +++ b/shell/ash_test/ash-parsing/quote1.right | |||
@@ -0,0 +1 @@ | |||
'1' | |||
diff --git a/shell/ash_test/ash-parsing/quote1.tests b/shell/ash_test/ash-parsing/quote1.tests new file mode 100755 index 000000000..f55895466 --- /dev/null +++ b/shell/ash_test/ash-parsing/quote1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | a=1 | ||
2 | echo "'$a'" | ||
diff --git a/shell/ash_test/ash-parsing/quote2.right b/shell/ash_test/ash-parsing/quote2.right new file mode 100644 index 000000000..3bc9edcd6 --- /dev/null +++ b/shell/ash_test/ash-parsing/quote2.right | |||
@@ -0,0 +1 @@ | |||
>1 | |||
diff --git a/shell/ash_test/ash-parsing/quote2.tests b/shell/ash_test/ash-parsing/quote2.tests new file mode 100755 index 000000000..bd966f30b --- /dev/null +++ b/shell/ash_test/ash-parsing/quote2.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | a=1 | ||
2 | echo ">$a" | ||
diff --git a/shell/ash_test/ash-parsing/quote3.right b/shell/ash_test/ash-parsing/quote3.right new file mode 100644 index 000000000..bbe46df67 --- /dev/null +++ b/shell/ash_test/ash-parsing/quote3.right | |||
@@ -0,0 +1,12 @@ | |||
1 | Testing: in "" | ||
2 | .. | ||
3 | Testing: in '' | ||
4 | .. | ||
5 | Testing: in $empty | ||
6 | Testing: in $empty"" | ||
7 | .. | ||
8 | Testing: in $empty'' | ||
9 | .. | ||
10 | Testing: in "$empty" | ||
11 | .. | ||
12 | Finished | ||
diff --git a/shell/ash_test/ash-parsing/quote3.tests b/shell/ash_test/ash-parsing/quote3.tests new file mode 100755 index 000000000..b5fd5978c --- /dev/null +++ b/shell/ash_test/ash-parsing/quote3.tests | |||
@@ -0,0 +1,21 @@ | |||
1 | empty='' | ||
2 | |||
3 | echo 'Testing: in ""' | ||
4 | for a in ""; do echo ".$a."; done | ||
5 | |||
6 | echo 'Testing: in '"''" | ||
7 | for a in ''; do echo ".$a."; done | ||
8 | |||
9 | echo 'Testing: in $empty' | ||
10 | for a in $empty; do echo ".$a."; done | ||
11 | |||
12 | echo 'Testing: in $empty""' | ||
13 | for a in $empty""; do echo ".$a."; done | ||
14 | |||
15 | echo 'Testing: in $empty'"''" | ||
16 | for a in $empty''; do echo ".$a."; done | ||
17 | |||
18 | echo 'Testing: in "$empty"' | ||
19 | for a in "$empty"; do echo ".$a."; done | ||
20 | |||
21 | echo Finished | ||
diff --git a/shell/ash_test/ash-parsing/quote4.right b/shell/ash_test/ash-parsing/quote4.right new file mode 100644 index 000000000..b2901ea97 --- /dev/null +++ b/shell/ash_test/ash-parsing/quote4.right | |||
@@ -0,0 +1 @@ | |||
a b | |||
diff --git a/shell/ash_test/ash-parsing/quote4.tests b/shell/ash_test/ash-parsing/quote4.tests new file mode 100755 index 000000000..f1dabfa54 --- /dev/null +++ b/shell/ash_test/ash-parsing/quote4.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | a_b='a b' | ||
2 | echo "$a_b" | ||
diff --git a/shell/ash_test/ash-parsing/starquoted.right b/shell/ash_test/ash-parsing/starquoted.right new file mode 100644 index 000000000..b56323fe1 --- /dev/null +++ b/shell/ash_test/ash-parsing/starquoted.right | |||
@@ -0,0 +1,8 @@ | |||
1 | .1 abc d e f. | ||
2 | .1. | ||
3 | .abc. | ||
4 | .d e f. | ||
5 | .-1 abc d e f-. | ||
6 | .-1. | ||
7 | .abc. | ||
8 | .d e f-. | ||
diff --git a/shell/ash_test/ash-parsing/starquoted.tests b/shell/ash_test/ash-parsing/starquoted.tests new file mode 100755 index 000000000..2fe49b1cd --- /dev/null +++ b/shell/ash_test/ash-parsing/starquoted.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | if test $# = 0; then | ||
2 | exec "$THIS_SH" "$0" 1 abc 'd e f' | ||
3 | fi | ||
4 | |||
5 | for a in "$*"; do echo ".$a."; done | ||
6 | for a in "$@"; do echo ".$a."; done | ||
7 | for a in "-$*-"; do echo ".$a."; done | ||
8 | for a in "-$@-"; do echo ".$a."; done | ||
diff --git a/shell/ash_test/ash-parsing/starquoted2.right b/shell/ash_test/ash-parsing/starquoted2.right new file mode 100644 index 000000000..1bff408ca --- /dev/null +++ b/shell/ash_test/ash-parsing/starquoted2.right | |||
@@ -0,0 +1,8 @@ | |||
1 | Should be printed | ||
2 | Would not be printed by bash | ||
3 | Would not be printed by bash | ||
4 | Would not be printed by bash | ||
5 | Should be printed | ||
6 | Empty: | ||
7 | Empty: | ||
8 | Empty: | ||
diff --git a/shell/ash_test/ash-parsing/starquoted2.tests b/shell/ash_test/ash-parsing/starquoted2.tests new file mode 100755 index 000000000..7c5ff45b8 --- /dev/null +++ b/shell/ash_test/ash-parsing/starquoted2.tests | |||
@@ -0,0 +1,19 @@ | |||
1 | if test $# != 0; then | ||
2 | exec "$THIS_SH" "$0" | ||
3 | fi | ||
4 | |||
5 | # No params! | ||
6 | for a in "$*"; do echo Should be printed; done | ||
7 | for a in "$@"; do echo Should not be printed; done | ||
8 | # Yes, believe it or not, bash is mesmerized by "$@" and stops | ||
9 | # treating "" as "this word cannot be expanded to nothing, | ||
10 | # but must be at least null string". Now it can be expanded to nothing. | ||
11 | for a in "$@"""; do echo Would not be printed by bash; done | ||
12 | for a in """$@"; do echo Would not be printed by bash; done | ||
13 | for a in """$@"''"$@"''; do echo Would not be printed by bash; done | ||
14 | for a in ""; do echo Should be printed; done | ||
15 | |||
16 | # Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice: | ||
17 | printf 'Empty:%s\n' "$@" | ||
18 | printf "Empty:%s\n" "$@" | ||
19 | printf "Empty:%s\\n" "$@" | ||
diff --git a/shell/ash_test/ash-quoting/mode_x.right b/shell/ash_test/ash-quoting/mode_x.right new file mode 100644 index 000000000..c2dd3550c --- /dev/null +++ b/shell/ash_test/ash-quoting/mode_x.right | |||
@@ -0,0 +1,10 @@ | |||
1 | + var1=val | ||
2 | + var2='one two' | ||
3 | + true '%s\n' one 'two '"'"'three' four | ||
4 | + this=command | ||
5 | + 'this=command' | ||
6 | ./mode_x.tests: line 1: this=command: not found | ||
7 | + true | ||
8 | + true | ||
9 | + 'if' true | ||
10 | ./mode_x.tests: line 1: if: not found | ||
diff --git a/shell/ash_test/ash-quoting/mode_x.tests b/shell/ash_test/ash-quoting/mode_x.tests new file mode 100755 index 000000000..16dae3f4b --- /dev/null +++ b/shell/ash_test/ash-quoting/mode_x.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | set -x | ||
2 | |||
3 | var1=val | ||
4 | var2='one two' | ||
5 | true %s\\n one "two 'three" four | ||
6 | |||
7 | # assignment: | ||
8 | this=command | ||
9 | # NOT assignment, +x code should show it quoted: | ||
10 | "this=command" | ||
11 | |||
12 | if true; then true; fi | ||
13 | # +x code should quote 'if' here: | ||
14 | "if" true | ||
diff --git a/shell/ash_test/ash-read/read_t0.right b/shell/ash_test/ash-read/read_t0.right new file mode 100644 index 000000000..f02105961 --- /dev/null +++ b/shell/ash_test/ash-read/read_t0.right | |||
@@ -0,0 +1,3 @@ | |||
1 | ><[0] | ||
2 | ><[0] | ||
3 | ><[1] | ||
diff --git a/shell/ash_test/ash-read/read_t0.tests b/shell/ash_test/ash-read/read_t0.tests new file mode 100755 index 000000000..6b7bc217b --- /dev/null +++ b/shell/ash_test/ash-read/read_t0.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | # ><[0] | ||
2 | echo Ok | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; } | ||
3 | |||
4 | # This would not be deterministic: returns 0 "data exists" if EOF is seen | ||
5 | # (true terminated) - because EOF is considered to be data (read will not block), | ||
6 | # else returns 1 "no data". | ||
7 | ## ><[????] | ||
8 | #true | { read -t 0 reply; echo ">$reply<[$?]"; } | ||
9 | |||
10 | # ><[0] | ||
11 | true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; } | ||
12 | |||
13 | # ><[1] | ||
14 | sleep 0.2 | { read -p IGNORED_PROMPT -t 0 reply; echo ">$reply<[$?]"; } | ||
diff --git a/shell/ash_test/ash-vars/readonly0.right b/shell/ash_test/ash-vars/readonly0.right new file mode 100644 index 000000000..f3a6bde9e --- /dev/null +++ b/shell/ash_test/ash-vars/readonly0.right | |||
@@ -0,0 +1,13 @@ | |||
1 | readonly a='A' | ||
2 | readonly b='B' | ||
3 | Ok:0 | ||
4 | |||
5 | ./readonly0.tests: line 19: a: is read only | ||
6 | Fail:2 | ||
7 | ./readonly0.tests: readonly: line 21: a: is read only | ||
8 | Fail:2 | ||
9 | |||
10 | ./readonly0.tests: export: line 27: a: is read only | ||
11 | Fail:2 | ||
12 | |||
13 | Fail:1 | ||
diff --git a/shell/ash_test/ash-vars/readonly0.tests b/shell/ash_test/ash-vars/readonly0.tests new file mode 100755 index 000000000..94af79060 --- /dev/null +++ b/shell/ash_test/ash-vars/readonly0.tests | |||
@@ -0,0 +1,45 @@ | |||
1 | unset a b | ||
2 | # | ||
3 | readonly a=A | ||
4 | b=B | ||
5 | readonly b | ||
6 | # readonly on already readonly var is harmless: | ||
7 | readonly b a | ||
8 | readonly | grep '^readonly [ab]=' | ||
9 | # this should work: | ||
10 | export a b | ||
11 | export -n a b | ||
12 | echo Ok:$? | ||
13 | env | grep -e^a= -e^b= # shows nothing | ||
14 | |||
15 | echo | ||
16 | # these should all fail (despite the same value being assigned) | ||
17 | # bash does not abort even in non-interactive more (in script) | ||
18 | # ash does, using subshell to continue | ||
19 | true; (a=A) | ||
20 | echo Fail:$? | ||
21 | true; (readonly a=A) | ||
22 | echo Fail:$? | ||
23 | |||
24 | echo | ||
25 | # in bash, assignment in export fails, but export succeeds! :) | ||
26 | # we don't mimic that! | ||
27 | true; (export a=Z) | ||
28 | echo Fail:$? | ||
29 | #env | grep '^a=' | ||
30 | #echo "^^^a is exported" | ||
31 | export -n a # undo that bashism, if it happens | ||
32 | |||
33 | ## ash: assignment errors in "a=Z CMD" lead to CMD not executed | ||
34 | ## echo | ||
35 | ## export b | ||
36 | ## # this fails to both set and export a: | ||
37 | ## a=Z env | echo grep '^[ab]=' | ||
38 | ## echo "^^^a is not exported" | ||
39 | ## # but external command does get executed, and $? is not mangled (stays 42): | ||
40 | ## (exit 42); a=Z env echo Visible:$? | ||
41 | |||
42 | echo | ||
43 | # ash: this fails *silently*, bug? bash says "cannot unset: readonly variable" | ||
44 | true; unset a | ||
45 | echo Fail:$? | ||
diff --git a/shell/ash_test/ash-vars/readonly1.right b/shell/ash_test/ash-vars/readonly1.right index 2b363e325..1f5be64c7 100644 --- a/shell/ash_test/ash-vars/readonly1.right +++ b/shell/ash_test/ash-vars/readonly1.right | |||
@@ -1,2 +1,2 @@ | |||
1 | One:1 | 1 | Fail:2 |
2 | One:1 | 2 | Fail:2 |
diff --git a/shell/ash_test/ash-vars/readonly1.tests b/shell/ash_test/ash-vars/readonly1.tests index 81b461f5f..f3cccd940 100755 --- a/shell/ash_test/ash-vars/readonly1.tests +++ b/shell/ash_test/ash-vars/readonly1.tests | |||
@@ -1,7 +1,7 @@ | |||
1 | readonly bla=123 | 1 | readonly bla=123 |
2 | # Bare "eval bla=123" should abort ("eval" is a special builtin): | 2 | # Bare "eval bla=123" should abort ("eval" is a special builtin): |
3 | (eval bla=123 2>/dev/null; echo BUG) | 3 | (eval bla=123 2>/dev/null; echo BUG) |
4 | echo One:$? | 4 | echo Fail:$? |
5 | # "command BLTIN" disables "special-ness", should not abort: | 5 | # "command BLTIN" disables "special-ness", should not abort: |
6 | command eval bla=123 2>/dev/null | 6 | command eval bla=123 2>/dev/null |
7 | echo One:$? | 7 | echo Fail:$? |
diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index 983e6d184..caf033577 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all | |||
@@ -2,10 +2,24 @@ | |||
2 | 2 | ||
3 | TOPDIR=`pwd` | 3 | TOPDIR=`pwd` |
4 | 4 | ||
5 | test -x ash || { | 5 | if test ! -x ash; then |
6 | echo "No ./ash - creating a link to ../../busybox" | 6 | if test ! -x ../../busybox; then |
7 | ln -s ../../busybox ash | 7 | echo "Can't run tests. Put ash binary into this directory (`pwd`)" |
8 | } | 8 | exit 1 |
9 | fi | ||
10 | echo "No ./ash - creating a link to ../../busybox" | ||
11 | ln -s ../../busybox ash | ||
12 | fi | ||
13 | if test ! -f .config; then | ||
14 | if test ! -f ../../.config; then | ||
15 | echo "Missing .config file" | ||
16 | exit 1 | ||
17 | fi | ||
18 | cp ../../.config . || exit 1 | ||
19 | fi | ||
20 | |||
21 | eval $(sed -e '/^#/d' -e '/^$/d' -e 's:^:export :' .config) | ||
22 | |||
9 | test -x printenv || gcc -O2 -o printenv printenv.c || exit $? | 23 | test -x printenv || gcc -O2 -o printenv printenv.c || exit $? |
10 | test -x recho || gcc -O2 -o recho recho.c || exit $? | 24 | test -x recho || gcc -O2 -o recho recho.c || exit $? |
11 | test -x zecho || gcc -O2 -o zecho zecho.c || exit $? | 25 | test -x zecho || gcc -O2 -o zecho zecho.c || exit $? |
diff --git a/shell/cttyhack.c b/shell/cttyhack.c index f9b59c263..9004b4763 100644 --- a/shell/cttyhack.c +++ b/shell/cttyhack.c | |||
@@ -11,48 +11,48 @@ | |||
11 | //kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o | 11 | //kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o |
12 | 12 | ||
13 | //config:config CTTYHACK | 13 | //config:config CTTYHACK |
14 | //config: bool "cttyhack" | 14 | //config: bool "cttyhack (2.5 kb)" |
15 | //config: default y | 15 | //config: default y |
16 | //config: help | 16 | //config: help |
17 | //config: One common problem reported on the mailing list is the "can't | 17 | //config: One common problem reported on the mailing list is the "can't |
18 | //config: access tty; job control turned off" error message, which typically | 18 | //config: access tty; job control turned off" error message, which typically |
19 | //config: appears when one tries to use a shell with stdin/stdout on | 19 | //config: appears when one tries to use a shell with stdin/stdout on |
20 | //config: /dev/console. | 20 | //config: /dev/console. |
21 | //config: This device is special - it cannot be a controlling tty. | 21 | //config: This device is special - it cannot be a controlling tty. |
22 | //config: | 22 | //config: |
23 | //config: The proper solution is to use the correct device instead of | 23 | //config: The proper solution is to use the correct device instead of |
24 | //config: /dev/console. | 24 | //config: /dev/console. |
25 | //config: | 25 | //config: |
26 | //config: cttyhack provides a "quick and dirty" solution to this problem. | 26 | //config: cttyhack provides a "quick and dirty" solution to this problem. |
27 | //config: It analyzes stdin with various ioctls, trying to determine whether | 27 | //config: It analyzes stdin with various ioctls, trying to determine whether |
28 | //config: it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line). | 28 | //config: it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line). |
29 | //config: On Linux it also checks sysfs for a pointer to the active console. | 29 | //config: On Linux it also checks sysfs for a pointer to the active console. |
30 | //config: If cttyhack is able to find the real console device, it closes | 30 | //config: If cttyhack is able to find the real console device, it closes |
31 | //config: stdin/out/err and reopens that device. | 31 | //config: stdin/out/err and reopens that device. |
32 | //config: Then it executes the given program. Opening the device will make | 32 | //config: Then it executes the given program. Opening the device will make |
33 | //config: that device a controlling tty. This may require cttyhack | 33 | //config: that device a controlling tty. This may require cttyhack |
34 | //config: to be a session leader. | 34 | //config: to be a session leader. |
35 | //config: | 35 | //config: |
36 | //config: Example for /etc/inittab (for busybox init): | 36 | //config: Example for /etc/inittab (for busybox init): |
37 | //config: | 37 | //config: |
38 | //config: ::respawn:/bin/cttyhack /bin/sh | 38 | //config: ::respawn:/bin/cttyhack /bin/sh |
39 | //config: | 39 | //config: |
40 | //config: Starting an interactive shell from boot shell script: | 40 | //config: Starting an interactive shell from boot shell script: |
41 | //config: | 41 | //config: |
42 | //config: setsid cttyhack sh | 42 | //config: setsid cttyhack sh |
43 | //config: | 43 | //config: |
44 | //config: Giving controlling tty to shell running with PID 1: | 44 | //config: Giving controlling tty to shell running with PID 1: |
45 | //config: | 45 | //config: |
46 | //config: # exec cttyhack sh | 46 | //config: # exec cttyhack sh |
47 | //config: | 47 | //config: |
48 | //config: Without cttyhack, you need to know exact tty name, | 48 | //config: Without cttyhack, you need to know exact tty name, |
49 | //config: and do something like this: | 49 | //config: and do something like this: |
50 | //config: | 50 | //config: |
51 | //config: # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1' | 51 | //config: # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1' |
52 | //config: | 52 | //config: |
53 | //config: Starting getty on a controlling tty from a shell script: | 53 | //config: Starting getty on a controlling tty from a shell script: |
54 | //config: | 54 | //config: |
55 | //config: # getty 115200 $(cttyhack) | 55 | //config: # getty 115200 $(cttyhack) |
56 | 56 | ||
57 | //usage:#define cttyhack_trivial_usage | 57 | //usage:#define cttyhack_trivial_usage |
58 | //usage: "[PROG ARGS]" | 58 | //usage: "[PROG ARGS]" |
diff --git a/shell/hush.c b/shell/hush.c index f6da826d3..309ed2139 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -101,18 +101,18 @@ | |||
101 | * aaa | 101 | * aaa |
102 | */ | 102 | */ |
103 | //config:config HUSH | 103 | //config:config HUSH |
104 | //config: bool "hush" | 104 | //config: bool "hush (64 kb)" |
105 | //config: default y | 105 | //config: default y |
106 | //config: help | 106 | //config: help |
107 | //config: hush is a small shell (25k). It handles the normal flow control | 107 | //config: hush is a small shell. It handles the normal flow control |
108 | //config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops, | 108 | //config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops, |
109 | //config: case/esac. Redirections, here documents, $((arithmetic)) | 109 | //config: case/esac. Redirections, here documents, $((arithmetic)) |
110 | //config: and functions are supported. | 110 | //config: and functions are supported. |
111 | //config: | 111 | //config: |
112 | //config: It will compile and work on no-mmu systems. | 112 | //config: It will compile and work on no-mmu systems. |
113 | //config: | 113 | //config: |
114 | //config: It does not handle select, aliases, tilde expansion, | 114 | //config: It does not handle select, aliases, tilde expansion, |
115 | //config: &>file and >&file redirection of stdout+stderr. | 115 | //config: &>file and >&file redirection of stdout+stderr. |
116 | //config: | 116 | //config: |
117 | //config:config HUSH_BASH_COMPAT | 117 | //config:config HUSH_BASH_COMPAT |
118 | //config: bool "bash-compatible extensions" | 118 | //config: bool "bash-compatible extensions" |
@@ -124,17 +124,17 @@ | |||
124 | //config: default y | 124 | //config: default y |
125 | //config: depends on HUSH_BASH_COMPAT | 125 | //config: depends on HUSH_BASH_COMPAT |
126 | //config: help | 126 | //config: help |
127 | //config: Enable {abc,def} extension. | 127 | //config: Enable {abc,def} extension. |
128 | //config: | 128 | //config: |
129 | //config:config HUSH_INTERACTIVE | 129 | //config:config HUSH_INTERACTIVE |
130 | //config: bool "Interactive mode" | 130 | //config: bool "Interactive mode" |
131 | //config: default y | 131 | //config: default y |
132 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 132 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
133 | //config: help | 133 | //config: help |
134 | //config: Enable interactive mode (prompt and command editing). | 134 | //config: Enable interactive mode (prompt and command editing). |
135 | //config: Without this, hush simply reads and executes commands | 135 | //config: Without this, hush simply reads and executes commands |
136 | //config: from stdin just like a shell script from a file. | 136 | //config: from stdin just like a shell script from a file. |
137 | //config: No prompt, no PS1/PS2 magic shell variables. | 137 | //config: No prompt, no PS1/PS2 magic shell variables. |
138 | //config: | 138 | //config: |
139 | //config:config HUSH_SAVEHISTORY | 139 | //config:config HUSH_SAVEHISTORY |
140 | //config: bool "Save command history to .hush_history" | 140 | //config: bool "Save command history to .hush_history" |
@@ -146,18 +146,18 @@ | |||
146 | //config: default y | 146 | //config: default y |
147 | //config: depends on HUSH_INTERACTIVE | 147 | //config: depends on HUSH_INTERACTIVE |
148 | //config: help | 148 | //config: help |
149 | //config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current | 149 | //config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current |
150 | //config: command (not entire shell), fg/bg builtins work. Without this option, | 150 | //config: command (not entire shell), fg/bg builtins work. Without this option, |
151 | //config: "cmd &" still works by simply spawning a process and immediately | 151 | //config: "cmd &" still works by simply spawning a process and immediately |
152 | //config: prompting for next command (or executing next command in a script), | 152 | //config: prompting for next command (or executing next command in a script), |
153 | //config: but no separate process group is formed. | 153 | //config: but no separate process group is formed. |
154 | //config: | 154 | //config: |
155 | //config:config HUSH_TICK | 155 | //config:config HUSH_TICK |
156 | //config: bool "Support process substitution" | 156 | //config: bool "Support process substitution" |
157 | //config: default y | 157 | //config: default y |
158 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 158 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
159 | //config: help | 159 | //config: help |
160 | //config: Enable `command` and $(command). | 160 | //config: Enable `command` and $(command). |
161 | //config: | 161 | //config: |
162 | //config:config HUSH_IF | 162 | //config:config HUSH_IF |
163 | //config: bool "Support if/then/elif/else/fi" | 163 | //config: bool "Support if/then/elif/else/fi" |
@@ -174,37 +174,37 @@ | |||
174 | //config: default y | 174 | //config: default y |
175 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 175 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
176 | //config: help | 176 | //config: help |
177 | //config: Enable case ... esac statement. +400 bytes. | 177 | //config: Enable case ... esac statement. +400 bytes. |
178 | //config: | 178 | //config: |
179 | //config:config HUSH_FUNCTIONS | 179 | //config:config HUSH_FUNCTIONS |
180 | //config: bool "Support funcname() { commands; } syntax" | 180 | //config: bool "Support funcname() { commands; } syntax" |
181 | //config: default y | 181 | //config: default y |
182 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 182 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
183 | //config: help | 183 | //config: help |
184 | //config: Enable support for shell functions. +800 bytes. | 184 | //config: Enable support for shell functions. +800 bytes. |
185 | //config: | 185 | //config: |
186 | //config:config HUSH_LOCAL | 186 | //config:config HUSH_LOCAL |
187 | //config: bool "local builtin" | 187 | //config: bool "local builtin" |
188 | //config: default y | 188 | //config: default y |
189 | //config: depends on HUSH_FUNCTIONS | 189 | //config: depends on HUSH_FUNCTIONS |
190 | //config: help | 190 | //config: help |
191 | //config: Enable support for local variables in functions. | 191 | //config: Enable support for local variables in functions. |
192 | //config: | 192 | //config: |
193 | //config:config HUSH_RANDOM_SUPPORT | 193 | //config:config HUSH_RANDOM_SUPPORT |
194 | //config: bool "Pseudorandom generator and $RANDOM variable" | 194 | //config: bool "Pseudorandom generator and $RANDOM variable" |
195 | //config: default y | 195 | //config: default y |
196 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 196 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
197 | //config: help | 197 | //config: help |
198 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". | 198 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". |
199 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. | 199 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. |
200 | //config: | 200 | //config: |
201 | //config:config HUSH_MODE_X | 201 | //config:config HUSH_MODE_X |
202 | //config: bool "Support 'hush -x' option and 'set -x' command" | 202 | //config: bool "Support 'hush -x' option and 'set -x' command" |
203 | //config: default y | 203 | //config: default y |
204 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 204 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
205 | //config: help | 205 | //config: help |
206 | //config: This instructs hush to print commands before execution. | 206 | //config: This instructs hush to print commands before execution. |
207 | //config: Adds ~300 bytes. | 207 | //config: Adds ~300 bytes. |
208 | //config: | 208 | //config: |
209 | //config:config HUSH_ECHO | 209 | //config:config HUSH_ECHO |
210 | //config: bool "echo builtin" | 210 | //config: bool "echo builtin" |
@@ -236,14 +236,14 @@ | |||
236 | //config: default y | 236 | //config: default y |
237 | //config: depends on HUSH_EXPORT | 237 | //config: depends on HUSH_EXPORT |
238 | //config: help | 238 | //config: help |
239 | //config: export -n unexports variables. It is a bash extension. | 239 | //config: export -n unexports variables. It is a bash extension. |
240 | //config: | 240 | //config: |
241 | //config:config HUSH_READONLY | 241 | //config:config HUSH_READONLY |
242 | //config: bool "readonly builtin" | 242 | //config: bool "readonly builtin" |
243 | //config: default y | 243 | //config: default y |
244 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 244 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
245 | //config: help | 245 | //config: help |
246 | //config: Enable support for read-only variables. | 246 | //config: Enable support for read-only variables. |
247 | //config: | 247 | //config: |
248 | //config:config HUSH_KILL | 248 | //config:config HUSH_KILL |
249 | //config: bool "kill builtin (supports kill %jobspec)" | 249 | //config: bool "kill builtin (supports kill %jobspec)" |
@@ -1068,7 +1068,7 @@ static const struct built_in_command bltins1[] = { | |||
1068 | BLTIN("export" , builtin_export , "Set environment variables"), | 1068 | BLTIN("export" , builtin_export , "Set environment variables"), |
1069 | #endif | 1069 | #endif |
1070 | #if ENABLE_HUSH_JOB | 1070 | #if ENABLE_HUSH_JOB |
1071 | BLTIN("fg" , builtin_fg_bg , "Bring job into foreground"), | 1071 | BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), |
1072 | #endif | 1072 | #endif |
1073 | #if ENABLE_HUSH_HELP | 1073 | #if ENABLE_HUSH_HELP |
1074 | BLTIN("help" , builtin_help , NULL), | 1074 | BLTIN("help" , builtin_help , NULL), |
@@ -1121,7 +1121,7 @@ static const struct built_in_command bltins1[] = { | |||
1121 | BLTIN("unset" , builtin_unset , "Unset variables"), | 1121 | BLTIN("unset" , builtin_unset , "Unset variables"), |
1122 | #endif | 1122 | #endif |
1123 | #if ENABLE_HUSH_WAIT | 1123 | #if ENABLE_HUSH_WAIT |
1124 | BLTIN("wait" , builtin_wait , "Wait for process"), | 1124 | BLTIN("wait" , builtin_wait , "Wait for process to finish"), |
1125 | #endif | 1125 | #endif |
1126 | }; | 1126 | }; |
1127 | /* These builtins won't be used if we are on NOMMU and need to re-exec | 1127 | /* These builtins won't be used if we are on NOMMU and need to re-exec |
@@ -2662,9 +2662,8 @@ static void o_delchr(o_string *o) | |||
2662 | static void o_addblock(o_string *o, const char *str, int len) | 2662 | static void o_addblock(o_string *o, const char *str, int len) |
2663 | { | 2663 | { |
2664 | o_grow_by(o, len); | 2664 | o_grow_by(o, len); |
2665 | memcpy(&o->data[o->length], str, len); | 2665 | ((char*)mempcpy(&o->data[o->length], str, len))[0] = '\0'; |
2666 | o->length += len; | 2666 | o->length += len; |
2667 | o->data[o->length] = '\0'; | ||
2668 | } | 2667 | } |
2669 | 2668 | ||
2670 | static void o_addstr(o_string *o, const char *str) | 2669 | static void o_addstr(o_string *o, const char *str) |
@@ -5519,17 +5518,15 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
5519 | break; | 5518 | break; |
5520 | 5519 | ||
5521 | result = xrealloc(result, res_len + (s - val) + repl_len + 1); | 5520 | result = xrealloc(result, res_len + (s - val) + repl_len + 1); |
5522 | memcpy(result + res_len, val, s - val); | 5521 | strcpy(mempcpy(result + res_len, val, s - val), repl); |
5523 | res_len += s - val; | 5522 | res_len += (s - val) + repl_len; |
5524 | strcpy(result + res_len, repl); | ||
5525 | res_len += repl_len; | ||
5526 | debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result); | 5523 | debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result); |
5527 | 5524 | ||
5528 | val = s + size; | 5525 | val = s + size; |
5529 | if (exp_op == '/') | 5526 | if (exp_op == '/') |
5530 | break; | 5527 | break; |
5531 | } | 5528 | } |
5532 | if (val[0] && result) { | 5529 | if (*val && result) { |
5533 | result = xrealloc(result, res_len + strlen(val) + 1); | 5530 | result = xrealloc(result, res_len + strlen(val) + 1); |
5534 | strcpy(result + res_len, val); | 5531 | strcpy(result + res_len, val); |
5535 | debug_printf_varexp("val:'%s' result:'%s'\n", val, result); | 5532 | debug_printf_varexp("val:'%s' result:'%s'\n", val, result); |
diff --git a/shell/hush_test/hush-read/read_t0.right b/shell/hush_test/hush-read/read_t0.right new file mode 100644 index 000000000..f02105961 --- /dev/null +++ b/shell/hush_test/hush-read/read_t0.right | |||
@@ -0,0 +1,3 @@ | |||
1 | ><[0] | ||
2 | ><[0] | ||
3 | ><[1] | ||
diff --git a/shell/hush_test/hush-read/read_t0.tests b/shell/hush_test/hush-read/read_t0.tests new file mode 100755 index 000000000..6b7bc217b --- /dev/null +++ b/shell/hush_test/hush-read/read_t0.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | # ><[0] | ||
2 | echo Ok | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; } | ||
3 | |||
4 | # This would not be deterministic: returns 0 "data exists" if EOF is seen | ||
5 | # (true terminated) - because EOF is considered to be data (read will not block), | ||
6 | # else returns 1 "no data". | ||
7 | ## ><[????] | ||
8 | #true | { read -t 0 reply; echo ">$reply<[$?]"; } | ||
9 | |||
10 | # ><[0] | ||
11 | true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; } | ||
12 | |||
13 | # ><[1] | ||
14 | sleep 0.2 | { read -p IGNORED_PROMPT -t 0 reply; echo ">$reply<[$?]"; } | ||
diff --git a/shell/shell_common.c b/shell/shell_common.c index 154b860f8..750adc5d8 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -61,9 +61,10 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
61 | const char *opt_u | 61 | const char *opt_u |
62 | ) | 62 | ) |
63 | { | 63 | { |
64 | struct pollfd pfd[1]; | ||
65 | #define fd (pfd[0].fd) /* -u FD */ | ||
64 | unsigned err; | 66 | unsigned err; |
65 | unsigned end_ms; /* -t TIMEOUT */ | 67 | unsigned end_ms; /* -t TIMEOUT */ |
66 | int fd; /* -u FD */ | ||
67 | int nchars; /* -n NUM */ | 68 | int nchars; /* -n NUM */ |
68 | char **pp; | 69 | char **pp; |
69 | char *buffer; | 70 | char *buffer; |
@@ -92,38 +93,43 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
92 | return "invalid count"; | 93 | return "invalid count"; |
93 | /* note: "-n 0": off (bash 3.2 does this too) */ | 94 | /* note: "-n 0": off (bash 3.2 does this too) */ |
94 | } | 95 | } |
96 | |||
95 | end_ms = 0; | 97 | end_ms = 0; |
96 | if (opt_t) { | 98 | if (opt_t && !ENABLE_FEATURE_SH_READ_FRAC) { |
97 | end_ms = bb_strtou(opt_t, NULL, 10); | 99 | end_ms = bb_strtou(opt_t, NULL, 10); |
98 | if (errno || end_ms > UINT_MAX / 2048) | 100 | if (errno) |
99 | return "invalid timeout"; | 101 | return "invalid timeout"; |
102 | if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */ | ||
103 | end_ms = UINT_MAX / 2048; | ||
100 | end_ms *= 1000; | 104 | end_ms *= 1000; |
101 | #if 0 /* even bash has no -t N.NNN support */ | 105 | } |
102 | ts.tv_sec = bb_strtou(opt_t, &p, 10); | 106 | if (opt_t && ENABLE_FEATURE_SH_READ_FRAC) { |
103 | ts.tv_usec = 0; | 107 | /* bash 4.3 (maybe earlier) supports -t N.NNNNNN */ |
104 | /* EINVAL means number is ok, but not terminated by NUL */ | 108 | char *p; |
105 | if (*p == '.' && errno == EINVAL) { | 109 | /* Eat up to three fractional digits */ |
106 | char *p2; | 110 | int frac_digits = 3 + 1; |
107 | if (*++p) { | 111 | |
108 | int scale; | 112 | end_ms = bb_strtou(opt_t, &p, 10); |
109 | ts.tv_usec = bb_strtou(p, &p2, 10); | 113 | if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */ |
110 | if (errno) | 114 | end_ms = UINT_MAX / 2048; |
111 | return "invalid timeout"; | 115 | |
112 | scale = p2 - p; | 116 | if (errno) { |
113 | /* normalize to usec */ | 117 | /* EINVAL = number is ok, but not NUL terminated */ |
114 | if (scale > 6) | 118 | if (errno != EINVAL || *p != '.') |
119 | return "invalid timeout"; | ||
120 | /* Do not check the rest: bash allows "0.123456xyz" */ | ||
121 | while (*++p && --frac_digits) { | ||
122 | end_ms *= 10; | ||
123 | end_ms += (*p - '0'); | ||
124 | if ((unsigned char)(*p - '0') > 9) | ||
115 | return "invalid timeout"; | 125 | return "invalid timeout"; |
116 | while (scale++ < 6) | ||
117 | ts.tv_usec *= 10; | ||
118 | } | 126 | } |
119 | } else if (ts.tv_sec < 0 || errno) { | ||
120 | return "invalid timeout"; | ||
121 | } | 127 | } |
122 | if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ | 128 | while (--frac_digits > 0) { |
123 | return "invalid timeout"; | 129 | end_ms *= 10; |
124 | } | 130 | } |
125 | #endif /* if 0 */ | ||
126 | } | 131 | } |
132 | |||
127 | fd = STDIN_FILENO; | 133 | fd = STDIN_FILENO; |
128 | if (opt_u) { | 134 | if (opt_u) { |
129 | fd = bb_strtou(opt_u, NULL, 10); | 135 | fd = bb_strtou(opt_u, NULL, 10); |
@@ -131,6 +137,19 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
131 | return "invalid file descriptor"; | 137 | return "invalid file descriptor"; |
132 | } | 138 | } |
133 | 139 | ||
140 | if (opt_t && end_ms == 0) { | ||
141 | /* "If timeout is 0, read returns immediately, without trying | ||
142 | * to read any data. The exit status is 0 if input is available | ||
143 | * on the specified file descriptor, non-zero otherwise." | ||
144 | * bash seems to ignore -p PROMPT for this use case. | ||
145 | */ | ||
146 | int r; | ||
147 | pfd[0].events = POLLIN; | ||
148 | r = poll(pfd, 1, /*timeout:*/ 0); | ||
149 | /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ | ||
150 | return (const char *)(uintptr_t)(r <= 0); | ||
151 | } | ||
152 | |||
134 | if (opt_p && isatty(fd)) { | 153 | if (opt_p && isatty(fd)) { |
135 | fputs(opt_p, stderr); | 154 | fputs(opt_p, stderr); |
136 | fflush_all(); | 155 | fflush_all(); |
@@ -165,21 +184,24 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
165 | retval = (const char *)(uintptr_t)0; | 184 | retval = (const char *)(uintptr_t)0; |
166 | startword = 1; | 185 | startword = 1; |
167 | backslash = 0; | 186 | backslash = 0; |
168 | if (end_ms) /* NB: end_ms stays nonzero: */ | 187 | if (opt_t) |
169 | end_ms = ((unsigned)monotonic_ms() + end_ms) | 1; | 188 | end_ms += (unsigned)monotonic_ms(); |
170 | buffer = NULL; | 189 | buffer = NULL; |
171 | bufpos = 0; | 190 | bufpos = 0; |
172 | do { | 191 | do { |
173 | char c; | 192 | char c; |
174 | struct pollfd pfd[1]; | ||
175 | int timeout; | 193 | int timeout; |
176 | 194 | ||
177 | if ((bufpos & 0xff) == 0) | 195 | if ((bufpos & 0xff) == 0) |
178 | buffer = xrealloc(buffer, bufpos + 0x101); | 196 | buffer = xrealloc(buffer, bufpos + 0x101); |
179 | 197 | ||
180 | timeout = -1; | 198 | timeout = -1; |
181 | if (end_ms) { | 199 | if (opt_t) { |
182 | timeout = end_ms - (unsigned)monotonic_ms(); | 200 | timeout = end_ms - (unsigned)monotonic_ms(); |
201 | /* ^^^^^^^^^^^^^ all values are unsigned, | ||
202 | * wrapping math is used here, good even if | ||
203 | * 32-bit unix time wrapped (year 2038+). | ||
204 | */ | ||
183 | if (timeout <= 0) { /* already late? */ | 205 | if (timeout <= 0) { /* already late? */ |
184 | retval = (const char *)(uintptr_t)1; | 206 | retval = (const char *)(uintptr_t)1; |
185 | goto ret; | 207 | goto ret; |
@@ -192,9 +214,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
192 | */ | 214 | */ |
193 | errno = 0; | 215 | errno = 0; |
194 | #if !ENABLE_PLATFORM_MINGW32 | 216 | #if !ENABLE_PLATFORM_MINGW32 |
195 | pfd[0].fd = fd; | ||
196 | pfd[0].events = POLLIN; | 217 | pfd[0].events = POLLIN; |
197 | if (poll(pfd, 1, timeout) != 1) { | 218 | if (poll(pfd, 1, timeout) <= 0) { |
198 | /* timed out, or EINTR */ | 219 | /* timed out, or EINTR */ |
199 | err = errno; | 220 | err = errno; |
200 | retval = (const char *)(uintptr_t)1; | 221 | retval = (const char *)(uintptr_t)1; |
@@ -278,6 +299,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
278 | 299 | ||
279 | errno = err; | 300 | errno = err; |
280 | return retval; | 301 | return retval; |
302 | #undef fd | ||
281 | } | 303 | } |
282 | 304 | ||
283 | /* ulimit builtin */ | 305 | /* ulimit builtin */ |