summaryrefslogtreecommitdiff
path: root/pathspec.c
diff options
context:
space:
mode:
authorBrandon Williams <bmwill@google.com>2017-03-13 18:23:22 (GMT)
committerJunio C Hamano <gitster@pobox.com>2017-03-13 22:28:56 (GMT)
commitc5af19f9ab4cd8582689ca9d9c84f188f4442d10 (patch)
treef148fdc5c2b8d223227a6328dc3cb53c8c06dd7e /pathspec.c
parentb0db70465246bb8309d3d12c9bc34ac3f0c1e203 (diff)
downloadgit-c5af19f9ab4cd8582689ca9d9c84f188f4442d10.zip
git-c5af19f9ab4cd8582689ca9d9c84f188f4442d10.tar.gz
git-c5af19f9ab4cd8582689ca9d9c84f188f4442d10.tar.bz2
pathspec: allow escaped query values
In our own .gitattributes file we have attributes such as: *.[ch] whitespace=indent,trail,space When querying for attributes we want to be able to ask for the exact value, i.e. git ls-files :(attr:whitespace=indent,trail,space) should work, but the commas are used in the attr magic to introduce the next attr, such that this query currently fails with fatal: Invalid pathspec magic 'trail' in ':(attr:whitespace=indent,trail,space)' This change allows escaping characters by a backslash, such that the query git ls-files :(attr:whitespace=indent\,trail\,space) will match all path that have the value "indent,trail,space" for the whitespace attribute. To accomplish this, we need to modify two places. First `parse_long_magic` needs to not stop early upon seeing a comma or closing paren that is escaped. As a second step we need to remove any escaping from the attr value. Based on a patch by Stefan Beller <sbeller@google.com> Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'pathspec.c')
-rw-r--r--pathspec.c52
1 files changed, 48 insertions, 4 deletions
diff --git a/pathspec.c b/pathspec.c
index 608511c..303efda 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -89,6 +89,51 @@ static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
strbuf_addf(sb, ",prefix:%d)", prefixlen);
}
+static size_t strcspn_escaped(const char *s, const char *stop)
+{
+ const char *i;
+
+ for (i = s; *i; i++) {
+ /* skip the escaped character */
+ if (i[0] == '\\' && i[1]) {
+ i++;
+ continue;
+ }
+
+ if (strchr(stop, *i))
+ break;
+ }
+ return i - s;
+}
+
+static inline int invalid_value_char(const char ch)
+{
+ if (isalnum(ch) || strchr(",-_", ch))
+ return 0;
+ return -1;
+}
+
+static char *attr_value_unescape(const char *value)
+{
+ const char *src;
+ char *dst, *ret;
+
+ ret = xmallocz(strlen(value));
+ for (src = value, dst = ret; *src; src++, dst++) {
+ if (*src == '\\') {
+ if (!src[1])
+ die(_("Escape character '\\' not allowed as "
+ "last character in attr value"));
+ src++;
+ }
+ if (invalid_value_char(*src))
+ die("cannot use '%c' for value matching", *src);
+ *dst = *src;
+ }
+ *dst = '\0';
+ return ret;
+}
+
static void parse_pathspec_attr_match(struct pathspec_item *item, const char *value)
{
struct string_list_item *si;
@@ -131,10 +176,9 @@ static void parse_pathspec_attr_match(struct pathspec_item *item, const char *va
if (attr[attr_len] != '=')
am->match_mode = MATCH_SET;
else {
+ const char *v = &attr[attr_len + 1];
am->match_mode = MATCH_VALUE;
- am->value = xstrdup(&attr[attr_len + 1]);
- if (strchr(am->value, '\\'))
- die(_("attr spec values must not contain backslashes"));
+ am->value = attr_value_unescape(v);
}
break;
}
@@ -239,7 +283,7 @@ static const char *parse_long_magic(unsigned *magic, int *prefix_len,
const char *nextat;
for (pos = elem + 2; *pos && *pos != ')'; pos = nextat) {
- size_t len = strcspn(pos, ",)");
+ size_t len = strcspn_escaped(pos, ",)");
int i;
if (pos[len] == ',')