implement most POSIX parameter expansions (~+500bytes)
This commit is contained in:
parent
42ab86520e
commit
6379bb4fde
158
shell/hush.c
158
shell/hush.c
@ -43,12 +43,13 @@
|
||||
* Here Documents ( << word )
|
||||
* Functions
|
||||
* Tilde Expansion
|
||||
* fancy forms of Parameter Expansion: ${var:-val}
|
||||
* Parameter Expansion for substring processing ${var#word} ${var%word}
|
||||
*
|
||||
* Bash stuff maybe optional enable:
|
||||
* &> and >& redirection of stdout+stderr
|
||||
* Brace expansion
|
||||
* reserved words: [[ ]] function select
|
||||
* substrings ${var:1:5}
|
||||
*
|
||||
* Major bugs:
|
||||
* job handling woefully incomplete and buggy (improved --vda)
|
||||
@ -589,18 +590,20 @@ static const struct built_in_command bltins[] = {
|
||||
|
||||
#if 1
|
||||
/* Normal */
|
||||
static void syntax(const char *msg)
|
||||
static void maybe_die(const char *notice, const char *msg)
|
||||
{
|
||||
#if ENABLE_HUSH_INTERACTIVE
|
||||
/* Was using fancy stuff:
|
||||
* (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...)
|
||||
* but it SEGVs. ?! Oh well... explicit temp ptr works around that */
|
||||
void FAST_FUNC (*fp)(const char *s, ...);
|
||||
void FAST_FUNC (*fp)(const char *s, ...) = bb_error_msg_and_die;
|
||||
#if ENABLE_HUSH_INTERACTIVE
|
||||
fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die);
|
||||
fp(msg ? "%s: %s" : "syntax error", "syntax error", msg);
|
||||
#else
|
||||
bb_error_msg_and_die(msg ? "%s: %s" : "syntax error", "syntax error", msg);
|
||||
#endif
|
||||
fp(msg ? "%s: %s" : notice, notice, msg);
|
||||
}
|
||||
static void syntax(const char *msg)
|
||||
{
|
||||
maybe_die("syntax error", msg);
|
||||
}
|
||||
#else
|
||||
/* Debug */
|
||||
@ -920,7 +923,13 @@ static const char *lookup_param(const char *src)
|
||||
}
|
||||
|
||||
/* str holds "NAME=VAL" and is expected to be malloced.
|
||||
* We take ownership of it. */
|
||||
* We take ownership of it.
|
||||
* flg_export is used by:
|
||||
* 0: do not export
|
||||
* 1: export
|
||||
* -1: if NAME is set, leave export status alone
|
||||
* if NAME is not set, do not export
|
||||
*/
|
||||
static int set_local_var(char *str, int flg_export)
|
||||
{
|
||||
struct variable *cur;
|
||||
@ -980,7 +989,7 @@ static int set_local_var(char *str, int flg_export)
|
||||
set_str_and_exp:
|
||||
cur->varstr = str;
|
||||
exp:
|
||||
if (flg_export)
|
||||
if (flg_export == 1)
|
||||
cur->flg_export = 1;
|
||||
if (cur->flg_export) {
|
||||
debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr);
|
||||
@ -1548,6 +1557,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
||||
val = utoa(G.last_return_code);
|
||||
break;
|
||||
case '#': /* argc */
|
||||
if (arg[1] != SPECIAL_VAR_SYMBOL)
|
||||
/* actually, it's a ${#var} */
|
||||
goto case_default;
|
||||
val = utoa(G.global_argc ? G.global_argc-1 : 0);
|
||||
break;
|
||||
case '*':
|
||||
@ -1615,20 +1627,79 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
||||
}
|
||||
#endif
|
||||
default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
|
||||
case_default: {
|
||||
bool exp_len = false, exp_null = false;
|
||||
char *var = arg, exp_save, exp_op, *exp_word;
|
||||
size_t exp_off = 0;
|
||||
*p = '\0';
|
||||
arg[0] = first_ch & 0x7f;
|
||||
if (isdigit(arg[0])) {
|
||||
i = xatoi_u(arg);
|
||||
|
||||
/* prepare for expansions */
|
||||
if (var[0] == '#') {
|
||||
/* handle length expansion ${#var} */
|
||||
exp_len = true;
|
||||
++var;
|
||||
} else {
|
||||
/* maybe handle parameter expansion */
|
||||
exp_off = strcspn(var, ":-=+?");
|
||||
if (!var[exp_off])
|
||||
exp_off = 0;
|
||||
if (exp_off) {
|
||||
exp_save = var[exp_off];
|
||||
exp_null = exp_save == ':';
|
||||
exp_word = var + exp_off;
|
||||
if (exp_null) ++exp_word;
|
||||
exp_op = *exp_word++;
|
||||
var[exp_off] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* lookup the variable in question */
|
||||
if (isdigit(var[0])) {
|
||||
i = xatoi_u(var);
|
||||
if (i < G.global_argc)
|
||||
val = G.global_argv[i];
|
||||
/* else val remains NULL: $N with too big N */
|
||||
} else
|
||||
val = lookup_param(arg);
|
||||
val = lookup_param(var);
|
||||
|
||||
/* handle any expansions */
|
||||
if (exp_len) {
|
||||
debug_printf_expand("expand: length of '%s' = ", val);
|
||||
val = utoa(val ? strlen(val) : 0);
|
||||
debug_printf_expand("%s\n", val);
|
||||
} else if (exp_off) {
|
||||
/* we need to do an expansion */
|
||||
int exp_test = (!val || (exp_null && !val[0]));
|
||||
if (exp_op == '+')
|
||||
exp_test = !exp_test;
|
||||
debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
|
||||
exp_null ? "true" : "false", exp_test);
|
||||
if (exp_test) {
|
||||
if (exp_op == '?')
|
||||
maybe_die(var, *exp_word ? exp_word : "parameter null or not set");
|
||||
else
|
||||
val = exp_word;
|
||||
|
||||
if (exp_op == '=') {
|
||||
if (isdigit(var[0]) || var[0] == '#') {
|
||||
maybe_die(var, "special vars cannot assign in this way");
|
||||
val = NULL;
|
||||
} else {
|
||||
char *new_var = xmalloc(strlen(var) + strlen(val) + 2);
|
||||
sprintf(new_var, "%s=%s", var, val);
|
||||
set_local_var(new_var, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
var[exp_off] = exp_save;
|
||||
}
|
||||
|
||||
arg[0] = first_ch;
|
||||
|
||||
#if ENABLE_HUSH_TICK
|
||||
store_val:
|
||||
#endif
|
||||
*p = SPECIAL_VAR_SYMBOL;
|
||||
if (!(first_ch & 0x80)) { /* unquoted $VAR */
|
||||
debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote);
|
||||
if (val) {
|
||||
@ -1643,9 +1714,13 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
||||
debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val) {
|
||||
o_addQstr(output, val, strlen(val));
|
||||
}
|
||||
/* Do the check to avoid writing to a const string */
|
||||
if (p && *p != SPECIAL_VAR_SYMBOL)
|
||||
*p = SPECIAL_VAR_SYMBOL;
|
||||
|
||||
#if ENABLE_HUSH_TICK
|
||||
o_free(&subst_result);
|
||||
@ -3625,6 +3700,7 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input)
|
||||
/* Return code: 0 for OK, 1 for syntax error */
|
||||
static int handle_dollar(o_string *dest, struct in_str *input)
|
||||
{
|
||||
int expansion;
|
||||
int ch = i_peek(input); /* first character after the $ */
|
||||
unsigned char quote_mask = dest->o_quote ? 0x80 : 0;
|
||||
|
||||
@ -3658,25 +3734,71 @@ static int handle_dollar(o_string *dest, struct in_str *input)
|
||||
case '*': /* args */
|
||||
case '@': /* args */
|
||||
goto make_one_char_var;
|
||||
case '{':
|
||||
case '{': {
|
||||
bool first_char;
|
||||
|
||||
o_addchr(dest, SPECIAL_VAR_SYMBOL);
|
||||
i_getch(input);
|
||||
/* XXX maybe someone will try to escape the '}' */
|
||||
expansion = 0;
|
||||
first_char = true;
|
||||
while (1) {
|
||||
ch = i_getch(input);
|
||||
if (ch == '}')
|
||||
break;
|
||||
if (!isalnum(ch) && ch != '_') {
|
||||
syntax("unterminated ${name}");
|
||||
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
|
||||
return 1;
|
||||
|
||||
if (ch == '#' && first_char)
|
||||
/* ${#var}: length of var contents */;
|
||||
|
||||
else if (expansion < 2 && !isalnum(ch) && ch != '_') {
|
||||
/* handle parameter expansions
|
||||
* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
|
||||
*/
|
||||
if (first_char)
|
||||
goto case_default;
|
||||
switch (ch) {
|
||||
case ':': /* null modifier */
|
||||
if (expansion == 0) {
|
||||
debug_printf_parse(": null modifier\n");
|
||||
++expansion;
|
||||
break;
|
||||
}
|
||||
goto case_default;
|
||||
|
||||
#if 0 /* not implemented yet :( */
|
||||
case '#': /* remove prefix */
|
||||
case '%': /* remove suffix */
|
||||
if (expansion == 0) {
|
||||
debug_printf_parse(": remove suffix/prefix\n");
|
||||
expansion = 2;
|
||||
break;
|
||||
}
|
||||
goto case_default;
|
||||
#endif
|
||||
|
||||
case '-': /* default value */
|
||||
case '=': /* assign default */
|
||||
case '+': /* alternative */
|
||||
case '?': /* error indicate */
|
||||
debug_printf_parse(": parameter expansion\n");
|
||||
expansion = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
case_default:
|
||||
syntax("unterminated ${name}");
|
||||
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
debug_printf_parse(": '%c'\n", ch);
|
||||
o_addchr(dest, ch | quote_mask);
|
||||
quote_mask = 0;
|
||||
first_char = false;
|
||||
}
|
||||
o_addchr(dest, SPECIAL_VAR_SYMBOL);
|
||||
break;
|
||||
}
|
||||
#if ENABLE_HUSH_TICK
|
||||
case '(': {
|
||||
//int pos = dest->length;
|
||||
|
Loading…
Reference in New Issue
Block a user