diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-17 03:35:31 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-17 03:35:31 +0100 |
commit | f3e2818895a6f570a365001f625946a1b75c085a (patch) | |
tree | 8bd5ba3dad83b32439be4f7e166aa0eb6ab6384e | |
parent | 160746b60398a8a018e1791e259504f9d97a1b33 (diff) | |
download | busybox-w32-f3e2818895a6f570a365001f625946a1b75c085a.tar.gz busybox-w32-f3e2818895a6f570a365001f625946a1b75c085a.tar.bz2 busybox-w32-f3e2818895a6f570a365001f625946a1b75c085a.zip |
hush: improve HUSH_BRACE_EXP code (still disabled). ~0 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 225 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/brace2.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/brace2.tests | 5 |
3 files changed, 171 insertions, 62 deletions
diff --git a/shell/hush.c b/shell/hush.c index 24134fd8a..2d6f55bc7 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -889,18 +889,6 @@ static void cmdedit_update_prompt(void); | |||
889 | 889 | ||
890 | /* Utility functions | 890 | /* Utility functions |
891 | */ | 891 | */ |
892 | static int glob_needed(const char *s) | ||
893 | { | ||
894 | while (*s) { | ||
895 | if (*s == '\\') | ||
896 | s++; | ||
897 | if (*s == '*' || *s == '[' || *s == '?') | ||
898 | return 1; | ||
899 | s++; | ||
900 | } | ||
901 | return 0; | ||
902 | } | ||
903 | |||
904 | static int is_well_formed_var_name(const char *s, char terminator) | 892 | static int is_well_formed_var_name(const char *s, char terminator) |
905 | { | 893 | { |
906 | if (!s || !(isalpha(*s) || *s == '_')) | 894 | if (!s || !(isalpha(*s) || *s == '_')) |
@@ -1856,13 +1844,31 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len | |||
1856 | } | 1844 | } |
1857 | } | 1845 | } |
1858 | 1846 | ||
1847 | #undef HUSH_BRACE_EXP | ||
1848 | /* | ||
1849 | * HUSH_BRACE_EXP code needs corresponding quoting on variable expansion side. | ||
1850 | * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v. | ||
1851 | * Apparently, on unquoted $v bash still does globbing | ||
1852 | * ("v='*.txt'; echo $v" prints all .txt files), | ||
1853 | * but NOT brace expansion! Thus, there should be TWO independent | ||
1854 | * quoting mechanisms on $v expansion side: one protects | ||
1855 | * $v from brace expansion, and other additionally protects "$v" against globbing. | ||
1856 | * We have only second one. | ||
1857 | */ | ||
1858 | |||
1859 | #ifdef HUSH_BRACE_EXP | ||
1860 | # define MAYBE_BRACES "{}" | ||
1861 | #else | ||
1862 | # define MAYBE_BRACES "" | ||
1863 | #endif | ||
1864 | |||
1859 | /* My analysis of quoting semantics tells me that state information | 1865 | /* My analysis of quoting semantics tells me that state information |
1860 | * is associated with a destination, not a source. | 1866 | * is associated with a destination, not a source. |
1861 | */ | 1867 | */ |
1862 | static void o_addqchr(o_string *o, int ch) | 1868 | static void o_addqchr(o_string *o, int ch) |
1863 | { | 1869 | { |
1864 | int sz = 1; | 1870 | int sz = 1; |
1865 | char *found = strchr("*?[\\", ch); | 1871 | char *found = strchr("*?[\\" MAYBE_BRACES, ch); |
1866 | if (found) | 1872 | if (found) |
1867 | sz++; | 1873 | sz++; |
1868 | o_grow_by(o, sz); | 1874 | o_grow_by(o, sz); |
@@ -1878,7 +1884,7 @@ static void o_addqchr(o_string *o, int ch) | |||
1878 | static void o_addQchr(o_string *o, int ch) | 1884 | static void o_addQchr(o_string *o, int ch) |
1879 | { | 1885 | { |
1880 | int sz = 1; | 1886 | int sz = 1; |
1881 | if (o->o_escape && strchr("*?[\\", ch)) { | 1887 | if (o->o_escape && strchr("*?[\\" MAYBE_BRACES, ch)) { |
1882 | sz++; | 1888 | sz++; |
1883 | o->data[o->length] = '\\'; | 1889 | o->data[o->length] = '\\'; |
1884 | o->length++; | 1890 | o->length++; |
@@ -1898,7 +1904,7 @@ static void o_addQstr(o_string *o, const char *str, int len) | |||
1898 | while (len) { | 1904 | while (len) { |
1899 | char ch; | 1905 | char ch; |
1900 | int sz; | 1906 | int sz; |
1901 | int ordinary_cnt = strcspn(str, "*?[\\"); | 1907 | int ordinary_cnt = strcspn(str, "*?[\\" MAYBE_BRACES); |
1902 | if (ordinary_cnt > len) /* paranoia */ | 1908 | if (ordinary_cnt > len) /* paranoia */ |
1903 | ordinary_cnt = len; | 1909 | ordinary_cnt = len; |
1904 | o_addblock(o, str, ordinary_cnt); | 1910 | o_addblock(o, str, ordinary_cnt); |
@@ -1909,7 +1915,7 @@ static void o_addQstr(o_string *o, const char *str, int len) | |||
1909 | 1915 | ||
1910 | ch = *str++; | 1916 | ch = *str++; |
1911 | sz = 1; | 1917 | sz = 1; |
1912 | if (ch) { /* it is necessarily one of "*?[\\" */ | 1918 | if (ch) { /* it is necessarily one of "*?[\\" MAYBE_BRACES */ |
1913 | sz++; | 1919 | sz++; |
1914 | o->data[o->length] = '\\'; | 1920 | o->data[o->length] = '\\'; |
1915 | o->length++; | 1921 | o->length++; |
@@ -2003,16 +2009,29 @@ static int o_get_last_ptr(o_string *o, int n) | |||
2003 | return ((int)(ptrdiff_t)list[n-1]) + string_start; | 2009 | return ((int)(ptrdiff_t)list[n-1]) + string_start; |
2004 | } | 2010 | } |
2005 | 2011 | ||
2006 | #undef HUSH_BRACE_EXP | 2012 | #ifdef HUSH_BRACE_EXP |
2007 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: | 2013 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: |
2008 | * first, it processes even {a} (no commas), second, | 2014 | * first, it processes even {a} (no commas), second, |
2009 | * I didn't manage to make it return strings when they don't match | 2015 | * I didn't manage to make it return strings when they don't match |
2010 | * existing files. Need to re-implement it. | 2016 | * existing files. Need to re-implement it. |
2011 | * | ||
2012 | * This code needs corresponding quoting on variable expansion side. | ||
2013 | * Currently, "a='{q,w}'; echo $a" erroneously expands braces in $a | ||
2014 | */ | 2017 | */ |
2015 | #ifdef HUSH_BRACE_EXP | 2018 | |
2019 | /* Helper */ | ||
2020 | static int glob_needed(const char *s) | ||
2021 | { | ||
2022 | while (*s) { | ||
2023 | if (*s == '\\') { | ||
2024 | if (!s[1]) | ||
2025 | return 0; | ||
2026 | s += 2; | ||
2027 | continue; | ||
2028 | } | ||
2029 | if (*s == '*' || *s == '[' || *s == '?' || *s == '{') | ||
2030 | return 1; | ||
2031 | s++; | ||
2032 | } | ||
2033 | return 0; | ||
2034 | } | ||
2016 | /* Return pointer to next closing brace or to comma */ | 2035 | /* Return pointer to next closing brace or to comma */ |
2017 | static const char *next_brace_sub(const char *cp) | 2036 | static const char *next_brace_sub(const char *cp) |
2018 | { | 2037 | { |
@@ -2033,29 +2052,29 @@ static const char *next_brace_sub(const char *cp) | |||
2033 | 2052 | ||
2034 | return *cp != '\0' ? cp : NULL; | 2053 | return *cp != '\0' ? cp : NULL; |
2035 | } | 2054 | } |
2036 | static int glob_brace(const char *pattern, int flags, glob_t *pglob) | 2055 | /* Recursive brace globber. Note: may garble pattern[]. */ |
2056 | static int glob_brace(char *pattern, o_string *o, int n) | ||
2037 | { | 2057 | { |
2058 | char *new_pattern_buf; | ||
2038 | const char *begin; | 2059 | const char *begin; |
2039 | char *alt_start; | ||
2040 | const char *p; | ||
2041 | const char *next; | 2060 | const char *next; |
2042 | const char *rest; | 2061 | const char *rest; |
2062 | const char *p; | ||
2043 | size_t rest_len; | 2063 | size_t rest_len; |
2044 | char *onealt; | ||
2045 | 2064 | ||
2046 | debug_printf_glob("glob_brace('%s')\n", pattern); | 2065 | debug_printf_glob("glob_brace('%s')\n", pattern); |
2047 | 2066 | ||
2048 | begin = pattern; | 2067 | begin = pattern; |
2049 | while (1) { | 2068 | while (1) { |
2050 | if (*begin == '\0') | 2069 | if (*begin == '\0') |
2051 | goto do_glob; | 2070 | goto simple_glob; |
2052 | if (*begin == '{') /*}*/ { | 2071 | if (*begin == '{') /*}*/ { |
2053 | /* Find the first sub-pattern and at the same time | 2072 | /* Find the first sub-pattern and at the same time |
2054 | * find the rest after the closing brace */ | 2073 | * find the rest after the closing brace */ |
2055 | next = next_brace_sub(begin); | 2074 | next = next_brace_sub(begin); |
2056 | if (next == NULL) { | 2075 | if (next == NULL) { |
2057 | /* An illegal expression */ | 2076 | /* An illegal expression */ |
2058 | goto do_glob; | 2077 | goto simple_glob; |
2059 | } | 2078 | } |
2060 | /*{*/ if (*next == '}') { | 2079 | /*{*/ if (*next == '}') { |
2061 | /* "{abc}" with no commas - illegal | 2080 | /* "{abc}" with no commas - illegal |
@@ -2078,7 +2097,7 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob) | |||
2078 | rest = next_brace_sub(rest); | 2097 | rest = next_brace_sub(rest); |
2079 | if (rest == NULL) { | 2098 | if (rest == NULL) { |
2080 | /* An illegal expression */ | 2099 | /* An illegal expression */ |
2081 | goto do_glob; | 2100 | goto simple_glob; |
2082 | } | 2101 | } |
2083 | debug_printf_glob("rest:%s\n", rest); | 2102 | debug_printf_glob("rest:%s\n", rest); |
2084 | } | 2103 | } |
@@ -2087,9 +2106,7 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob) | |||
2087 | /* We are sure the brace expression is well-formed */ | 2106 | /* We are sure the brace expression is well-formed */ |
2088 | 2107 | ||
2089 | /* Allocate working buffer large enough for our work */ | 2108 | /* Allocate working buffer large enough for our work */ |
2090 | onealt = alloca(strlen(pattern)); | 2109 | new_pattern_buf = xmalloc(strlen(pattern)); |
2091 | /* We know the prefix for all sub-patterns */ | ||
2092 | alt_start = mempcpy(onealt, pattern, begin - pattern); | ||
2093 | 2110 | ||
2094 | /* We have a brace expression. BEGIN points to the opening {, | 2111 | /* We have a brace expression. BEGIN points to the opening {, |
2095 | * NEXT points past the terminator of the first element, and REST | 2112 | * NEXT points past the terminator of the first element, and REST |
@@ -2099,18 +2116,19 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob) | |||
2099 | 2116 | ||
2100 | p = begin + 1; | 2117 | p = begin + 1; |
2101 | while (1) { | 2118 | while (1) { |
2102 | int result; | ||
2103 | |||
2104 | /* Construct the new glob expression */ | 2119 | /* Construct the new glob expression */ |
2105 | memcpy(mempcpy(alt_start, p, next - p), rest, rest_len); | 2120 | memcpy( |
2106 | 2121 | mempcpy( | |
2107 | result = glob_brace(onealt, flags, pglob); | 2122 | mempcpy(new_pattern_buf, |
2108 | /* If we got an error, return it */ | 2123 | /* We know the prefix for all sub-patterns */ |
2109 | if (result && result != GLOB_NOMATCH) | 2124 | pattern, begin - pattern), |
2110 | return result; | 2125 | p, next - p), |
2111 | 2126 | rest, rest_len); | |
2112 | flags |= GLOB_APPEND; | 2127 | |
2113 | 2128 | /* Note: glob_brace() may garble new_pattern_buf[]. | |
2129 | * That's why we re-copy prefix every time (1st memcpy above). | ||
2130 | */ | ||
2131 | n = glob_brace(new_pattern_buf, o, n); | ||
2114 | /*{*/ if (*next == '}') { | 2132 | /*{*/ if (*next == '}') { |
2115 | /* We saw the last entry */ | 2133 | /* We saw the last entry */ |
2116 | break; | 2134 | break; |
@@ -2118,17 +2136,96 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob) | |||
2118 | p = next + 1; | 2136 | p = next + 1; |
2119 | next = next_brace_sub(next); | 2137 | next = next_brace_sub(next); |
2120 | } | 2138 | } |
2139 | free(new_pattern_buf); | ||
2140 | return n; | ||
2121 | 2141 | ||
2122 | /* We found some entries */ | 2142 | simple_glob: |
2123 | return 0; | 2143 | { |
2144 | int gr; | ||
2145 | glob_t globdata; | ||
2146 | |||
2147 | memset(&globdata, 0, sizeof(globdata)); | ||
2148 | gr = glob(pattern, 0, NULL, &globdata); | ||
2149 | debug_printf_glob("glob('%s'):%d\n", pattern, gr); | ||
2150 | if (gr != 0) { | ||
2151 | if (gr == GLOB_NOMATCH) { | ||
2152 | globfree(&globdata); | ||
2153 | /* NB: garbles parameter */ | ||
2154 | unbackslash(pattern); | ||
2155 | o_addstr_with_NUL(o, pattern); | ||
2156 | debug_printf_glob("glob pattern '%s' is literal\n", pattern); | ||
2157 | return o_save_ptr_helper(o, n); | ||
2158 | } | ||
2159 | if (gr == GLOB_NOSPACE) | ||
2160 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
2161 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, | ||
2162 | * but we didn't specify it. Paranoia again. */ | ||
2163 | bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); | ||
2164 | } | ||
2165 | if (globdata.gl_pathv && globdata.gl_pathv[0]) { | ||
2166 | char **argv = globdata.gl_pathv; | ||
2167 | while (1) { | ||
2168 | o_addstr_with_NUL(o, *argv); | ||
2169 | n = o_save_ptr_helper(o, n); | ||
2170 | argv++; | ||
2171 | if (!*argv) | ||
2172 | break; | ||
2173 | } | ||
2174 | } | ||
2175 | globfree(&globdata); | ||
2176 | } | ||
2177 | return n; | ||
2178 | } | ||
2179 | /* Performs globbing on last list[], | ||
2180 | * saving each result as a new list[]. | ||
2181 | */ | ||
2182 | static int o_glob(o_string *o, int n) | ||
2183 | { | ||
2184 | char *pattern, *copy; | ||
2185 | |||
2186 | debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); | ||
2187 | if (!o->data) | ||
2188 | return o_save_ptr_helper(o, n); | ||
2189 | pattern = o->data + o_get_last_ptr(o, n); | ||
2190 | debug_printf_glob("glob pattern '%s'\n", pattern); | ||
2191 | if (!glob_needed(pattern)) { | ||
2192 | /* unbackslash last string in o in place, fix length */ | ||
2193 | o->length = unbackslash(pattern) - o->data; | ||
2194 | debug_printf_glob("glob pattern '%s' is literal\n", pattern); | ||
2195 | return o_save_ptr_helper(o, n); | ||
2196 | } | ||
2124 | 2197 | ||
2125 | do_glob: | 2198 | copy = xstrdup(pattern); |
2126 | return glob(pattern, flags, NULL, pglob); | 2199 | /* "forget" pattern in o */ |
2200 | o->length = pattern - o->data; | ||
2201 | n = glob_brace(copy, o, n); | ||
2202 | free(copy); | ||
2203 | if (DEBUG_GLOB) | ||
2204 | debug_print_list("o_glob returning", o, n); | ||
2205 | return n; | ||
2127 | } | 2206 | } |
2128 | #endif | ||
2129 | 2207 | ||
2130 | /* o_glob performs globbing on last list[], saving each result | 2208 | #else |
2131 | * as a new list[]. */ | 2209 | |
2210 | /* Helper */ | ||
2211 | static int glob_needed(const char *s) | ||
2212 | { | ||
2213 | while (*s) { | ||
2214 | if (*s == '\\') { | ||
2215 | if (!s[1]) | ||
2216 | return 0; | ||
2217 | s += 2; | ||
2218 | continue; | ||
2219 | } | ||
2220 | if (*s == '*' || *s == '[' || *s == '?') | ||
2221 | return 1; | ||
2222 | s++; | ||
2223 | } | ||
2224 | return 0; | ||
2225 | } | ||
2226 | /* Performs globbing on last list[], | ||
2227 | * saving each result as a new list[]. | ||
2228 | */ | ||
2132 | static int o_glob(o_string *o, int n) | 2229 | static int o_glob(o_string *o, int n) |
2133 | { | 2230 | { |
2134 | glob_t globdata; | 2231 | glob_t globdata; |
@@ -2142,33 +2239,35 @@ static int o_glob(o_string *o, int n) | |||
2142 | debug_printf_glob("glob pattern '%s'\n", pattern); | 2239 | debug_printf_glob("glob pattern '%s'\n", pattern); |
2143 | if (!glob_needed(pattern)) { | 2240 | if (!glob_needed(pattern)) { |
2144 | literal: | 2241 | literal: |
2242 | /* unbackslash last string in o in place, fix length */ | ||
2145 | o->length = unbackslash(pattern) - o->data; | 2243 | o->length = unbackslash(pattern) - o->data; |
2146 | debug_printf_glob("glob pattern '%s' is literal\n", pattern); | 2244 | debug_printf_glob("glob pattern '%s' is literal\n", pattern); |
2147 | return o_save_ptr_helper(o, n); | 2245 | return o_save_ptr_helper(o, n); |
2148 | } | 2246 | } |
2149 | 2247 | ||
2150 | memset(&globdata, 0, sizeof(globdata)); | 2248 | memset(&globdata, 0, sizeof(globdata)); |
2151 | #ifdef HUSH_BRACE_EXP | 2249 | /* Can't use GLOB_NOCHECK: it does not unescape the string. |
2152 | gr = glob_brace(pattern, GLOB_NOCHECK, &globdata); | 2250 | * If we glob "*.\*" and don't find anything, we need |
2153 | debug_printf_glob("glob_brace('%s'):%d\n", pattern, gr); | 2251 | * to fall back to using literal "*.*", but GLOB_NOCHECK |
2154 | #else | 2252 | * will return "*.\*"! |
2253 | */ | ||
2155 | gr = glob(pattern, 0, NULL, &globdata); | 2254 | gr = glob(pattern, 0, NULL, &globdata); |
2156 | debug_printf_glob("glob('%s'):%d\n", pattern, gr); | 2255 | debug_printf_glob("glob('%s'):%d\n", pattern, gr); |
2157 | #endif | ||
2158 | if (gr == GLOB_NOSPACE) | ||
2159 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
2160 | if (gr == GLOB_NOMATCH) { | ||
2161 | globfree(&globdata); | ||
2162 | goto literal; | ||
2163 | } | ||
2164 | if (gr != 0) { | 2256 | if (gr != 0) { |
2257 | if (gr == GLOB_NOMATCH) { | ||
2258 | globfree(&globdata); | ||
2259 | goto literal; | ||
2260 | } | ||
2261 | if (gr == GLOB_NOSPACE) | ||
2262 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
2165 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, | 2263 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, |
2166 | * but we didn't specify it. Paranoia again. */ | 2264 | * but we didn't specify it. Paranoia again. */ |
2167 | bb_error_msg("glob(3) error %d on '%s'", gr, pattern); | 2265 | bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); |
2168 | } | 2266 | } |
2169 | if (globdata.gl_pathv && globdata.gl_pathv[0]) { | 2267 | if (globdata.gl_pathv && globdata.gl_pathv[0]) { |
2170 | char **argv = globdata.gl_pathv; | 2268 | char **argv = globdata.gl_pathv; |
2171 | o->length = pattern - o->data; /* "forget" pattern */ | 2269 | /* "forget" pattern in o */ |
2270 | o->length = pattern - o->data; | ||
2172 | while (1) { | 2271 | while (1) { |
2173 | o_addstr_with_NUL(o, *argv); | 2272 | o_addstr_with_NUL(o, *argv); |
2174 | n = o_save_ptr_helper(o, n); | 2273 | n = o_save_ptr_helper(o, n); |
@@ -2183,6 +2282,8 @@ static int o_glob(o_string *o, int n) | |||
2183 | return n; | 2282 | return n; |
2184 | } | 2283 | } |
2185 | 2284 | ||
2285 | #endif | ||
2286 | |||
2186 | /* If o->o_glob == 1, glob the string so far remembered. | 2287 | /* If o->o_glob == 1, glob the string so far remembered. |
2187 | * Otherwise, just finish current list[] and start new */ | 2288 | * Otherwise, just finish current list[] and start new */ |
2188 | static int o_save_ptr(o_string *o, int n) | 2289 | static int o_save_ptr(o_string *o, int n) |
diff --git a/shell/hush_test/hush-parsing/brace2.right b/shell/hush_test/hush-parsing/brace2.right new file mode 100644 index 000000000..37a966654 --- /dev/null +++ b/shell/hush_test/hush-parsing/brace2.right | |||
@@ -0,0 +1,3 @@ | |||
1 | {q,w} | ||
2 | {q,w} | ||
3 | Done | ||
diff --git a/shell/hush_test/hush-parsing/brace2.tests b/shell/hush_test/hush-parsing/brace2.tests new file mode 100755 index 000000000..ef75f0b70 --- /dev/null +++ b/shell/hush_test/hush-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 | ||