diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-16 05:49:36 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-16 05:49:36 +0100 |
commit | 5b2db97703630885b8f4c1ce0371415d1d2501a9 (patch) | |
tree | 59bf827229f0f52b63f7f10ffd3e105a09de41de /shell | |
parent | d8389ad76028a536d1869ec163c15fd817dc7b65 (diff) | |
download | busybox-w32-5b2db97703630885b8f4c1ce0371415d1d2501a9.tar.gz busybox-w32-5b2db97703630885b8f4c1ce0371415d1d2501a9.tar.bz2 busybox-w32-5b2db97703630885b8f4c1ce0371415d1d2501a9.zip |
hush: initial stab at brace expansion support
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 137 |
1 files changed, 133 insertions, 4 deletions
diff --git a/shell/hush.c b/shell/hush.c index 235cee9ee..171b73940 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -2003,6 +2003,130 @@ static int o_get_last_ptr(o_string *o, int n) | |||
2003 | return ((int)(ptrdiff_t)list[n-1]) + string_start; | 2003 | return ((int)(ptrdiff_t)list[n-1]) + string_start; |
2004 | } | 2004 | } |
2005 | 2005 | ||
2006 | #undef HUSH_BRACE_EXP | ||
2007 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: | ||
2008 | * first, it processes even {a} (no commas), second, | ||
2009 | * I didn't manage to make it return strings when they don't match | ||
2010 | # 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 | */ | ||
2015 | #ifdef HUSH_BRACE_EXP | ||
2016 | /* Return pointer to next closing brace or to comma */ | ||
2017 | static const char *next_brace_sub(const char *cp) | ||
2018 | { | ||
2019 | unsigned depth = 0; | ||
2020 | cp++; | ||
2021 | while (*cp != '\0') { | ||
2022 | if (*cp == '\\') { | ||
2023 | if (*++cp == '\0') | ||
2024 | break; | ||
2025 | cp++; | ||
2026 | continue; | ||
2027 | } | ||
2028 | /*{*/ if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) | ||
2029 | break; | ||
2030 | if (*cp++ == '{') /*}*/ | ||
2031 | depth++; | ||
2032 | } | ||
2033 | |||
2034 | return *cp != '\0' ? cp : NULL; | ||
2035 | } | ||
2036 | static int glob_brace(const char *pattern, int flags, glob_t *pglob) | ||
2037 | { | ||
2038 | const char *begin; | ||
2039 | char *alt_start; | ||
2040 | const char *p; | ||
2041 | const char *next; | ||
2042 | const char *rest; | ||
2043 | size_t rest_len; | ||
2044 | char *onealt; | ||
2045 | |||
2046 | debug_printf_glob("glob_brace('%s')\n", pattern); | ||
2047 | |||
2048 | begin = pattern; | ||
2049 | while (1) { | ||
2050 | if (*begin == '\0') | ||
2051 | goto do_glob; | ||
2052 | if (*begin == '{') /*}*/ { | ||
2053 | /* Find the first sub-pattern and at the same time | ||
2054 | * find the rest after the closing brace */ | ||
2055 | next = next_brace_sub(begin); | ||
2056 | if (next == NULL) { | ||
2057 | /* An illegal expression */ | ||
2058 | goto do_glob; | ||
2059 | } | ||
2060 | /*{*/ if (*next == '}') { | ||
2061 | /* "{abc}" with no commas - illegal | ||
2062 | * brace expr, disregard and skip it */ | ||
2063 | begin = next + 1; | ||
2064 | continue; | ||
2065 | } | ||
2066 | break; | ||
2067 | } | ||
2068 | if (*begin == '\\' && begin[1] != '\0') | ||
2069 | begin++; | ||
2070 | begin++; | ||
2071 | } | ||
2072 | debug_printf_glob("begin:%s\n", begin); | ||
2073 | debug_printf_glob("next:%s\n", next); | ||
2074 | |||
2075 | /* Now find the end of the whole brace expression */ | ||
2076 | rest = next; | ||
2077 | /*{*/ while (*rest != '}') { | ||
2078 | rest = next_brace_sub(rest); | ||
2079 | if (rest == NULL) { | ||
2080 | /* An illegal expression */ | ||
2081 | goto do_glob; | ||
2082 | } | ||
2083 | debug_printf_glob("rest:%s\n", rest); | ||
2084 | } | ||
2085 | rest_len = strlen(++rest) + 1; | ||
2086 | |||
2087 | /* We are sure the brace expression is well-formed */ | ||
2088 | |||
2089 | /* Allocate working buffer large enough for our work */ | ||
2090 | onealt = alloca(strlen(pattern)); | ||
2091 | /* We know the prefix for all sub-patterns */ | ||
2092 | alt_start = mempcpy(onealt, pattern, begin - pattern); | ||
2093 | |||
2094 | /* We have a brace expression. BEGIN points to the opening {, | ||
2095 | * NEXT points past the terminator of the first element, and REST | ||
2096 | * points past the final }. We will accumulate result names from | ||
2097 | * recursive runs for each brace alternative in the buffer using | ||
2098 | * GLOB_APPEND. */ | ||
2099 | |||
2100 | p = begin + 1; | ||
2101 | while (1) { | ||
2102 | int result; | ||
2103 | |||
2104 | /* Construct the new glob expression */ | ||
2105 | memcpy(mempcpy(alt_start, p, next - p), rest, rest_len); | ||
2106 | |||
2107 | result = glob_brace(onealt, flags, pglob); | ||
2108 | /* If we got an error, return it */ | ||
2109 | if (result && result != GLOB_NOMATCH) | ||
2110 | return result; | ||
2111 | |||
2112 | flags |= GLOB_APPEND; | ||
2113 | |||
2114 | /*{*/ if (*next == '}') { | ||
2115 | /* We saw the last entry */ | ||
2116 | break; | ||
2117 | } | ||
2118 | p = next + 1; | ||
2119 | next = next_brace_sub(next); | ||
2120 | } | ||
2121 | |||
2122 | /* We found some entries */ | ||
2123 | return 0; | ||
2124 | |||
2125 | do_glob: | ||
2126 | return glob(pattern, flags, NULL, pglob); | ||
2127 | } | ||
2128 | #endif | ||
2129 | |||
2006 | /* o_glob performs globbing on last list[], saving each result | 2130 | /* o_glob performs globbing on last list[], saving each result |
2007 | * as a new list[]. */ | 2131 | * as a new list[]. */ |
2008 | static int o_glob(o_string *o, int n) | 2132 | static int o_glob(o_string *o, int n) |
@@ -2024,17 +2148,22 @@ static int o_glob(o_string *o, int n) | |||
2024 | } | 2148 | } |
2025 | 2149 | ||
2026 | memset(&globdata, 0, sizeof(globdata)); | 2150 | memset(&globdata, 0, sizeof(globdata)); |
2027 | //TODO: can use GLOB_BRACE | GLOB_TILDE here: | 2151 | #ifdef HUSH_BRACE_EXP |
2152 | gr = glob_brace(pattern, GLOB_NOCHECK, &globdata); | ||
2153 | debug_printf_glob("glob_brace('%s'):%d\n", pattern, gr); | ||
2154 | #else | ||
2028 | gr = glob(pattern, 0, NULL, &globdata); | 2155 | gr = glob(pattern, 0, NULL, &globdata); |
2029 | debug_printf_glob("glob('%s'):%d\n", pattern, gr); | 2156 | debug_printf_glob("glob('%s'):%d\n", pattern, gr); |
2157 | #endif | ||
2030 | if (gr == GLOB_NOSPACE) | 2158 | if (gr == GLOB_NOSPACE) |
2031 | bb_error_msg_and_die("out of memory during glob"); | 2159 | bb_error_msg_and_die(bb_msg_memory_exhausted); |
2032 | if (gr == GLOB_NOMATCH) { | 2160 | if (gr == GLOB_NOMATCH) { |
2033 | globfree(&globdata); | 2161 | globfree(&globdata); |
2034 | goto literal; | 2162 | goto literal; |
2035 | } | 2163 | } |
2036 | if (gr != 0) { /* GLOB_ABORTED ? */ | 2164 | if (gr != 0) { |
2037 | /* TODO: testcase for bad glob pattern behavior */ | 2165 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, |
2166 | * but we didn't specify it. Paranoia again. */ | ||
2038 | bb_error_msg("glob(3) error %d on '%s'", gr, pattern); | 2167 | bb_error_msg("glob(3) error %d on '%s'", gr, pattern); |
2039 | } | 2168 | } |
2040 | if (globdata.gl_pathv && globdata.gl_pathv[0]) { | 2169 | if (globdata.gl_pathv && globdata.gl_pathv[0]) { |