File src/engine/options.c changed (mode: 100644) (index 12fdff9c5..d83615761) |
... |
... |
static int find_val(const opt_t *opt, const char value[]); |
94 |
94 |
static int set_print(const opt_t *opt); |
static int set_print(const opt_t *opt); |
95 |
95 |
static char * extract_option(const char **argsp, int completion); |
static char * extract_option(const char **argsp, int completion); |
96 |
96 |
static char * skip_alphas(const char str[]); |
static char * skip_alphas(const char str[]); |
|
97 |
|
static char * escape_as_squoted(const char str[]); |
97 |
98 |
static void complete_option_name(const char buf[], int bool_only, int pseudo, |
static void complete_option_name(const char buf[], int bool_only, int pseudo, |
98 |
99 |
OPT_SCOPE scope); |
OPT_SCOPE scope); |
99 |
100 |
static int option_matches(const opt_t *opt, OPT_SCOPE scope); |
static int option_matches(const opt_t *opt, OPT_SCOPE scope); |
|
... |
... |
vle_opts_complete(const char args[], const char **start, OPT_SCOPE scope) |
1409 |
1410 |
} |
} |
1410 |
1411 |
else if(strcmp(p, "'") == 0) |
else if(strcmp(p, "'") == 0) |
1411 |
1412 |
{ |
{ |
1412 |
|
char *escaped = escape_for_squotes(str_val, /*offset=*/0); |
|
1413 |
|
if(escaped != NULL) |
|
1414 |
|
{ |
|
1415 |
|
vle_compl_put_match(format_str("'%s'", escaped), ""); |
|
1416 |
|
free(escaped); |
|
1417 |
|
} |
|
|
1413 |
|
vle_compl_put_match(escape_as_squoted(str_val), ""); |
1418 |
1414 |
} |
} |
1419 |
1415 |
else if(strcmp(p, "\"") == 0) |
else if(strcmp(p, "\"") == 0) |
1420 |
1416 |
{ |
{ |
|
... |
... |
skip_alphas(const char str[]) |
1572 |
1568 |
return (char *)str; |
return (char *)str; |
1573 |
1569 |
} |
} |
1574 |
1570 |
|
|
|
1571 |
|
/* Produces a value quoted in single quotes as much as possible (embedded single |
|
1572 |
|
* quotes have to be escaped with a slash outside of single quotes). Returns |
|
1573 |
|
* newly allocated memory or NULL on error. */ |
|
1574 |
|
static char * |
|
1575 |
|
escape_as_squoted(const char str[]) |
|
1576 |
|
{ |
|
1577 |
|
enum |
|
1578 |
|
{ |
|
1579 |
|
START, |
|
1580 |
|
NONQUOTE, |
|
1581 |
|
QUOTE, |
|
1582 |
|
} |
|
1583 |
|
state = START; |
|
1584 |
|
|
|
1585 |
|
/* The worst case is when quotes are interleaved with non-quotes: x'y'z'... |
|
1586 |
|
* That gets expanded to 3 characters per non-quote and 2 per quote, so each |
|
1587 |
|
* pair of input characters becomes 5 characters. */ |
|
1588 |
|
const size_t max_escaped = strlen(str)*5/2; |
|
1589 |
|
|
|
1590 |
|
char *escaped = malloc(MAX(2U, max_escaped) + 1); |
|
1591 |
|
if(escaped == NULL) |
|
1592 |
|
{ |
|
1593 |
|
return NULL; |
|
1594 |
|
} |
|
1595 |
|
|
|
1596 |
|
char *out = escaped; |
|
1597 |
|
|
|
1598 |
|
while(*str != '\0') |
|
1599 |
|
{ |
|
1600 |
|
if(*str == '\'') |
|
1601 |
|
{ |
|
1602 |
|
if(state == NONQUOTE) |
|
1603 |
|
{ |
|
1604 |
|
*out++ = '\''; |
|
1605 |
|
} |
|
1606 |
|
|
|
1607 |
|
*out++ = '\\'; |
|
1608 |
|
*out++ = *str++; |
|
1609 |
|
state = QUOTE; |
|
1610 |
|
} |
|
1611 |
|
else |
|
1612 |
|
{ |
|
1613 |
|
if(state != NONQUOTE) |
|
1614 |
|
{ |
|
1615 |
|
*out++ = '\''; |
|
1616 |
|
} |
|
1617 |
|
|
|
1618 |
|
*out++ = *str++; |
|
1619 |
|
state = NONQUOTE; |
|
1620 |
|
} |
|
1621 |
|
} |
|
1622 |
|
|
|
1623 |
|
if(state == START) |
|
1624 |
|
{ |
|
1625 |
|
*out++ = '\''; |
|
1626 |
|
} |
|
1627 |
|
if(state != QUOTE) |
|
1628 |
|
{ |
|
1629 |
|
*out++ = '\''; |
|
1630 |
|
} |
|
1631 |
|
|
|
1632 |
|
*out = '\0'; |
|
1633 |
|
|
|
1634 |
|
return escaped; |
|
1635 |
|
} |
|
1636 |
|
|
1575 |
1637 |
void |
void |
1576 |
1638 |
vle_opts_complete_real(const char beginning[], OPT_SCOPE scope) |
vle_opts_complete_real(const char beginning[], OPT_SCOPE scope) |
1577 |
1639 |
{ |
{ |
File tests/options/opt_completion.c changed (mode: 100644) (index 0a478b21b..a54540b01) |
... |
... |
TEST(after_equal_sign_completion_spaces_ok) |
214 |
214 |
ASSERT_NEXT_MATCH("\"8\""); |
ASSERT_NEXT_MATCH("\"8\""); |
215 |
215 |
} |
} |
216 |
216 |
|
|
|
217 |
|
TEST(after_equal_sign_completion_corner_cases) |
|
218 |
|
{ |
|
219 |
|
const char *start; |
|
220 |
|
|
|
221 |
|
optval_t val; |
|
222 |
|
|
|
223 |
|
val.str_val = "/home' 'directory/tmp"; |
|
224 |
|
vle_opts_assign("fusehome", val, OPT_GLOBAL); |
|
225 |
|
vle_compl_reset(); |
|
226 |
|
vle_opts_complete("fusehome='", &start, OPT_GLOBAL); |
|
227 |
|
ASSERT_NEXT_MATCH("'/home'\\'' '\\''directory/tmp'"); |
|
228 |
|
|
|
229 |
|
val.str_val = "'''"; |
|
230 |
|
vle_opts_assign("fusehome", val, OPT_GLOBAL); |
|
231 |
|
vle_compl_reset(); |
|
232 |
|
vle_opts_complete("fusehome='", &start, OPT_GLOBAL); |
|
233 |
|
ASSERT_NEXT_MATCH("\\'\\'\\'"); |
|
234 |
|
|
|
235 |
|
val.str_val = ""; |
|
236 |
|
vle_opts_assign("fusehome", val, OPT_GLOBAL); |
|
237 |
|
vle_compl_reset(); |
|
238 |
|
vle_opts_complete("fusehome='", &start, OPT_GLOBAL); |
|
239 |
|
ASSERT_NEXT_MATCH("''"); |
|
240 |
|
} |
|
241 |
|
|
217 |
242 |
TEST(after_fake_equal_sign_completion_fail) |
TEST(after_fake_equal_sign_completion_fail) |
218 |
243 |
{ |
{ |
219 |
244 |
const char *start; |
const char *start; |