implement support for parameter substitution via #/% operators

This commit is contained in:
Mike Frysinger 2009-04-07 06:03:22 +00:00
parent 6c9be7f451
commit a4f331d3c3
6 changed files with 242 additions and 24 deletions

View File

@ -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

View File

@ -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 */

View File

@ -0,0 +1,17 @@
abcdcd
abcdcd
abcdcd
cdcd
babcdcd
babcdcd
ababcdcd
ababcd
ababcd
ababcd
abab
ababcdc
ababcdc
ababcdcd
end

View 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
View 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
View 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;
}
}