On Mon, Jun 03, 2019 at 08:34:35AM -0400, Jeff King wrote:
> Great. We might want to stop there, but it's possible could reuse even
> more code. I didn't look closely before, but it seems this code is
> decoding a URL. We already have a url_decode() routine in url.c. Could
> it be reused?

Very nice. Here is an interdiff and the changes will be included in v3 of my
patchset:

diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index ed02c88eb6..0f135602a7 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -1,19 +1,20 @@
 #include "cache.h"
 #include "commit.h"
 #include "config.h"
 #include "revision.h"
 #include "argv-array.h"
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "trace.h"
+#include "url.h"
 
 static int parse_combine_filter(
        struct list_objects_filter_options *filter_options,
        const char *arg,
        struct strbuf *errbuf);
 
 /*
  * Parse value of the argument to the "filter" keyword.
  * On the command line this looks like:
  *       --filter=<arg>
@@ -84,54 +85,20 @@ static int gently_parse_list_objects_filter(
         * Please update _git_fetch() in git-completion.bash when you
         * add new filters
         */
 
        strbuf_addf(errbuf, "invalid filter-spec '%s'", arg);
 
        memset(filter_options, 0, sizeof(*filter_options));
        return 1;
 }
 
-static int url_decode(struct strbuf *s, struct strbuf *errbuf)
-{
-       char *dest = s->buf;
-       char *src = s->buf;
-       size_t new_len;
-
-       while (*src) {
-               if (src[0] != '%') {
-                       *dest++ = *src++;
-                       continue;
-               }
-
-               if (hex_to_bytes((unsigned char *)dest, src + 1, 1)) {
-                       strbuf_addstr(errbuf,
-                                     "error in filter-spec - "
-                                     "invalid hex sequence after %");
-                       return 1;
-               }
-
-               if (!*dest) {
-                       strbuf_addstr(errbuf,
-                                     "error in filter-spec - unexpected %00");
-                       return 1;
-               }
-
-               src += 3;
-               dest++;
-       }
-       new_len = dest - s->buf;
-       strbuf_remove(s, new_len, s->len - new_len);
-
-       return 0;
-}
-
 static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?";
 
 static int has_reserved_character(
        struct strbuf *sub_spec, struct strbuf *errbuf)
 {
        const char *c = sub_spec->buf;
        while (*c) {
                if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) {
                        strbuf_addf(errbuf,
                                    "must escape char in sub-filter-spec: '%c'",
@@ -147,56 +114,57 @@ static int has_reserved_character(
 static int parse_combine_subfilter(
        struct list_objects_filter_options *filter_options,
        struct strbuf *subspec,
        struct strbuf *errbuf)
 {
        size_t new_index = filter_options->sub_nr;
 
        ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
                      filter_options->sub_alloc);
 
-       return has_reserved_character(subspec, errbuf) ||
-               url_decode(subspec, errbuf) ||
-               gently_parse_list_objects_filter(
-                       &filter_options->sub[new_index], subspec->buf, errbuf);
+       decoded = url_percent_decode(subspec->buf);
+
+       result = gently_parse_list_objects_filter(
+               &filter_options->sub[new_index], decoded, errbuf);
+
+       free(decoded);
+       return result;
 }
 
 static int parse_combine_filter(
        struct list_objects_filter_options *filter_options,
        const char *arg,
        struct strbuf *errbuf)
 {
        struct strbuf **subspecs = strbuf_split_str(arg, '+', 0);
        size_t sub;
-       int result;
+       int result = 0;
 
        if (!subspecs[0]) {
                strbuf_addf(errbuf,
                            _("expected something after combine:"));
                result = 1;
                goto cleanup;
        }
 
-       for (sub = 0; subspecs[sub]; sub++) {
+       for (sub = 0; subspecs[sub] && !result; sub++) {
                if (subspecs[sub + 1]) {
                        /*
                         * This is not the last subspec. Remove trailing "+" so
                         * we can parse it.
                         */
                        size_t last = subspecs[sub]->len - 1;
                        assert(subspecs[sub]->buf[last] == '+');
                        strbuf_remove(subspecs[sub], last, 1);
                }
                result = parse_combine_subfilter(
                        filter_options, subspecs[sub], errbuf);
-               if (result)
-                       goto cleanup;
        }
 
        filter_options->choice = LOFC_COMBINE;
 
 cleanup:
        strbuf_list_free(subspecs);
        if (result) {
                list_objects_filter_release(filter_options);
                memset(filter_options, 0, sizeof(*filter_options));
        }
diff --git a/t/t6112-rev-list-filters-objects.sh 
b/t/t6112-rev-list-filters-objects.sh
index 7fb5e50cde..e1bf3ed038 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -405,32 +405,20 @@ test_expect_success 'combine:... while URL-encoding 
things that should not be' '
 
 test_expect_success 'combine: with nothing after the :' '
        expect_invalid_filter_spec combine: "expected something after combine:"
 '
 
 test_expect_success 'parse error in first sub-filter in combine:' '
        expect_invalid_filter_spec combine:tree:asdf+blob:none \
                "expected .tree:<depth>."
 '
 
-test_expect_success 'combine:... with invalid URL-encoded sequences' '
-       # Not enough hex chars
-       expect_invalid_filter_spec combine:tree:2+blob:non%a \
-               "error in filter-spec - invalid hex sequence after %" &&
-       # Non-hex digit after %
-       expect_invalid_filter_spec combine:tree:2+blob%G5none \
-               "error in filter-spec - invalid hex sequence after %" &&
-       # Null byte encoded by %
-       expect_invalid_filter_spec combine:tree:2+blob%00none \
-               "error in filter-spec - unexpected %00"
-'
-
 test_expect_success 'combine:... with non-encoded reserved chars' '
        expect_invalid_filter_spec combine:tree:2+sparse:@xyz \
                "must escape char in sub-filter-spec: .@." &&
        expect_invalid_filter_spec combine:tree:2+sparse:\` \
                "must escape char in sub-filter-spec: .\`." &&
        expect_invalid_filter_spec combine:tree:2+sparse:~abc \
                "must escape char in sub-filter-spec: .\~."
 '
 
 test_expect_success 'validate err msg for "combine:<valid-filter>+"' '
diff --git a/url.c b/url.c
index 25576c390b..bdede647bc 100644
--- a/url.c
+++ b/url.c
@@ -79,20 +79,26 @@ char *url_decode_mem(const char *url, int len)
 
        /* Skip protocol part if present */
        if (colon && url < colon) {
                strbuf_add(&out, url, colon - url);
                len -= colon - url;
                url = colon;
        }
        return url_decode_internal(&url, len, NULL, &out, 0);
 }
 
+char *url_percent_decode(const char *encoded)
+{
+       struct strbuf out = STRBUF_INIT;
+       return url_decode_internal(&encoded, strlen(encoded), NULL, &out, 0);
+}
+
 char *url_decode_parameter_name(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
        return url_decode_internal(query, -1, "&=", &out, 1);
 }
 
 char *url_decode_parameter_value(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
        return url_decode_internal(query, -1, "&", &out, 1);
diff --git a/url.h b/url.h
index 00b7d58c33..2a27c34277 100644
--- a/url.h
+++ b/url.h
@@ -1,16 +1,24 @@
 #ifndef URL_H
 #define URL_H
 
 struct strbuf;
 
 int is_url(const char *url);
 int is_urlschemechar(int first_flag, int ch);
 char *url_decode(const char *url);
 char *url_decode_mem(const char *url, int len);
+
+/*
+ * Similar to the url_decode_{,mem} methods above, but doesn't assume there
+ * is a scheme followed by a : at the start of the string. Instead, %-sequences
+ * before any : are also parsed.
+ */
+char *url_percent_decode(const char *encoded);
+
 char *url_decode_parameter_name(const char **query);
 char *url_decode_parameter_value(const char **query);
 
 void end_url_with_slash(struct strbuf *buf, const char *url);
 void str_end_url_with_slash(const char *url, char **dest);
 
 #endif /* URL_H */

Reply via email to