implement support for parameter substitution via #/% operators
This commit is contained in:
parent
6c9be7f451
commit
a4f331d3c3
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
lib-y:=
|
lib-y:=
|
||||||
lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o
|
lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o
|
||||||
lib-$(CONFIG_HUSH) += hush.o
|
lib-$(CONFIG_HUSH) += hush.o match.o
|
||||||
lib-$(CONFIG_MSH) += msh.o
|
lib-$(CONFIG_MSH) += msh.o
|
||||||
lib-$(CONFIG_CTTYHACK) += cttyhack.o
|
lib-$(CONFIG_CTTYHACK) += cttyhack.o
|
||||||
lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
|
lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
|
||||||
|
63
shell/hush.c
63
shell/hush.c
@ -43,7 +43,6 @@
|
|||||||
* Here Documents ( << word )
|
* Here Documents ( << word )
|
||||||
* Functions
|
* Functions
|
||||||
* Tilde Expansion
|
* Tilde Expansion
|
||||||
* Parameter Expansion for substring processing ${var#word} ${var%word}
|
|
||||||
*
|
*
|
||||||
* Bash stuff (maybe optionally enable?):
|
* Bash stuff (maybe optionally enable?):
|
||||||
* &> and >& redirection of stdout+stderr
|
* &> and >& redirection of stdout+stderr
|
||||||
@ -76,6 +75,7 @@
|
|||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
#endif
|
#endif
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
|
#include "match.h"
|
||||||
|
|
||||||
#ifdef WANT_TO_TEST_NOMMU
|
#ifdef WANT_TO_TEST_NOMMU
|
||||||
# undef BB_MMU
|
# undef BB_MMU
|
||||||
@ -1667,8 +1667,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
char first_ch, ored_ch;
|
char first_ch, ored_ch;
|
||||||
int i;
|
int i;
|
||||||
const char *val;
|
const char *val;
|
||||||
char *p;
|
char *dyn_val, *p;
|
||||||
|
|
||||||
|
dyn_val = NULL;
|
||||||
ored_ch = 0;
|
ored_ch = 0;
|
||||||
|
|
||||||
debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg);
|
debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg);
|
||||||
@ -1844,7 +1845,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
++var;
|
++var;
|
||||||
} else {
|
} else {
|
||||||
/* maybe handle parameter expansion */
|
/* maybe handle parameter expansion */
|
||||||
exp_off = strcspn(var, ":-=+?");
|
exp_off = strcspn(var, ":-=+?%#");
|
||||||
if (!var[exp_off])
|
if (!var[exp_off])
|
||||||
exp_off = 0;
|
exp_off = 0;
|
||||||
if (exp_off) {
|
if (exp_off) {
|
||||||
@ -1873,29 +1874,45 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
val = utoa(val ? strlen(val) : 0);
|
val = utoa(val ? strlen(val) : 0);
|
||||||
debug_printf_expand("%s\n", val);
|
debug_printf_expand("%s\n", val);
|
||||||
} else if (exp_off) {
|
} else if (exp_off) {
|
||||||
/* we need to do an expansion */
|
if (exp_op == '%' || exp_op == '#') {
|
||||||
int exp_test = (!val || (exp_null && !val[0]));
|
/* we need to do a pattern match */
|
||||||
if (exp_op == '+')
|
bool zero;
|
||||||
exp_test = !exp_test;
|
char *loc;
|
||||||
debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
|
scan_t scan = pick_scan(exp_op, *exp_word, &zero);
|
||||||
exp_null ? "true" : "false", exp_test);
|
if (exp_op == *exp_word) /* ## or %% */
|
||||||
if (exp_test) {
|
++exp_word;
|
||||||
if (exp_op == '?')
|
val = dyn_val = xstrdup(val);
|
||||||
maybe_die(var, *exp_word ? exp_word : "parameter null or not set");
|
loc = scan(dyn_val, exp_word, zero);
|
||||||
|
if (zero)
|
||||||
|
val = loc;
|
||||||
else
|
else
|
||||||
val = exp_word;
|
*loc = '\0';
|
||||||
|
} else {
|
||||||
|
/* 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 (exp_op == '=') {
|
||||||
if (isdigit(var[0]) || var[0] == '#') {
|
if (isdigit(var[0]) || var[0] == '#') {
|
||||||
maybe_die(var, "special vars cannot assign in this way");
|
maybe_die(var, "special vars cannot assign in this way");
|
||||||
val = NULL;
|
val = NULL;
|
||||||
} else {
|
} else {
|
||||||
char *new_var = xmalloc(strlen(var) + strlen(val) + 2);
|
char *new_var = xmalloc(strlen(var) + strlen(val) + 2);
|
||||||
sprintf(new_var, "%s=%s", var, val);
|
sprintf(new_var, "%s=%s", var, val);
|
||||||
set_local_var(new_var, -1, 0);
|
set_local_var(new_var, -1, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var[exp_off] = exp_save;
|
var[exp_off] = exp_save;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1921,6 +1938,8 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
if (val) {
|
if (val) {
|
||||||
o_addQstr(output, val, strlen(val));
|
o_addQstr(output, val, strlen(val));
|
||||||
}
|
}
|
||||||
|
free(dyn_val);
|
||||||
|
dyn_val = NULL;
|
||||||
/* Do the check to avoid writing to a const string */
|
/* Do the check to avoid writing to a const string */
|
||||||
if (*p != SPECIAL_VAR_SYMBOL)
|
if (*p != SPECIAL_VAR_SYMBOL)
|
||||||
*p = SPECIAL_VAR_SYMBOL;
|
*p = SPECIAL_VAR_SYMBOL;
|
||||||
@ -4428,7 +4447,6 @@ static int handle_dollar(o_string *as_string,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
goto case_default;
|
goto case_default;
|
||||||
#if 0 /* not implemented yet :( */
|
|
||||||
case '#': /* remove prefix */
|
case '#': /* remove prefix */
|
||||||
case '%': /* remove suffix */
|
case '%': /* remove suffix */
|
||||||
if (expansion == 0) {
|
if (expansion == 0) {
|
||||||
@ -4437,7 +4455,6 @@ static int handle_dollar(o_string *as_string,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
goto case_default;
|
goto case_default;
|
||||||
#endif
|
|
||||||
case '-': /* default value */
|
case '-': /* default value */
|
||||||
case '=': /* assign default */
|
case '=': /* assign default */
|
||||||
case '+': /* alternative */
|
case '+': /* alternative */
|
||||||
|
17
shell/hush_test/hush-vars/var_posix1.right
Normal file
17
shell/hush_test/hush-vars/var_posix1.right
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
abcdcd
|
||||||
|
abcdcd
|
||||||
|
abcdcd
|
||||||
|
cdcd
|
||||||
|
babcdcd
|
||||||
|
babcdcd
|
||||||
|
ababcdcd
|
||||||
|
|
||||||
|
ababcd
|
||||||
|
ababcd
|
||||||
|
ababcd
|
||||||
|
abab
|
||||||
|
ababcdc
|
||||||
|
ababcdc
|
||||||
|
ababcdcd
|
||||||
|
|
||||||
|
end
|
21
shell/hush_test/hush-vars/var_posix1.tests
Executable file
21
shell/hush_test/hush-vars/var_posix1.tests
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
var=ababcdcd
|
||||||
|
|
||||||
|
echo ${var#ab}
|
||||||
|
echo ${var##ab}
|
||||||
|
echo ${var#a*b}
|
||||||
|
echo ${var##a*b}
|
||||||
|
echo ${var#?}
|
||||||
|
echo ${var##?}
|
||||||
|
echo ${var#*}
|
||||||
|
echo ${var##*}
|
||||||
|
|
||||||
|
echo ${var%cd}
|
||||||
|
echo ${var%%cd}
|
||||||
|
echo ${var%c*d}
|
||||||
|
echo ${var%%c*d}
|
||||||
|
echo ${var%?}
|
||||||
|
echo ${var%%?}
|
||||||
|
echo ${var%*}
|
||||||
|
echo ${var%%*}
|
||||||
|
|
||||||
|
echo end
|
141
shell/match.c
Normal file
141
shell/match.c
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* ##/%% variable matching code ripped out of ash shell for code sharing
|
||||||
|
*
|
||||||
|
* Copyright (c) 1989, 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
|
||||||
|
* was re-ported from NetBSD and debianized.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to Berkeley by
|
||||||
|
* Kenneth Almquist.
|
||||||
|
*
|
||||||
|
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
|
||||||
|
*
|
||||||
|
* Original BSD copyright notice is retained at the end of this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
# include <stdbool.h>
|
||||||
|
# include <stdio.h>
|
||||||
|
# include <stdlib.h>
|
||||||
|
# include <string.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#else
|
||||||
|
# include "busybox.h"
|
||||||
|
#endif
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include "match.h"
|
||||||
|
|
||||||
|
#define pmatch(a, b) !fnmatch((a), (b), 0)
|
||||||
|
|
||||||
|
char *scanleft(char *string, char *pattern, bool zero)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
char *loc = string;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int match;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
c = *loc;
|
||||||
|
if (zero) {
|
||||||
|
*loc = '\0';
|
||||||
|
s = string;
|
||||||
|
} else
|
||||||
|
s = loc;
|
||||||
|
match = pmatch(pattern, s);
|
||||||
|
*loc = c;
|
||||||
|
|
||||||
|
if (match)
|
||||||
|
return loc;
|
||||||
|
|
||||||
|
loc++;
|
||||||
|
} while (c);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *scanright(char *string, char *pattern, bool zero)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
char *loc = string + strlen(string);
|
||||||
|
|
||||||
|
while (loc >= string) {
|
||||||
|
int match;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
c = *loc;
|
||||||
|
if (zero) {
|
||||||
|
*loc = '\0';
|
||||||
|
s = string;
|
||||||
|
} else
|
||||||
|
s = loc;
|
||||||
|
match = pmatch(pattern, s);
|
||||||
|
*loc = c;
|
||||||
|
|
||||||
|
if (match)
|
||||||
|
return loc;
|
||||||
|
|
||||||
|
loc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *string;
|
||||||
|
char *op;
|
||||||
|
char *pattern;
|
||||||
|
bool zero;
|
||||||
|
char *loc;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
|
puts(
|
||||||
|
"Usage: match <test> [test...]\n\n"
|
||||||
|
"Where a <test> is the form: <string><op><match>\n"
|
||||||
|
"This is to test the shell ${var#val} expression type.\n\n"
|
||||||
|
"e.g. `match 'abc#a*'` -> bc"
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < argc; ++i) {
|
||||||
|
size_t off;
|
||||||
|
scan_t scan;
|
||||||
|
|
||||||
|
printf("'%s': ", argv[i]);
|
||||||
|
|
||||||
|
string = strdup(argv[i]);
|
||||||
|
off = strcspn(string, "#%");
|
||||||
|
if (!off) {
|
||||||
|
printf("invalid format\n");
|
||||||
|
free(string);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
op = string + off;
|
||||||
|
scan = pick_scan(op[0], op[1], &zero);
|
||||||
|
pattern = op + 1;
|
||||||
|
if (op[0] == op[1])
|
||||||
|
op[1] = '\0', ++pattern;
|
||||||
|
op[0] = '\0';
|
||||||
|
|
||||||
|
loc = scan(string, pattern, zero);
|
||||||
|
|
||||||
|
if (zero) {
|
||||||
|
printf("'%s'\n", loc);
|
||||||
|
} else {
|
||||||
|
*loc = '\0';
|
||||||
|
printf("'%s'\n", string);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
22
shell/match.h
Normal file
22
shell/match.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* match.h - interface to shell ##/%% matching code */
|
||||||
|
|
||||||
|
typedef char *(*scan_t)(char *string, char *match, bool zero);
|
||||||
|
|
||||||
|
char *scanleft(char *string, char *match, bool zero);
|
||||||
|
char *scanright(char *string, char *match, bool zero);
|
||||||
|
|
||||||
|
static inline scan_t pick_scan(char op1, char op2, bool *zero)
|
||||||
|
{
|
||||||
|
/* # - scanleft
|
||||||
|
* ## - scanright
|
||||||
|
* % - scanright
|
||||||
|
* %% - scanleft
|
||||||
|
*/
|
||||||
|
if (op1 == '#') {
|
||||||
|
*zero = true;
|
||||||
|
return op1 == op2 ? scanright : scanleft;
|
||||||
|
} else {
|
||||||
|
*zero = false;
|
||||||
|
return op1 == op2 ? scanleft : scanright;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user