hush: improve HUSH_BRACE_EXP code (still disabled). ~0 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
160746b603
commit
f3e2818895
223
shell/hush.c
223
shell/hush.c
@ -889,18 +889,6 @@ static void cmdedit_update_prompt(void);
|
|||||||
|
|
||||||
/* Utility functions
|
/* Utility functions
|
||||||
*/
|
*/
|
||||||
static int glob_needed(const char *s)
|
|
||||||
{
|
|
||||||
while (*s) {
|
|
||||||
if (*s == '\\')
|
|
||||||
s++;
|
|
||||||
if (*s == '*' || *s == '[' || *s == '?')
|
|
||||||
return 1;
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_well_formed_var_name(const char *s, char terminator)
|
static int is_well_formed_var_name(const char *s, char terminator)
|
||||||
{
|
{
|
||||||
if (!s || !(isalpha(*s) || *s == '_'))
|
if (!s || !(isalpha(*s) || *s == '_'))
|
||||||
@ -1856,13 +1844,31 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef HUSH_BRACE_EXP
|
||||||
|
/*
|
||||||
|
* HUSH_BRACE_EXP code needs corresponding quoting on variable expansion side.
|
||||||
|
* Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
|
||||||
|
* Apparently, on unquoted $v bash still does globbing
|
||||||
|
* ("v='*.txt'; echo $v" prints all .txt files),
|
||||||
|
* but NOT brace expansion! Thus, there should be TWO independent
|
||||||
|
* quoting mechanisms on $v expansion side: one protects
|
||||||
|
* $v from brace expansion, and other additionally protects "$v" against globbing.
|
||||||
|
* We have only second one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HUSH_BRACE_EXP
|
||||||
|
# define MAYBE_BRACES "{}"
|
||||||
|
#else
|
||||||
|
# define MAYBE_BRACES ""
|
||||||
|
#endif
|
||||||
|
|
||||||
/* My analysis of quoting semantics tells me that state information
|
/* My analysis of quoting semantics tells me that state information
|
||||||
* is associated with a destination, not a source.
|
* is associated with a destination, not a source.
|
||||||
*/
|
*/
|
||||||
static void o_addqchr(o_string *o, int ch)
|
static void o_addqchr(o_string *o, int ch)
|
||||||
{
|
{
|
||||||
int sz = 1;
|
int sz = 1;
|
||||||
char *found = strchr("*?[\\", ch);
|
char *found = strchr("*?[\\" MAYBE_BRACES, ch);
|
||||||
if (found)
|
if (found)
|
||||||
sz++;
|
sz++;
|
||||||
o_grow_by(o, sz);
|
o_grow_by(o, sz);
|
||||||
@ -1878,7 +1884,7 @@ static void o_addqchr(o_string *o, int ch)
|
|||||||
static void o_addQchr(o_string *o, int ch)
|
static void o_addQchr(o_string *o, int ch)
|
||||||
{
|
{
|
||||||
int sz = 1;
|
int sz = 1;
|
||||||
if (o->o_escape && strchr("*?[\\", ch)) {
|
if (o->o_escape && strchr("*?[\\" MAYBE_BRACES, ch)) {
|
||||||
sz++;
|
sz++;
|
||||||
o->data[o->length] = '\\';
|
o->data[o->length] = '\\';
|
||||||
o->length++;
|
o->length++;
|
||||||
@ -1898,7 +1904,7 @@ static void o_addQstr(o_string *o, const char *str, int len)
|
|||||||
while (len) {
|
while (len) {
|
||||||
char ch;
|
char ch;
|
||||||
int sz;
|
int sz;
|
||||||
int ordinary_cnt = strcspn(str, "*?[\\");
|
int ordinary_cnt = strcspn(str, "*?[\\" MAYBE_BRACES);
|
||||||
if (ordinary_cnt > len) /* paranoia */
|
if (ordinary_cnt > len) /* paranoia */
|
||||||
ordinary_cnt = len;
|
ordinary_cnt = len;
|
||||||
o_addblock(o, str, ordinary_cnt);
|
o_addblock(o, str, ordinary_cnt);
|
||||||
@ -1909,7 +1915,7 @@ static void o_addQstr(o_string *o, const char *str, int len)
|
|||||||
|
|
||||||
ch = *str++;
|
ch = *str++;
|
||||||
sz = 1;
|
sz = 1;
|
||||||
if (ch) { /* it is necessarily one of "*?[\\" */
|
if (ch) { /* it is necessarily one of "*?[\\" MAYBE_BRACES */
|
||||||
sz++;
|
sz++;
|
||||||
o->data[o->length] = '\\';
|
o->data[o->length] = '\\';
|
||||||
o->length++;
|
o->length++;
|
||||||
@ -2003,16 +2009,29 @@ static int o_get_last_ptr(o_string *o, int n)
|
|||||||
return ((int)(ptrdiff_t)list[n-1]) + string_start;
|
return ((int)(ptrdiff_t)list[n-1]) + string_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef HUSH_BRACE_EXP
|
#ifdef HUSH_BRACE_EXP
|
||||||
/* There in a GNU extension, GLOB_BRACE, but it is not usable:
|
/* There in a GNU extension, GLOB_BRACE, but it is not usable:
|
||||||
* first, it processes even {a} (no commas), second,
|
* first, it processes even {a} (no commas), second,
|
||||||
* I didn't manage to make it return strings when they don't match
|
* I didn't manage to make it return strings when they don't match
|
||||||
* existing files. Need to re-implement it.
|
* existing files. Need to re-implement it.
|
||||||
*
|
|
||||||
* This code needs corresponding quoting on variable expansion side.
|
|
||||||
* Currently, "a='{q,w}'; echo $a" erroneously expands braces in $a
|
|
||||||
*/
|
*/
|
||||||
#ifdef HUSH_BRACE_EXP
|
|
||||||
|
/* Helper */
|
||||||
|
static int glob_needed(const char *s)
|
||||||
|
{
|
||||||
|
while (*s) {
|
||||||
|
if (*s == '\\') {
|
||||||
|
if (!s[1])
|
||||||
|
return 0;
|
||||||
|
s += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
|
||||||
|
return 1;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
/* Return pointer to next closing brace or to comma */
|
/* Return pointer to next closing brace or to comma */
|
||||||
static const char *next_brace_sub(const char *cp)
|
static const char *next_brace_sub(const char *cp)
|
||||||
{
|
{
|
||||||
@ -2033,29 +2052,29 @@ static const char *next_brace_sub(const char *cp)
|
|||||||
|
|
||||||
return *cp != '\0' ? cp : NULL;
|
return *cp != '\0' ? cp : NULL;
|
||||||
}
|
}
|
||||||
static int glob_brace(const char *pattern, int flags, glob_t *pglob)
|
/* Recursive brace globber. Note: may garble pattern[]. */
|
||||||
|
static int glob_brace(char *pattern, o_string *o, int n)
|
||||||
{
|
{
|
||||||
|
char *new_pattern_buf;
|
||||||
const char *begin;
|
const char *begin;
|
||||||
char *alt_start;
|
|
||||||
const char *p;
|
|
||||||
const char *next;
|
const char *next;
|
||||||
const char *rest;
|
const char *rest;
|
||||||
|
const char *p;
|
||||||
size_t rest_len;
|
size_t rest_len;
|
||||||
char *onealt;
|
|
||||||
|
|
||||||
debug_printf_glob("glob_brace('%s')\n", pattern);
|
debug_printf_glob("glob_brace('%s')\n", pattern);
|
||||||
|
|
||||||
begin = pattern;
|
begin = pattern;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (*begin == '\0')
|
if (*begin == '\0')
|
||||||
goto do_glob;
|
goto simple_glob;
|
||||||
if (*begin == '{') /*}*/ {
|
if (*begin == '{') /*}*/ {
|
||||||
/* Find the first sub-pattern and at the same time
|
/* Find the first sub-pattern and at the same time
|
||||||
* find the rest after the closing brace */
|
* find the rest after the closing brace */
|
||||||
next = next_brace_sub(begin);
|
next = next_brace_sub(begin);
|
||||||
if (next == NULL) {
|
if (next == NULL) {
|
||||||
/* An illegal expression */
|
/* An illegal expression */
|
||||||
goto do_glob;
|
goto simple_glob;
|
||||||
}
|
}
|
||||||
/*{*/ if (*next == '}') {
|
/*{*/ if (*next == '}') {
|
||||||
/* "{abc}" with no commas - illegal
|
/* "{abc}" with no commas - illegal
|
||||||
@ -2078,7 +2097,7 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob)
|
|||||||
rest = next_brace_sub(rest);
|
rest = next_brace_sub(rest);
|
||||||
if (rest == NULL) {
|
if (rest == NULL) {
|
||||||
/* An illegal expression */
|
/* An illegal expression */
|
||||||
goto do_glob;
|
goto simple_glob;
|
||||||
}
|
}
|
||||||
debug_printf_glob("rest:%s\n", rest);
|
debug_printf_glob("rest:%s\n", rest);
|
||||||
}
|
}
|
||||||
@ -2087,9 +2106,7 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob)
|
|||||||
/* We are sure the brace expression is well-formed */
|
/* We are sure the brace expression is well-formed */
|
||||||
|
|
||||||
/* Allocate working buffer large enough for our work */
|
/* Allocate working buffer large enough for our work */
|
||||||
onealt = alloca(strlen(pattern));
|
new_pattern_buf = xmalloc(strlen(pattern));
|
||||||
/* We know the prefix for all sub-patterns */
|
|
||||||
alt_start = mempcpy(onealt, pattern, begin - pattern);
|
|
||||||
|
|
||||||
/* We have a brace expression. BEGIN points to the opening {,
|
/* We have a brace expression. BEGIN points to the opening {,
|
||||||
* NEXT points past the terminator of the first element, and REST
|
* 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)
|
|||||||
|
|
||||||
p = begin + 1;
|
p = begin + 1;
|
||||||
while (1) {
|
while (1) {
|
||||||
int result;
|
|
||||||
|
|
||||||
/* Construct the new glob expression */
|
/* Construct the new glob expression */
|
||||||
memcpy(mempcpy(alt_start, p, next - p), rest, rest_len);
|
memcpy(
|
||||||
|
mempcpy(
|
||||||
result = glob_brace(onealt, flags, pglob);
|
mempcpy(new_pattern_buf,
|
||||||
/* If we got an error, return it */
|
/* We know the prefix for all sub-patterns */
|
||||||
if (result && result != GLOB_NOMATCH)
|
pattern, begin - pattern),
|
||||||
return result;
|
p, next - p),
|
||||||
|
rest, rest_len);
|
||||||
flags |= GLOB_APPEND;
|
|
||||||
|
|
||||||
|
/* Note: glob_brace() may garble new_pattern_buf[].
|
||||||
|
* That's why we re-copy prefix every time (1st memcpy above).
|
||||||
|
*/
|
||||||
|
n = glob_brace(new_pattern_buf, o, n);
|
||||||
/*{*/ if (*next == '}') {
|
/*{*/ if (*next == '}') {
|
||||||
/* We saw the last entry */
|
/* We saw the last entry */
|
||||||
break;
|
break;
|
||||||
@ -2118,17 +2136,96 @@ static int glob_brace(const char *pattern, int flags, glob_t *pglob)
|
|||||||
p = next + 1;
|
p = next + 1;
|
||||||
next = next_brace_sub(next);
|
next = next_brace_sub(next);
|
||||||
}
|
}
|
||||||
|
free(new_pattern_buf);
|
||||||
|
return n;
|
||||||
|
|
||||||
/* We found some entries */
|
simple_glob:
|
||||||
return 0;
|
{
|
||||||
|
int gr;
|
||||||
|
glob_t globdata;
|
||||||
|
|
||||||
do_glob:
|
memset(&globdata, 0, sizeof(globdata));
|
||||||
return glob(pattern, flags, NULL, pglob);
|
gr = glob(pattern, 0, NULL, &globdata);
|
||||||
|
debug_printf_glob("glob('%s'):%d\n", pattern, gr);
|
||||||
|
if (gr != 0) {
|
||||||
|
if (gr == GLOB_NOMATCH) {
|
||||||
|
globfree(&globdata);
|
||||||
|
/* NB: garbles parameter */
|
||||||
|
unbackslash(pattern);
|
||||||
|
o_addstr_with_NUL(o, pattern);
|
||||||
|
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
|
||||||
|
return o_save_ptr_helper(o, n);
|
||||||
|
}
|
||||||
|
if (gr == GLOB_NOSPACE)
|
||||||
|
bb_error_msg_and_die(bb_msg_memory_exhausted);
|
||||||
|
/* GLOB_ABORTED? Only happens with GLOB_ERR flag,
|
||||||
|
* but we didn't specify it. Paranoia again. */
|
||||||
|
bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
|
||||||
|
}
|
||||||
|
if (globdata.gl_pathv && globdata.gl_pathv[0]) {
|
||||||
|
char **argv = globdata.gl_pathv;
|
||||||
|
while (1) {
|
||||||
|
o_addstr_with_NUL(o, *argv);
|
||||||
|
n = o_save_ptr_helper(o, n);
|
||||||
|
argv++;
|
||||||
|
if (!*argv)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globfree(&globdata);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
#endif
|
/* Performs globbing on last list[],
|
||||||
|
* saving each result as a new list[].
|
||||||
|
*/
|
||||||
|
static int o_glob(o_string *o, int n)
|
||||||
|
{
|
||||||
|
char *pattern, *copy;
|
||||||
|
|
||||||
/* o_glob performs globbing on last list[], saving each result
|
debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data);
|
||||||
* as a new list[]. */
|
if (!o->data)
|
||||||
|
return o_save_ptr_helper(o, n);
|
||||||
|
pattern = o->data + o_get_last_ptr(o, n);
|
||||||
|
debug_printf_glob("glob pattern '%s'\n", pattern);
|
||||||
|
if (!glob_needed(pattern)) {
|
||||||
|
/* unbackslash last string in o in place, fix length */
|
||||||
|
o->length = unbackslash(pattern) - o->data;
|
||||||
|
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
|
||||||
|
return o_save_ptr_helper(o, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy = xstrdup(pattern);
|
||||||
|
/* "forget" pattern in o */
|
||||||
|
o->length = pattern - o->data;
|
||||||
|
n = glob_brace(copy, o, n);
|
||||||
|
free(copy);
|
||||||
|
if (DEBUG_GLOB)
|
||||||
|
debug_print_list("o_glob returning", o, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Helper */
|
||||||
|
static int glob_needed(const char *s)
|
||||||
|
{
|
||||||
|
while (*s) {
|
||||||
|
if (*s == '\\') {
|
||||||
|
if (!s[1])
|
||||||
|
return 0;
|
||||||
|
s += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*s == '*' || *s == '[' || *s == '?')
|
||||||
|
return 1;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Performs globbing on last list[],
|
||||||
|
* saving each result as a new list[].
|
||||||
|
*/
|
||||||
static int o_glob(o_string *o, int n)
|
static int o_glob(o_string *o, int n)
|
||||||
{
|
{
|
||||||
glob_t globdata;
|
glob_t globdata;
|
||||||
@ -2142,33 +2239,35 @@ static int o_glob(o_string *o, int n)
|
|||||||
debug_printf_glob("glob pattern '%s'\n", pattern);
|
debug_printf_glob("glob pattern '%s'\n", pattern);
|
||||||
if (!glob_needed(pattern)) {
|
if (!glob_needed(pattern)) {
|
||||||
literal:
|
literal:
|
||||||
|
/* unbackslash last string in o in place, fix length */
|
||||||
o->length = unbackslash(pattern) - o->data;
|
o->length = unbackslash(pattern) - o->data;
|
||||||
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
|
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
|
||||||
return o_save_ptr_helper(o, n);
|
return o_save_ptr_helper(o, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&globdata, 0, sizeof(globdata));
|
memset(&globdata, 0, sizeof(globdata));
|
||||||
#ifdef HUSH_BRACE_EXP
|
/* Can't use GLOB_NOCHECK: it does not unescape the string.
|
||||||
gr = glob_brace(pattern, GLOB_NOCHECK, &globdata);
|
* If we glob "*.\*" and don't find anything, we need
|
||||||
debug_printf_glob("glob_brace('%s'):%d\n", pattern, gr);
|
* to fall back to using literal "*.*", but GLOB_NOCHECK
|
||||||
#else
|
* will return "*.\*"!
|
||||||
|
*/
|
||||||
gr = glob(pattern, 0, NULL, &globdata);
|
gr = glob(pattern, 0, NULL, &globdata);
|
||||||
debug_printf_glob("glob('%s'):%d\n", pattern, gr);
|
debug_printf_glob("glob('%s'):%d\n", pattern, gr);
|
||||||
#endif
|
|
||||||
if (gr == GLOB_NOSPACE)
|
|
||||||
bb_error_msg_and_die(bb_msg_memory_exhausted);
|
|
||||||
if (gr == GLOB_NOMATCH) {
|
|
||||||
globfree(&globdata);
|
|
||||||
goto literal;
|
|
||||||
}
|
|
||||||
if (gr != 0) {
|
if (gr != 0) {
|
||||||
|
if (gr == GLOB_NOMATCH) {
|
||||||
|
globfree(&globdata);
|
||||||
|
goto literal;
|
||||||
|
}
|
||||||
|
if (gr == GLOB_NOSPACE)
|
||||||
|
bb_error_msg_and_die(bb_msg_memory_exhausted);
|
||||||
/* GLOB_ABORTED? Only happens with GLOB_ERR flag,
|
/* GLOB_ABORTED? Only happens with GLOB_ERR flag,
|
||||||
* but we didn't specify it. Paranoia again. */
|
* but we didn't specify it. Paranoia again. */
|
||||||
bb_error_msg("glob(3) error %d on '%s'", gr, pattern);
|
bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
|
||||||
}
|
}
|
||||||
if (globdata.gl_pathv && globdata.gl_pathv[0]) {
|
if (globdata.gl_pathv && globdata.gl_pathv[0]) {
|
||||||
char **argv = globdata.gl_pathv;
|
char **argv = globdata.gl_pathv;
|
||||||
o->length = pattern - o->data; /* "forget" pattern */
|
/* "forget" pattern in o */
|
||||||
|
o->length = pattern - o->data;
|
||||||
while (1) {
|
while (1) {
|
||||||
o_addstr_with_NUL(o, *argv);
|
o_addstr_with_NUL(o, *argv);
|
||||||
n = o_save_ptr_helper(o, n);
|
n = o_save_ptr_helper(o, n);
|
||||||
@ -2183,6 +2282,8 @@ static int o_glob(o_string *o, int n)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* If o->o_glob == 1, glob the string so far remembered.
|
/* If o->o_glob == 1, glob the string so far remembered.
|
||||||
* Otherwise, just finish current list[] and start new */
|
* Otherwise, just finish current list[] and start new */
|
||||||
static int o_save_ptr(o_string *o, int n)
|
static int o_save_ptr(o_string *o, int n)
|
||||||
|
3
shell/hush_test/hush-parsing/brace2.right
Normal file
3
shell/hush_test/hush-parsing/brace2.right
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{q,w}
|
||||||
|
{q,w}
|
||||||
|
Done
|
5
shell/hush_test/hush-parsing/brace2.tests
Executable file
5
shell/hush_test/hush-parsing/brace2.tests
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
v='{q,w}'
|
||||||
|
# Should not brace-expand v value
|
||||||
|
echo $v
|
||||||
|
echo "$v"
|
||||||
|
echo Done
|
Loading…
Reference in New Issue
Block a user