From b8dc5a7f6d52df98a546ad3b39104f4b8e7b8daf Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 20 Jun 2019 11:13:05 +0100 Subject: 44435: Handling digita arguments for :h and :t. Pick number of leading or trailing path components to substitute. Active in history, brace parameters, glob qualifiers. Add tests for all three environments. --- Src/Zle/compctl.c | 2 +- Src/glob.c | 2 +- Src/hist.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++-------- Src/subst.c | 37 ++++++++++++++++------ 4 files changed, 109 insertions(+), 24 deletions(-) (limited to 'Src') diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index f963d5712..f242e1b28 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -2511,7 +2511,7 @@ makecomplistcmd(char *os, int incmd, int flags) else if (!(cmdstr && (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) && (cc = ccp->cc)) || - ((s = dupstring(cmdstr)) && remlpaths(&s) && + ((s = dupstring(cmdstr)) && remlpaths(&s, 1) && (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && (cc = ccp->cc))))) { if (flags & CFN_DEFAULT) diff --git a/Src/glob.c b/Src/glob.c index ed2c90bd8..92fd64e7c 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -400,7 +400,7 @@ insert(char *s, int checked) if (colonmod) { /* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */ char *mod = colonmod; - modify(&news, &mod); + modify(&news, &mod, 1); } if (!statted && (gf_sorts & GS_NORMAL)) { statfullpath(s, &buf, 1); diff --git a/Src/hist.c b/Src/hist.c index 901cd3b1a..fd5606dc3 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -555,6 +555,27 @@ substfailed(void) return -1; } +/* + * Return a count given by decimal digits after a modifier. + */ +static int +digitcount(void) +{ + int c = ingetc(), count; + + if (idigit(c)) { + count = 0; + do { + count = 10 * count + (c - '0'); + c = ingetc(); + } while (idigit(c)); + } + else + count = 0; + inungetc(c); + return count; +} + /* Perform history substitution, returning the next character afterwards. */ /**/ @@ -835,7 +856,7 @@ histsubchar(int c) } break; case 'h': - if (!remtpath(&sline)) { + if (!remtpath(&sline, digitcount())) { herrflush(); zerr("modifier failed: h"); return -1; @@ -856,7 +877,7 @@ histsubchar(int c) } break; case 't': - if (!remlpaths(&sline)) { + if (!remlpaths(&sline, digitcount())) { herrflush(); zerr("modifier failed: t"); return -1; @@ -1974,16 +1995,18 @@ chrealpath(char **junkptr) /**/ int -remtpath(char **junkptr) +remtpath(char **junkptr, int count) { char *str = strend(*junkptr); /* ignore trailing slashes */ while (str >= *junkptr && IS_DIRSEP(*str)) --str; - /* skip filename */ - while (str >= *junkptr && !IS_DIRSEP(*str)) - --str; + if (!count) { + /* skip filename */ + while (str >= *junkptr && !IS_DIRSEP(*str)) + --str; + } if (str < *junkptr) { if (IS_DIRSEP(**junkptr)) *junkptr = dupstring ("/"); @@ -1992,6 +2015,34 @@ remtpath(char **junkptr) return 0; } + + if (count) + { + /* + * Return this many components, so start from the front. + * Leading slash counts as one component, consistent with + * behaviour of repeated applications of :h. + */ + char *strp = *junkptr; + while (strp < str) { + if (IS_DIRSEP(*strp)) { + if (--count <= 0) { + if (strp == *junkptr) + ++strp; + *strp = '\0'; + return 1; + } + /* Count consecutive separators as one */ + while (IS_DIRSEP(strp[1])) + ++strp; + } + ++strp; + } + + /* Full string needed */ + return 1; + } + /* repeated slashes are considered like a single slash */ while (str > *junkptr && IS_DIRSEP(str[-1])) --str; @@ -2040,7 +2091,7 @@ rembutext(char **junkptr) /**/ mod_export int -remlpaths(char **junkptr) +remlpaths(char **junkptr, int count) { char *str = strend(*junkptr); @@ -2050,12 +2101,29 @@ remlpaths(char **junkptr) --str; str[1] = '\0'; } - for (; str >= *junkptr; --str) - if (IS_DIRSEP(*str)) { - *str = '\0'; - *junkptr = dupstring(str + 1); - return 1; + for (;;) { + for (; str >= *junkptr; --str) { + if (IS_DIRSEP(*str)) { + if (--count > 0) { + if (str > *junkptr) { + --str; + break; + } else { + /* Whole string needed */ + return 1; + } + } + *str = '\0'; + *junkptr = dupstring(str + 1); + return 1; + } } + /* Count consecutive separators as 1 */ + while (str >= *junkptr && IS_DIRSEP(*str)) + --str; + if (str <= *junkptr) + break; + } return 0; } diff --git a/Src/subst.c b/Src/subst.c index 60eb33390..b132f251b 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3438,7 +3438,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, s--; if (unset(KSHARRAYS) || inbrace) { if (!isarr) - modify(&val, &s); + modify(&val, &s, inbrace); else { char *ss; char **ap = aval; @@ -3447,12 +3447,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, while ((*pp = *ap++)) { ss = s; - modify(pp++, &ss); + modify(pp++, &ss, inbrace); } if (pp == aval) { char *t = ""; ss = s; - modify(&t, &ss); + modify(&t, &ss, inbrace); } s = ss; } @@ -4182,6 +4182,12 @@ arithsubst(char *a, char **bptr, char *rest) * PTR is an in/out parameter. On entry it contains the string of colon * modifiers. On return it points past the last recognised modifier. * + * INBRACE is non-zero if we are in some form of a bracketed or + * parenthesised expression; it is zero for modifiers ocurring + * in an an unbracketed variable substitution. This means that + * $foo:t222 is treated ias ${foo:t}222 rather than ${foo:t222} + * for backward compatibility. + * * Example: * ENTRY: *str is "." *ptr is ":AN" * RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N" @@ -4189,7 +4195,7 @@ arithsubst(char *a, char **bptr, char *rest) /**/ void -modify(char **str, char **ptr) +modify(char **str, char **ptr, int inbrace) { char *ptr1, *ptr2, *ptr3, *lptr, c, *test, *sep, *t, *tt, tc, *e; char *copy, *all, *tmp, sav, sav1, *ptr1end; @@ -4202,6 +4208,8 @@ modify(char **str, char **ptr) *str = dupstring(*str); while (**ptr == ':') { + int count = 0; + lptr = *ptr; (*ptr)++; wall = gbal = 0; @@ -4214,10 +4222,8 @@ modify(char **str, char **ptr) case 'a': case 'A': case 'c': - case 'h': case 'r': case 'e': - case 't': case 'l': case 'u': case 'q': @@ -4226,6 +4232,17 @@ modify(char **str, char **ptr) c = **ptr; break; + case 'h': + case 't': + c = **ptr; + if (inbrace && idigit((*ptr)[1])) { + do { + count = 10 * count + ((*ptr)[1] - '0'); + ++(*ptr); + } while (idigit((*ptr)[1])); + } + break; + case 's': c = **ptr; (*ptr)++; @@ -4392,7 +4409,7 @@ modify(char **str, char **ptr) break; } case 'h': - remtpath(©); + remtpath(©, count); break; case 'r': remtext(©); @@ -4401,7 +4418,7 @@ modify(char **str, char **ptr) rembutext(©); break; case 't': - remlpaths(©); + remlpaths(©, count); break; case 'l': copy = casemodify(tt, CASMOD_LOWER); @@ -4478,7 +4495,7 @@ modify(char **str, char **ptr) break; } case 'h': - remtpath(str); + remtpath(str, count); break; case 'r': remtext(str); @@ -4487,7 +4504,7 @@ modify(char **str, char **ptr) rembutext(str); break; case 't': - remlpaths(str); + remlpaths(str, count); break; case 'l': *str = casemodify(*str, CASMOD_LOWER); -- cgit 1.4.1