diff options
author | Mike Frysinger <vapier@gentoo.org> | 2009-04-07 06:03:22 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2009-04-07 06:03:22 +0000 |
commit | a4f331d3c3ea5b358613992a48556cc9cbfdf139 (patch) | |
tree | 316143ec21a1efd2eb7e135121134c0b8b86221e | |
parent | 6c9be7f4518bf5594f5b9aaf981ed5dcc4a6939c (diff) | |
download | busybox-w32-a4f331d3c3ea5b358613992a48556cc9cbfdf139.tar.gz busybox-w32-a4f331d3c3ea5b358613992a48556cc9cbfdf139.tar.bz2 busybox-w32-a4f331d3c3ea5b358613992a48556cc9cbfdf139.zip |
implement support for parameter substitution via #/% operators
-rw-r--r-- | shell/Kbuild | 2 | ||||
-rw-r--r-- | shell/hush.c | 65 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_posix1.right | 17 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_posix1.tests | 21 | ||||
-rw-r--r-- | shell/match.c | 141 | ||||
-rw-r--r-- | shell/match.h | 22 |
6 files changed, 243 insertions, 25 deletions
diff --git a/shell/Kbuild b/shell/Kbuild index 2b461b3bd..8b693ecc3 100644 --- a/shell/Kbuild +++ b/shell/Kbuild | |||
@@ -6,7 +6,7 @@ | |||
6 | 6 | ||
7 | lib-y:= | 7 | lib-y:= |
8 | lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o | 8 | lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o |
9 | lib-$(CONFIG_HUSH) += hush.o | 9 | lib-$(CONFIG_HUSH) += hush.o match.o |
10 | lib-$(CONFIG_MSH) += msh.o | 10 | lib-$(CONFIG_MSH) += msh.o |
11 | lib-$(CONFIG_CTTYHACK) += cttyhack.o | 11 | lib-$(CONFIG_CTTYHACK) += cttyhack.o |
12 | lib-$(CONFIG_SH_MATH_SUPPORT) += math.o | 12 | lib-$(CONFIG_SH_MATH_SUPPORT) += math.o |
diff --git a/shell/hush.c b/shell/hush.c index 792886944..1740187d4 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -43,7 +43,6 @@ | |||
43 | * Here Documents ( << word ) | 43 | * Here Documents ( << word ) |
44 | * Functions | 44 | * Functions |
45 | * Tilde Expansion | 45 | * Tilde Expansion |
46 | * Parameter Expansion for substring processing ${var#word} ${var%word} | ||
47 | * | 46 | * |
48 | * Bash stuff (maybe optionally enable?): | 47 | * Bash stuff (maybe optionally enable?): |
49 | * &> and >& redirection of stdout+stderr | 48 | * &> and >& redirection of stdout+stderr |
@@ -76,6 +75,7 @@ | |||
76 | #include <fnmatch.h> | 75 | #include <fnmatch.h> |
77 | #endif | 76 | #endif |
78 | #include "math.h" | 77 | #include "math.h" |
78 | #include "match.h" | ||
79 | 79 | ||
80 | #ifdef WANT_TO_TEST_NOMMU | 80 | #ifdef WANT_TO_TEST_NOMMU |
81 | # undef BB_MMU | 81 | # undef BB_MMU |
@@ -1667,8 +1667,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1667 | char first_ch, ored_ch; | 1667 | char first_ch, ored_ch; |
1668 | int i; | 1668 | int i; |
1669 | const char *val; | 1669 | const char *val; |
1670 | char *p; | 1670 | char *dyn_val, *p; |
1671 | 1671 | ||
1672 | dyn_val = NULL; | ||
1672 | ored_ch = 0; | 1673 | ored_ch = 0; |
1673 | 1674 | ||
1674 | debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); | 1675 | 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) | |||
1844 | ++var; | 1845 | ++var; |
1845 | } else { | 1846 | } else { |
1846 | /* maybe handle parameter expansion */ | 1847 | /* maybe handle parameter expansion */ |
1847 | exp_off = strcspn(var, ":-=+?"); | 1848 | exp_off = strcspn(var, ":-=+?%#"); |
1848 | if (!var[exp_off]) | 1849 | if (!var[exp_off]) |
1849 | exp_off = 0; | 1850 | exp_off = 0; |
1850 | if (exp_off) { | 1851 | if (exp_off) { |
@@ -1873,29 +1874,45 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1873 | val = utoa(val ? strlen(val) : 0); | 1874 | val = utoa(val ? strlen(val) : 0); |
1874 | debug_printf_expand("%s\n", val); | 1875 | debug_printf_expand("%s\n", val); |
1875 | } else if (exp_off) { | 1876 | } else if (exp_off) { |
1876 | /* we need to do an expansion */ | 1877 | if (exp_op == '%' || exp_op == '#') { |
1877 | int exp_test = (!val || (exp_null && !val[0])); | 1878 | /* we need to do a pattern match */ |
1878 | if (exp_op == '+') | 1879 | bool zero; |
1879 | exp_test = !exp_test; | 1880 | char *loc; |
1880 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, | 1881 | scan_t scan = pick_scan(exp_op, *exp_word, &zero); |
1881 | exp_null ? "true" : "false", exp_test); | 1882 | if (exp_op == *exp_word) /* ## or %% */ |
1882 | if (exp_test) { | 1883 | ++exp_word; |
1883 | if (exp_op == '?') | 1884 | val = dyn_val = xstrdup(val); |
1884 | maybe_die(var, *exp_word ? exp_word : "parameter null or not set"); | 1885 | loc = scan(dyn_val, exp_word, zero); |
1886 | if (zero) | ||
1887 | val = loc; | ||
1885 | else | 1888 | else |
1886 | val = exp_word; | 1889 | *loc = '\0'; |
1887 | 1890 | } else { | |
1888 | if (exp_op == '=') { | 1891 | /* we need to do an expansion */ |
1889 | if (isdigit(var[0]) || var[0] == '#') { | 1892 | int exp_test = (!val || (exp_null && !val[0])); |
1890 | maybe_die(var, "special vars cannot assign in this way"); | 1893 | if (exp_op == '+') |
1891 | val = NULL; | 1894 | exp_test = !exp_test; |
1892 | } else { | 1895 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, |
1893 | char *new_var = xmalloc(strlen(var) + strlen(val) + 2); | 1896 | exp_null ? "true" : "false", exp_test); |
1894 | sprintf(new_var, "%s=%s", var, val); | 1897 | if (exp_test) { |
1895 | set_local_var(new_var, -1, 0); | 1898 | if (exp_op == '?') |
1899 | maybe_die(var, *exp_word ? exp_word : "parameter null or not set"); | ||
1900 | else | ||
1901 | val = exp_word; | ||
1902 | |||
1903 | if (exp_op == '=') { | ||
1904 | if (isdigit(var[0]) || var[0] == '#') { | ||
1905 | maybe_die(var, "special vars cannot assign in this way"); | ||
1906 | val = NULL; | ||
1907 | } else { | ||
1908 | char *new_var = xmalloc(strlen(var) + strlen(val) + 2); | ||
1909 | sprintf(new_var, "%s=%s", var, val); | ||
1910 | set_local_var(new_var, -1, 0); | ||
1911 | } | ||
1896 | } | 1912 | } |
1897 | } | 1913 | } |
1898 | } | 1914 | } |
1915 | |||
1899 | var[exp_off] = exp_save; | 1916 | var[exp_off] = exp_save; |
1900 | } | 1917 | } |
1901 | 1918 | ||
@@ -1921,6 +1938,8 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1921 | if (val) { | 1938 | if (val) { |
1922 | o_addQstr(output, val, strlen(val)); | 1939 | o_addQstr(output, val, strlen(val)); |
1923 | } | 1940 | } |
1941 | free(dyn_val); | ||
1942 | dyn_val = NULL; | ||
1924 | /* Do the check to avoid writing to a const string */ | 1943 | /* Do the check to avoid writing to a const string */ |
1925 | if (*p != SPECIAL_VAR_SYMBOL) | 1944 | if (*p != SPECIAL_VAR_SYMBOL) |
1926 | *p = SPECIAL_VAR_SYMBOL; | 1945 | *p = SPECIAL_VAR_SYMBOL; |
@@ -4428,7 +4447,6 @@ static int handle_dollar(o_string *as_string, | |||
4428 | break; | 4447 | break; |
4429 | } | 4448 | } |
4430 | goto case_default; | 4449 | goto case_default; |
4431 | #if 0 /* not implemented yet :( */ | ||
4432 | case '#': /* remove prefix */ | 4450 | case '#': /* remove prefix */ |
4433 | case '%': /* remove suffix */ | 4451 | case '%': /* remove suffix */ |
4434 | if (expansion == 0) { | 4452 | if (expansion == 0) { |
@@ -4437,7 +4455,6 @@ static int handle_dollar(o_string *as_string, | |||
4437 | break; | 4455 | break; |
4438 | } | 4456 | } |
4439 | goto case_default; | 4457 | goto case_default; |
4440 | #endif | ||
4441 | case '-': /* default value */ | 4458 | case '-': /* default value */ |
4442 | case '=': /* assign default */ | 4459 | case '=': /* assign default */ |
4443 | case '+': /* alternative */ | 4460 | case '+': /* alternative */ |
diff --git a/shell/hush_test/hush-vars/var_posix1.right b/shell/hush_test/hush-vars/var_posix1.right new file mode 100644 index 000000000..55f35798a --- /dev/null +++ b/shell/hush_test/hush-vars/var_posix1.right | |||
@@ -0,0 +1,17 @@ | |||
1 | abcdcd | ||
2 | abcdcd | ||
3 | abcdcd | ||
4 | cdcd | ||
5 | babcdcd | ||
6 | babcdcd | ||
7 | ababcdcd | ||
8 | |||
9 | ababcd | ||
10 | ababcd | ||
11 | ababcd | ||
12 | abab | ||
13 | ababcdc | ||
14 | ababcdc | ||
15 | ababcdcd | ||
16 | |||
17 | end | ||
diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests new file mode 100755 index 000000000..4139e2cc3 --- /dev/null +++ b/shell/hush_test/hush-vars/var_posix1.tests | |||
@@ -0,0 +1,21 @@ | |||
1 | var=ababcdcd | ||
2 | |||
3 | echo ${var#ab} | ||
4 | echo ${var##ab} | ||
5 | echo ${var#a*b} | ||
6 | echo ${var##a*b} | ||
7 | echo ${var#?} | ||
8 | echo ${var##?} | ||
9 | echo ${var#*} | ||
10 | echo ${var##*} | ||
11 | |||
12 | echo ${var%cd} | ||
13 | echo ${var%%cd} | ||
14 | echo ${var%c*d} | ||
15 | echo ${var%%c*d} | ||
16 | echo ${var%?} | ||
17 | echo ${var%%?} | ||
18 | echo ${var%*} | ||
19 | echo ${var%%*} | ||
20 | |||
21 | echo end | ||
diff --git a/shell/match.c b/shell/match.c new file mode 100644 index 000000000..0810fd8f6 --- /dev/null +++ b/shell/match.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * ##/%% variable matching code ripped out of ash shell for code sharing | ||
3 | * | ||
4 | * Copyright (c) 1989, 1991, 1993, 1994 | ||
5 | * The Regents of the University of California. All rights reserved. | ||
6 | * | ||
7 | * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> | ||
8 | * was re-ported from NetBSD and debianized. | ||
9 | * | ||
10 | * This code is derived from software contributed to Berkeley by | ||
11 | * Kenneth Almquist. | ||
12 | * | ||
13 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
14 | * | ||
15 | * Original BSD copyright notice is retained at the end of this file. | ||
16 | */ | ||
17 | |||
18 | #ifdef STANDALONE | ||
19 | # include <stdbool.h> | ||
20 | # include <stdio.h> | ||
21 | # include <stdlib.h> | ||
22 | # include <string.h> | ||
23 | # include <unistd.h> | ||
24 | #else | ||
25 | # include "busybox.h" | ||
26 | #endif | ||
27 | #include <fnmatch.h> | ||
28 | #include "match.h" | ||
29 | |||
30 | #define pmatch(a, b) !fnmatch((a), (b), 0) | ||
31 | |||
32 | char *scanleft(char *string, char *pattern, bool zero) | ||
33 | { | ||
34 | char c; | ||
35 | char *loc = string; | ||
36 | |||
37 | do { | ||
38 | int match; | ||
39 | const char *s; | ||
40 | |||
41 | c = *loc; | ||
42 | if (zero) { | ||
43 | *loc = '\0'; | ||
44 | s = string; | ||
45 | } else | ||
46 | s = loc; | ||
47 | match = pmatch(pattern, s); | ||
48 | *loc = c; | ||
49 | |||
50 | if (match) | ||
51 | return loc; | ||
52 | |||
53 | loc++; | ||
54 | } while (c); | ||
55 | |||
56 | return NULL; | ||
57 | } | ||
58 | |||
59 | char *scanright(char *string, char *pattern, bool zero) | ||
60 | { | ||
61 | char c; | ||
62 | char *loc = string + strlen(string); | ||
63 | |||
64 | while (loc >= string) { | ||
65 | int match; | ||
66 | const char *s; | ||
67 | |||
68 | c = *loc; | ||
69 | if (zero) { | ||
70 | *loc = '\0'; | ||
71 | s = string; | ||
72 | } else | ||
73 | s = loc; | ||
74 | match = pmatch(pattern, s); | ||
75 | *loc = c; | ||
76 | |||
77 | if (match) | ||
78 | return loc; | ||
79 | |||
80 | loc--; | ||
81 | } | ||
82 | |||
83 | return NULL; | ||
84 | } | ||
85 | |||
86 | #ifdef STANDALONE | ||
87 | int main(int argc, char *argv[]) | ||
88 | { | ||
89 | char *string; | ||
90 | char *op; | ||
91 | char *pattern; | ||
92 | bool zero; | ||
93 | char *loc; | ||
94 | |||
95 | int i; | ||
96 | |||
97 | if (argc == 1) { | ||
98 | puts( | ||
99 | "Usage: match <test> [test...]\n\n" | ||
100 | "Where a <test> is the form: <string><op><match>\n" | ||
101 | "This is to test the shell ${var#val} expression type.\n\n" | ||
102 | "e.g. `match 'abc#a*'` -> bc" | ||
103 | ); | ||
104 | return 1; | ||
105 | } | ||
106 | |||
107 | for (i = 1; i < argc; ++i) { | ||
108 | size_t off; | ||
109 | scan_t scan; | ||
110 | |||
111 | printf("'%s': ", argv[i]); | ||
112 | |||
113 | string = strdup(argv[i]); | ||
114 | off = strcspn(string, "#%"); | ||
115 | if (!off) { | ||
116 | printf("invalid format\n"); | ||
117 | free(string); | ||
118 | continue; | ||
119 | } | ||
120 | op = string + off; | ||
121 | scan = pick_scan(op[0], op[1], &zero); | ||
122 | pattern = op + 1; | ||
123 | if (op[0] == op[1]) | ||
124 | op[1] = '\0', ++pattern; | ||
125 | op[0] = '\0'; | ||
126 | |||
127 | loc = scan(string, pattern, zero); | ||
128 | |||
129 | if (zero) { | ||
130 | printf("'%s'\n", loc); | ||
131 | } else { | ||
132 | *loc = '\0'; | ||
133 | printf("'%s'\n", string); | ||
134 | } | ||
135 | |||
136 | free(string); | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | #endif | ||
diff --git a/shell/match.h b/shell/match.h new file mode 100644 index 000000000..863f52539 --- /dev/null +++ b/shell/match.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* match.h - interface to shell ##/%% matching code */ | ||
2 | |||
3 | typedef char *(*scan_t)(char *string, char *match, bool zero); | ||
4 | |||
5 | char *scanleft(char *string, char *match, bool zero); | ||
6 | char *scanright(char *string, char *match, bool zero); | ||
7 | |||
8 | static inline scan_t pick_scan(char op1, char op2, bool *zero) | ||
9 | { | ||
10 | /* # - scanleft | ||
11 | * ## - scanright | ||
12 | * % - scanright | ||
13 | * %% - scanleft | ||
14 | */ | ||
15 | if (op1 == '#') { | ||
16 | *zero = true; | ||
17 | return op1 == op2 ? scanright : scanleft; | ||
18 | } else { | ||
19 | *zero = false; | ||
20 | return op1 == op2 ? scanleft : scanright; | ||
21 | } | ||
22 | } | ||