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