aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2020-10-04 02:07:49 -0500
committerRob Landley <rob@landley.net>2020-10-04 02:07:49 -0500
commitce683cdce2629721339f526c5059af9b3042958e (patch)
tree3a55674063a5bcd2f5fc3c6344cdd4bf0473b927
parent07c316a2bbf0ad0f41229ca668def609a5f50f2c (diff)
downloadtoybox-ce683cdce2629721339f526c5059af9b3042958e.tar.gz
Implement ${x#y} and ${x##y}
Debug wildcard * match, teach skipslash() to fill out a wildcard deck, make collect_wildcards() flush remove the parsing-only 0th entry.
-rw-r--r--tests/sh.test7
-rw-r--r--toys/pending/sh.c89
2 files changed, 61 insertions, 35 deletions
diff --git a/tests/sh.test b/tests/sh.test
index 1adbd97f..f0764858 100644
--- a/tests/sh.test
+++ b/tests/sh.test
@@ -163,6 +163,13 @@ testing "brackets9" 'echo A{B{C,D}E{N,O},F{G,H}I}J{K,L}M' \
for i in /root /var/root /; do [ -e $i ] && EXPECT=$i && break; done
testing "bracket+tilde" "echo {~,~root}/pwd" "$HOME/pwd $EXPECT/pwd\n" "" ""
+# Slices
+
+testing '${x#prefix}' 'x=abcde; echo ${x#abc}' 'de\n' '' ''
+testing '${x#short} ${x##long}' 'x=banana; echo ${x#b*n} ${x##b*n}' \
+ 'ana a\n' '' ''
+toyonly testing '${x#utf8}' 'x=aそcde; echo ${x##a?c}' 'de\n' '' ''
+
#testing "backtick1" 'X=fred; echo `echo $x`' 'fred\n' "" ""
#testing "backtick2" 'X=fred; echo `x=y; echo $x`' 'y\n' "" ""
testing '$(( ) )' 'echo ab$((echo hello) | tr e x)cd' "abhxllocd\n" "" ""
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 9b3f6445..3e809425 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -814,21 +814,7 @@ char *getvar_special(char *str, int len, int *used, struct arg_list **delete)
return s;
}
-// Copy string until } including escaped }
-char *slashcopy(char *s, char c)
-{
- char *ss;
- int ii, jj;
-
- for (ii = 0; s[ii] != c; ii++) if (s[ii] == '\\') ii++;
- ss = xmalloc(ii+1);
- for (ii = jj = 0; s[jj] != c; ii++)
- if ('\\'==(ss[ii] = s[jj++])) ss[ii] = s[jj++];
- ss[ii] = 0;
-
- return ss;
-}
-
+// Return length of utf8 char @s fitting in len, writing value into *cc
int getutf8(char *s, int len, int *cc)
{
wchar_t wc;
@@ -914,9 +900,9 @@ static int wildcard_match(char *str, int len, char *pattern, int plen,
// TODO: seek to next | in paren
while (ant.c) {
if ((c = pattern[(long)deck->v[--dd]])=='*') {
- if (len<(ss = (long)ant.v[ant.c-2]+ (long)++ant.v[ant.c-1])) ant.c -= 2;
+ if (len<(ss = (long)ant.v[ant.c-2]+(long)++ant.v[ant.c-1])) ant.c -= 2;
else {
- dd++;
+ pp = (long)deck->v[dd++]+1;
break;
}
} else if (c == '(') dprintf(2, "TODO: (");
@@ -930,18 +916,19 @@ static int wildcard_match(char *str, int len, char *pattern, int plen,
}
// Skip to next slash in wildcard path. Needs deck to know what [ranges] to skip
-char *wildcard_skipslash(char *pattern, struct sh_arg *deck, int *doff)
+// idx = pointer to index in deck, updated as we pass wildcards
+char *wildcard_skipslash(char *pattern, struct sh_arg *deck, int *idx)
{
char *p;
int i = 0;
for (p = pattern; *p && *p!='/'; p++) {
// Skip [] and nested () ranges within deck
- if (*doff<deck->c && p-pattern == (long)deck->v[*doff]) {
- ++*doff;
- if (*p=='[') p = deck->v[(*doff)++];
- else if (*p=='(') while (*++p) if (p-pattern == (long)deck->v[*doff]) {
- ++*doff;
+ if (*idx<deck->c && p-pattern == (long)deck->v[*idx]) {
+ ++*idx;
+ if (*p=='[') p = deck->v[(*idx)++];
+ else if (*p=='(') while (*++p) if (p-pattern == (long)deck->v[*idx]) {
+ ++*idx;
if (*p == ')') {
if (!i) break;
i--;
@@ -966,7 +953,7 @@ int do_wildcard_files(struct dirtree *node)
struct sh_arg ant;
// Find active pattern range
- for (nn = node, lvl = ll = 1;; lvl = ll, pattern = p) {
+ for (nn = node, lvl = ll = 0;; lvl = ll, pattern = p) {
p = wildcard_skipslash(pattern, TT.wcdeck, &ll);
if (lvl != ll && !(nn = nn->parent)) break;
while (*p == '/') p++;
@@ -1020,7 +1007,7 @@ static void collect_wildcards(char *new, long oo, struct sh_arg *deck)
if (!cc) {
long ii = 0, jj = 65535&*vv, kk;
- for (*vv = 0, kk = deck->c; jj;) {
+ for (kk = deck->c; jj;) {
if (')' == (cc = new[vv[--kk]])) ii++;
else if ('(' == cc) {
if (ii) ii--;
@@ -1030,6 +1017,7 @@ static void collect_wildcards(char *new, long oo, struct sh_arg *deck)
}
}
}
+ if (deck->c) memmove(vv, vv+1, sizeof(long)*deck->c--);
return;
}
@@ -1074,10 +1062,11 @@ static void wildcard_add_files(struct sh_arg *arg, char *pattern,
char *p, *pp;
int lvl, ll;
+ collect_wildcards("", 0, deck);
+
// fast path: when no wildcards, add pattern verbatim
- if (deck->c<2) return arg_add(arg, pattern);
+ if (!deck->c) return arg_add(arg, pattern);
- if (*deck->v) collect_wildcards("", 0, deck);
TT.wcdeck = deck;
TT.wcpat = pattern;
@@ -1107,9 +1096,28 @@ static void wildcard_add_files(struct sh_arg *arg, char *pattern,
} while (dt && !dt->child);
}
+ deck->c = 0;
+
// TODO: test .*/../
}
+// Copy string until } including escaped }
+char *slashcopy(char *s, char c, struct sh_arg *deck)
+{
+ char *ss;
+ int ii, jj;
+
+ for (ii = 0; s[ii] != c; ii++) if (s[ii] == '\\') ii++;
+ ss = xmalloc(ii+1);
+ for (ii = jj = 0; s[jj] != c; ii++)
+ if ('\\'==(ss[ii] = s[jj++])) ss[ii] = s[jj++];
+ else if (deck) collect_wildcards(ss, ii, deck);
+ ss[ii] = 0;
+ if (deck) collect_wildcards("", 0, deck);
+
+ return ss;
+}
+
#define NO_QUOTE (1<<0) // quote removal
#define NO_PATH (1<<1) // path expansion (wildcards)
#define NO_SPLIT (1<<2) // word splitting
@@ -1333,6 +1341,9 @@ barf:
}
// combine before/ifs/after sections & split words on $IFS in ifs
+ // keep oo bytes of str before (already parsed)
+ // insert ifs (active for wildcards+splitting)
+ // keep str+ii after (still to parse)
// Fetch separator to glue string back together with
*sep = 0;
@@ -1358,13 +1369,14 @@ barf:
dd = slice[xx = (*slice == ':')];
if (!ifs || (xx && !*ifs)) {
if (strchr("-?=", dd)) { // - use default = assign default ? error
- push_arg(delete, ifs = slashcopy(slice+xx+1, '}'));
+ push_arg(delete, ifs = slashcopy(slice+xx+1, '}', 0));
if (dd == '?' || (dd == '=' &&
!(setvar(s = xmprintf("%.*s=%s", (int)(slice-ss), ss, ifs)))))
goto barf;
}
// use alternate value
- } else if (dd == '+') push_arg(delete, ifs = slashcopy(slice+xx+1,'}'));
+ } else if (dd == '+')
+ push_arg(delete, ifs = slashcopy(slice+xx+1, '}', 0));
else if (xx) { // ${x::}
long long la, lb, lc;
@@ -1400,14 +1412,24 @@ barf:
for (dd = 0; dd<lb ; dd++) if (!(ifs[dd] = ifs[dd+la])) break;
ifs[dd] = 0;
}
+ // ${x#y} remove shortest prefix ${x##y} remove longest prefix
+ } else if (*slice=='#') {
+ struct sh_arg wild = {0};
+
+ s = slashcopy(slice+(xx = slice[1]=='#')+1, '}', &wild);
+ dd = wildcard_match(ifs, strlen(ifs), s, strlen(s), &wild,
+ WILD_SHORT*!xx);
+ free(s);
+ free(wild.v);
+ if (dd>0) ifs += dd;
+
+// TODO test x can be @ or *
} else {
// TODO test ${-abc} as error
ifs = slice;
goto barf;
}
-// ${x#y} remove shortest prefix ${x##y} remove longest prefix
-// x can be @ or *
// ${x%y} ${x%%y} suffix
// ${x/pat/sub} substitute ${x//pat/sub} global ${x/#pat/sub} begin
// ${x/%pat/sub} end ${x/pat} delete pat
@@ -1493,10 +1515,7 @@ barf:
fail:
if (str != new) free(new);
free(deck.v);
- if (ant!=&deck && ant->v) {
- collect_wildcards("", 0, ant);
- memmove(ant->v, ant->v+1, ant->c--*sizeof(long));
- }
+ if (ant!=&deck && ant->v) collect_wildcards("", 0, ant);
return !!arg;
}